在C#中检查图像是否为空

7
我已经到处查找,但似乎没有一个标准(我能看到的)来检查图像是否为空。在C#中,我有一种方法可以做到这一点,但我很想知道正确的方法是什么,这样每个人将来也会知道。我不打算复制粘贴一堆代码,如果你想让我这样做,我很愿意,但我首先想解释一下如何检查图像是否为空。您需要获取.jpg图像的宽度,例如500像素,然后将其除以2,得到250。然后,您检查位于(250宽度和i高度)位置的每个像素的颜色(其中您遍历图像的高度)。这样做只会垂直地检查图像的中间行像素。它遍历所有像素,检查颜色是否为除白色以外的任何颜色。我这样做是为了避免搜索所有500*height像素,因为您几乎总是会遇到页面中央的颜色。
它正在工作……有点慢……一定有更好的方法吧?您可以将其更改为垂直搜索2/3/4行,以增加发现非空白页面的机会,但这将需要更长时间。
(还要注意,使用图像大小来检查是否包含某些内容在这种情况下不起作用,因为具有两个句子和空白页的大小相近)
解决方案添加后。
资源可帮助实施和理解解决方案。

(请注意,在第一个网站上,所述的Pizelformat实际上是Pixelformat)-我知道这是一个小错误,只是提一下,可能会对某些人造成困惑。

在我实现了加速像素搜索的方法后,速度并没有增加太多。所以我认为我做错了什么。

旧时间= 15.63,用于40张图片。

新时间= 40张图像的15.43秒 我在DocMax的伟大文章引用中看到,代码会“锁定”一组像素。(或者这就是我的理解) 所以我锁定了每个页面的中间行像素。这样做是否正确?
private int testPixels(String sourceDir)
    {
         //iterate through images
        string[] fileEntries = Directory.GetFiles(sourceDir).Where(x => x.Contains("JPG")).ToArray();

        var q = from string x in Directory.GetFiles(sourceDir)
                where x.ToLower().EndsWith(".jpg")
                select new FileInfo(x);

        int holder = 1;
        foreach (var z in q)
        {
            Bitmap mybm= Bitmap.FromFile(z.FullName) as Bitmap;
            int blank = getPixelData2(mybm);
           
            if (blank == 0)
            {
                holder = 0;
                break;
            }
        }
        return holder;
    }

然后这个类

private unsafe int getPixelData2(Bitmap bm)
        {
            BitmapData bmd = bm.LockBits(new System.Drawing.Rectangle((bm.Width / 2), 0, 1, bm.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);

            int blue;
            int green;
            int red;

            int width = bmd.Width / 2;
            for (int y = 0; y < bmd.Height; y++)
            {
                byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);

                blue = row[width * 3];
                green = row[width * 2];
                red = row[width * 1];

                // Console.WriteLine("Blue= " + blue + " Green= " + green + " Red= " + red);
                //Check to see if there is some form of color
                if ((blue != 255) || (green != 255) || (red != 255))
                {
                    bm.Dispose();
                    return 1;
                }
            }
            bm.Dispose();
            return 0;
        }

你有计时拍摄所需时间吗?如果有,那么时间是多少?图片的高度又是多少呢? - matthewr
1
你的实现肯定有问题,因为你的算法看起来是正确的。扫描位图的一列应该不需要任何时间。 - Jonathon Reinhart
32张图片的总时间=00:00:12.9187389高度=6552 宽度=4580 - Ruan
可能有一些外部因素起到了作用吗?例如电脑速度等等? - Ruan
如果您可以发布整个测试代码,我们可能会发现一些问题。从经验来看,使用LockBits读取600x800图像中的所有像素根本不需要时间(如果有的话,也只需几毫秒)。 - Eli Algranti
当您遍历文件时,我猜想使用Parallel.ForEach会大大提高效率。通常对于我来说,调整地图瓦片集大小也是如此。(我知道这很老,但人们仍然阅读旧问题) - rocketsarefast
3个回答

9

如果您可以容忍出错的几率,这种方法似乎不错;在我的案例中,我做了类似的事情,尽管我总是有一个可视化确认来处理错误。

至于性能问题,关键的未解之谜是如何获取要测试的像素。如果您使用 Bitmap.GetPixel,那么您就会遇到性能问题。(在Google中搜索 "Bitmap.GetPixel slow",就会看到很多讨论。)

较好的性能将来自一次性获取所有像素,然后对它们进行循环。我个人喜欢 Bob Powell's LockBits discussion,因为它清晰且完整。使用这种方法,根据您的性能需求,检查所有像素可能合理。


你说得很对,我正在使用Bitmap.GetPixel。 谢谢,我会尝试这个解决方案,并在这里发布我的发现,以便有一个完整的解决方案和“我敢说”的标准来检查图像。 - Ruan
所以我实现了这个解决方案。唯一的问题是,时间几乎相同。旧时间= 40张图像的15.63。新时间= 40张图像的15.43。我将在我的问题上面额外说明我做了什么。 - Ruan
它确实可以加快速度,但只是一点点。我认为在我拥有的数据量下很难看到增加。谢谢。 - Ruan
很高兴它对你有所帮助。也许计时执行的各个部分会有所帮助,以查看哪些是缓慢的:是BitMap.FromFileLockBits调用、for循环还是其他什么。在15秒的处理时间内,使用带有诊断开始/停止调用的Stopwatch实例应该足够了。 - DocMax

2
如果你正在使用System.Drawing.Bitmap,你可以通过以下方式加快速度(相当大的提升):
  1. 不要使用GetPixel来访问像素,使用LockBits和UnlockBits将图像位图复制到常规内存中。有关用法,请参见MSDN文档上的示例。
  2. 不要在循环中调用Width、Height或Size属性。调用Size一次,在局部变量中存储值,并在循环中使用这些值。
注意:
  1. 当使用System.Drawing.Bitmap时,你的图像可能在设备内存中,访问它可能需要时间。
  2. 我不记得将图像加载到Bitmap中是否已将其转换为RGB格式,因为其他格式更难处理,但如果不是这种情况,你可以创建一个与原始图像大小相同的RGB Bitmap,获取它的Graphic对象(Graphics.FromImage),并使用DrawImage在RGB位图中绘制原始图像。
编辑:被DocMax抢先了。
无论如何,为了提高速度,你也可以尝试使用其他库,如优秀的FreeImage,其中包括C#包装器。

0

将图像缩放为1x1,然后检查一个像素

new Bitmap(previousImage, new Size(1, 1));


这很棒,但不幸的是,它在我尝试过的大多数图像上都不起作用(一组带有ARGB颜色值的64x56个瓷砖)。即使源包含某些图像数据,单个像素经常为0,0,0,0。 - PeteB
我进行了进一步的测试,使用2x2并检查所有四个像素。这样做效果更好,但当瓦片中有少量内容时仍会报告为空。 - PeteB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接