其它的各种经典的图像修复算法Matlab源代码都可以从网上下载。
摘自:http://kghcy.blog.163.com/blog/static/267197622009101210154595/
2004年的文章,不算老也不够新。inpainting做好还是挺好玩的,至少就不需要拿photoshop的印章手动一点一滴的涂抹。而且程序上的计算判断有数据的支持原则上比人眼精度高,只是该如何涂抹远没有人智能,如果有合适的准则选择,那就可以又弄个小应用玩玩。
初看“Object removal by exemplar-based inpainting”,被其中效果图吸引,而且作者很厚道的提供对应代码,我感动没流涕之余,花了几小时分析了他的代码和文章,花了几小时写了个自己的版本,发现稳定性不够好,速度偏慢,可惜了。
inpainting就是对给定一张图片,用户选取其中一块区域(目前的算法,这块区域还是要比较精确选取的),然后通过程序运算,把这块用原图信息无痕自然替代掉。很常见的看到某某拍的照片里猛然有路人闯进,某某又很在意怕侵犯人家肖像权,自然而然会想销掉,于是ps的印章就被盖来盖去。
文章贡献了2点法,值得思考。
1、给出了一个优先权值计算。给定一个区域,作者选9X9。当这个9X9的块里有保留信息和要消除的区域时,就对这个块进行优先权值评估。评估准则:包含的消除点越少,权值越高;交界处的梯度信息越大权值越高。
有了这个权值,就可以每次取最高权值得小块进行涂抹。
2、文中对全图在所有保留区域搜索,以块中保留区域的数据与参考对应位置的数据求方差,找到最小方差块,然后填充块里要清除的区域。
如此反复,直到没有消除点。
慢,1个很大的原因就是计算多,特别是作者的代码每次都全图找1遍,其实考虑图像信息连贯性,在该点附件区域找就可以。文中用9X9的区域寻找,然后用9x9来填充,我觉得不大好,我考虑了non-local mean的思想,用11x11的区域查找,然后只修复其中3x3的块里的少数点。在文中"Figure 10"的实验数据上,取得比文章好很多的效果。
其实,通篇我都只用这张图测试,我失望的原因在于,当我改变块大小与搜索范围时,竟然得到了太多不一致的结果,差别太大,所以我觉得算法有失稳定,不够实用。
怎么办呢,有空再想。
仅此验证,在这记之,留给兴趣者,一丝参考。
//163的blog本就不适合贴代码。既然有人喜欢,show下我的code,虽然被这网页格式糟蹋了。
//---------------------------------------------------------------//
#pragma once
//Object Removal by Exemplar-Based Inpainting
class Rinpaint
{
public:
Rinpaint(void);
~Rinpaint(void);
#define btMASK 255
#define btFILL 0
long Inpaint(float *pInput, long Width, long Height, long Channel, BYTE *pMask, long Radius);
private:
void CalcPriority(float *pP, RECT Roi, long Radius, float DeNormI);
float CalcConfidence(long x, long y, long Radius);
RECT FindMaxPriority(long *x, long *y, float *pP, RECT Roi);
bool BestExemplar(RECT *updateRc, long x, long y, long matchRadius, long searchRadius);
void Update(RECT bestRc, RECT fillRc);
float CalcDifferent(RECT rc, float *ptrI, long ofi, BYTE *ptrM, long ofm, float RefError);
bool IsValidRect(RECT rc);
private:
float *mpIn;
long mnW, mnH, mnC, mnL;
BYTE *mpMask;
};
//---------------------------------------------------------------//
#include "StdAfx.h"
#include "Rinpaint.h"
#include "math.h"
Rinpaint::Rinpaint(void)
{
mpIn = NULL;
mpMask = NULL;
}
Rinpaint::~Rinpaint(void)
{
}
long Rinpaint::Inpaint(float *pInput, long Width, long Height, long Channel, BYTE *pMask, long Radius)
{
if(!pInput || Width <= 0 || Height <= 0 || Channel <= 0 || !pMask)
return hInvalid;
mpIn = pInput;
mnW = Width;
mnH = Height;
mnC = Channel;
mnL = mnW*mnC;
mpMask = pMask;
float *pPrior = NULL;
pPrior = new float [mnW*mnH];
if(!pPrior)
return hOutOfMemory;
RECT Roi, updateRc;
Roi.left = Roi.top = 0;
Roi.right = mnW-1;
Roi.bottom = mnH-1;
long matchRadius = Radius < 0 ? 4 : Radius;
long searchRadius = matchRadius + 5*Radius;
float DeNormI = 1.0f/(mnC*255.0f);
CalcPriority(pPrior, Roi, matchRadius, DeNormI);
long x, y;
while(true)
{
Roi = FindMaxPriority(&x, &y, pPrior, Roi);
if(x < 0 || y < 0)
break;
if(!BestExemplar(&updateRc, x, y, matchRadius, searchRadius))
break;
CalcPriority(pPrior, updateRc, matchRadius, DeNormI);
}
delete[] pPrior;
return hOk;
}
void Rinpaint::CalcPriority(float *pP, RECT Roi, long Radius, float DeNormI)
{
long Ex = mnW-1, Ey = mnH-1;
float Lambda = 0.001f;
long sp = Roi.top*mnW + Roi.left;
float *pp = pP + sp;
BYTE *pm = mpMask + sp, *pm0, *pm1;
long of = mnW - (Roi.right-Roi.left+1);
float *pi = mpIn + sp*mnC, *pi0, *pi1;
long ofi = of*mnC;
long i, j, k, x0, x1;
float Ix, Iy, Nx, Ny, dt;
for(j = Roi.top; j <= Roi.bottom; j++, pp += of, pm += of, pi += ofi)
{
if(j > 0) { pm0 = pm - mnW; pi0 = pi - mnL; }
else { pm0 = pm; pi0 = pi; }
if(j < Ey) { pm1 = pm + mnW; pi1 = pi + mnL; }
else { pm1 = pm; pi1 = pi; }
for(i = Roi.left; i <= Roi.right; i++, pp++, pm++, pm0++, pm1++)
{
if(*pm == btMASK)
{
pi += mnC; pi0 += mnC; pi1 += mnC;
*pp = -1.0f;
continue;
}
x0 = i > 0 ? -1 : 0;
x1 = i < Ex ? 1 : 0;
if(pm0[x0] == (*pm) && (*pm0) == (*pm) && pm0[x1] == (*pm)
&& pm[x0] == (*pm) && pm[x1] == (*pm)
&& pm1[x0] == (*pm) && (*pm1) == (*pm) && pm1[x1] == (*pm))
{
pi += mnC; pi0 += mnC; pi1 += mnC;
*pp = -1.0f;
continue;
}
//confidence term
*pp = CalcConfidence(i, j, Radius);
//date term
//->mask normalize
Nx = 0.5f * (pm[x0] - pm[x1]);
Ny = 0.5f * ((*pm0) - (*pm1));
if(Nx == 0.0f && Ny == 0.0f)
{
pi += mnC; pi0 += mnC; pi1 += mnC;
(*pp) *= Lambda;
continue;
}
if(Nx == 0.0f)
Ny = Ny < 0.0f ? -1.0f : 1.0f;
else if(Ny == 0.0f)
Nx = Nx < 0.0f ? -1.0f : 1.0f;
else
{
Nx = Nx < 0.0f ? -0.70710678f : 0.70710678f;
Ny = Ny < 0.0f ? -0.70710678f : 0.70710678f;
//dt = sqrt(Nx*Nx + Ny*Ny);
//Nx /= dt;
//Ny /= dt;
}
//->isophote value
Ix = Iy = 0.0f;
for(k = 0; k < mnC; k++, pi++, pi0++, pi1++)
{
Ix += 0.5f * ((i < Ex ? pi[mnC] : pi[0]) - (i > 0 ? pi[-mnC] : pi[0]));
Iy += 0.5f * ((*pi1) - (*pi0));
}
//->nomalize & rotate 90
Ix *= DeNormI;
Iy *= DeNormI;
dt = Ix; Ix = -Iy; Iy = dt;
//priorities = confidence term * data term
(*pp) *= (fabs(Ix*Nx + Iy*Ny) + Lambda);
}
}
}
float Rinpaint::CalcConfidence(long x, long y, long Radius)
{
RECT rc;
rc.left = max(0, x - Radius);
rc.top = max(0, y - Radius);
rc.right = min(mnW-1, x + Radius);
rc.bottom = min(mnH-1, y + Radius);
long nC = 0, nArea = 0, i, j, of = mnW - (rc.right - rc.left+1);
BYTE *pm = mpMask + rc.top*mnW + rc.left;
for(j = rc.top; j <= rc.bottom; j++, pm += of)
{
for(i = rc.left; i <= rc.right; i++, pm++)
{
nArea++;
if(*pm == btMASK)
continue;
nC++;
}
}
return (nArea == 0 ? 0.0f : (float)nC/nArea);
}
RECT Rinpaint::FindMaxPriority(long *x, long *y, float *pP, RECT Roi)
{
RECT newRoi;
newRoi.left = Roi.right;
newRoi.top = Roi.bottom;
newRoi.right = Roi.left;
newRoi.bottom = Roi.top;
long i, j, of = mnW - (Roi.right-Roi.left+1);
float *pp = pP + Roi.top*mnW + Roi.left;
float Max = 0.0f;
*x = *y = -1;
for(j = Roi.top; j <= Roi.bottom; j++, pp += of)
{
for(i = Roi.left; i <= Roi.right; i++, pp++)
{
if(*pp < 0.0f)
continue;
if(j < newRoi.top) newRoi.top = j;
if(j > newRoi.bottom) newRoi.bottom = j;
if(i < newRoi.left) newRoi.left = i;
if(i > newRoi.right) newRoi.right = i;
if(Max < (*pp))
{
Max = *pp;
*x = i;
*y = j;
}
}
}
return newRoi;
}
bool Rinpaint::BestExemplar(RECT *updateRc, long x, long y, long matchRadius, long searchRadius)
{
RECT modRc, bestRc;
modRc.left = max(0, x - matchRadius);
modRc.right = min(mnW-1, x + matchRadius);
modRc.top = max(0, y - matchRadius);
modRc.bottom = min(mnH-1, y + matchRadius);
long modX = modRc.right - modRc.left;
long modY = modRc.bottom - modRc.top;
long ofm = mnW - (modX+1);
long ofi = mnC*ofm;
long sp = modRc.top * mnW + modRc.left;
float *ptrI = mpIn + sp*mnC;
BYTE *ptrM = mpMask + sp;
long sx = max(matchRadius, modRc.left - searchRadius);
long ex = min(mnW-matchRadius, modRc.right + searchRadius);
long sy = max(matchRadius, modRc.top - searchRadius);
long ey = min(mnH-matchRadius, modRc.bottom + searchRadius);
float MinError = 255e10f, fError;
RECT refRc;
long i;
for(; sy < ey; sy++)
{
refRc.top = sy - matchRadius;
refRc.bottom = refRc.top + modY;
for(i = sx; i < ex; i++)
{
refRc.left = i - matchRadius;
refRc.right = refRc.left + modX;
if(!IsValidRect(refRc))
continue;
fError = CalcDifferent(refRc, ptrI, ofi, ptrM, ofm, MinError);
if(fError < MinError)
{
MinError = fError;
bestRc = refRc;
}
if(MinError == 0.0f)
break;
}
}
if(MinError != 255e10f)
{
long fillRadius = 1;
RECT fillRc;
fillRc.left = max(0, x - fillRadius);
fillRc.right = min(mnW-1, x + fillRadius);
fillRc.top = max(0, y - fillRadius);
fillRc.bottom = min(mnH-1, y + fillRadius);
bestRc.left -= (modRc.left - fillRc.left);
bestRc.top -= (modRc.top - fillRc.top);
bestRc.right = bestRc.left + (fillRc.right-fillRc.left);
bestRc.bottom = bestRc.top + (fillRc.bottom-fillRc.top);
*updateRc = fillRc;
Update(bestRc, fillRc);
return true;
}
return false;
}
void Rinpaint::Update(RECT bestRc, RECT fillRc)
{
long ofm = mnW - (fillRc.right - fillRc.left + 1);
long ofi = ofm*mnC;
long i, j, k, sp = fillRc.top*mnW + fillRc.left;
float *pb = mpIn + (bestRc.top*mnW + bestRc.left)*mnC;
float *pi = mpIn + sp*mnC;
BYTE *pm = mpMask + sp;
for(j = bestRc.top; j <= bestRc.bottom; j++, pi += ofi, pb += ofi, pm += ofm)
{
for(i = bestRc.left; i <= bestRc.right; i++, pm++)
{
if(*pm != btMASK)
{
pb += mnC;
pi += mnC;
continue;
}
for(k = 0; k < mnC; k++, pb++, pi++)
{
*pm = btFILL;
*pi = *pb;
}
}
}
}
float Rinpaint::CalcDifferent(RECT rc, float *ptrI, long ofi, BYTE *ptrM, long ofm, float RefError)
{
float Error = 0.0f;
long i, j, k;
float *pi0 = ptrI;
float *pi1 = mpIn + (rc.top*mnW + rc.left)*mnC;
BYTE *pm = ptrM;
for(j = rc.top; j <= rc.bottom; j++, pi0 += ofi, pi1 += ofi, pm += ofm)
{
for(i = rc.left; i <= rc.right; i++, pm++)
{
if(*pm == btMASK)
{
pi0 += mnC;
pi1 += mnC;
continue;
}
for(k = 0; k < mnC; k++, pi0++, pi1++)
Error += fabs((*pi0) - (*pi1));
if(Error > RefError)
break;
}
}
return Error;
}
bool Rinpaint::IsValidRect(RECT rc)
{
long i, j, of = mnW - (rc.right-rc.left+1);
BYTE *pm = mpMask + rc.top*mnW + rc.left;
bool bValid = (*pm) != btMASK;
for(j = rc.top; bValid && j <= rc.bottom; j++, pm += of)
{
for(i = rc.left; bValid && i <= rc.right; i++, pm++)
bValid = (*pm) != btMASK;
}
return bValid;
}