【问题标题】:Color segmentation颜色分割
【发布时间】:2011-07-29 04:49:50
【问题描述】:

我需要像彩虹一样用 7 种颜色(红色、橙色、黄色、绿色、浅蓝色、蓝色、紫色)分割图像。你知道怎么做吗?任何论文或算法都可能是。例如,可以通过为每个三元组 (r, g, b) 分配颜色来完成。但它并不有效,因为我们有 255^3 种组合。

【问题讨论】:

  • 我不明白你的问题 - 你想将图像减少到 7 种颜色,将图像中的每种颜色映射到七种颜色中的一种?
  • 那通常不叫分割,叫量化。
  • 是的,为了简化接下来的步骤,我想首先将图像颜色减少到 7,或者可能是 +- 更多。目的是按颜色进行一些分组。蓝色有很多种(更暗,更亮一点),作为一个例子,我希望蓝色的所有变体只给出一个值(r=0, g=0, 255)
  • 对不起,不明白,有或没有抖动是什么意思?
  • en.wikipedia.org/wiki/… 解释抖动。

标签: algorithm computer-vision image-segmentation


【解决方案1】:

HSV 色彩空间http://en.wikipedia.org/wiki/HSL_and_HSV 的“H”分量将为您提供一个合理的数字,代表(连续)彩虹上的位置。

然后很容易将连续空间分成您选择的七个部分。

【讨论】:

  • 我尝试转换为 HSV 空间,但如果更改饱和度或 V 空间,颜色会变为灰色。以及如何处理?类似于 S 和 V 空间的阈值?
  • 我不确定你所说的 S 和 V 被改变是什么意思,我假设你只是有一些输入 RGB 像素,你想把它们分成七个篮子。诚然,无论您使用什么算法,您的一些 RGB 像素可能是无色的。要处理它们,您需要第八种颜色(灰色),或者您可以根据您选择的任意规则将它们分配给您的七种颜色中的一种。
  • 最好的选择确实是阿德里安所说的。引入八种“灰色”颜色,饱和度低于某个阈值的所有像素都分配给该颜色。请注意,这将包括黑色和白色像素,以及介于两者之间的所有灰色。
  • @maximus:将 HSV 色彩空间视为圆柱体。 V 告诉你沿着圆柱体的高度,S 告诉你离中心有多远,H 是一个角度,告诉你移动了多远。圆柱轴附近的颜色(任意V和H,低S)范围从黑色(低V)到白色(高V),但不管V和H,如果你有低S,颜色的轻微变化可以改变H极大地。这就是为什么你通过阈值 S(并且只是 S,而不是 V)来切掉圆柱体的核心。
  • @maximus:您可以通过使用一些将 S 与 H 和 V 的阈值相关联的函数从圆柱体中切割出更复杂的核心。查看以柱坐标表示的形状以获得想法 (en.wikipedia.org/wiki/Cylindrical_coordinate_system)
【解决方案2】:

由于您已经拥有所需的 7 种颜色,因此无需使用聚类。一个合理的起点是:对于图像中的每个像素,找出最接近它的 7 种颜色(使用 RGB 上的 L2 距离)并将最接近的颜色分配给该像素。通过首先转换为其他颜色空间(如 CIE XYZ),您可能可以获得更好(在感知上更相似)的结果,但这需要进行实验。

【讨论】:

  • 似乎是个好主意,但 L2 距离是否足够?抱歉,您能解释一下 L2 距离是什么意思吗?
  • 模板8种颜色设置为红色为(255, 0, 0),粉色为(255, 0, 255)。
  • 想法不错,但我有很多不匹配的地方,例如:绿色被检测为黑色。或者白纸的照片被相机拍了一些散射,边缘可以检测为浅蓝色等。
【解决方案3】:

如果颜色是预定义的,那么解决方案就是遍历每个像素并用最接近的代表进行替换。正如carlosdc 所说,可能一些色彩空间转换可以提供比(r1-r2)**2 + (g1-g2)**2 + (b1-b2)**2 更好的结果。

为了让事情变得更快,一个可能的技巧是交换一些内存并缓存给定 RGB 三元组的结果......即

// Initialize the cache to 255
std::vector<unsigned char> cache(256*256*256, 255);

for (int y=0; y<h; y++)
{
    unsigned char *pixel = img + y*w*3 + x;
    for int (x=0; x<w; x++, pixel+=3)
    {
        int r = pixel[0], g = pixel[1], b = pixel[2];
        int key = r + (g<<8) + (b<<16);
        int converted = cache[key];
        if (converted == 255)
        {
            ... find closest representative ...
            cache[key] = converted;
        }
        pixel[0] = red[converted];
        pixel[1] = green[converted];
        pixel[2] = blue[converted];
    }
}

如果颜色数量较少,您可以使用较少的内存。例如,将代表的数量限制为 15 个,每个颜色条目只需要 4 位(空间的一半),如下所示

std::vector<unsigned char> cache(256*256*256/2, 255);

...
int converted = (key&1) ? (cache[key>>1] >> 4) : (cache[key>>1] & 0x0F);
if (converted == 15) // Empty slot
{
    ...
    cache[key>>1] ^= (key & 1) ? ((converted << 4)^0xF0) : (converted^0x0F);
}
...

如果相反,您知道可能的输入颜色数量会很少,而代表的数量会很大,那么标准 std::map 可能是一个有效的替代方案。

【讨论】:

    【解决方案4】:

    为什么不使用一种聚类方法(算法)?例如,k-means 算法。否则,谷歌“按颜色分割图像”。

    【讨论】:

      【解决方案5】:

      如果你想让它看起来不错,你会想要使用抖动,例如弗洛伊德斯坦伯格抖动:http://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering

      【讨论】:

      • 我需要减少颜色,而不是创建深度。
      • @maximus “创造深度”与“抖动”有什么关系?抖动是在减少颜色时分散错误的过程,因此不是映射,而是使用最接近的颜色将 [Light Purple, Light Purple, Light Purple, Light Purple, Light Purple] 说成 [Red, Red, Red, Red, Red]方法您可以改为使用查看累积颜色错误的抖动方法将其映射到 [Red, Blue, Red, White, Blue]。因此,总体而言,您的图像看起来(如果您眯着眼睛)与原始图像的颜色相同。
      猜你喜欢
      • 2011-08-02
      • 2013-07-20
      • 2014-10-30
      • 2018-07-21
      • 1970-01-01
      • 1970-01-01
      • 2017-07-24
      • 1970-01-01
      • 2015-03-11
      相关资源
      最近更新 更多