基于等照度线和窗口匹配的图像修补算法

news2024/11/19 6:40:31

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

一、关于图像修补

       图像修补的目的是基于已有的图像信息或数据库内信息,对缺失区域进行合理地修复。在诸多领域如电影、摄像、医疗等行业,有广泛的应用。

       传统上,图像修补由专业的修复师进行,修复师凭借自身丰富的工作经验和生活阅历,不仅能基于客观信息对图像缺失进行填充,更能进行主观创作,使得二次修复的图片更加生动形象。

       但在日益智能化的今天,针对数字图像的修补工作逐渐由人工转向了自动化,这不仅节省了大量人工成本,而且计算机凭借优越的算法和庞大的数据库,对图像的修复效果更高效且优质。通过近几个月风风火火的chatGPT,相信大家也看到了AI的魅力,这是未来的大趋势不可逆。

       针对图像修补,本文提出了一种基于等照度线和窗口匹配的图像修补算法,接下来将简单介绍下算法原理和流程,并展示相关的效果图。

二、算法原理和流程

       对图像缺失部分进行填充,首先要确定填充哪些内容,谁优先填充谁最后填充;其次要判断用什么数据来填充,能使得填充后的结果更贴近真实,显得不违和。做到了这两点,图像修补工作基本就完成了。

2.1 优先级计算

       我们先来讨论填充的优先级,如下图所示,图像白色部分是手动绘制的掩膜区域,该区域的真实信息被擦除了,我们现在要对其进行复原。

       在图中红线部分就是等照线,该线的两侧往往有较大的数值差异,因此它也与黑线所示的梯度线呈垂直关系;当等照线与掩膜边界呈垂直时,此时等照线上的像素点特征是非常强烈且明显的,通俗的讲,被填充的点极大可能和它那条等照线上的点类似,沿着这个等照线绘制下去,就可以了。

       那我们怎么判断当前点是不是处于与掩膜边界垂直的等照线上呢?可以通过掩膜边界(黑线)的法线和红色等照度线这两个向量判断。等照线与掩膜边界垂直时,那与掩膜边界的法线自然平行,此时两个向量点乘可以使得值最大,因此该值可以作为填补优先级评判的指标,定义为D(x,y)。

       除此之外,我们还需要用到一个指标,叫可信度,定义为C(x,y),就是被填补像素所在窗口内,源数据的占比,加入窗口为7*7的尺寸,里面有31个源数据,18个代填补数据,那它的可信度也就是31/49。可信度越高的像素,说明窗口内缺失的数据越少,对它们填补更容易且贴实。

       综上,代填补像素的优先级可以定义为P(x,y)=C(x,y)*D(x,y),当然也可以定义为别的,比如乘法变加法等等,大家可以自行发挥。

2.2 数据填充

       确认好代填补像素的优先级后,我们对优先级最高的像素所在窗口进行填充,填充基于窗口匹配实现。

       窗口匹配顾名思义就是从全图中寻找一个最像要填补的窗口的源数据窗口,把它粘过去即可。如下图所示,黑色窗口的实心部分是代填补窗口中的源数据,空心部分是代填补的数据。我们将黑色窗口实心部分和红色窗口实心部分进行三通道数据减法,对差值平方和累加取平均,可以得到一个匹配误差matchError,寻找全图最小的误差作为最小误差minError,此时对应的红色窗口就是匹配好的源数据。

       但是,当出现两个同样的匹配误差后该怎么取舍呢?这时需要用到第二个匹配的指标——最小窗内方差minVarience。即对源数据中空心部分三通道数值求方差,数值减去平均值后平方和累加。方差低则说明数据平稳,不容易出现异常突兀的噪声数据,这样可以让填补的数据更贴实。

2.3 算法流程图

       综上,该算法的流程图可简化为:

三、填补效果图

3.1 干涉条纹图填补视频

       该案例特征是图像黑白色系相对稳定,近似区域多,因此修补效果也是最好的。

