作者:翟天保Steven
版權(quán)聲明:著作權(quán)歸作者所有,商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處
一、關(guān)于圖像修補(bǔ)
? ? ? ?圖像修補(bǔ)的目的是基于已有的圖像信息或數(shù)據(jù)庫內(nèi)信息,對缺失區(qū)域進(jìn)行合理地修復(fù)。在諸多領(lǐng)域如電影、攝像、醫(yī)療等行業(yè),有廣泛的應(yīng)用。
? ? ? ?傳統(tǒng)上,圖像修補(bǔ)由專業(yè)的修復(fù)師進(jìn)行,修復(fù)師憑借自身豐富的工作經(jīng)驗(yàn)和生活閱歷,不僅能基于客觀信息對圖像缺失進(jìn)行填充,更能進(jìn)行主觀創(chuàng)作,使得二次修復(fù)的圖片更加生動(dòng)形象。
? ? ? ?但在日益智能化的今天,針對數(shù)字圖像的修補(bǔ)工作逐漸由人工轉(zhuǎn)向了自動(dòng)化,這不僅節(jié)省了大量人工成本,而且計(jì)算機(jī)憑借優(yōu)越的算法和龐大的數(shù)據(jù)庫,對圖像的修復(fù)效果更高效且優(yōu)質(zhì)。通過近幾個(gè)月風(fēng)風(fēng)火火的chatGPT,相信大家也看到了AI的魅力,這是未來的大趨勢不可逆。
? ? ? ?針對圖像修補(bǔ),本文提出了一種基于等照度線和窗口匹配的圖像修補(bǔ)算法,接下來將簡單介紹下算法原理和流程,并展示相關(guān)的效果圖。
二、算法原理和流程
? ? ? ?對圖像缺失部分進(jìn)行填充,首先要確定填充哪些內(nèi)容,誰優(yōu)先填充誰最后填充;其次要判斷用什么數(shù)據(jù)來填充,能使得填充后的結(jié)果更貼近真實(shí),顯得不違和。做到了這兩點(diǎn),圖像修補(bǔ)工作基本就完成了。
2.1 優(yōu)先級計(jì)算
? ? ? ?我們先來討論填充的優(yōu)先級,如下圖所示,圖像白色部分是手動(dòng)繪制的掩膜區(qū)域,該區(qū)域的真實(shí)信息被擦除了,我們現(xiàn)在要對其進(jìn)行復(fù)原。
? ? ? ?在圖中紅線部分就是等照線,該線的兩側(cè)往往有較大的數(shù)值差異,因此它也與黑線所示的梯度線呈垂直關(guān)系;當(dāng)?shù)日站€與掩膜邊界呈垂直時(shí),此時(shí)等照線上的像素點(diǎn)特征是非常強(qiáng)烈且明顯的,通俗的講,被填充的點(diǎn)極大可能和它那條等照線上的點(diǎn)類似,沿著這個(gè)等照線繪制下去,就可以了。
? ? ? ?那我們怎么判斷當(dāng)前點(diǎn)是不是處于與掩膜邊界垂直的等照線上呢?可以通過掩膜邊界(黑線)的法線和紅色等照度線這兩個(gè)向量判斷。等照線與掩膜邊界垂直時(shí),那與掩膜邊界的法線自然平行,此時(shí)兩個(gè)向量點(diǎn)乘可以使得值最大,因此該值可以作為填補(bǔ)優(yōu)先級評判的指標(biāo),定義為D(x,y)。
? ? ? ?除此之外,我們還需要用到一個(gè)指標(biāo),叫可信度,定義為C(x,y),就是被填補(bǔ)像素所在窗口內(nèi),源數(shù)據(jù)的占比,加入窗口為7*7的尺寸,里面有31個(gè)源數(shù)據(jù),18個(gè)代填補(bǔ)數(shù)據(jù),那它的可信度也就是31/49。可信度越高的像素,說明窗口內(nèi)缺失的數(shù)據(jù)越少,對它們填補(bǔ)更容易且貼實(shí)。
? ? ? ?綜上,代填補(bǔ)像素的優(yōu)先級可以定義為P(x,y)=C(x,y)*D(x,y),當(dāng)然也可以定義為別的,比如乘法變加法等等,大家可以自行發(fā)揮。
2.2 數(shù)據(jù)填充
? ? ? ?確認(rèn)好代填補(bǔ)像素的優(yōu)先級后,我們對優(yōu)先級最高的像素所在窗口進(jìn)行填充,填充基于窗口匹配實(shí)現(xiàn)。
? ? ? ?窗口匹配顧名思義就是從全圖中尋找一個(gè)最像要填補(bǔ)的窗口的源數(shù)據(jù)窗口,把它粘過去即可。如下圖所示,黑色窗口的實(shí)心部分是代填補(bǔ)窗口中的源數(shù)據(jù),空心部分是代填補(bǔ)的數(shù)據(jù)。我們將黑色窗口實(shí)心部分和紅色窗口實(shí)心部分進(jìn)行三通道數(shù)據(jù)減法,對差值平方和累加取平均,可以得到一個(gè)匹配誤差matchError,尋找全圖最小的誤差作為最小誤差minError,此時(shí)對應(yīng)的紅色窗口就是匹配好的源數(shù)據(jù)。
? ? ? ?但是,當(dāng)出現(xiàn)兩個(gè)同樣的匹配誤差后該怎么取舍呢?這時(shí)需要用到第二個(gè)匹配的指標(biāo)——最小窗內(nèi)方差minVarience。即對源數(shù)據(jù)中空心部分三通道數(shù)值求方差,數(shù)值減去平均值后平方和累加。方差低則說明數(shù)據(jù)平穩(wěn),不容易出現(xiàn)異常突兀的噪聲數(shù)據(jù),這樣可以讓填補(bǔ)的數(shù)據(jù)更貼實(shí)。
2.3 算法流程圖
? ? ? ?綜上,該算法的流程圖可簡化為:
三、填補(bǔ)效果圖
3.1 干涉條紋圖填補(bǔ)視頻
? ? ? ?該案例特征是圖像黑白色系相對穩(wěn)定,近似區(qū)域多,因此修補(bǔ)效果也是最好的。
圖像修補(bǔ)算法示例視頻1
3.2 地圖填補(bǔ)視頻?
? ? ? ?該案例特征是圖像分為極大區(qū)域,如海洋、雪山、陸地、森林,色系復(fù)雜,圖像細(xì)節(jié)多且雜。對其修補(bǔ)效果也是相對好的,因?yàn)楸旧眙垭s的顏色系統(tǒng)中適當(dāng)混入一些不和諧因素,憑借肉眼較難準(zhǔn)確識別。
圖像修補(bǔ)算法示例視頻2
3.3 花卉修補(bǔ)
? ? ? 該案例特征是圖像色彩相對單調(diào),花瓣區(qū)域紋理明顯。這類圖像色彩簡單又不簡單,顏色相近又各有區(qū)分,修補(bǔ)難度極大。經(jīng)過圖像修補(bǔ)后,可以發(fā)現(xiàn)花的邊緣修補(bǔ)較成功,但是肉眼還是能看出內(nèi)部區(qū)間存在一定的修補(bǔ)痕跡。
3.4 房屋修補(bǔ)
? ? ? 該案例特征是房屋顏色與天空接近,此時(shí)填補(bǔ)區(qū)域如果是屋頂瓦磚,便補(bǔ)的很好,因?yàn)榧y理緣故,匹配的數(shù)據(jù)也是瓦磚。
? ? ? ?但如果填補(bǔ)區(qū)域是屋頂側(cè)面,則易出問題,若天空是藍(lán)色還好,但恰巧天空也是棕黃色系,所以填補(bǔ)痕跡就突出了。
? ? ? ?該案例也是很不好找,特地找出來做評估。感興趣的伙伴可以優(yōu)化窗口匹配函數(shù),比如全局和局部結(jié)合匹配等等。
四、代碼分享
main.cpp
#include "Inpaint.h"
// 全局變量
int thickness = 5;
cv::Point sPoint(-1, -1);
cv::Mat image, mask;
// 鼠標(biāo)事件
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);
}
}
// 主函數(shù)
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);
// 設(shè)置鼠標(biāo)事件
cv::namedWindow("image", 1);
cv::imshow("image", image);
cv::setMouseCallback("image", onMouse, 0);
// 循環(huán)處理
while(true){
// 鍵盤事件
char key = (char)cv::waitKey();
// 按'b'跳出循環(huán),結(jié)束程序
if (key == 'b')
break;
// 按'r'恢復(fù)原始圖像
if (key == 'r'){
mask = cv::Scalar::all(0);
image = originalImage.clone();
cv::imshow("image", image);
}
// 按'空格'執(zhí)行算法
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;
// 填補(bǔ)策略
enum INPAINT_METHOD {
TEMPLATE_MATCHING, // 模板匹配
};
// 錯(cuò)誤類型
enum ERROR_TYPE {
ERROR_TYPE_OK, // OK
ERROR_TYPE_INPUT, // 輸入圖像異常
ERROR_TYPE_MASK, // 掩膜異常
ERROR_TYPE_WINDOWSIZE, // 窗尺寸異常
};
// 定義圖像填補(bǔ)接口
class Inpaint
{
public:
// 構(gòu)造函數(shù)
Inpaint(cv::Mat inputImage, cv::Mat mask, int windowSize);
// 檢查異常
void checkError();
// 執(zhí)行
virtual void execute() = 0;
public:
ERROR_TYPE errorType; // 錯(cuò)誤碼
int m_windowSize = 9; // 窗尺寸
cv::Mat m_inputImage; // 輸入圖像
cv::Mat m_mask; // 掩膜
cv::Mat m_outputImage; // 輸出圖像
};
// 實(shí)現(xiàn)具體策略-模板匹配
class TemplateMatching :public Inpaint
{
public:
// 構(gòu)造函數(shù)
TemplateMatching(cv::Mat inputImage, cv::Mat mask, int windowSize);
// 執(zhí)行
virtual void execute();
private:
// 初始化
void init();
// 檢查是否存在未填補(bǔ)信息
bool checkUnfilled();
// 尋找目標(biāo)位置集合
void findTargetPoints();
// 計(jì)算可信度
void calcConfidence();
// 獲取優(yōu)先級
void getPriority();
// 尋找最佳匹配
void findBestMatch();
// 更新圖像
void updateImage();
private:
int targetIndex; // 目標(biāo)點(diǎn)序號
cv::Point2i m_bestMatchUL; // 最佳匹配點(diǎn)左上角位置
cv::Mat m_updatedImage; // 更新中的圖像
cv::Mat m_updatedMask; // 更新中的掩膜
cv::Mat m_confidence; // 可信度
cv::Mat m_oriSourceRegion; // 原始源位置
cv::Mat m_sourceRegion; // 源位置
cv::Mat m_targetRegion; // 目標(biāo)位置
cv::Mat m_gradientX; // 梯度X
cv::Mat m_gradientY; // 梯度Y
vector<cv::Point2i> targetPoints; // 目標(biāo)位置點(diǎn)集合
vector<pair<float, float>> normals; // 法線集合
};
// 應(yīng)用類-圖像修補(bǔ)算法調(diào)用
class InpaintAlgorithm
{
public:
// 構(gòu)造函數(shù)
InpaintAlgorithm(cv::Mat inputImage, cv::Mat mask, int windowSize, INPAINT_METHOD method);
// 析構(gòu)函數(shù)
~InpaintAlgorithm();
// 設(shè)置修補(bǔ)策略
void setInpaintAlgorithm(INPAINT_METHOD method);
// 執(zhí)行圖像修補(bǔ)
void executeInpaint();
public:
int m_windowSize; // 窗尺寸
cv::Mat m_inputImage; // 輸入圖像
cv::Mat m_mask; // 掩膜
cv::Mat m_outputImage; // 輸出圖像
private:
Inpaint* m_inpaint; // 圖像填補(bǔ)類實(shí)例
};
#endif
? ? ? ?C++完整代碼不免費(fèi)分享,有意獲取者可以私我。算法不是魔法,不能解決一切問題。該算法的核心邏輯可用于工程開發(fā),但仍有許多需要結(jié)合實(shí)際完善的地方,不建議直接拷貝使用。文章來源:http://www.zghlxwxcb.cn/news/detail-440795.html
? ? ? ?注意:當(dāng)缺失的面積過大或者沒有近似的窗口源數(shù)據(jù)時(shí),填補(bǔ)效果會(huì)相對失真,這也是合理的。文章來源地址http://www.zghlxwxcb.cn/news/detail-440795.html
到了這里,關(guān)于基于等照度線和窗口匹配的圖像修補(bǔ)算法的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!