C#,Lockbits 图像处理与人工制品,改变像素格式
C#, Lockbits image processing with artefacts, changing pixel format
我正在实现自己的功能来对指纹图像进行二值化。在我的方法中,我第一次尝试使用 LockBits
。
你能解释一下吗,为什么我的图像上有很多人工制品?示例:
在左边的图片上,我用 Get/SetPixel
对图像进行了二值化,它工作得很好,但为什么我不能在右边的图像上得到同样好的结果(很多红点)?我是忘记了还是不知道什么?
private Bitmap Binarization(Bitmap tempBmp)
{
int threshold = otsuValue(tempBmp); //calculating threshold with Otsu method
unsafe
{
BitmapData bmpData = tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, tempBmp.PixelFormat);
byte* ptr = (byte*)bmpData.Scan0;
int height = tempBmp.Height;
int width = bmpData.Width * 4;
Parallel.For(0, height, y =>
{
byte* offset = ptr + (y * bmpData.Stride); //set row
for (int x = 0; x < width; x = x + 4)
{
//changing pixel value
offset[x] = offset[x] > threshold ? Byte.MaxValue : Byte.MinValue;
offset[x+1] = offset[x+1] > threshold ? Byte.MaxValue : Byte.MinValue;
offset[x+2] = offset[x+2] > threshold ? Byte.MaxValue : Byte.MinValue;
offset[x+3] = offset[x+3] > threshold ? Byte.MaxValue : Byte.MinValue;
}
});
tempBmp.UnlockBits(bmpData);
}
return tempBmp;
}
同样的历史,当我想从图像中删除一些字节,但问题看起来有点复杂。
为什么它甚至没有进入良好的 "if" 状态?
private Bitmap Binarization(Bitmap tempBmp)
{
int threshold = otsuValue(tempBmp);
unsafe
{
BitmapData bmpData = tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
//Format8bpp, not pixel format from image
byte* ptr = (byte*)bmpData.Scan0;
int height = tempBmp.Height;
int width = bmpData.Width; //i cut "* 4" here because of one channel image
Parallel.For(0, height, y =>
{
byte* offset = ptr + (y * bmpData.Stride); //set row
for (int x = 0; x < width; x++)
{
//changing pixel values
offset[x] = offset[x] > threshold ? Byte.MaxValue : Byte.MinValue;
}
});
tempBmp.UnlockBits(bmpData);
}
return tempBmp;
}
感谢您提出改进我功能的建议。
1) 您使用的算法不保证 b/w 结果。
您正在 测试 RGB 通道 单独 ,因此如果 R > threshold
但 G or B < threshold
它将被打开上,但 G and/or B
不等..
事实上,真正的问题是为什么没有出现更多的工件。必须与源图像有关。
解决方案:添加所有三个通道并与阈值* 3进行比较;然后将所有设置为相同的黑色或白色值!
2) 还有一个问题:你从某处复制的代码盲目地假设图像有 32 bpp。但是虽然您发布的图像是 png
它仍然只有 24 bpp。因此你的代码会做各种有趣的事情..
更新和更正示例:
..
int pixWidth = tempBmp.PixelFormat == IMG.PixelFormat.Format24bppRgb ? 3 :
tempBmp.PixelFormat == IMG.PixelFormat.Format32bppArgb ? 4 : 4;
byte* ptr = (byte*)bmpData.Scan0;
threshold3 = threshold * 3;
int height = tempBmp.Height;
int width = bmpData.Width * pixWidth;
Parallel.For(0, height, y =>
{
byte* offset = ptr + (y * bmpData.Stride); //set row
for (int x = 0; x < width; x = x + pixWidth)
{
//changing pixel value
int v = (offset[x] + offset[x + 1] + offset[x + 2]) > threshold3 ?
Byte.MaxValue : Byte.MinValue;;
offset[x] = (byte)v ;
offset[x+1] = (byte)v;
offset[x+2] = (byte)v;
if (pixWidth == 4) offset[x+3] = 255;
}
});
对于这个问题,我有如下解决方案:
public static bool Offset(Bitmap b_mapa, int threshold)
{
int threshold3 = threshold * 3;
int red, green, blue;
BitmapData bmData = b_mapa.LockBits(new Rectangle(0, 0, b_mapa.Width, b_mapa.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b_mapa.Width * 3;
for (int y = 0; y < b_mapa.Height; ++y)
{
for (int x = 0; x < b_mapa.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
int v = ((int)(blue + green + red) > threshold3 ? (int)255 : (int)0);
p[0] = p[1] = p[2] = (byte)v;
p += 3;
}
p += nOffset;
}
}
b_mapa.UnlockBits(bmData);
return true;
}
此函数使用名为 b_mapa 的共享资源,我们的函数应 return 一个布尔值,具体取决于处理结果。我在这里不使用并行功能,因为当我们使用 LockBits 时,我们直接使用内存,而不使用可以快 1000 倍并且没有必要并行化它的嵌入式函数。如果过滤器是卷积的,那么并行化可以用于更快一点的代码。顺便说一句,当我加载图像时,我将它们转换为位图,以后做所有事情都更简单,不要考虑我有多少位用于颜色表示。
如您所见,位图中的数组值是 BGR 而不是 RGB,只是想让您知道,因为很多人都犯了这个错误并弄乱了颜色。
我正在实现自己的功能来对指纹图像进行二值化。在我的方法中,我第一次尝试使用 LockBits
。
你能解释一下吗,为什么我的图像上有很多人工制品?示例:
在左边的图片上,我用 Get/SetPixel
对图像进行了二值化,它工作得很好,但为什么我不能在右边的图像上得到同样好的结果(很多红点)?我是忘记了还是不知道什么?
private Bitmap Binarization(Bitmap tempBmp)
{
int threshold = otsuValue(tempBmp); //calculating threshold with Otsu method
unsafe
{
BitmapData bmpData = tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, tempBmp.PixelFormat);
byte* ptr = (byte*)bmpData.Scan0;
int height = tempBmp.Height;
int width = bmpData.Width * 4;
Parallel.For(0, height, y =>
{
byte* offset = ptr + (y * bmpData.Stride); //set row
for (int x = 0; x < width; x = x + 4)
{
//changing pixel value
offset[x] = offset[x] > threshold ? Byte.MaxValue : Byte.MinValue;
offset[x+1] = offset[x+1] > threshold ? Byte.MaxValue : Byte.MinValue;
offset[x+2] = offset[x+2] > threshold ? Byte.MaxValue : Byte.MinValue;
offset[x+3] = offset[x+3] > threshold ? Byte.MaxValue : Byte.MinValue;
}
});
tempBmp.UnlockBits(bmpData);
}
return tempBmp;
}
同样的历史,当我想从图像中删除一些字节,但问题看起来有点复杂。
为什么它甚至没有进入良好的 "if" 状态?
private Bitmap Binarization(Bitmap tempBmp)
{
int threshold = otsuValue(tempBmp);
unsafe
{
BitmapData bmpData = tempBmp.LockBits(new System.Drawing.Rectangle(0, 0, tempBmp.Width, tempBmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
//Format8bpp, not pixel format from image
byte* ptr = (byte*)bmpData.Scan0;
int height = tempBmp.Height;
int width = bmpData.Width; //i cut "* 4" here because of one channel image
Parallel.For(0, height, y =>
{
byte* offset = ptr + (y * bmpData.Stride); //set row
for (int x = 0; x < width; x++)
{
//changing pixel values
offset[x] = offset[x] > threshold ? Byte.MaxValue : Byte.MinValue;
}
});
tempBmp.UnlockBits(bmpData);
}
return tempBmp;
}
感谢您提出改进我功能的建议。
1) 您使用的算法不保证 b/w 结果。
您正在 测试 RGB 通道 单独 ,因此如果 R > threshold
但 G or B < threshold
它将被打开上,但 G and/or B
不等..
事实上,真正的问题是为什么没有出现更多的工件。必须与源图像有关。
解决方案:添加所有三个通道并与阈值* 3进行比较;然后将所有设置为相同的黑色或白色值!
2) 还有一个问题:你从某处复制的代码盲目地假设图像有 32 bpp。但是虽然您发布的图像是 png
它仍然只有 24 bpp。因此你的代码会做各种有趣的事情..
更新和更正示例:
..
int pixWidth = tempBmp.PixelFormat == IMG.PixelFormat.Format24bppRgb ? 3 :
tempBmp.PixelFormat == IMG.PixelFormat.Format32bppArgb ? 4 : 4;
byte* ptr = (byte*)bmpData.Scan0;
threshold3 = threshold * 3;
int height = tempBmp.Height;
int width = bmpData.Width * pixWidth;
Parallel.For(0, height, y =>
{
byte* offset = ptr + (y * bmpData.Stride); //set row
for (int x = 0; x < width; x = x + pixWidth)
{
//changing pixel value
int v = (offset[x] + offset[x + 1] + offset[x + 2]) > threshold3 ?
Byte.MaxValue : Byte.MinValue;;
offset[x] = (byte)v ;
offset[x+1] = (byte)v;
offset[x+2] = (byte)v;
if (pixWidth == 4) offset[x+3] = 255;
}
});
对于这个问题,我有如下解决方案:
public static bool Offset(Bitmap b_mapa, int threshold)
{
int threshold3 = threshold * 3;
int red, green, blue;
BitmapData bmData = b_mapa.LockBits(new Rectangle(0, 0, b_mapa.Width, b_mapa.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
int stride = bmData.Stride;
System.IntPtr Scan0 = bmData.Scan0;
unsafe
{
byte* p = (byte*)(void*)Scan0;
int nOffset = stride - b_mapa.Width * 3;
for (int y = 0; y < b_mapa.Height; ++y)
{
for (int x = 0; x < b_mapa.Width; ++x)
{
blue = p[0];
green = p[1];
red = p[2];
int v = ((int)(blue + green + red) > threshold3 ? (int)255 : (int)0);
p[0] = p[1] = p[2] = (byte)v;
p += 3;
}
p += nOffset;
}
}
b_mapa.UnlockBits(bmData);
return true;
}
此函数使用名为 b_mapa 的共享资源,我们的函数应 return 一个布尔值,具体取决于处理结果。我在这里不使用并行功能,因为当我们使用 LockBits 时,我们直接使用内存,而不使用可以快 1000 倍并且没有必要并行化它的嵌入式函数。如果过滤器是卷积的,那么并行化可以用于更快一点的代码。顺便说一句,当我加载图像时,我将它们转换为位图,以后做所有事情都更简单,不要考虑我有多少位用于颜色表示。
如您所见,位图中的数组值是 BGR 而不是 RGB,只是想让您知道,因为很多人都犯了这个错误并弄乱了颜色。