我想将一个图像的alpha通道与~1000个其他图像进行比较。我的比较方法如下所示:
public static unsafe double Similiarity (Bitmap a, Bitmap b)
{
BitmapData aData = a.LockBits (
new Rectangle (0, 0, a.Width, a.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, a.PixelFormat);
BitmapData bData = b.LockBits (
new Rectangle (0, 0, b.Width, b.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, b.PixelFormat);
int PixelSize = 4;
double sum = 0;
for (int y=0; y<aData.Height; y++) {
byte* aRow = (byte *)aData.Scan0 + (y * aData.Stride);
byte* bRow = (byte *)bData.Scan0 + (y * bData.Stride);
for (int x=0; x<aData.Width; x++) {
byte aWeight = aRow [x * PixelSize + 3];
byte bWeight = bRow [x * PixelSize + 3];
sum += Math.Abs (aWeight - bWeight);
}
}
a.UnlockBits (aData);
b.UnlockBits (bData);
return 1 - ((sum / 255) / (a.Width * a.Height));
}
我认为加速计算的最简单方法是使用PLINQ:
var list = from Bitmap img in imageList.AsParallel where (Similiarity (referenceImage, img) > 0.5) select img;
但在执行时,gdiplus中存在异常:
System.InvalidOperationException: The operation is invalid [GDI+ status: Win32Error]
at System.Drawing.GDIPlus.CheckStatus (Status status) [0x00000] in <filename unknown>:0
at System.Drawing.Bitmap.LockBits (Rectangle rect, ImageLockMode flags, PixelFormat format, System.Drawing.Imaging.BitmapData bitmapData) [0x00000] in <filename unknown>:0
我知道gdiplus必须在不同的进程中执行,但我认为PLINQ正在这样做。我的假设出了什么问题?
答案 0 :(得分:3)
我建议将体重计算与相似性比较分开,如下所示:
public static unsafe byte[,] GetWeight(Bitmap a)
{
BitmapData aData = a.LockBits(new Rectangle(0, 0, a.Width, a.Height), ImageLockMode.ReadOnly, a.PixelFormat);
const int pixelSize = 4;
byte[,] weight = new byte[aData.Width, aData.Height];
for (int y = 0; y < aData.Height; y++)
{
byte* aRow = (byte*)aData.Scan0 + (y * aData.Stride);
for (int x = 0; x < aData.Width; x++)
{
byte aWeight = aRow[x * pixelSize + 3];
weight[x, y] = aWeight;
}
}
a.UnlockBits(aData);
return weight;
}
public static double GetSimilarity(byte[,] weightsA, byte[,] weightsB)
{
double sum = 0;
int height = weightsA.GetLength(1);
int width = weightsA.GetLength(0);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte aWeight = weightsA[x,y];
byte bWeight = weightsB[x, y];
sum += Math.Abs(aWeight - bWeight);
}
}
return 1 - ((sum / 255) / (width * height));
}
呼叫本身将如下所示:
var referenceWeigth = GetWeight(referenceImage);
var list =
imageList
.AsParallel()
.Select(image => new {@Image = image, @Weight = GetWeight(image)})
.Where(imageAndWeight => GetSimilarity(imageAndWeight.Weight, referenceWeigth) > 0.5)
.Select(imageAndWeight => imageAndWeight.Image);
答案 1 :(得分:1)
我认为问题可能是您尝试同时锁定不同线程中的相同位。一个可能的解决方案(因为您只是阅读)应首先从每个位图中提取位,并仅使用这些位并行比较它们。
答案 2 :(得分:1)
我看到的问题是,虽然img
是从列表中获取的,但referenceImage
是在多个线程中使用的相同实例,每个线程都试图锁定它。
考虑将参考图像锁定在外面,然后传递BitmapData。