【问题标题】:Detect uniform images that (most probably) are not photographs检测(很可能)不是照片的统一图像
【发布时间】:2014-03-12 14:01:51
【问题描述】:

看看这两个示例图像:

我希望能够在大量照片和类似图像中识别这些类型的图像。 photograph 我指的是人物、风景、动物等的照片。

我不介意某些照片被错误地识别为这些uniform images,但我真的不想通过将它们识别为照片来“错过”其中一些。

我想到的最简单的事情是逐个像素地分析图像以找到最高和最低的 R、G、B 值(每个通道分别)。如果最低值和最高值之间的差异很大,那么颜色变化很大,这样的图像很可能是一张照片。

其他想法是以类似的方式分析每个像素的色调值。问题在于,在 HSL 模型中,橙红色和粉红色在顺时针方向看时相差大约 350 度,在逆时针方向看时相差 10 度。所以我不能只比较每个像素的色调分量,因为我会得到一些奇怪的结果。

此外,还有一个噪音问题——一个白色或黑色像素会破坏这样的测试。因此,如果只有少数像素具有这样的极端值,我将需要以某种方式排除极端值。但在这一点上,它变得越来越复杂,我觉得这不是最好的方法。

我也在考虑将对比度提高到最大值,然后像上面描述的 RGB 那样运行测试。它可能会使事情变得更容易,但仍然有一两个异常像素会破坏测试。遇到这种情况如何处理?

我不介意运行几种涵盖不同图像类型的不同算法。但请注意,我正在处理来自数码相机的图像,因此 6MP、12MP 甚至 16MP 都很常见。因此,不需要运行计算密集型算法。我要处理成百上千张图像,并且只有有限的 CPU 资源用于图像处理。可以说每张大图像一两秒是我能接受的最大值。

我知道,例如蓝天的照片可能会引发误报,但这没关系。误报总比漏报好。

【问题讨论】:

  • 你研究过纹理分类吗?您可能会发现,对于您的“统一”图像,一个或多个纹理分类器将在整个图像中产生一致的度量。如果图像的 > N% 由给定纹理组成,那么您可能可以将其归类为“均匀”。这个建议太随意了,但也许会有所帮助。局部二进制模式是最近的纹理分类方法:en.wikipedia.org/wiki/Local_binary_patterns

标签: algorithm image-processing computer-vision


【解决方案1】:

我会这样做(下面的整个方法,在帖子的底部,但只是从上到下阅读):

您的报价:

“我所说的照片是指人物、风景、动物的照片 等等”

我对您的报价的回应:

这意味着此类图像具有边缘、轮廓。你的形象 试图分离出来,没有边缘或小轮廓(第二 至少是示例图片)

您的报价:

一个白色或黑色像素会破坏这样的测试。所以我需要 如果只有少数像素具有这样的像素,则以某种方式排除极值 极端

我的回答:

通过DoG(高斯差)等方法最小化噪声将减少 嘈杂的单个像素

所以我已经拍摄了您的图像并通过以下代码运行它:

cv::cvtColor(image, imagec, CV_BGR2GRAY); // where image is the example image you shown
cv::GaussianBlur(imagec,imagec, cv::Size(3,3), 0, 0, cv::BORDER_DEFAULT ); //blur image
cv::Canny(imagec, imagec, 20, 60, 3);

您提供的示例图像 1 的结果:

正如您在浏览代码后看到的那样,图像变为空白(全黑)。图像很大,因此很难在一个窗口中显示所有内容。

您向我展示的示例 2 的结果:

可以看到轮廓,但是解决这个问题的一种方法是,从图像的尺寸中引入大约 20 到 30 像素的 ROI,例如,如果图像尺寸为 640x320,则 ROI 可能为 610x 290 ,它被放置在图像的中心。

那么现在,给大家介绍一下我的真正方法:

1) 通过上面的代码运行所有图像以找到边缘

