使用基于局部二值模式的纹理修复的图像修复技术





5.00/5 (9投票s)
使用局部二值模式轻松进行图像修复
引言
首先,图像修复并非新鲜事物。它是一种从图像中移除对象和恢复图像的技术。您标记的图像中的任何区域都应由相邻像素或像素块替换,从而使整个图像看起来均匀。
然而,该方向上的一些论文将问题呈现得比应有的更复杂。我在互联网上搜索了一些适合用C#编写的图像修复技术。但我未能找到一个。因此,我编写了这种简单而有效的图像修复技术。它完全用C#代码编写。我们没有使用任何外部库或功能。所以代码可能比较粗糙且未优化。但您仍然可以对此图像修复技术有一个大致的了解。
背景
Bertalmio提出了第一个已知的基于拉普拉斯扩散的图像修复技术。还有几种其他方法,如Criminasi提出的FOE、基于示例的方法等。基本上,图像修复技术分为两类:结构修复和纹理修复。
结构是指所有像素颜色相同的图案,例如清晰的天空或墙壁的图像。而纹理则是由一组像素形成的图案,其中块内的像素颜色不同,但整体块代表一个明确的图案。例如,我们心脏的图像或地板的瓷砖。
使用代码
在这项工作中,我们将尝试实现一种简单的图像修复技术,该技术基于LBP的纹理表示和测量,同时进行结构和纹理修复。
图1:一般情况下的图像修复问题
观察这张图。假设中心像素需要修复。您将如何做?您不能用绿色或蓝色填充它。人类的视角是将其涂成红色块。
您是如何理解这个想法的?它很简单。您环顾四周的相邻像素,确定出现频率最高的像素,然后将其建议用于修复。
因此,从人类的角度来看,我们可以将图像修复问题简化为:
1)找到需要修复的像素集。假设图像Im与原始图像Is大小相同,但在需要填充的每个像素位置为1,否则为0。
2)遍历Im中的每个像素,检查当前像素是否为1,如果是,则原始图像中相同位置的像素需要填充。定义一个大小为N的块。观察第一张图。这里块大小为一。因为中心像素的每个方向上都有一个像素。
3)通过收集待修复像素的所有邻域的像素,从Is中检索一个(2N+1)*(2N +1)大小的子图像。排除缺陷像素或待修复像素(中心那个)。
现在,这些像素中的任何一个都可以替换中心像素。但问题是哪个?所以替换一个像素并检查同质性。同质性可以通过中心像素被邻域像素替换时,所有邻域像素与中心像素的平均颜色距离来计算。
那么,我们先来看看如何生成掩码。
Bitmap ObtainMask(Bitmap Src)
{
//1. Take a local image bmp which we will use for mark scanning
Bitmap bmp = (Bitmap)Src.Clone();
int NumRow = pictureBox1.Height;
int numCol = pictureBox1.Width;
//2. Mask image is initially an image of the same size of source
Bitmap mask = new Bitmap(pictureBox1.Width, pictureBox1.Height);// GRAY is the resultant matrix
//3. We will define a structuring element of size bnd for dilating the mask
// You can obtain this mask thickening variable from user. but 3 is standard for 256x256 images
int bnd=3;
//4. Loop through the rows and columns of images
for (int i = 0; i < NumRow; i++)
{
for (int j = 0; j < numCol; j++)
{
Color c = bmp.GetPixel(j, i);// Extract the color of a pixel
int rd = c.R; int gr = c.G; int bl = c.B;// extract the red,green, blue components from the color.
// Note that you had painted the region with red. But image is resized, which is resampling
// in resizing process, the color tend to get changed. so we will look for more redish pixels rater than red.
//you can also update this by picking the mask color through mouse click.
if ((rd > 220) && (gr < 80) && (bl < 80))
{
Color c2 = Color.FromArgb(255, 255, 255);
//5. set the marked pixel od Is as white in Im
mask.SetPixel(j, i, c2);
//6. Perform dilation( extending the mask area to nullify edge effect
for (int ib = i - bnd; ib < i + bnd; ib++)
{
for (int jb = j - bnd; jb < j + bnd; jb++)
{
try
{
// see we are making the boundary of pixels also white
mask.SetPixel(jb, ib, c2);
}
catch (Exception ex)
{
}
}
}
}
else
{
//7. all other pixels are black
Color c2 = Color.FromArgb(0, 0, 0);
mask.SetPixel(j, i, c2);
try
{
}
catch (Exception ex)
{
}
}
}
}
return mask;
}
最简单的相似性度量之一是计算差异,即像素的R、G、B差值之和。但正如我们所定义的,纹理可能以明确的方式包含不同颜色的像素。如果您检查颜色,它就是简单的结构修复。所以我们将进一步定义一种使用局部二值模式(LBP)的纹理模式。此外,这种纹理表示是从灰度图像中提取的。所以我们需要先将图像转换为灰度。
这是LBP的算法:
1)定义一个窗口大小W
2)扫描图像中的每个像素,提取其WxW的邻域。
3)检查邻域像素颜色是否>中心像素,如果是,则在矩阵中放入1,否则为0。
4)这样我们就得到一个“1”和“0”的数组。将其转换为二进制,然后将二进制转换为十进制,并必须替换中心像素。请记住,W值越大,数值越大。例如,对于W=4,您将得到一个16位的二进制数。但灰度图像只能包含8位颜色。因此,一旦提取了整个图像的LBP,就需要对其进行归一化。
5)归一化计算为(包含局部二值模式的图像)*255/(图像中的最大值)。
重要的一点是,如果您取W=1,则生成的图像将是清晰的边缘检测图像。因此,您也可以选择使用此理论从图像中提取边缘。
Bitmap LBP(Bitmap srcBmp,int R)
{
// We want to get LBP image from srcBmp and window R
Bitmap bmp = srcBmp;
//1. Extract rows and columns from srcImage . Note Source image is Gray scale Converted Image
int NumRow = srcBmp.Height;
int numCol = srcBmp.Width;
Bitmap lbp = new Bitmap(numCol, NumRow);
Bitmap GRAY = new Bitmap(pictureBox1.Width, pictureBox1.Height);// GRAY is the resultant matrix
double[,] MAT = new double[numCol, NumRow];
double max = 0.0;
//2. Loop through Pixels
for (int i = 0; i < NumRow; i++)
{
for (int j = 0; j < numCol; j++)
{
// Color c1=Color.FromArgb(0,0,0);
MAT[j, i] = 0;
//lbp.SetPixel(j, i,c1) ;
//define boundary condition, other wise say if you are looking at pixel (0,0), it does not have any suitable neighbors
if ((i > R) && (j > R) && (i < (NumRow - R)) && (j < (numCol - R)))
{
// we want to store binary values in a List
List<int> vals = new List<int>();
try
{
for (int i1 = i - R; i1 < (i + R); i1++)
{
for (int j1 = j - R; j1 < (j + R); j1++)
{
int acPixel = srcBmp.GetPixel(j, i).R;
int nbrPixel = srcBmp.GetPixel(j1, i1).R;
// 3. This is the main Logic of LBP
if (nbrPixel > acPixel)
{
vals.Add(1);
}
else
{
vals.Add(0);
}
}
}
}
catch (Exception ex)
{
}
//4. Once we have a list of 1's and 0's , convert the list to decimal
// Also for normalization purpose calculate Max value
double d1 = Bin2Dec(vals);
MAT[j, i] = d1;
if (d1 > max)
{
max = d1;
}
}
//////////////////
}
}
//5. Normalize LBP matrix MAT an obtain LBP image lbp
lbp = NormalizeLbpMatrix(MAT, lbp, max);
return lbp;
}
归一化代码如下:
Bitmap NormalizeLbpMatrix(double[,]Mat,Bitmap lbp,double max)
{
int NumRow = lbp.Height;
int numCol = lbp.Width;
for (int i = 0; i < NumRow; i++)
{
for (int j = 0; j < numCol; j++)
{
// see the Normalization process of dividing pixel by max value and multiplying with 255
double d = Mat[j, i] / max;
int v = (int)(d * 255);
Color c = Color.FromArgb(v, v, v);
lbp.SetPixel(j, i, c);
}
}
return lbp;
}
好了,我们现在有了源图像Is,LBP图像Ib和掩码图像Im。
我们将转到我们的主算法,并修改该算法中的第三点如下:
1)从掩码中提取位置(x,y)处的像素p,检查它是否为“1”。
如果是,则从Ib中提取一个以p为中心、区域为(x-B:x+B,y-B:y+B)的子图像S,其中B是块大小。
比较S中的每个像素与S中的所有其他像素。找到像素Ps,其差值最小。所以像素Ps就是其颜色需要放入p的像素。
在Is中映射Ps,提取Is(ps)并将其放在Is(p)的位置。对整个图像继续执行此操作。您就完成了!
那么,让我们看看Inpaint函数。
void Inpaint()
{
//1. Obtain Mask
Bitmap mask = (Bitmap)pictureBox4.Image;
int NumRow = pictureBox1.Height;
int numCol = pictureBox1.Width;
//2. Define resultant image same as source region. As algo proceeds, we need to replace the marked blocks
Rslt = (Bitmap)pictureBox1.Image;// GRAY is the resultant matrix
Bitmap src = (Bitmap)pictureBox3.Image;
//3. Define the block for Inpainting purpose
int Blk = int.Parse(textBox2.Text);
for (int i = 0; i < NumRow; i++)
{
for (int j = 0; j < numCol; j++)
{
Color c = mask.GetPixel(j, i);// Extract the color of a pixel from mask (p)
int rd = c.R; int gr = c.G; int bl = c.B;// extract the red,green, blue components from the color.
int ti = -1, tj = -1;
double dst = 99999999999999.0;
//4. check if the pixel is white ( that means marked pixel in source)
if ((rd == 255) && (gr == 255) && (bl == 255))
{
//5. Generate the neighbors List
List<int[]> Nbrs = new List<int[]>();
for (int i1 = i - Blk; i1 < i + Blk; i1++)
{
for (int j1 = j; j1 < j + Blk; j1++)
{
try
{
Color c1 = src.GetPixel(j1, i1);// Extract the color of a pixel from LBP image
int rd1 = c1.R; int gr1 = c1.G; int bl1 = c1.B;// extract the red,green, blue components from the color.
Color c2 = mask.GetPixel(j1, i1);// Extract the color of a mask pixel
// remember list can not contain a pixel which also is within mask region
int rd2 = c2.R; int gr2 = c2.G; int bl2 = c2.B;// extract the red,green, blue components from the color.
// form the list with non marked pixel
if ((rd2 == 0) && (gr2 == 0) && (bl2 == 0))
{
// add first pixel as it is, as there is nothing to compare for
if (Nbrs.Count == 0)
{
Nbrs.Add(new int[] { i1, j1 });
}
else
{
double d = 0;
//6. calculate mean distance of the current pixel with all neighbors
for (int k = 0; k < Nbrs.Count; k++)
{
int[] pos = Nbrs[k];
d = d + Math.Abs(Rslt.GetPixel(pos[1], pos[0]).R - rd2);
}
d = d / (double)Nbrs.Count;
// 7. update ps value which will be used to replace p in original image
if (d < dst)
{
dst = d;
ti = i1;
tj = j1;
}
}
}
}
catch (Exception ex)
{
}
}
}
//8. replace p with ps in the actual image
Rslt.SetPixel(j, i, Rslt.GetPixel(tj, ti));
System.Threading.Thread.Sleep(10);
}
else
{
}
}
}
s = "DONE";
}
请注意,修复过程通常需要很长时间。较大的图像尺寸真的可能需要数小时才能完成。因此,您需要从一个线程调用该函数,并定期更新结果图片框显示。这有助于您看到修复正在进行。
这就是通过迭代进行修复的方式:
关注点
我在这里实现的距离度量很简单。但是,如果改变距离度量,可以获得更好的结果。d=(Cnm+Cmn+1)/(Cmm+Cnn+1),其中Cnm是修复中第n个像素相对于第m个像素的成本,将是一个不错的选择。欢迎提出您的建议。
历史
版本V1.0 发布于2012年9月16日