什么是一种有效的方法来判断位图是否完全是黑色的?

时间:2010-03-31 21:05:01

标签: c# .net image bitmap

我想知道是否有一种超级有效的方法来确认一个Image对象引用一个完全黑色的图像,所以位图中的每个像素都是ARGB(255,0,0,0)。

你会推荐什么?这些位图中的大多数将是1024 x 6000像素(尽管假设它们总是那么大,这是不安全的。)

我需要这个,因为我们遇到了PrintWindow API的问题。我们发现近20%的时间,至少图像的某些部分将是黑色方块(后续捕获将成功)。我想解决这个问题的方法是在每个子窗口中调用PrintWindow或WM_PRINT,然后将窗口的整个图像重新组合在一起。如果我能找到一种有效的方法来检测PrintWindow为特定子窗口返回黑色图像,那么我可以在该捕获上再次快速调用PrintWindow。它很糟糕,但PrintWindow是捕获适用于所有窗口(我想要的)窗口的唯一方法,并支持捕获隐藏和/或屏幕外的窗口。

当PrintWindow失败时,它不会设置错误代码或返回任何指示失败的内容。当它有这个黑色方块问题时,它总是整个窗口或子窗口返回黑色。因此,通过分别捕获每个子窗口,我可以确定每个捕获都有效,只要它包含至少一个非黑色像素。

PrintWindow显然在Vista及更高版本中更好,但在这种情况下,我们仅限于Server 2003.

12 个答案:

答案 0 :(得分:20)

我建议您使用System.Drawing.Bitmap类型的LockBits方法锁定内存中的位图。此方法返回BitmapData类型,您可以从中接收指向锁定内存区域的指针。然后遍历内存,搜索非零字节(通过扫描Int32甚至Int64值,实际上,更快,具体取决于您使用的平台)。 代码如下所示:

// Lock the bitmap's bits.  
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData =bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);

// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;

// Declare an array to hold the bytes of the bitmap.
int bytes  = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];

// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, bytes);

// Scanning for non-zero bytes
bool allBlack = true;
for (int index = 0; index < rgbValues.Length; index++)
    if (rgbValues[index] != 0) 
    {
       allBlack = false;
       break;
    }
// Unlock the bits.
bmp.UnlockBits(bmpData);

考虑使用不安全代码和直接内存访问(使用指针)来提高性能。

答案 1 :(得分:7)

如果您对图像非黑色的条件有更多了解,那就更容易了。例如,当图像的非黑色时,图像的边缘或中心是什么样的。从本质上讲,您创建的是启发式算法,可以猜测非黑色图像,并对那些能够为您提供最快读取的区域进行采样。如果您的启发式指示全黑图像,那么您可以决定它是全黑的还是对所有像素进行全面检查。但这很大程度上取决于你的图像。如果您必须能够在随机位置区分全黑图像和包含单个非黑色像素的图像,则必须全部检查它们。

答案 2 :(得分:7)

这篇文章的第一个答案是真棒。我修改了代码以更一般地确定图像是否都是一种颜色(全黑,全白,所有洋红等等)。假设您有一个包含4个颜色值ARGB的位图,请将每种颜色与左上角的颜色进行比较(如果有任何不同),则图像不是全部一种颜色。

private bool AllOneColor(Bitmap bmp)
{
    // Lock the bitmap's bits.  
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);

    // Get the address of the first line.
    IntPtr ptr = bmpData.Scan0;

    // Declare an array to hold the bytes of the bitmap.
    int bytes = bmpData.Stride * bmp.Height;
    byte[] rgbValues = new byte[bytes];

    // Copy the RGB values into the array.

    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

    bool AllOneColor = true;
    for (int index = 0; index < rgbValues.Length; index++)
    {
        //compare the current A or R or G or B with the A or R or G or B at position 0,0.
        if (rgbValues[index] != rgbValues[index % 4])
        {
            AllOneColor= false;
            break;
        }
    }
    // Unlock the bits.
    bmp.UnlockBits(bmpData);
    return AllOneColor;
}

答案 3 :(得分:6)

Lock the bitmap into memory并按位操作扫描。不要使用GetPixel之类的东西;那很慢。

答案 4 :(得分:3)

使用AForgeNET库(http://www.aforgenet.com)也可以是一个解决方案:

public bool IsNotBlackImage()
{
    Assembly assembly = this.GetType().Assembly;
    var imgTest = new Bitmap(assembly.GetManifestResourceStream("TestImage.png"));
    var imgStatistics = new ImageStatistics(imgTest);             
    return imgStatistics.PixelsCountWithoutBlack != 0;
}

对于ImageStatistics类,在项目中引用AForge.Imaging.dll。

http://code.google.com/p/aforge/source/browse/trunk/Sources/Imaging/ImageStatistics.cs

答案 5 :(得分:2)

使用对角线为3 x 255的ColorMatrix绘制位图,将任何非黑色像素吹为纯白色。然后将该位图绘制为较小的位图,其宽度为4的倍数并具有Format24bppRgb格式。这样可以消除alpha,减小大小,如果位图真的是黑色则只留下零。

您必须尝试查看制作位图的小小,使用只有一个白色像素的示例,以查看插补器何时使其消失。我猜你可以走得很远。

答案 6 :(得分:1)

要完全确定图像的黑度,您必须检查每个像素,并且访问不安全块中的像素数据可能是最快的方法。当然,可以针对非黑色情况进行优化并尝试更早地找到它们,但在最坏的情况下,您将始终必须检查每个像素。

答案 7 :(得分:1)

只是一些随意的想法:

  • 也许你可以apply a ColorMatrix原版 位图(完全转为 黑色)。然后比较结果 原文。
  • 或创建相同大小的位图 (充满纯黑色)然后 与原始位图比较。

答案 8 :(得分:1)

我有一个想法是在盒子外面。

CRC checksum怎么样?您可以先检查图像的尺寸,然后计算校验和,并将其与相同尺寸的全黑图像的已知(预先计算的)校验和进行比较。

编辑:我怀疑这会比@ leonard的方法更快。唯一的原因可能是原始文件不是位图,而是压缩图像格式。这样,CRC校验和算法就不必在运行之前解压缩图像。

答案 9 :(得分:1)

一种相当可靠的方法是检查图像的文件大小。也就是说,如果不是全黑的图像具有相对正常的颜色分布。

如果你知道文件类型,你就会知道一些关于平均压缩率的基本知识。并且您可以非常轻松地确定文件的尺寸,而无需遍历整个文件。

使用压缩文件格式的任何尺寸的全黑图像与具有相当正常颜色分布的相同尺寸的图像相比将具有非常小的文件大小。

这种方法需要花费一点时间来测试和建立一个知识库,知道全黑图像的文件大小应该与非全黑图像进行比较,但它会非常快

如果您有许多非全黑图像非常接近全黑的情况,那么这种方法显然不起作用。

答案 10 :(得分:0)

    private bool AllOneColor(Bitmap bmp)
    {
        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed);
        byte[] rgbValues = new byte[bmpData.Stride * bmpData.Height];
        System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, rgbValues, 0, rgbValues.Length);
        bmp.UnlockBits(bmpData);
        return !rgbValues.Where((v, i) => i % bmpData.Stride < bmp.Width && v != rgbValues[0]).Any();
    }

答案 11 :(得分:0)

也可以做到的一个技巧是将指示像素放在某处,它总是具有相同的颜色,除非图像捕获失败,在这种情况下,一切都将是完全黑色我假设