Exemplar-based image inpainting(+code)

其它的各种经典的图像修复算法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;
}


发布了12 篇原创文章 · 获赞 7 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/yujiang5/article/details/43532049