C#原生图片模板匹配
C# native Image template match
我在这里遇到了一些性能问题。
我的图像处理教授让我在没有任何库的情况下进行模板匹配,所以我用 c# 做了......它做得很好,但非常非常慢
在一张 500x500 的图像中找到一个 80x80 的模板,这需要几分钟的时间,而且我的 cpu
的使用率不超过 20%
我是不是做错了什么?
这是我的代码,所有图片都是灰色的。
public Task<Point[]> MatchImage(Bitmap orig, Bitmap template, int limit = 30)
{
Task<Point[]> tsk =
new Task<Point[]>(() =>
{
Point p;
List<Point> l = new List<Point>();
int oh = orig.Height, ow = orig.Width;
int th = template.Height, tw = template.Width;
for (int x = 0; x < oh - th; x++)
for (int y = 0; y < ow - tw; y++)
{
p = new Point(x, y);
if (sumBitArea(orig, template, p) <= limit)
l.Add(p);
}
return l.ToArray();
});
tsk.Start();
return tsk;
}
/// <summary>
/// Soma os pixels de uma determinada imagem
/// PS PARA USAR MANDE A IMAGEM RECORTADA !
/// SE MANDAR INTEIRA VAI RETORNAR O VALOR TOTAL.
/// USEM A GRAPHCS E UM RECTANGLE !
/// </summary>
/// <param name="image">treixo da imagem original</param>
/// <param name="template">imagem template</param>
/// <param name="p">ponto inicial de referencia</param>
/// <returns>o valor da soma</returns>
private int sumBitArea(Bitmap image, Bitmap template, Point p)
{
int value = 0;
int height = p.X + template.Height;
int width = p.Y + template.Width;
for (int x = p.X, xt = 0; x < height; x++, xt++)
for (int y = p.Y, yt = 0; y < width; y++,yt++)
{
int tmp = Math.Abs(image.GetPixel(y, x).R - template.GetPixel(yt, xt).R);
value += tmp;
}
return value;
}
解决方案 - 解决了锁定位
这里是解决代码
public Task<Point[]> MatchImage(Bitmap orig, Bitmap template, int limit = 30)
{
Task<Point[]> tsk =
new Task<Point[]>(() =>
{
BitmapData lockedOrig = orig.LockBits(
new Rectangle(0, 0, orig.Width, orig.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
//bloqueando a leitura da memoria em que a imagem nova sera colocada
BitmapData lockedTemplate = template.LockBits(
new Rectangle(0, 0, template.Width, template.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
Point p;
List<Point> l = new List<Point>();
int oh = orig.Height, ow = orig.Width;
int th = template.Height, tw = template.Width;
for (int y = 0; y < oh - th; y++)
{
for (int x = 0; x < ow - tw; x++)
{
p = new Point(x, y);
if (sumBitArea(lockedOrig, lockedTemplate, p) <= limit)
l.Add(p);
}
}
orig.UnlockBits(lockedOrig);
template.UnlockBits(lockedTemplate);
return l.ToArray();
});
tsk.Start();
return tsk;
}
unsafe private int sumBitArea(BitmapData image, BitmapData template, Point p)
{
int value = 0;
int pixelSize = 3;
int height = p.Y + template.Height;
int width = p.X + template.Width;
int x = p.X, xt = 0;
int y = p.Y, yt = 0;
byte* tRow;
byte* oRow;
for (yt = 0 ; y < height; y++, yt++)
{
//criando um ponteiro para a imagem original
oRow = (byte*)image.Scan0 + (y * image.Stride);
//criando um ponteiro para a nova imagem
tRow = (byte*)template.Scan0 + (yt * template.Stride);
for (xt = 0; x < width; x++, xt++)
{
int tmp = Math.Abs((byte)oRow[x * pixelSize + 2] - (byte)tRow[xt * pixelSize + 2]);
value += tmp;
}
}
return value;
}
您是否尝试过锁定位或使用 FastBitmap 实现?您应该能够找到大量这样的实现。它应该加速多次获取像素。您只需要妥善处理它即可。
我在这里遇到了一些性能问题。 我的图像处理教授让我在没有任何库的情况下进行模板匹配,所以我用 c# 做了......它做得很好,但非常非常慢 在一张 500x500 的图像中找到一个 80x80 的模板,这需要几分钟的时间,而且我的 cpu
的使用率不超过 20%我是不是做错了什么?
这是我的代码,所有图片都是灰色的。
public Task<Point[]> MatchImage(Bitmap orig, Bitmap template, int limit = 30)
{
Task<Point[]> tsk =
new Task<Point[]>(() =>
{
Point p;
List<Point> l = new List<Point>();
int oh = orig.Height, ow = orig.Width;
int th = template.Height, tw = template.Width;
for (int x = 0; x < oh - th; x++)
for (int y = 0; y < ow - tw; y++)
{
p = new Point(x, y);
if (sumBitArea(orig, template, p) <= limit)
l.Add(p);
}
return l.ToArray();
});
tsk.Start();
return tsk;
}
/// <summary>
/// Soma os pixels de uma determinada imagem
/// PS PARA USAR MANDE A IMAGEM RECORTADA !
/// SE MANDAR INTEIRA VAI RETORNAR O VALOR TOTAL.
/// USEM A GRAPHCS E UM RECTANGLE !
/// </summary>
/// <param name="image">treixo da imagem original</param>
/// <param name="template">imagem template</param>
/// <param name="p">ponto inicial de referencia</param>
/// <returns>o valor da soma</returns>
private int sumBitArea(Bitmap image, Bitmap template, Point p)
{
int value = 0;
int height = p.X + template.Height;
int width = p.Y + template.Width;
for (int x = p.X, xt = 0; x < height; x++, xt++)
for (int y = p.Y, yt = 0; y < width; y++,yt++)
{
int tmp = Math.Abs(image.GetPixel(y, x).R - template.GetPixel(yt, xt).R);
value += tmp;
}
return value;
}
解决方案 - 解决了锁定位
这里是解决代码
public Task<Point[]> MatchImage(Bitmap orig, Bitmap template, int limit = 30)
{
Task<Point[]> tsk =
new Task<Point[]>(() =>
{
BitmapData lockedOrig = orig.LockBits(
new Rectangle(0, 0, orig.Width, orig.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
//bloqueando a leitura da memoria em que a imagem nova sera colocada
BitmapData lockedTemplate = template.LockBits(
new Rectangle(0, 0, template.Width, template.Height),
ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
Point p;
List<Point> l = new List<Point>();
int oh = orig.Height, ow = orig.Width;
int th = template.Height, tw = template.Width;
for (int y = 0; y < oh - th; y++)
{
for (int x = 0; x < ow - tw; x++)
{
p = new Point(x, y);
if (sumBitArea(lockedOrig, lockedTemplate, p) <= limit)
l.Add(p);
}
}
orig.UnlockBits(lockedOrig);
template.UnlockBits(lockedTemplate);
return l.ToArray();
});
tsk.Start();
return tsk;
}
unsafe private int sumBitArea(BitmapData image, BitmapData template, Point p)
{
int value = 0;
int pixelSize = 3;
int height = p.Y + template.Height;
int width = p.X + template.Width;
int x = p.X, xt = 0;
int y = p.Y, yt = 0;
byte* tRow;
byte* oRow;
for (yt = 0 ; y < height; y++, yt++)
{
//criando um ponteiro para a imagem original
oRow = (byte*)image.Scan0 + (y * image.Stride);
//criando um ponteiro para a nova imagem
tRow = (byte*)template.Scan0 + (yt * template.Stride);
for (xt = 0; x < width; x++, xt++)
{
int tmp = Math.Abs((byte)oRow[x * pixelSize + 2] - (byte)tRow[xt * pixelSize + 2]);
value += tmp;
}
}
return value;
}
您是否尝试过锁定位或使用 FastBitmap 实现?您应该能够找到大量这样的实现。它应该加速多次获取像素。您只需要妥善处理它即可。