图像修补算法示例视频1

3.2 地图填补视频 

       该案例特征是图像分为极大区域,如海洋、雪山、陆地、森林,色系复杂,图像细节多且杂。对其修补效果也是相对好的,因为本身糅杂的颜色系统中适当混入一些不和谐因素,凭借肉眼较难准确识别。

图像修补算法示例视频2

3.3 花卉修补

      该案例特征是图像色彩相对单调,花瓣区域纹理明显。这类图像色彩简单又不简单,颜色相近又各有区分,修补难度极大。经过图像修补后,可以发现花的边缘修补较成功,但是肉眼还是能看出内部区间存在一定的修补痕迹。

3.4 房屋修补

      该案例特征是房屋颜色与天空接近,此时填补区域如果是屋顶瓦砖,便补的很好,因为纹理缘故,匹配的数据也是瓦砖。

       但如果填补区域是屋顶侧面,则易出问题,若天空是蓝色还好,但恰巧天空也是棕黄色系,所以填补痕迹就突出了。

       该案例也是很不好找,特地找出来做评估。感兴趣的伙伴可以优化窗口匹配函数,比如全局和局部结合匹配等等。

四、代码分享

main.cpp

#include "Inpaint.h"

// 全局变量
int thickness = 5;
cv::Point sPoint(-1, -1);
cv::Mat image, mask;

// 鼠标事件
static void onMouse(int event, int x, int y, int flags, void*){
	if (event == cv::EVENT_LBUTTONUP || !(flags & cv::EVENT_FLAG_LBUTTON))
		sPoint = cv::Point(-1, -1);
	else if (event == cv::EVENT_LBUTTONDOWN)
		sPoint = cv::Point(x, y);
    else if( event == cv::EVENT_MOUSEMOVE && (flags & cv::EVENT_FLAG_LBUTTON)){
        cv::Point ePoint(x,y);
        if( sPoint.x < 0 )
            sPoint = ePoint;
        cv::line( mask, sPoint, ePoint, cv::Scalar::all(255), thickness);
        cv::line( image, sPoint, ePoint, cv::Scalar::all(255), thickness);
        sPoint = ePoint;
		cv::imshow("image", image);
    }
}

// 主函数
int main(){
	cv::Mat originalImage = cv::imread("6.jpg", 1);
	
	// 无输入图像
    if(!originalImage.data){
		cout << "Error unable to open input image" << endl;
        return 0;
    }

	// 拷贝图像
    image=originalImage.clone();
	mask = cv::Mat::zeros(image.size(), CV_8U);

	// 设置鼠标事件
	cv::namedWindow("image", 1);
	cv::imshow("image", image);
	cv::setMouseCallback("image", onMouse, 0);

	// 循环处理
	while(true){
		// 键盘事件
		char key = (char)cv::waitKey();

		// 按'b'跳出循环,结束程序
		if (key == 'b')
			break;

		// 按'r'恢复原始图像
		if (key == 'r'){
			mask = cv::Scalar::all(0);
			image = originalImage.clone();
			cv::imshow("image", image);
		}

		// 按'空格'执行算法
		if (key == ' '){
			int r = 3;
			InpaintAlgorithm *m_algorithm = new InpaintAlgorithm(image, mask, 2 * r + 1, TEMPLATE_MATCHING);
			m_algorithm->executeInpaint();
			cv::namedWindow("result");
			cv::imshow("result", m_algorithm->m_outputImage);
		}

		// 按'w'增加画笔厚度
		if (key == 'w') {
			thickness++;
			if (thickness > 20)
				thickness = 20;
			cout << "Thickness = " << thickness << endl;
		}

		// 按's'减少画笔厚度
		if (key == 's') {
			thickness--;
			if (thickness < 1)
				thickness = 1;
			cout << "Thickness = " << thickness << endl;
		}
	}
    return 0;
}

Inpaint.h

#ifndef INPAINT_H
#define INPAINT_H
#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/photo/photo.hpp"