2) 检查哪些图像没有任何边缘(没有边缘的图像 将有 0 个像素的值大于 0 或小像素的值大于 0,所以设置一个稍高的阈值以保证安全?你相应地调整,多少像素来识别你的图像)

3) 保存/命名所有没有边缘的图像,这将是图像 你正试图与其他人分开。

4) 结束。

编辑(回答评论,会回复,但我的回复太长):

关于模糊部分是正确的。为了尽量减少模糊的使用,您可以先进行“类似消除的过程”,这样图像 1 中的那些类似平滑的图像将已经被分离并分类为您要查找的图像。

从那里您对剩余的图像进行第二次测试,这将是“模糊”。

如果您真的希望避免模糊,我注意到您的示例图像 1 可以归类为“光滑表面”,而示例图像 2 可以归类为“粗糙表面”,这意味着它很嘈杂,这让我首先介绍了模糊。

根据我的经验,如果我没记错的话,这种粗糙的表面在“分水岭”或“通过颜色聚类”方法中非常好,它们融合得非常好,不像光滑的图像。

由于残留图像很可能是粗糙图像,您可以尝试分水岭方法,并且canny,如果我没有记错的话,您会发现它是黑色图像。试试这样一行:

pyrMeanShiftFiltering( image, images, 10, 20, 3)

我不太确定这种方法是否会比高斯模糊更昂贵。但是您可以尝试两者并比较两者的计算速度。

关于您对灰度图像的评论:

转换为灰度听起来有风险 - 丢失颜色信息可能 引发大量误报

我的回答:

我真的不这么认为。如果您尝试分割的图像 是一种颜色,更改为灰度无关紧要。当然,如果 你拍一张蓝天的照片,它可能会导致假阴性, 但正如你所说,这些都可以。

如果你仔细想想,里面有人物等的图像,强度 变化相差很大。 (当然,除非你的照片有 极端情况,比如草地上的绿球)

我承认转换为灰度会丢失信息。但是在你的 情况下,我怀疑它会影响很多,实际上,使用灰度 图片速度更快,成本更低。

【讨论】:

  • 忽略透明像素会更有意义 :) 转换为灰度听起来有风险 - 丢失颜色信息可能会引发大量误报。模糊非常昂贵。 3x3 内核很小,但我认为它对大图像没用。例如,高分辨率摄像头的尘点可能超过 10x10 像素。增加内核大小会使处理更加密集。请注意,我展示的图像尺寸已相当小。较大的最初大约有 10MP,我可能会遇到两倍大小的图像(高达 30MP)。我什至不想考虑模糊它们;)
  • @SiliconMind,我的回复有点太长,请检查我的回答,我对您的评论的回复的“编辑”部分。干杯(:希望我能帮到你一点,或者给你更多的选择来解决这个问题。
【解决方案2】:

我会使用基于熵的方法。我没有任何自定义代码要分享,但下面的博客条目应该会推动您朝着正确的方向前进。

http://envalo.com/image-cropping-php-using-entropy-explained/

问题是,与其中包含有趣内容的图像相比,均匀图像的熵非常低。

所以问题是找到正确的阈值并处理整个集合。

【讨论】:

  • 这个想法很棒,不幸的是它看起来计算成本很高。作者使用了如此多的过滤器和计算,我怀疑它在我的情况下是否有意义。它可能适用于小型网络图像,但使用 20MP 图像做所有这些事情会融化我的 CPU :) 但是 +1 是一个有趣的想法。
  • 哇,这是我的博文。只是……哇。尽管我写的废话永远不会出现在任何地方。我不确定熵方法是否是最好的,但它可以工作。我不是熵代码的原始作者,也不是专家,但是如果您缩小源图像,甚至在进行熵之前对结果进行非常弱的模糊,您可能能够确定是否图像很容易有任何有趣的东西。仍然很贵,但有可能。
【解决方案3】:

以下是我的回答。我用 C 写了一个简单的演示来解释我的想法。你可以在gist 找到它。

