前言
? ? ? ? 在實際應用中,我們的圖像常常會被噪聲腐蝕,這些噪聲或是鏡頭上的灰塵或水滴,或是舊照片的劃痕,或者是圖像遭到人為的涂畫(比如馬賽克)或者圖像的部分本身已經損壞。如果我們想讓這些受到破壞的額圖片盡可能恢復到原樣,Opencv能幫我們做到嗎?OpenCV真的有這個妙手回春的功能!別以為圖像修補的工作只能用PS或者美圖秀秀那些軟件去做,其實由程序員自己寫代碼去做更加高效!
圖像修復技術的原理是什么呢?
簡而言之,就是利用那些已經被破壞的區(qū)域的邊緣, 即邊緣的顏色和結構,根據這些圖像留下的信息去推斷被破壞的信息區(qū)的信息內容,然后對破壞區(qū)進行填補 ,以達到圖像修補的目的。
在OpenCV的“photo.hpp”中定義了一個inpaint函數(shù),可以用來實現(xiàn)圖像的修復和復原功能,inpaint函數(shù)的原型如下:
void inpaint( InputArray src, InputArray inpaintMask,
OutputArray dst, double inpaintRadius, int flags );
第一個參數(shù)src,輸入的單通道或三通道圖像;
第二個參數(shù)inpaintMask,圖像的掩碼,單通道圖像,大小跟原圖像一致,inpaintMask圖像上除了需要修復的部分之外其他部分的像素值全部為0;
第三個參數(shù)dst,輸出的經過修復的圖像;
第四個參數(shù)inpaintRadius,修復算法取的鄰域半徑,用于計算當前像素點的差值;
第五個參數(shù)flags,修復算法,有兩種:INPAINT_NS 和I NPAINT_TELEA;
函數(shù)實現(xiàn)關鍵是圖像掩碼的確定,可以通過閾值篩選或者手工選定,按照這個思路,用三種方法生成掩碼,對比圖像修復的效果。
方法一、全區(qū)域閾值處理+Mask膨脹處理
[cpp] view plain copy print?
#include 《imgprocimgproc.hpp》
#include 《highguihighgui.hpp》
#include 《photophoto.hpp》
using namespace cv;
//全區(qū)域閾值處理+Mask膨脹處理
int main()
{
Mat imageSource = imread(“Test.jpg”);
if (!imageSource.data)
{
return -1;
}
imshow(“原圖”, imageSource);
Mat imageGray;
//轉換為灰度圖
cvtColor(imageSource, imageGray, CV_RGB2GRAY, 0);
Mat imageMask = Mat(imageSource.size(), CV_8UC1, Scalar::all(0));
//通過閾值處理生成Mask
threshold(imageGray, imageMask, 240, 255, CV_THRESH_BINARY);
Mat Kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
//對Mask膨脹處理,增加Mask面積
dilate(imageMask, imageMask, Kernel);
//圖像修復
inpaint(imageSource, imageMask, imageSource, 5, INPAINT_TELEA);
imshow(“Mask”, imageMask);
imshow(“修復后”, imageSource);
waitKey();
}
原始圖像:
?
根據閾值處理得到的圖像掩碼:
?
圖像復原結果:
?
由于是圖像全區(qū)域做閾值處理獲得的掩碼,圖像上部分區(qū)域也被當做掩碼對待,導致部分圖像受損。
方法二、鼠標框選區(qū)域+閾值處理+Mask膨脹處理
[cpp] view plain copy print?
#include 《imgproc/imgproc.hpp》
#include 《highgui/highgui.hpp》
#include 《core/core.hpp》
#include 《photo/photo.hpp》
using namespace cv;
Point ptL, ptR; //鼠標畫出矩形框的起點和終點
Mat imageSource, imageSourceCopy;
Mat ROI; //原圖需要修復區(qū)域的ROI
//鼠標回調函數(shù)
void OnMouse(int event, int x, int y, int flag, void *ustg);
//鼠標圈定區(qū)域閾值處理+Mask膨脹處理
int main()
{
imageSource = imread(“Test.jpg”);
if (!imageSource.data)
{
return -1;
}
imshow(“原圖”, imageSource);
setMouseCallback(“原圖”, OnMouse);
waitKey();
}
void OnMouse(int event, int x, int y, int flag, void *ustg)
{
if (event == CV_EVENT_LBUTTONDOWN)
{
ptL = Point(x, y);
ptR = Point(x, y);
}
if (flag == CV_EVENT_FLAG_LBUTTON)
{
ptR = Point(x, y);
imageSourceCopy = imageSource.clone();
rectangle(imageSourceCopy, ptL, ptR, Scalar(255, 0, 0));
imshow(“原圖”, imageSourceCopy);
}
if (event == CV_EVENT_LBUTTONUP)
{
if (ptL != ptR)
{
ROI = imageSource(Rect(ptL, ptR));
imshow(“ROI”, ROI);
waitKey();
}
}
//單擊鼠標右鍵開始圖像修復
if (event == CV_EVENT_RBUTTONDOWN)
{
imageSourceCopy = ROI.clone();
Mat imageGray;
cvtColor(ROI, imageGray, CV_RGB2GRAY); //轉換為灰度圖
Mat imageMask = Mat(ROI.size(), CV_8UC1, Scalar::all(0));
//通過閾值處理生成Mask
threshold(imageGray, imageMask, 235, 255, CV_THRESH_BINARY);
Mat Kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imageMask, imageMask, Kernel); //對Mask膨脹處理
inpaint(ROI, imageMask, ROI, 9, INPAINT_TELEA); //圖像修復
imshow(“Mask”, imageMask);
imshow(“修復后”, imageSource);
}
}
鼠標圈定的ROI:
?
圖像復原結果:
?
選定區(qū)域之外的圖像不受修復影響,沒有額外的損傷。
方法三、鼠標劃定整個區(qū)域作為修復對象
這個方法選定一個矩形區(qū)域,把整個矩形區(qū)域作為要修復的對象,該方法適用于圖像結構比較簡單,特別是純色圖像,并且選定區(qū)域面積占比不大的情況,效果較好。
[cpp] view plain copy print?
#include 《imgproc/imgproc.hpp》
#include 《highgui/highgui.hpp》
#include 《core/core.hpp》
#include 《photo/photo.hpp》
using namespace cv;
Point ptL, ptR; //鼠標畫出矩形框的起點和終點
Mat imageSource, imageSourceCopy;
Mat ROI; //原圖需要修復區(qū)域的ROI
//鼠標回調函數(shù)
void OnMouse(int event, int x, int y, int flag, void *ustg);
//鼠標圈定區(qū)域
int main()
{
imageSource = imread(“Test.jpg”);
if (!imageSource.data)
{
return -1;
}
imshow(“原圖”, imageSource);
setMouseCallback(“原圖”, OnMouse);
waitKey();
}
void OnMouse(int event, int x, int y, int flag, void *ustg)
{
if (event == CV_EVENT_LBUTTONDOWN)
{
ptL = Point(x, y);
ptR = Point(x, y);
}
if (flag == CV_EVENT_FLAG_LBUTTON)
{
ptR = Point(x, y);
imageSourceCopy = imageSource.clone();
rectangle(imageSourceCopy, ptL, ptR, Scalar(255, 0, 0));
imshow(“原圖”, imageSourceCopy);
}
if (event == CV_EVENT_LBUTTONUP)
{
if (ptL != ptR)
{
ROI = imageSource(Rect(ptL, ptR));
imshow(“ROI”, ROI);
waitKey();
}
}
//單擊鼠標右鍵開始圖像修復
if (event == CV_EVENT_RBUTTONDOWN)
{
imageSourceCopy = Mat(imageSource.size(), CV_8UC1, Scalar::all(0));
Mat imageMask = imageSourceCopy(Rect(ptL, ptR));
//生成一個跟ROI大小一樣的值全為1的區(qū)域
Mat imageMaskCopy = Mat(imageMask.size(), CV_8UC1, Scalar::all(1));
imageMaskCopy.copyTo(imageMask);
inpaint(imageSource, imageSourceCopy, imageSource, 9, INPAINT_TELEA); //圖像修復
imshow(“Mask”, imageSourceCopy);
imshow(“修復后”, imageSource);
}
}
原始圖像:
?
圖像復原結果:
?
評論
查看更多