【问题标题】:C# Comparing 2 Bitmaps with tolerance?C# 比较 2 个带公差的位图?
【发布时间】:2018-07-17 03:30:22
【问题描述】:

我想比较两个位图,但有公差。仅当两个图像完全相同时,我当前的代码才会显示“ciCompareOk”。

两者的大小和形状相同,有时颜色更深或更浅。
这怎么能容忍?

public static CompareResult Compare(Bitmap bmp1, Bitmap bmp2)
{
    CompareResult cr = CompareResult.ciCompareOk;

    //Test to see if we have the same size of image
    if (bmp1.Size != bmp2.Size)
    {
        cr = CompareResult.ciSizeMismatch;
    }
    else
    {
        //Sizes are the same so start comparing pixels
        for (int x = 0; x < bmp1.Width
             && cr == CompareResult.ciCompareOk; x++)
        {
            for (int y = 0; y < bmp1.Height
                         && cr == CompareResult.ciCompareOk; y++)
            {
                if (bmp1.GetPixel(x, y) != bmp2.GetPixel(x, y))
                    cr = CompareResult.ciPixelMismatch;
            }
        }
    }

    return cr;
}

【问题讨论】:

    标签: c# image bitmap compare


    【解决方案1】:

    有几个可能的测试用例可以测试“不同”图像的程度,而您需要采取的“匹配”它们的方法变得越来越难。

    • 最简单的情况是您希望图像大小相同并包含相同的特征,除了您所说的稍微更亮或更暗的像素。例如,如果您要将位图图像保存为 24 位,然后再保存为 8 位(不进行抖动)。这两张图片在同一个地方具有相同的特征,但每个像素的颜色略有不同。
    • 另一种可能性是您拍摄位图图像,并裁剪左侧的 1 行像素以创建第一张图像,并从右侧裁剪一条线以创建第二张图像。
    • 第三种可能性是您保存位图图像,然后在两个方向上将图像大小加倍以创建第二个(因此源中的一个像素变成输出中的四个像素)。我接受您已经有尺寸检查代码来检测这种情况。
    • 第四种可能性(更多是测试用例)是有一个只有白色的大图像和一个只有黑色的大图像。我们希望这会返回 ciPixelMismatch,但不会引发异常。

    如果我们只考虑第一种情况,那么您真正想要的是一个函数来比较两个Colors 并返回一个值来说明它们的差异程度。比较两种颜色的一种简单方法是计算红绿蓝分量之间的毕达哥拉斯距离,例如

    static int CompareColours(Color x, Color y)
    {
        return (int)(Math.Pow((int)x.R - y.R, 2) + Math.Pow((int)x.B - y.B, 2) + Math.Pow((int)x.G - y.G, 2));
    }
    

    这将返回一个介于 0(当 Colors 相同时)和 198608(介于黑白之间,即 Math.Pow(256, 2) * 3)之间的数字。

    有了这个,您可以将函数应用于每对像素(每个图像中的一个)并累积误差。在像素数上平均该误差以确定整个图像的平均像素误差,然后将其与阈值进行比较以确定它们是否“相同”:

    const decimal errorThreshold = 0.0001D
    decimal totalError = 0;
    for (int x = 0; x < bmp1.Width; x++)
    {
        for (int y = 0; y < bmp1.Height; y++)
        {
            totalError += CompareColours(bmp1.GetPixel(x, y), bmp2.GetPixel(x, y)) / 198608D;
        }
    }
    decimal averageError = totalError / (bmp1.Width * bmp1.Height);
    if ( averageError > errorThreshold ) cr = CompareResult.ciPixelMismatch;
    

    (我除以198608D 以避免在添加时出现整数溢出的可能性。averageError 是一个介于 0D 相同的值和 1D 之间的值。)

    我还建议您查看 StackOverflow 上的其他一些问题。虽然这种像素颜色匹配适用于最简单的情况,但不适用于其他情况。如果您需要更复杂的东西,其他问题的答案中给出的方法将很有用:

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多