准备好了:

  • 一种颜色/像素包含三个通道(如果您有 Alpha 数据,则为四个通道)
  • 每个通道共有 8 位 (256)

做一些定义:

#define IMAGEWIDTH      20 // Assumed
#define IMAGEHEIGHT     20 // Assumed
#define CHANNELBIT      8
#define COLORLEVEL      256

typedef struct tagPixel
{
    unsigned int R : CHANNELBIT;
    unsigned int G : CHANNELBIT;
    unsigned int B : CHANNELBIT;
} Pixel;

收集每个通道中每个COLORLEVEL 的颜色计数:

void TraverseAndCount(Pixel image_data[IMAGEWIDTH][IMAGEHEIGHT]
    , unsigned int red_counts[COLORLEVEL]
    , unsigned int green_counts[COLORLEVEL]
    , unsigned int blue_counts[COLORLEVEL]);

下一步非常重要。 分析颜色计数

// just a very simple way to smooth the curve of the counts of colors
// and you can replace it with another way you want
unsigned int CalculateRange(unsigned int min_count
    , unsigned int blur_size
    , unsigned int color_counts[COLORLEVEL]);

这个函数做了:

  • 我通过blur_size 平滑了轴上每个通道计数的曲线 - COLORLEVEL(你可以通过其他方式平滑它)
  • 计算大于min_count的计数范围

最后,计算每个通道的范围平均值:

// calculate the average of the range for each channel of color
// the value is bigger if the image is more probably photographs
float AverageRange(unsigned int min_count, unsigned int blur_size
    , unsigned int red_counts[COLORLEVEL]
    , unsigned int green_counts[COLORLEVEL]
    , unsigned int blue_counts[COLORLEVEL]);

注意:

  • 结果取决于min_countmin_count 应该大于 0。
  • 更大的结果是图像更有可能是照片。
  • 对于一张照片,较大的结果更可能是较小的min_count

