Opencv 线性混合操作的实现与API:addWeighted()

这篇博客探讨了OpenCV中的addWeighted()函数,用于实现线性混合图像操作。通过示例代码展示了如何手动实现线性混合,并与OpenCV自带的函数进行了对比,发现两者存在细微差异,可能源于OpenCV内部的优化处理。此外,文章提到了使用该函数时需要注意的输出数组深度问题,可能导致内存溢出或计算错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Opencv 线性混合操作的实现与API:addWeighted()

我有一个梦想,我写的代码,可以像诗一样优美。我有一个梦想,我做的设计,能恰到好处,既不过度,也无不足。

    线性混合操作是像素级的操作,理论公式:
				  g(x) = (1 -a)f(x) + fa(x);
/*@param src1 first input array.
@param alpha weight of the first array elements.
@param src2 second input array of the same size and channel number as src1.
@param beta weight of the second array elements.
@param gamma scalar added to each sum.
@param dst output array that has the same size and number of channels as the input arrays.
@param dtype optional depth of the output array; when both input arrays have the same depth, dtype
can be set to -1, which will be equivalent to src1.depth().*/
void addWeighted(InputArray src1, double alpha, InputArray src2,
                              double beta, double gamma, OutputArray dst, int dtype = -1);

简单介绍下参数:
src1: 输入混合的第一个数组(图像);
alpha: 第一个数组混合时所占的权重;
src2: 输入混合的第二个数组,尺寸和通道数同一个相同。
beta: 第二个数组的混合权重;
gamma: 加在每个混合后的和上的标量值。
dst: 混合后输出的数组,和输入的数组拥有相同的尺寸和通道数;
dtype: 输出阵列的可选深度,默认-1,当两个数组深度相同,参数设为 -1;

addWeighted()理论公式如下:
dst = src1[i] * alpha + src2[i] * beta + gamma;

好,到这理论就完了,下面开始看实现与对比:

void MyAddWeighted()
{
	Mat mat(10, 10, CV_8UC1);
	randu(mat, Scalar(0), Scalar(255));

	Mat mat2(10, 10, CV_8UC1);
	randu(mat2, Scalar(0), Scalar(255));

	Mat dst;

   double alpha = 0.7, beta = 0.3, gamma = 0.;
	/// mat rows cols == mat rows cols

	CV_Assert(mat.type() == mat2.type() && mat.type() == CV_8UC1);
	dst.create(10, 10, mat.type());
	for (int i = 0; i < mat.rows; i++)
	{
		for (int j = 0; j < mat.cols; j++)
		{
			dst.at<uchar>(i, j) = cv::saturate_cast<uchar>(mat.at<uchar>(i, j) * alpha + mat2.at<uchar>(i, j) * beta + gamma);
		}
	}
	cout << "dst = " << dst << endl;

	Mat dst2;
	addWeighted(mat, alpha, mat2, beta, gamma, dst2);
	cout << "dst2 = " << dst2 << endl;
	
	Mat matTemp = dst - dst2;
	cout << matTemp << endl;
	Mat matTemp2 = dst2 - dst;
	cout << matTemp2 << endl;
}


=========输出结果========

dst = [ 93,  62, 115, 189,  60, 182, 225,  33, 177, 190;
  62, 228, 180, 155,  50, 162, 176, 120, 119,  78;
 154,  92, 160, 159, 155, 199, 167, 186, 171, 210;
 155, 187,  99,  77, 171, 206,  59,  78,  68, 192;
 198, 106, 198, 133,  52,  68,  18, 128, 190, 141;
 213,  84, 215,  93, 185,  75, 221,  69, 120, 121;
  81,  60, 102, 198, 171, 101, 162, 144,  57,  71;
 121, 116, 159,  88, 136,  32,  34, 220,  91,  82;
  18, 153, 183, 135, 189,  81, 107,  86,  98,  68;
 168,  88,  97, 124, 193, 112, 153, 216, 155, 250]
dst2 = [ 93,  62, 115, 189,  60, 182, 225,  33, 177, 190;
  62, 228, 180, 155,  50, 162, 176, 120, 119,  78;
 154,  92, 160, 159, 155, 200, 167, 186, 171, 210;
 155, 187,  99,  77, 171, 206,  59,  78,  68, 192;
 198, 106, 198, 133,  52,  68,  18, 128, 190, 141;
 213,  84, 215,  93, 186,  75, 221,  69, 120, 121;
  81,  60, 102, 198, 171, 101, 162, 144,  57,  71;
 121, 116, 159,  88, 136,  32,  34, 220,  91,  82;
  18, 153, 183, 135, 189,  81, 107,  86,  98,  68;
 168,  88,  97, 124, 193, 112, 153, 216, 155, 250]
[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0]
[  0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   1,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   1,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0;
   0,   0,   0,   0,   0,   0,   0,   0,   0,   0]

可以看到,结果还是有点差异的,说明opencv在这个理论公式上还做了自己的一些细节上的处理,进入源码,跟了几层后发现下面一段代码:

//以下代码按调用先后截取部分关键代码
//片段1
for (; x <= width - 4; x += 4)
{
            T1 t0 = op::r(src1[x], src2[x], scalar);
            T1 t1 = op::r(src1[x + 1], src2[x + 1], scalar);
            dst[x] = t0; dst[x + 1] = t1;

            t0 = op::r(src1[x + 2], src2[x + 2], scalar);
            t1 = op::r(src1[x + 3], src2[x + 3], scalar);
            dst[x + 2] = t0; dst[x + 3] = t1;
}

//片段二
    static inline T1 r(T1 a, T1 b, const T2* scalars)
    { return c_add(a, b, scalars[0], scalars[1], scalars[2]); }
//片段三    
inline uchar c_add<uchar, float>(uchar a, uchar b, float alpha, float beta, float gamma)
{ return saturate_cast<uchar>(CV_8TO32F(a) * alpha + CV_8TO32F(b) * beta + gamma); }

//片段四
extern const float g_8x32fTab[];
#define CV_8TO32F(x)  cv::g_8x32fTab[(x)+128]

可以看到opencv中是把输入的像素值先经过CV_8TO32F(x)做了某种转化,在按照理论公式进行计算,至于CV_8TO32F(x)这个具体转化没能找到(-_-),有了解的评论区告诉我一下。
不过这个也无伤大雅,知道原理有那么回事就行,毕竟也不会真的用自己实现的函数去做不是。

注意:输出数组的深度为CV_32S时,此函数就不适应了,会造成内存溢出,或计算结果不正确(此句话取自毛星云opencv入门编程)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值