efficient way to detect if an image is empty
I need a very fast method to detect if an image is empty. Im my case then all pixels are white and transparant. The images are png's. My current method is to load them in a memory bitmap and check each pixel value, but this is way to slow. Is there a more efficient w开发者_如何学Pythonay?
This is my current code:
'Lock the bitmap bits.
Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(rectBmp, _
Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat)
Try
Dim x As Integer
Dim y As Integer
For y = 0 To bmpData.Height - 1
For x = 0 To bmpData.Width - 1
If System.Runtime.InteropServices.Marshal.ReadByte(bmpData.Scan0, (bmpData.Stride * y) + (4 * x) + 3) <> 0 Then
Return True
Exit For
End If
Next
Next
Finally
bmp.UnlockBits(bmpData)
End Try
You can "map" a GDI+ bitmap onto a pinned byte[]
(or int[]
or even uint[]
) and by this directly use the array to read and write the ARGB bitmap data.
The following CodeProject article explains how to do it: http://www.codeproject.com/KB/GDI-plus/pointerlessimageproc.aspx
Sure, divide the image by a set amount, for each set start a new thread to only check for non blank pixels (eg not white/transparent).
Create an event that fires (and sets a flag) only if a non empty pixel is found. Have each thread to check this flag (basically a while loop).
Using Marshal.ReadInt64() will give you an immediate x 8 speed boost. Watch out for overshooting the Width though, you'll need ReadByte() for the last few pixels in a scan line. Writing this code in C# in a helper assembly is probably the quickest fix, it allows you to use a pointer.
Using C# (I know you sample is in VB.Net), you can use unsafe and get very fast access to your bitmap:
const int ALPHA_PIXEL = 3;
const int RED_PIXEL = 2;
const int GREEN_PIXEL = 1;
const int BLUE_PIXEL = 0;
try
{
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
int bytesPerPixel = (bmp.PixelFormat == PixelFormat.Format24bppRgb ? 3 : 4);
int stride = bmData.Stride;
unsafe
{
byte* pixel = (byte*)(void*)bmData.Scan0;
for (int y = 0; y < bmp.Height; y++)
{
int yPos = y * stride;
for (int x = 0; x < bmp.Width; x++)
{
int pos = yPos + (x * bytesPerPixel);
if (pixel[pos + RED_PIXEL] != 255 || pixel[pos + GREEN_PIXEL] != 255 || pixel[pos + BLUE_PIXEL] != 255 ||
pixel[pos + ALPHA_PIXEL] != 0)
{
return true;
}
}
}
}
}
finally
{
bmp.UnlockBits(bmData);
}
return false;
If you can't use C#, then the fastest way would be to use Marshal.Copy as a block:
Dim bmData As BitmapData = bmp.LockBits(New Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat)
Try
Dim bytesPerPixel As Integer = If(bmp.PixelFormat = PixelFormat.Format24bppRgb, 3, 4)
Dim stride As Integer = bmData.Stride
Dim imageSize As Integer = stride * bmp.Height
Dim pixel As Byte()
ReDim pixel(imageSize)
Marshal.Copy(bmData.Scan0, pixel, 0, imageSize)
For y As Integer = 0 To bmp.Height - 1
Dim yPos As Integer = y * stride
For x As Integer = 0 To bmp.Width - 1
Dim pos As Integer = yPos + (x * bytesPerPixel)
If pixel(pos + RED_PIXEL) <> 255 OrElse pixel(pos + GREEN_PIXEL) <> 255 OrElse pixel(pos + BLUE_PIXEL) <> 255 OrElse pixel(pos + ALPHA_PIXEL) <> 0 Then
Return
End If
Next
Next
Finally
bmp.UnlockBits(bmData)
End Try
Return
The most efficient way would be to manually parse the file, directly jump to the datastream and read the compressed zLib stream until you find a pixel that is not white and transparent.
Depending on where the pictures come from and how much they make use of various PNG "specials" (filters, special palette...), this can be much effort, so read "efficient" as "a lot of work, but very fast".
Useful resources for this are the PNG datastream specification and the ZLIB specification (or, much easier, a zLib library).
There is a c# wrapper for Image Magick:
http://sourceforge.net/projects/imagemagickapp/
Use the identify feature of ImageMagik CLI as given here:
http://www.imagemagick.org/script/identify.php
With command:
identify -format "%#" source.png
If the number of colors is 1, you have a blank page.
You can also use the command:
identify -verbose source.png
The standard deviation, skew and kurtosis will be 0 for a blank image.
I would use GDI+ to convert the PNG file to BMP and save it to a temporary location. Then I would leverage its uncompressed nature, seek beyond the header into the pixels, prepare a medium-sized (say 16384 B) temporary string full of zero bytes and then read 16384B chunks until the end of file, comparing them to the temporary string. Since Windows XP and higher gives zero bytes when reading past end of file, no special treatment of the end is needed, just read a bit over the end with your last iteration. Make sure the bitmap is saved in 32-bit mode or black opaque pixels won't be detected. This mode will also take care of any palettes and other quirks so that you can even seek to a fixed post-header offset.
精彩评论