【OpenCV C++20 学习笔记】图片融合

图片融合

原理

关于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.jpgWindowsLogo.jpg。将它们复制进项目文件夹,导入语句如下:

cpp 复制代码
Mat src1 { imread("LinuxLogo.jpg") };
Mat src2 { imread("WindowsLogo.jpg") };

注意:被熔合的两张图片必须大小(长和宽)一致,类型也必须一致

接着确定 α \alpha α值,并使用线性熔合算法:

cpp 复制代码
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);

  • src1src2分别是要熔合的两张图片的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,说明不对熔合后的像素进行任何偏移。

结果展示

创建窗口,展示图片熔合的结果

cpp 复制代码
imshow("线性熔合", dst);
waitKey(0);

输出结果如下:

完整代码

注意:

  • 因为C++中也有std::beta,所以为了避免名称冲突,这里没有使用整个std命名空间
  • 完整代码需要用户自己输入 α \alpha α参数的值
  • 完整代码对图片的导入进行了检测
cpp 复制代码
#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;
}
相关推荐
西岸行者4 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意4 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码4 天前
嵌入式学习路线
学习
毛小茛4 天前
计算机系统概论——校验码
学习
babe小鑫4 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms4 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下4 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。4 天前
2026.2.25监控学习
学习
im_AMBER4 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J4 天前
从“Hello World“ 开始 C++
c语言·c++·学习