图片融合
原理
关于OpenCV的配置和基础用法,请参阅本专栏的其他文章:垚武田的OpenCV合集
这里采用的图片熔合的算法来自Richard Szeliski的书《Computer Vision: Algorithms and Applications》(《计算机视觉:算法和应用》)。
该算法使用了一个两参的线性熔合算子来操作图片的像素:
g ( x ) = ( 1 − α ) f 0 ( x ) + α f 1 ( x ) g(x) = (1-\alpha)f_0(x)+\alpha f_1(x) g(x)=(1−α)f0(x)+αf1(x)
α \alpha α的值从0-1不等。利用这个 α \alpha α值,该算法可以对两个图片或者视频执行一个临时的交叉溶解(criss-dissolve),就像在幻灯片或胶片中那样(很酷是不是?)
实现
在OpenCV中使用addWeighted()
函数来实现上述的线性熔合的方法。
首先导入要熔合的两张图片。这两张图片是OpenCV库中自带的,可以在安装目录的子路径...\opencv\sources\samples\data
中找到它们:LinuxLogo.jpg
和WindowsLogo.jpg
。将它们复制进项目文件夹,导入语句如下:
Mat src1 { imread("LinuxLogo.jpg") }; Mat src2 { imread("WindowsLogo.jpg") };
注意:被熔合的两张图片必须大小(长和宽)一致,类型也必须一致
接着确定 α \alpha α值,并使用线性熔合算法:
double alpha{ 0.5 }; double beta{ 1.0 - alpha }; Mat dst; addWeighted( src1, alpha, src2, beta, 0.0, dst);
这里将 α \alpha α设为0.5; β \beta β其实是算法中的 1 − α 1- \alpha 1−α。这里再回到算法公式:
g ( x ) = ( 1 − α ) f 0 ( x ) + α f 1 ( x ) g(x) = (1-\alpha)f_0(x)+\alpha f_1(x) g(x)=(1−α)f0(x)+αf1(x)
- 两张图片中的每个像素值,分别就是公式中的 f 0 ( x ) f_0(x) f0(x)和 f 1 ( x ) f_1(x) f1(x)
- g ( x ) g(x) g(x)就是熔合后的像素值
- 1 − α 1- \alpha 1−α(即代码中的 β \beta β)和 α \alpha α分别是两张图片的权重
然后我们来看一下在addWeighted()
函数中,是怎样用各个参数来确定线性熔合算法的——addWeighted( src1, alpha, src2, beta, 0.0, dst);
:
src1
和src2
分别是要熔合的两张图片的Mat
对象alpha
是公式中的 α \alpha α,即src1
所代表的图片的权重beta
是公式中的 1 − α 1- \alpha 1−α,即src2
所代表的图片的权重0.0
是公式中没有的一个常数 γ \gamma γ,用来对熔合后的像素值进行偏移dst
为储存熔合结果的Mat
对象
这样,addWeighted()
函数的实际算法可以用以下公式表示:
d s t = α ∗ s r c 1 + β ∗ s r c 2 + γ dst = \alpha *src1 + \beta *src2 + \gamma dst=α∗src1+β∗src2+γ
代码中的 γ \gamma γ为0.0,说明不对熔合后的像素进行任何偏移。
结果展示
创建窗口,展示图片熔合的结果
imshow("线性熔合", dst); waitKey(0);
输出结果如下:
完整代码
注意:
- 因为C++中也有std::beta,所以为了避免名称冲突,这里没有使用整个std命名空间
- 完整代码需要用户自己输入 α \alpha α参数的值
- 完整代码对图片的导入进行了检测
#include <opencv2/core.hpp> #include <opencv2/imgcodecs.hpp> #include <opencv2/highgui.hpp> import <iostream>; using namespace cv; //因为C++中有std::beta,为了避免名称冲突,这里没有使用整个std命名空间 using std::cin; using std::cout; using std::endl; int main() { double alpha{ 0.5 }; double beta, input; Mat src1, src2, dst; cout << "简单线性混合" << endl; cout << "-----------" << endl; cout << "* 输入α值 [0.0-1.0]"; cin >> input; if (input >= 0 && input <= 1) alpha = input; src1 = imread("LinuxLogo.jpg"); src2 = imread("WindowsLogo.jpg"); if (src1.empty()) { cout << "图片1导入错误" << endl; return EXIT_FAILURE; } if (src2.empty()) { cout << "图片2导入错误" << endl; return EXIT_FAILURE; } beta = 1.0 - alpha; addWeighted(src1, alpha, src2, beta, 0.0, dst); imshow("线性混合", dst); waitKey(0); return 0; }