using namespace std;
using namespace cv;

// 填补策略
enum INPAINT_METHOD {
	TEMPLATE_MATCHING,                              // 模板匹配
};

// 错误类型
enum ERROR_TYPE {
	ERROR_TYPE_OK,                                  // OK
	ERROR_TYPE_INPUT,								// 输入图像异常
	ERROR_TYPE_MASK,								// 掩膜异常
	ERROR_TYPE_WINDOWSIZE,							// 窗尺寸异常
};

// 定义图像填补接口
class Inpaint
{
public:
	// 构造函数
	Inpaint(cv::Mat inputImage, cv::Mat mask, int windowSize);

	// 检查异常
	void checkError();

	// 执行
	virtual void execute() = 0;

public:
	ERROR_TYPE errorType;                           // 错误码
	int m_windowSize = 9;                           // 窗尺寸
	cv::Mat m_inputImage;							// 输入图像
	cv::Mat m_mask;									// 掩膜
	cv::Mat m_outputImage;							// 输出图像
};

// 实现具体策略-模板匹配
class TemplateMatching :public Inpaint
{
public:
	// 构造函数
	TemplateMatching(cv::Mat inputImage, cv::Mat mask, int windowSize);

	// 执行
	virtual void execute();

private:
	// 初始化
	void init();

	// 检查是否存在未填补信息
	bool checkUnfilled();
	
	// 寻找目标位置集合
	void findTargetPoints();

	// 计算可信度
	void calcConfidence();

	// 获取优先级
	void getPriority();

	// 寻找最佳匹配
	void findBestMatch();

	// 更新图像
	void updateImage();

private:
	int targetIndex;							   // 目标点序号
	cv::Point2i m_bestMatchUL;					   // 最佳匹配点左上角位置
	cv::Mat m_updatedImage;                        // 更新中的图像
	cv::Mat m_updatedMask;						   // 更新中的掩膜
	cv::Mat m_confidence;						   // 可信度
	cv::Mat m_oriSourceRegion;					   // 原始源位置
	cv::Mat m_sourceRegion;						   // 源位置
	cv::Mat m_targetRegion;						   // 目标位置
	cv::Mat m_gradientX;						   // 梯度X
	cv::Mat m_gradientY;						   // 梯度Y
	vector<cv::Point2i> targetPoints;			   // 目标位置点集合
	vector<pair<float, float>> normals;            // 法线集合
};

// 应用类-图像修补算法调用
class InpaintAlgorithm
{
public:
	// 构造函数
	InpaintAlgorithm(cv::Mat inputImage, cv::Mat mask, int windowSize, INPAINT_METHOD method);

	// 析构函数
	~InpaintAlgorithm();

	// 设置修补策略
	void setInpaintAlgorithm(INPAINT_METHOD method);

	// 执行图像修补
	void executeInpaint();

public:
	int m_windowSize;							   // 窗尺寸
	cv::Mat m_inputImage;						   // 输入图像
	cv::Mat m_mask;								   // 掩膜
	cv::Mat m_outputImage;                         // 输出图像

private:
	Inpaint* m_inpaint;							   // 图像填补类实例
};

#endif

       C++完整代码不免费分享,有意获取者可以私我。算法不是魔法,不能解决一切问题。该算法的核心逻辑可用于工程开发,但仍有许多需要结合实际完善的地方,不建议直接拷贝使用。

       注意:当缺失的面积过大或者没有近似的窗口源数据时,填补效果会相对失真,这也是合理的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/517060.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

35个开源的工业软件-工业4.0

不同的工业流程&#xff0c;需要不同的工业软件。面向研发设计环节的开源软件&#xff0c;今天就来介绍一下面向生产控制环节的开源软件&#xff0c;主要为可编程逻辑控制器&#xff08;PLC)、分布式控制系统&#xff08;DCS&#xff09;、生产执行系统&#xff08;MES&#xf…

以“阵地战”打法,多样性算力攻坚数字经济基础设施

