如何更快地对图片进行二值化
How to binarize pictures faster
我目前正在尝试对图像进行二值化,以下是我使用的代码:
private static Bitmap PBinary(Bitmap src, int v)
{
int w = src.Width;
int h = src.Height;
Bitmap dstBitmap = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData srcData = src.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData dstData = dstBitmap.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
unsafe
{
byte* pIn = (byte*)srcData.Scan0.ToPointer();
byte* pOut = (byte*)dstData.Scan0.ToPointer();
byte* p;
int stride = srcData.Stride;
int r, g, b;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
p = pIn;
r = p[2];
g = p[1];
b = p[0];
pOut[0] = pOut[1] = pOut[2] = (byte)(((byte)(0.2125 * r + 0.7154 * g + 0.0721 * b) >= v) ? 255 : 0);
pIn += 3;
pOut += 3;
}
pIn += srcData.Stride - w * 3;
pOut += srcData.Stride - w * 3;
}
src.UnlockBits(srcData);
dstBitmap.UnlockBits(dstData);
return dstBitmap;
}
}
我可以成功将图片二值化,但是当我添加一个滑块动态调整参数时,我发现图片显示的时候很卡
private void BinarySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (originalImg!=null)
{
BinaryPara = (int)e.NewValue;
PreviewImg = PBinary(originalImg, BinaryPara);
this.Dispatcher.Invoke(() =>
{
img.Source = null;
img.Source = BitmapToBitmapSource(PreviewImg);
});
}
}
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
public static BitmapSource BitmapToBitmapSource(Bitmap bitmap)
{
IntPtr hBitmap = bitmap.GetHbitmap();
BitmapSource retval;
try
{
retval = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
return retval;
}
再次尝试使用opencvsharp,发现使用opencvsharp动态调整二值化的参数,画面显示非常流畅,没有卡顿。
但是目前我不想使用第三方库。有什么方法可以更快地二值化图像吗?
谢谢大家!
这可能不完全您所需要的,但它应该让您基本了解如何从相同的、未修改的原始像素缓冲区创建不同的二值化位图。
第一步,它从原始 BitmapSource 创建一个每像素 8 位的灰度 BitmapSource,并将其缓冲区复制到一个字节数组。缓冲区中的每个字节都是 0 到 255 范围内的灰度值。诀窍是将其重新用作具有 256 个条目的调色板的索引。
现在每次阈值(即范围为 0 到 255 的滑块的值)发生变化时,都会创建一个包含 256 个条目的调色板,但它只包含 Black
低于阈值的索引, White
以上指数。
private int pixelWidth;
private int pixelHeight;
private byte[] pixelBuffer;
public MainWindow()
{
var source = new BitmapImage(...);
// create grayscale bitmap from original
var grayscale = new FormatConvertedBitmap(
source, PixelFormats.Gray8, null, 0d);
pixelWidth = grayscale.PixelWidth;
pixelHeight = grayscale.PixelHeight;
pixelBuffer = new byte[pixelWidth * pixelHeight];
// get raw pixel buffer
grayscale.CopyPixels(pixelBuffer, pixelWidth, 0);
InitializeComponent();
}
private void UpdateImage(byte threshold)
{
var colors = new Color[256];
for (int i = 0; i < threshold; i++)
{
colors[i] = Colors.Black;
}
for (int i = threshold; i < 256; i++)
{
colors[i] = Colors.White;
}
// set the Source of an Image element declared in XAML
// pixelBuffer is now used as an array of indices into a color palette
image.Source = BitmapSource.Create(
pixelWidth, pixelHeight, 96, 96,
PixelFormats.Indexed8, new BitmapPalette(colors),
pixelBuffer, pixelWidth);
}
private void Slider_ValueChanged(
object sender, RoutedPropertyChangedEventArgs<double> e)
{
UpdateImage((byte)e.NewValue);
}
我目前正在尝试对图像进行二值化,以下是我使用的代码:
private static Bitmap PBinary(Bitmap src, int v)
{
int w = src.Width;
int h = src.Height;
Bitmap dstBitmap = new Bitmap(src.Width, src.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData srcData = src.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
System.Drawing.Imaging.BitmapData dstData = dstBitmap.LockBits(new Rectangle(0, 0, w, h), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
unsafe
{
byte* pIn = (byte*)srcData.Scan0.ToPointer();
byte* pOut = (byte*)dstData.Scan0.ToPointer();
byte* p;
int stride = srcData.Stride;
int r, g, b;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
p = pIn;
r = p[2];
g = p[1];
b = p[0];
pOut[0] = pOut[1] = pOut[2] = (byte)(((byte)(0.2125 * r + 0.7154 * g + 0.0721 * b) >= v) ? 255 : 0);
pIn += 3;
pOut += 3;
}
pIn += srcData.Stride - w * 3;
pOut += srcData.Stride - w * 3;
}
src.UnlockBits(srcData);
dstBitmap.UnlockBits(dstData);
return dstBitmap;
}
}
我可以成功将图片二值化,但是当我添加一个滑块动态调整参数时,我发现图片显示的时候很卡
private void BinarySlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (originalImg!=null)
{
BinaryPara = (int)e.NewValue;
PreviewImg = PBinary(originalImg, BinaryPara);
this.Dispatcher.Invoke(() =>
{
img.Source = null;
img.Source = BitmapToBitmapSource(PreviewImg);
});
}
}
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
public static BitmapSource BitmapToBitmapSource(Bitmap bitmap)
{
IntPtr hBitmap = bitmap.GetHbitmap();
BitmapSource retval;
try
{
retval = Imaging.CreateBitmapSourceFromHBitmap(
hBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap);
}
return retval;
}
再次尝试使用opencvsharp,发现使用opencvsharp动态调整二值化的参数,画面显示非常流畅,没有卡顿。
但是目前我不想使用第三方库。有什么方法可以更快地二值化图像吗?
谢谢大家!
这可能不完全您所需要的,但它应该让您基本了解如何从相同的、未修改的原始像素缓冲区创建不同的二值化位图。
第一步,它从原始 BitmapSource 创建一个每像素 8 位的灰度 BitmapSource,并将其缓冲区复制到一个字节数组。缓冲区中的每个字节都是 0 到 255 范围内的灰度值。诀窍是将其重新用作具有 256 个条目的调色板的索引。
现在每次阈值(即范围为 0 到 255 的滑块的值)发生变化时,都会创建一个包含 256 个条目的调色板,但它只包含 Black
低于阈值的索引, White
以上指数。
private int pixelWidth;
private int pixelHeight;
private byte[] pixelBuffer;
public MainWindow()
{
var source = new BitmapImage(...);
// create grayscale bitmap from original
var grayscale = new FormatConvertedBitmap(
source, PixelFormats.Gray8, null, 0d);
pixelWidth = grayscale.PixelWidth;
pixelHeight = grayscale.PixelHeight;
pixelBuffer = new byte[pixelWidth * pixelHeight];
// get raw pixel buffer
grayscale.CopyPixels(pixelBuffer, pixelWidth, 0);
InitializeComponent();
}
private void UpdateImage(byte threshold)
{
var colors = new Color[256];
for (int i = 0; i < threshold; i++)
{
colors[i] = Colors.Black;
}
for (int i = threshold; i < 256; i++)
{
colors[i] = Colors.White;
}
// set the Source of an Image element declared in XAML
// pixelBuffer is now used as an array of indices into a color palette
image.Source = BitmapSource.Create(
pixelWidth, pixelHeight, 96, 96,
PixelFormats.Indexed8, new BitmapPalette(colors),
pixelBuffer, pixelWidth);
}
private void Slider_ValueChanged(
object sender, RoutedPropertyChangedEventArgs<double> e)
{
UpdateImage((byte)e.NewValue);
}