【讨论】:

    【解决方案4】:

    您可能可以使用机器学习(分类)来解决您的问题。这比听起来容易。我举个例子:

    1 - 特征提取:计算所有图像的颜色直方图(RGB 值的直方图)。可能您会希望减少 R、G 和 B 的可能值的数量,因此您的直方图不会增长得这么大(这称为重新量化)。例如,您可以制作一个接受 4 个不同的 R、G 和 B 值的直方图,生成一个具有 4*4*4 个 bin 的直方图:[(R=1, G=1, B=1), (R=1 , G=1, B=2), ... (R=4, G=4, B=4)]。

    2 - 手动标记一些知道不是照片的图像。

    3 - 训练分类器:现在您已经有了照片和非照片图像的示例,您可以使用此信息来训练分类器。这个分类器,给定一个直方图,可以用来预测图像是不是摄影。

    如果你不想花时间在分类器上,你可以尝试一个更简单的方法:

    • 从你想知道它是否是摄影的图像中计算直方图;
    • 将It的直方图与所有标记图像的直方图进行比较,找到最相似的直方图(例如,可以对bin之间的差异求和);
    • 如果直方图最相似的图像是摄影,则将图像归类为摄影。否则,将其归类为不是摄影

    【讨论】:

    • 我有成千上万张图片,而且每天都有新的。所以听起来这个解决方案需要大量的处理时间来收集数据和大量的内存来存储和比较直方图。
    【解决方案5】:

    对于每个 R、G、B 通道,计算强度的标准偏差。如果它足够低,你就有一个统一的图像。

    如果您担心有不同的均匀区域,请分别计算每个 20x20 正方形的标准差,然后计算标准差的平均值。

    【讨论】:

      【解决方案6】:

      以下方法可能有用。

      1. 在以每个像素为中心的 5x5 窗口中导出局部二进制模式。因此,对于一个像素,您有 15 个布尔值。在某个方向(顺时针或逆时针)计算数字 1-0 和 0-1 的变化。这是中心像素的特征值。

      2. 对于所有 20x20 窗口推导出像素特征值的方差。

      3. 如果你取方差的方差,对于一个统一的图像,它应该接近于零。而对于其他图像,它会相当高。通过这种方式,可能不需要固定阈值,并且局部二值模式会处理潜在的不均匀照明。

      【讨论】:

      • 15 boolean values - 我想你的意思是 25 :) 无论如何,这个想法很诱人。问题是这意味着我必须为每张图像分析 n*25 像素。对于 10 MP 图像,这是一个很大的数字。我没有对此进行测试,但听起来相当耗时。
      • 在这种情况下,您可以先使用cse.oulu.fi/CMV/Downloads/LBPMatlab 中描述的算法或精明的边缘检测来获取边缘。然后只对边缘像素进行分析。无论如何,其余的都将为零。
      【解决方案7】:

      这就是我的解决方法:

      • 求整个图像的平均 R、G 和 B 值
      • 计算每个像素的值,该值是每个通道与平均值的差值之和
      • 删除前 0.1% 的值以忽略异常值
      • 根据阈值检查最大的剩余差异(您可能需要通过反复试验来确定此阈值)

      【讨论】:

      • 如果我理解正确,那么这意味着我必须为每张图像使用双倍的内存,只是为了存储计算结果,对吧?
      • 不幸的是,是的,尽管您只能在任何给定时间保留前 0.1%,并在最后比较最低值,而不是按照初始算法中的规定将它们删除。
      • 听起来合理。用于存储极值的n * 0.001 像素缓冲区看起来要好得多。最简单的解决方案有时太简单了 :) 这与我一开始考虑的几乎相同,但现在看起来很有希望。也许我会将此与对比度碰撞结合起来以获得更好的效果?
      • 我认为没有理由改变对比度,因为它根本不会改善结果。异常值仍将是异常值,其他一切仍将是其他一切。
      • 如果我添加对比度碰撞,我可能会稍微简化一下,只使用一次而不计算平均值。均匀的图像将变黑或白色。我可以计算不符合这种模式的像素,如果我遇到一些边界,那么我知道它不是一个统一的图像。不过这只是一个理论。
      【解决方案8】:

      我会为每个图像生成一个颜色直方图,并比较它们与给定模式的差异。

      也许您想先将亮度归一化以简化匹配。

      【讨论】:

      • 如果我理解正确,那么这个解决方案将不起作用。因为非常“偏红”图像的直方图与非常“偏蓝”图像的直方图完全不同。即使两个图像具有相同的模式。例如,如果你拍摄我上传的第一张照片并显着改变它的色调,你会得到不同的直方图,因为使用了不同的颜色。
      • 我认为你需要更具体一些,你想检测什么样的图像。您给出的示例具有非常具体的颜色直方图。您的示例图像中仅使用了少数非常相似的颜色。所以我的尝试是,检测仅使用很少且相似颜色的图像(这可以通过分析直方图来完成)。
      • 您的答案是将直方图与给定的模式进行比较。问题是,如果我得到的图像与我上传的图像非常相似,但颜色为蓝色,则图案将不匹配。问题是我可以遇到各种颜色。图像可能更蓝、更绿等等。没有规则。所以我必须为每个可能的图像模式、颜色和强度(更大、更亮、更绿、更蓝等)都有一个“证明”直方图。我认为不可能按照您建议的方式执行此操作。
      • @SiliconMind:但它们都只有一小部分非常显着的颜色,可以通过直方图检测到。包含例如的图像一张照片几乎总是有相当大的散光,看起来非常不同。
      • 嗯,也许我不完全理解您的解决方案是如何工作的。你能举个例子或写更多这应该如何工作吗?
      猜你喜欢
      • 2012-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-27
      • 1970-01-01
      • 1970-01-01
      • 2021-05-24
      • 2014-06-20
      相关资源
      最近更新 更多