作者 | 曾响铃 文 | 响铃说 人人都知道数字化转型进入深水区后&#xff0c;对算力的渴求在不断增长。 包括政务、能源、金融、制造业等领域的数字化转型铆足了劲头&#xff0c;不断向那些数字基础设施索要源源不断的算力。 但是&#xff0c;更应该注意到的是&#xff0c;在…

Debian 12 “Bookworm” 的新特性和发布日期

导读Debian 12 即将发布。了解一下更多关于其新特性和发布日期的相关信息。 debian 12 Debian 即将发布系统代号为 “书虫” 的新版本。与 Debian 11 “Bullseye” 相比&#xff0c;有许多改进和新功能。 Debian 12 “Bookworm” 包含了超过 11200 个新软件包&#xff0c;软件…

泰克RSA306B频谱分析仪测试信道功率方法

泰克RSA306B实时频谱分析仪是一种用于无线信号分析的仪器。它可以实时监控无线信号的频谱&#xff0c;帮助用户分析信号特征&#xff0c;掌握信号的功率、频率、调制等关键信息。在无线通信中&#xff0c;信道功率是一个非常重要的指标&#xff0c;它反映了信号在传输过程中的强…

基于matlab使用广义互相关和三角测量来确定宽带信号源的位置

一、前言 此示例说明如何使用广义互相关 &#xff08;GCC&#xff09; 和三角测量来确定宽带信号源的位置。为简单起见&#xff0c;此示例仅限于由一个源和两个接收传感器阵列组成的二维方案。您可以将此方法扩展到两个以上的传感器或传感器阵列以及三维。 二、介绍 源定位不同…

速锐得工业物联网技术无线自动化解决方案在物流业的应用

物流业一直走在工业物联网的最前沿&#xff0c;因为许多工业物联网的机会和技术与物流业完美匹配。因此&#xff0c;物流业多年依赖一直使用许多工业物联网相关的传感器和技术。 例如&#xff0c;多年来&#xff0c;物流业一直在包装、托盘和集装箱中使用条形码&#xff0c;从9…

Redis(18. 面试题简析)学习笔记

上一篇 &#xff1a;17. Redis 分布式锁 - 周阳 下一篇 &#xff1a;18. 面试题简析 文章目录 1. 为什么要用缓存&#xff1f;2. redis 和 memcached 有什么区别&#xff1f;3. redis 的线程模型4. 为啥 redis 单线程模型也能效率这么高&#xff1f;5. redis 都有哪些数据类型…

CodeArts持续发力输出,华为云重塑软件开发

配图来自Canva可画 互联网时代&#xff0c;软件技术对企业成功的助益越来越明显。不管是为了顺遂大众趋势&#xff0c;还是迫于生存压力&#xff0c;可以确定的是&#xff0c;目前各类软件的开发已经被纳入众多企业的发展规划当中。 只是&#xff0c;长期以来国内软件行业一直…

机器学习回归任务指标评价及Sklearn神经网络模型评价实践

机器学习回归模型评价是指对回归模型的性能进行评估&#xff0c;以便选择最佳的回归模型。其中&#xff0c;MAE、MSE、RMSE 用于衡量模型预测值与真实值之间的误差大小&#xff0c;R 用于衡量模型对数据的拟合程度。在实际应用中&#xff0c;我们可以使用这些指标来评估回归模型…

git 撤销中间某次提交,保留其他提交的方法

今天上班脑抽了&#xff0c;吧test直接合到了uat,因为项目近期就我一个人开发&#xff0c;自己拉个三个分支再改不同的东西&#xff0c;最后都是发到test分支发测试&#xff0c;发生产的时候一个个和嫌麻烦&#xff0c;直接吧test分支怼到了uat&#xff0c;结果生产就出问题了&…

Revit插件 | 精装模块15个新功能正式上线,快来体验

