图片融合
- 原理
- 实现
- 结果展示
- 完整代码
原理
关于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;
}



















