如何使用C++ OpenCV将带透明度的图片绘制到另一张同规格图片上
实现方案
前置条件
- 两张图片均为4通道BGRA格式(OpenCV默认读取带alpha通道图片的存储格式),宽高尺寸完全一致
- 读入图片时必须保留alpha通道,不要使用默认参数转成3通道BGR
核心逻辑
我们要实现的是标准的前景Over背景的alpha合成规则,对每个像素的计算逻辑如下:
设前景图(第二张图)像素为
(F_r, F_g, F_b, F_a),背景图(第一张图)像素为(B_r, B_g, B_b, B_a),alpha取值归一化到[0,1]区间
合成后像素RGB分量:R = F_r * F_a + B_r * (1 - F_a),G = F_g * F_a + B_g * (1 - F_a),B = F_b * F_a + B_b * (1 - F_a)
合成后像素alpha分量:A = F_a + B_a * (1 - F_a)(如果只需要输出3通道结果可忽略alpha分量计算)
完整代码示例
#include <opencv2/opencv.hpp> int main() { // 1. 读入两张图片,必须加IMREAD_UNCHANGED参数保留alpha通道 cv::Mat bg = cv::imread("background.png", cv::IMREAD_UNCHANGED); cv::Mat fg = cv::imread("foreground.png", cv::IMREAD_UNCHANGED); // 合法性校验,可根据需求扩展校验规则 CV_Assert(bg.type() == CV_8UC4 && fg.type() == CV_8UC4); CV_Assert(bg.size() == fg.size()); // 2. 逐像素做alpha合成 cv::Mat result = bg.clone(); // 先复制背景作为合成基础 for (int y = 0; y < bg.rows; y++) { for (int x = 0; x < bg.cols; x++) { // 取前景当前像素的4个分量,OpenCV存储顺序为B G R A cv::Vec4b& fg_pix = fg.at<cv::Vec4b>(y, x); cv::Vec4b& res_pix = result.at<cv::Vec4b>(y, x); // alpha归一化到0-1区间 double alpha = fg_pix[3] / 255.0; double inv_alpha = 1.0 - alpha; // 计算各通道值,用saturate_cast避免数值溢出 res_pix[0] = cv::saturate_cast<uchar>(fg_pix[0] * alpha + res_pix[0] * inv_alpha); res_pix[1] = cv::saturate_cast<uchar>(fg_pix[1] * alpha + res_pix[1] * inv_alpha); res_pix[2] = cv::saturate_cast<uchar>(fg_pix[2] * alpha + res_pix[2] * inv_alpha); // 若需要保留合成后的alpha通道,可打开下面注释 // res_pix[3] = cv::saturate_cast<uchar>(fg_pix[3] + res_pix[3] * inv_alpha); } } // 3. 输出结果,存png格式可保留alpha通道,存jpg会自动丢弃alpha转3通道 cv::imwrite("result.png", result); return 0; }
特殊场景简化方案
如果不需要做半透明混合,仅需要把前景的非透明区域完全覆盖背景,或者前景alpha只有0/255两种取值(完全透明/完全不透明),可以用更简单的掩码拷贝实现:
std::vector<cv::Mat> fg_channels; cv::split(fg, fg_channels); cv::Mat mask = fg_channels[3]; // 仅拷贝前景掩码非0的区域到背景上 fg.copyTo(bg, mask);
性能优化提示
- 上述逐像素遍历写法可读性最好,若需要更高性能,可使用OpenCV通道分离+矩阵运算代替循环,降低逐像素访问开销
- 处理大尺寸图片时,建议开启OpenCV的多线程优化选项
内容的提问来源于stack exchange,提问作者Ale_
相关产品推荐
相关产品推荐