大家好&#xff0c;这里是建模助手。 建模助手建模、算量、出图&#xff0c;一站搞定的——精装模块&#xff0c;正式上线了! 简单来说&#xff0c;精装模块就是辅助用户快速搭建精装修模型&#xff0c;便于后续的模型渲染、出量、出图工作。 目前暂时上线15个功能&#xff0…

ShardingSphere数据分片、读写分离、数据屏蔽教程

本文不讨论框架实现原理以及源码分析&#xff0c;只做功能使用案例说明 数据分片&#xff1a; 表分片可以帮助评论应用程序更有效地管理其不断增长的评论表&#xff0c;提高性能和可扩展性&#xff0c;同时还使备份和维护任务更易于管理 Apache ShardingSphere 有两种形式&am…

公众号推文添加附件秀米添加附件?三步轻松实现

很多人都不知道&#xff0c;其实公众号文章正文是支持添加附件的&#xff0c;只是需要借助一个叫做“文章附件”的网站来实现。公众号自身是不支持直接上传文件的&#xff0c;但是我们可以通过另外的方式来实现。 其实原理很简单&#xff0c;公众号文章中是可以添加小程序链接…

AOP(小卡拉米!!!)温故!

前面我们说了AOP底层是使用代理模式进行实现&#xff0c;spring写的接口是通过代理反射&#xff0c;实现方法&#xff0c;然后定义切入点&#xff1a; springAOP接口定义的方法有&#xff1a;被加强的方法前执行&#xff0c;被加强的方法后执行&#xff0c;出错了执行&#xf…

docker数据卷volume详细配置案例讲解

docker数据卷 文章目录 docker数据卷1.docker挂载宿主机数据卷1.2.具体配置1.3.挂载命令1.3.1访问页面 2.多端口的容器nginx配置2.1.创建目录位置2.2.访问结果 3.数据卷volume持久化配置3.1.语法格式3.2.详细参数3.3.操作案例3.4.数据改动3.5.查看卷的详细属性 4.与某个容器使用…

如何利用技术做到脱颖而出?亚马逊云科技泛娱乐高峰论坛为你揭秘

互联网技术的飞速进步与数字内容消费的蓬勃发展&#xff0c;使得泛娱乐市场越来越红火&#xff0c;用户对于高品质内容的个性化需求也在不断提升&#xff0c;这对技术底座也提出了更加严苛的要求&#xff0c;时代潮流已至&#xff0c;如何利用技术在一片红海中做到脱颖而出&…

【溯源反制】CDN域前置云函数-流量分析|溯源

文章目录 CDN隐藏C2地址环境搭建上传至威胁感知平台直接分析使用DNSQuerySniffer和Process Monitor定位进程网络流量分析文件属性(IDAPro Ollydbg) 域前置隐藏环境搭建威胁感知流量分析 云服务API网关/云函数云函数使用HTTPcs的流量可以简单的分为三个阶段 云函数使用HTTPS 总结…

kali Linux root密码修改

kali root 密码修改有时候是经常发生的,要么忘记,要么是必须限时要改的,今天记录下,修改方法,以便后续。 Kali Linux的前身是BackTrack Linux发行版。Kali Linux是一个基于Debian的Linux发行版,它被认为是最好的渗透测试的 Linux 发行版之一,而且名副其实。 作为一名从…

Vivado使用技巧:时钟的约束方法

时钟的基础知识 数字设计中&#xff0c;“时钟”表示在寄存器之间可靠地传输数据所需的参考时间&#xff1b;Vivado的时序引擎利用时钟特征来计算时序路径需求&#xff0c;通过计算时间裕量&#xff08;Slack&#xff09;的方法报告设计的时序空余&#xff1b;时钟必须…

C++11 -- 入门基础知识

文章目录 C11简介列表初始化std::initializer_list 变量类型推导nullptr范围for循环STL中的一些变化 C11简介 在2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)&#xff0c;使得C03这个名字已经取代了C98称为C11之前的最新C标准名称。不过由于C03(TC1)主要是对C98标准中…