计算机系统应用教程网站

网站首页 > 技术文章 正文

「技术分享」截图录屏-截长图的实现原理

btikc 2024-09-27 01:08:45 技术文章 3 ℃ 0 评论

今天给大家分享一下截图录屏中截长图功能的实现原理及流程。

截长图功能,虽然在移动端已经屡见不鲜,但在PC端长久以来依然存在较大局限。以企业微信界面为例,主要的差异和难点如下:

移动端:界面布局大都为纵向设计(即:上、中、下三部分),可滚动区域和截图区域位于中间位置,基本固定,所以处理相对简单;

PC端:界面布局更复杂(包含:左,右,上,下,中五部分),界面交叉重叠,可滚动区域布局不明朗,而截图区域又由用户自由框选,区域不固定、不可控、单次截图滚动距离难以预测。

长图截取的过程,究其实质是在滚动过程中进行多次截图,同时进行上下拼接起来的结果,拼接的过程中会过滤掉上下重复的部分。主要流程如下:

1、在主线程上,根据鼠标滚轮事件触发截图,并将其保存到队列中(滚轮事件,既可以是自动滚动模拟发出的事件,也可以是在手动滚动模式下用户通过鼠标滚轮滚动发出的事件);

2、在子线程上,将截图队列中的图片进行编辑处理(比如:识别顶部与底部的固定区域并裁剪掉然后进行拼接,在最终结束时再将前面裁剪掉的顶部区域与底部区域还原到长图的顶部与底部)。

也就是说,截长图的核心流程就在于子线程上的“图像拼接”。

对图像拼接的实现过程是采用基于模板匹配的拼接算法,基本实现原理如下:

在截图B中截取部分公共区域ROI作为模板,利用模板在截图A中匹配,得到最佳匹配位置后计算Y方向需要平移的像素距离(上下拼接,只需计算纵坐标位置),将图B数据完全拷贝到图A中(0, Y)的起始位置。

OpenCV库提供了基于模板匹配的算法实现,详细实现流程如下:

1、将图片A、B转灰度图像;

2、获取图片B的前50行数据作为模板图像;

3、模板与图片A做模板匹配;

4、匹配结果矩阵阈值化处理;

5、如果匹配值是否大于阈值,则判断匹配成功,即基于匹配坐标位置拼接图片。

OpenCV提供了六种匹配算法:

1、CV_TM_SQDIFF 平方差匹配,最好匹配为0,值越大匹配越差

2、CV_TM_SQDIFF_NORMED 归一化平方差匹配

3、CV_TM_CCORR 相关匹配,采用乘法操作,值越大表明匹配越好

4、CV_TM_CCORR_NORMED 归一化相关匹配

5、CV_TM_CCOEFF 相关系数匹配,最好匹配为1,-1表示最差匹配

6、CV_TM_CCOEFF_NORMED 归一化相关系数匹配

从简单的匹配测量算法“平方差匹配”到更复杂的匹配测量算法“相关系数”,匹配精确度随之增加,但同时也意味着越来越大的计算代价。

为了同时兼顾速度和精度的最佳选择,在验证了所有匹配算法后,最后截长图在拼接时采用了CV_TM_CCOEFF_NORMED 归一化相关系数匹配。

归一化相关系数匹配是 OpenCV 支持的最复杂的一种相似度算法,运用了数理统计学科的相关系数计算方法。具体就是在减去了各自的平均值之外,还要各自除以各自的方差。

经过这两步操作之后,待检图像与模板都被标准化了,这样可以保证图像和模板分别改变光照亮度都不会影响计算结果。计算出的相关系数被限制在了 [-1, 1]区间。其中,1 表示完全相同,-1 表示两幅图像的亮度正好相反,0 表示两幅图像之间没有线性关系。

流程关键代码如下:

Mat ImageMerger::getMerageImageBasedOnTemplate1(const Mat &image1, const Mat &image2)
{
//转灰度图像
Mat image1_gray, image2_gray;
cvtColor(image1, image1_gray, CV_BGR2GRAY);
cvtColor(image2, image2_gray, CV_BGR2GRAY);
//1-50行选做模板
Mat temp = image2_gray(Range(1, 50), Range::all());
//结果矩阵图像,大小,数据类型
Mat res(image1_gray.rows - temp.rows + 1, image2_gray.cols - temp.cols + 1, CV_32FC1);
//模板匹配,采用归一化相关系数匹配
matchTemplate(image1_gray, temp, res, CV_TM_CCOEFF_NORMED);
//结果矩阵阈值化处理
threshold(res, res, 0.8, 1, CV_THRESH_TOZERO);
double minVal, maxVal, thresholdv = 0.8;
Point minLoc, maxLoc;
minMaxLoc(res, &minVal, &maxVal, &minLoc, &maxLoc);
//图像拼接
Mat temp1, result;
if (maxVal >= thresholdv)//只有度量值大于阈值才认为是匹配
{
//result:拼接后的图像
result = Mat::zeros(cvSize(image1.cols, maxLoc.y + image2.rows), image1.type());
//temp1:原图1的非模板部分
temp1 = image1(Rect(0, 0, image1.cols, maxLoc.y));
//将图1的非模板部分和图2拷贝到result
temp1.copyTo(Mat(result, Rect(0, 0, image1.cols, maxLoc.y)));
image2.copyTo(Mat(result, Rect(0, maxLoc.y - 1, image2.cols, image2.rows)));
}
//将合并后的图像保存
imwrite("merge.jpg", result);
return result;

当前截长图功能已经能满足大多数场景,同时还存在以下不足:

1、拼接处不能有影响算法识别的区域, 例如大片的空白区域,或重复相同区域;

2、框选区域中不能有动态变化的视频或是动画;

后续我们将继续调研和优化现有的算法,进而完善截长图功能。也欢迎感兴趣的朋友给我们留言讨论。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表