判断位图是否全黑的有效方法是什么?

发布于 2024-08-27 11:06:49 字数 603 浏览 5 评论 0原文

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

你会推荐什么?大多数位图的尺寸为 1024 x 6000 像素(尽管假设它们始终是该尺寸并不安全)。

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

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

显然,PrintWindow 在 Vista 及更高版本中更好,但在本例中我们仅限于 Server 2003。

I'm wondering if there's a super-efficient way of confirming that an Image object references an entirely black image, so every pixel within the bitmap is ARGB(255, 0, 0, 0).

What would you recommend? Most of these bitmaps will be 1024 x 6000 pixels (although it's not safe to assume they'll always be that size).

I need this because we're having problems with the PrintWindow API. We find that nearly 20% of the time, at least some part of the image will be a black square (a subsequent capture will succeed). My idea to work around this was to call PrintWindow or WM_PRINT with each child window, then piece the whole image of the window back together. If I can find an efficient way of detecting that PrintWindow returned a black image for a particular child window, then I can quickly call PrintWindow again on that capture. It sucks, but PrintWindow is the only method of capturing a window that works on all windows (that I want, anyway) and supports capturing windows that are hidden and/or off-screen.

When PrintWindow fails, it doesn't set an error code or return anything that indicates it failed. When it has this black square problem, it's always an entire window or child window that returns black. So by capturing each child window separately, I can be sure that each of my captures will have worked, providing it contains at least one non-black pixel.

PrintWindow is better in Vista and above, apparently, but in this case we're limited to Server 2003.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

乖乖哒 2024-09-03 11:06:49

我建议您使用 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);

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

I'd recommend you to lock the bitmap in the memory using the LockBits method of the System.Drawing.Bitmap type. This method returns the BitmapData type, from which you can receive a pointer to the locked memory region. Then iterate through the memory, searching for the non-zero bytes (really, faster by scanning for the Int32 or even Int64 values, depending on the platform you use).
Code will look like this:

// 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);

Consider using the unsafe code and direct memory access (using pointers) to improve performance.

北方的韩爷 2024-09-03 11:06:49

这篇文章的第一个答案很棒。我修改了代码以更通用地确定图像是否都是一种颜色(全黑、全白、全洋红色等)。假设您有一个包含 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;
}

The first answer to this post is Awesome. I modified the code to more generically determine if the image is all one color(all black, all white, all magenta, etc...). Assuming you have a bitmap with 4 part color values ARGB, compare each color to the color in the top left if any is different then the image isn't all one color.

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;
}
独自←快乐 2024-09-03 11:06:49

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

If you knew more about the conditions under which the image would be non-black, it would be easier. For example, what do the edges or center of the image look like when it's non-black. Essentially, what you create is heuristic to guess at a non-black image and sample those areas that will give you the quickest read on it. If your heuristic indicates a all-black image, then you can either decide that it is all-black or do a full check of all pixels. That's heavily dependent on your images, though. If you have to be able to distinguish between an all-black image and one containing a single non-black pixel in a random location, you'll have to check them all.

嘿哥们儿 2024-09-03 11:06:49

将位图锁定到内存中并使用按位运算对其进行扫描。不要使用GetPixel之类的;那很慢。

Lock the bitmap into memory and scan it with bitwise operations. Don't use GetPixel and the like; that's slow.

抠脚大汉 2024-09-03 11:06:49

使用 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

Using AForgeNET library (http://www.aforgenet.com) could also be a solution:

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;
}

For ImageStatistics class reference AForge.Imaging.dll in your project.

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

能否归途做我良人 2024-09-03 11:06:49

使用对角线为 3 x 255 的 ColorMatrix 绘制位图,这会将所有非黑色像素吹成纯白色。然后将该位图绘制为宽度为 4 的倍数且格式为 Format24bppRgb 的较小位图。如果位图确实是黑色的话,这会消除 Alpha,减小尺寸并仅留下零。

您必须进行实验,看看可以将位图制作多小,使用只有一个白色像素的示例来查看插值器何时使其消失。我猜你可以走得很远。

Draw the bitmap with a ColorMatrix that has 3 x 255 in the diagonal, that will blow any non-black pixel to pure white. Then draw that bitmap to a smaller one whose width is a multiple of 4 and has the Format24bppRgb format. That eliminates the alpha, reduces the size and leaves only zeros if the bitmap is truly black.

You'll have to experiment to see how small you can make the bitmap, use a sample one that has only one white pixel to see when the interpolator makes it disappear. I'm guessing you can go pretty far.

听你说爱我 2024-09-03 11:06:49

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

To be entirely sure of the image's blackness, you will have to check every pixel, and accessing the pixel data in an unsafe block is likely the fastest way to do it. Of course it's possible to optimize for the non-black case and try to find those earlier, but in the worst case you'll always have to check each and every pixel.

柳若烟 2024-09-03 11:06:49

只是一些随机的想法:

  • 也许你可以 应用一个
    ColorMatrix
    恢复为原来的
    位图(将其完全变成
    黑色的)。然后将结果与
    原来的。
  • 或者创建相同大小的位图
    (填充纯黑色)然后
    与原始位图进行比较。

Just some random thoughts:

  • Maybe you could apply a
    ColorMatrix
    to the original
    bitmap (to completely turn it to
    black). Then compare the result with
    the original.
  • Or create a bitmap of identical size
    (filled with pure black) and then
    compare to the original bitmap.
桃酥萝莉 2024-09-03 11:06:49

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

如果您知道文件类型,您就会了解有关平均压缩比的一些基本知识。您可以很容易地确定文件的尺寸,而无需循环浏览整个文件。

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

此方法需要一点时间来测试并建立一个知识库,以了解全黑图像与非全黑图像的文件大小应如何比较,但它会非常快。

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

A reasonably reliable method would be to check the file size of the image. That is, if the images that are not all black have a relatively normal distribution of colors.

If you know the file type, you know some basic things about the average compression ratios. And you can determine the dimensions of the file pretty readily without cycling through the whole file.

An all black image, of any dimension, using a compressed file format, is going to have a very small file size compared to an image of identical dimensions with a fairly normal distribution of colors.

This method would take a little bit of time to test and build up a knowledge base of just what the file size of an all-black image should be compared to a non-all-black image, but it would be very fast.

If you have many instances where the non-all-black images are pretty close to all-black, obviously then this method would not work.

尽揽少女心 2024-09-03 11:06:49

我有一个打破常规的想法。

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

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

I have an idea that's outside the box.

What about a CRC checksum? You could first check the dimensions of the image, then calculate the checksum and compare that to known (precalculated) checksums of an all-black image of the same dimensions.

EDIT: I doubt this would be any faster than @leonard's method. The only reason it might is if the original file was not a bitmap, but was a compressed image format. That way, the CRC checksum algorithm wouldn't have to uncompress the image before running.

风尘浪孓 2024-09-03 11:06:49
    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();
    }
    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();
    }
冷心人i 2024-09-03 11:06:49

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

One trick that may do it as well is put an indicator pixel somewhere, which always has the same color, unless the image capture fails, in which case everything would be completely black I assume

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文