我需要一种非常快速的方法来检测图像是否为空。在我的情况下,所有像素都是白色和透明的。 图像是png的。我当前的方法是将它们加载到内存位图并检查每个像素值,但这是慢的方法。 有更有效的方法吗?
这是我目前的代码:
'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
答案 0 :(得分:2)
您可以将GDI +位图“映射”到固定的byte[]
(或int[]
或甚至uint[]
),然后直接使用该数组来读取和写入ARGB位图数据。
以下CodeProject文章介绍了如何执行此操作:http://www.codeproject.com/KB/GDI-plus/pointerlessimageproc.aspx
答案 1 :(得分:1)
使用Marshal.ReadInt64()将立即为您提供x 8速度提升。注意过度宽度,你需要ReadByte()来获取扫描线中的最后几个像素。在帮助器程序集中用C#编写此代码可能是最快的修复,它允许您使用指针。
答案 2 :(得分:1)
当然,将图像除以设定的量,对于每个组,启动一个新线程仅检查非空白像素(例如,不是白色/透明)。
仅在找到非空像素时才创建触发(并设置标志)的事件。让每个线程检查这个标志(基本上是一个while循环)。
答案 3 :(得分:1)
使用C#(我知道你的样本是在VB.Net中),你可以使用不安全的方法并快速访问你的位图:
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;
如果您不能使用C#,那么最快的方法是使用Marshal.Copy作为块:
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
答案 4 :(得分:0)
最有效的方法是手动解析文件,直接跳转到数据流并读取压缩的zLib流,直到找到非白色和透明的像素。
取决于图片的来源以及它们使用各种PNG“特殊”(过滤器,特殊调色板......)的程度,这可能需要付出很多努力,因此将“高效”视为“大量工作,但很快“。
有用的资源是PNG datastream specification和ZLIB specification(或者更简单的zLib库)。
答案 5 :(得分:0)
Image Magick有一个c#包装器:
http://sourceforge.net/projects/imagemagickapp/
使用ImageMagik CLI的识别功能,如下所示:
http://www.imagemagick.org/script/identify.php
使用命令:
identify -format "%#" source.png
如果颜色数为1,则表示空白页。
您也可以使用命令:
identify -verbose source.png
空白图像的标准偏差,偏斜和峰度为0。
答案 6 :(得分:0)
我会使用GDI +将PNG文件转换为BMP并将其保存到临时位置。然后我将利用其未压缩的特性,在标题之外寻找像素,准备一个中等大小(比如16384 B)的零字节的临时字符串,然后读取16384B块直到文件结尾,将它们与临时字符串进行比较。由于Windows XP及更高版本在读取文件末尾时给出零字节,因此无需对末尾进行特殊处理,只需在最后一次迭代时读取一些内容即可。 确保位图以32位模式保存,否则将无法检测到黑色不透明像素。此模式还将处理任何调色板和其他怪癖,以便您甚至可以寻找固定的后标题偏移。