OPENCV——图像叠加

一、图像叠加功能简介

图像叠加顾名思义就是在原图像里面,添加一些其他图像数据,最常见的就是在原图像中添加一些水印图像。这些水印图像可以是:时间戳、LOGO图像等等。

如上图,原图像是山的背景,在这个图像的左上角叠加绿色的LOGO标志,然后两个图像就可以融合在一起。上面这个例子,就是最经典的图像叠加案例。

二、图像叠加最常用的两个核心 API

OpenCV 中图像叠加场景最核心、最常用的两个 API 是 cv::copyTo(掩码拷贝)cv::addWeighted(加权融合),分别对应「硬覆盖 / 带透明贴图」和「半透明融合」两类主流需求,下面结合你的 logo 贴图场景详细说明。

2.1 copyTo:区域拷贝(支持掩码透明叠加)

1. 功能定位

将一张源图完整复制到目标图像的指定区域,是最基础的贴图、水印实现方式;配合掩码图可以实现 PNG 透明背景叠加,透明区域不会覆盖原图。

2. 函数原型

复制代码
// 基础版:直接全像素覆盖拷贝
void copyTo(OutputArray dst) const;

// 带掩码版:只在掩码非零的区域拷贝像素
void copyTo(OutputArray dst, InputArray mask) const;

3. 参数说明

  • dst:目标图像,通常是原图的 ROI 感兴趣区域
  • mask:掩码图(单通道 8 位),只有掩码中像素值非 0 的位置,才会拷贝源图像素;掩码为 0 的位置保留目标图原有像素,以此实现透明效果。

4. 典型用法

(1)基础硬覆盖(你之前代码的写法)

直接将 logo 完整覆盖到原图左上角,PNG 透明区域会变成黑色:

复制代码
Mat src = imread("frame1.jpg");
Mat logo = imread("OIP-C.png");

// 截取原图左上角和logo等大的ROI区域
Mat roi = src(Rect(0, 0, logo.cols, logo.rows));
// logo像素直接覆盖ROI区域
logo.copyTo(roi);

(2)带透明 PNG 叠加(进阶常用写法)

读取带 alpha 通道的 PNG,提取透明通道作为掩码,实现背景透明的贴图效果:

复制代码
// 原样读取PNG,保留alpha通道(4通道BGRA)
Mat logo = imread("OIP-C.png", IMREAD_UNCHANGED);
Mat src = imread("frame1.jpg");

// 拆分logo的通道,提取第4个alpha通道作为掩码
vector<Mat> channels;
split(logo, channels);
Mat mask = channels[3];  // alpha通道:透明处为0,不透明处为255

// 提取logo的BGR三通道部分(只拷贝颜色,掩码控制透明)
Mat logo_bgr;
cvtColor(logo, logo_bgr, COLOR_BGRA2BGR);

// 截取原图ROI,带掩码拷贝
Mat roi = src(Rect(0, 0, logo.cols, logo.rows));
logo_bgr.copyTo(roi, mask);

5. 核心注意事项

  1. 目标图与源图必须尺寸一致、通道数一致(掩码必须是单通道);通常配合 ROI 使用,保证目标区域和源图尺寸相同。
  2. 不带掩码的版本是内存块级拷贝,性能极高,是所有叠加方式里最快的,适合嵌入式场景。
  3. 掩码图是二值逻辑:像素为 0 的区域完全不拷贝,非 0 区域完整拷贝,没有半透明过渡。

2.2 addWeighted:加权线性融合

1. 功能定位

对两张尺寸、类型完全相同的图像,按权重比例做逐像素线性叠加,实现半透明融合效果,是半透明水印、图像叠底、双曝光效果的标准实现。

2. 函数原型 & 计算公式

复制代码
void addWeighted(InputArray src1, double alpha, 
                 InputArray src2, double beta, 
                 double gamma, OutputArray dst);

运算公式dst(x,y) = src1(x,y) * alpha + src2(x,y) * beta + gamma

3. 参数说明

参数 作用
src1 第一张输入图像
alpha 第一张图的权重系数(透明度,0~1,0 完全透明,1 完全不透明)
src2 第二张输入图像,尺寸、通道数必须和 src1 完全一致
beta 第二张图的权重系数;通常 alpha + beta = 1.0 保证整体亮度不变
gamma 整体亮度偏移量,叠加后整体加 / 减亮度,一般填 0
dst 输出的融合结果图

4. 典型用法:半透明水印

将 logo 以半透明的方式叠加在原图左上角,实现淡水印效果:

复制代码
Mat src = imread("frame1.jpg");
Mat logo = imread("OIP-C.png");

// 截取原图对应区域
Mat roi = src(Rect(0, 0, logo.cols, logo.rows));
Mat dst_roi;

// 加权融合:原图权重0.8,logo权重0.2,logo呈现20%透明度
addWeighted(roi, 0.8, logo, 0.2, 0, dst_roi);

// 将融合结果写回原图
dst_roi.copyTo(roi);

5. 核心注意事项

  1. 自动饱和截断:OpenCV 内置像素值溢出保护,计算结果超过 255 自动截断为 255,低于 0 自动截断为 0,不会出现手动写循环时的无符号溢出 bug。
  2. 两张输入图必须尺寸、通道数、像素类型完全一致,否则直接报错。
  3. 属于逐像素算术运算,性能比不带掩码的copyTo略低,但远快于手动双层循环。

2.3 两个 API 核心对比

对比项 copyTo(带掩码) addWeighted
核心效果 要么完全覆盖、要么完全透明,无半过渡 按权重半透明融合,过渡平滑
适用场景 带透明背景的图标、logo 贴图 半透明水印、图像融合、曝光叠加
性能 更高(掩码判断 + 块拷贝) 略低(逐像素加权计算)
透明度粒度 0/1 二值透明 0~1 连续可调透明度
是否修改原图 直接修改目标图 生成新的结果图,不修改原图

2.4 RV1126 嵌入式场景建议

  1. 优先用copyTo实现贴图需求,性能最优,CPU 占用更低;需要半透明效果再用addWeighted
  2. 避免手动写双层循环做像素叠加,OpenCV 内置 API 做了指令集优化,速度和稳定性都远优于手动实现。
  3. 叠加前务必检查图像是否为空、尺寸是否匹配,防止越界导致程序闪退。

三、代码

copyTo:

复制代码
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main(int argc, char * argv[])
{
   Mat src1 = imread(argv[1]);
   Mat src2 = imread(argv[2]);

    if(src1.size != src2.size)
    {
        Mat image_roi = src1(Rect(20, 20, src2.cols, src2.rows));
        double alpha = 0.4;
        double beta = 1 - alpha;
        int gama = 0;
        addWeighted(image_roi, alpha, src2, beta, gama, image_roi);
        imwrite("addweighted_norsamesize.jpg",src1);
    }
    else
    {

        double alpha = 0.4;
        double beta = 1 - alpha;
        int gama = 0;
        Mat dst;
        addWeighted(src1, alpha, src2, beta, gama, dst);
        imwrite("addweighted_samesize.jpg",dst);
    }

    return 0;
}