【问题标题】:What is the distribution of colour in an image relative to defined centroids相对于定义的质心,图像中的颜色分布是什么
【发布时间】:2018-04-21 18:28:25
【问题描述】:

假设我有以下图片:

以下“参考”颜色是静态的,不是从图像中确定的(RGB 格式)

  1. [90, 160, 155]
  2. [245, 210, 110]
  3. [20,20,20]
  4. [150, 150, 120]

我如何获得一个返回对象,该对象根据参考颜色告诉我图像中与参考颜色“最接近”的颜色百分比,返回值如下:

{
  "colour1": 0.25, // These aren't the actual distributions, but the answer
  "colour2": 0.1,  // this is trying to represent is that 25% of the colour
  "colour3": 0.6,  // in the image is closest to "colour1", 10% of the
  "colour4": 0.05  // image is closest to "colour2", etc.
}

有没有一种高效的方法来做到这一点,以便可以在几秒钟内为一张大图像返回一个结果,并带有十几种参考颜色?

答案不需要是精确的,10% 以内的近似值就可以了,所以如果大幅减小图片的大小是加快速度的方法,那就可以了。


我的第一个(而且很糟糕 - 只是为了快速验证概念)通过解决方案是:

  1. 循环遍历图片中的所有像素
  2. 遍历所有参考颜色
  3. 计算像素颜色和参考颜色之间的欧几里得距离
  4. 返回欧几里得距离最近的参考颜色
  5. 计算结果中每种参考颜色的出现次数,然后除以总像素数得到百分比

当然,这很慢

接下来的步骤,我一直在考虑尝试几种不同的选择:

  1. 使用 opencv 生成颜色直方图,并以某种方式使用其结果来获取每种参考颜色的百分比

  2. 使用 K-means 聚类,将参考颜色设置为质心,并使用其结果

但我想在深入研究之前先在这里询问一下,看看是否有人对去哪里有建议,或者更好的是,如果有一个简单的库可以轻松解决这个问题。

【问题讨论】:

  • 你试过了吗?你遇到了什么问题?
  • 对于每个颜色范围,使用inRange 函数,计算目标图像中所有非零像素,然后将该数字除以 src 图像中的总像素数。
  • 到目前为止,我已经用我的方法/猜测更新了这个问题@mkrieger1
  • @zindarod - 我已经尝试过这个,但效果不佳。也许最好的解决方案就是将图像缩小到较小的尺寸并针对它运行此策略。
  • 0) 在 0 处初始化 # 颜色直方图,1) 循环图像,2) 循环颜色,3) 找到最接近的颜色 4) 在最接近颜色索引的直方图中添加 1,5) 标准化 L1直方图,6)完成。我不明白这怎么可能需要超过 2 秒。在 python 中循环更慢......可能一些索引方法更快。但是 2 秒应该绰绰有余

标签: python opencv image-processing cv2


【解决方案1】:

我使用数组操作来提高性能。粗略地说,我做了同样的事情:评估每个像素和每种颜色的距离并计算获胜者(小距离)。

我刚刚看到你的问题是关于 Python 的,而我的代码是 C++ 的。但是看看它是否可以帮助您在 Python 中找到解决方案。

Scalar color1(155, 160, 90);
Scalar color2(110, 210, 245);
Scalar color3(20, 20, 20);
Scalar color4(120, 150, 150);

void compare(Mat& targetDistance, Mat& distanceToCompareA,
        Mat& distanceToCompareB, Mat& distanceToCompareC, String name) {

    Mat compareA = targetDistance - distanceToCompareA;
    Mat thresholdMatA;
    threshold(compareA, thresholdMatA, 0, 1, THRESH_BINARY_INV);

    Mat compareB = targetDistance - distanceToCompareB;
    Mat thresholdMatB;
    threshold(compareB, thresholdMatB, 0, 1, THRESH_BINARY_INV);

    Mat compareC = targetDistance - distanceToCompareC;
    Mat thresholdMatC;
    threshold(compareC, thresholdMatC, 0, 1, THRESH_BINARY_INV);

    Mat thresholdMat;
    bitwise_and(thresholdMatA, thresholdMatB, thresholdMat);

    bitwise_and(thresholdMat, thresholdMatC, thresholdMat);

    Size size = thresholdMat.size();

    double distro = ceil(sum(thresholdMat)[0] * 100 / (size.height * size.width))/100.0;

    cout << name << " " << distro << endl;

    Mat dest = thresholdMat * 255.0;

    Mat output;
    dest.convertTo(output, CV_8UC1);

    namedWindow(name, WINDOW_AUTOSIZE);
    imshow(name, output);
}

int main() {

    Mat origin = imread("XwAAB.jpg");

    Mat src;
    origin.convertTo(src, CV_16SC1);

    Mat matColor1 = Mat(src.size(), src.type());
    matColor1.setTo(color1);

    Mat matColor2 = Mat(src.size(), src.type());
    matColor2.setTo(color2);

    Mat matColor3 = Mat(src.size(), src.type());
    matColor3.setTo(color3);

    Mat matColor4 = Mat(src.size(), src.type());
    matColor4.setTo(color4);

    Mat sub = src - matColor1;
    Mat pow = sub.mul(sub);
    Mat distance1;
    transform(pow, distance1, cv::Matx13f(1, 1, 1));

    sub = src - matColor2;
    pow = sub.mul(sub);
    Mat distance2;
    transform(pow, distance2, cv::Matx13f(1, 1, 1));

    sub = src - matColor3;
    pow = sub.mul(sub);
    Mat distance3;
    transform(pow, distance3, cv::Matx13f(1, 1, 1));

    sub = src - matColor4;
    pow = sub.mul(sub);
    Mat distance4;
    transform(pow, distance4, cv::Matx13f(1, 1, 1));

    compare(distance1, distance2, distance3, distance4, "color1");

    compare(distance2, distance1, distance3, distance4, "color2");

    compare(distance3, distance1, distance2, distance4, "color3");

    compare(distance4, distance1, distance2, distance3, "color4");

    namedWindow("origin", WINDOW_AUTOSIZE);
    imshow("origin", origin);
    waitKey();
    return 0;
}

【讨论】:

    【解决方案2】:

    我正在考虑当图像具有大而均匀的区域时可以实现显着的加速。

    如果您将图像分解为马赛克,则图块中的像素将占用一定体积的 RGB 空间。如果事实证明这个体积的所有点都映射到同一个参考,您可以一次性分配所有像素。

    使用我在另一篇文章中描述的查找表技巧,在初始化表时,您可以确定哪些箱(RGB 空间中的立方体)映射到单一参考颜色。然后,如果体积的边界框完全适合立方体,那么您就完成了。

    更复杂的版本将使用 RGB 空间的八叉树表示,其中每个节点(对应于某个大小的立方体)包含其内容映射到的最近颜色列表(为了参考颜色的良好分布,许多节点将具有一种或两种颜色的列表。然后对于给定的图块,您计算它包含的颜色的边界框,并且在八叉树内的搜索将返回可能颜色的列表。如果列表是单例,您完成。否则,您将根据减少的颜色数量进行分类。

    【讨论】:

      【解决方案3】:

      不幸的是,对于少量参考颜色,您的“糟糕”解决方案可能是最快的。对于每个像素,您需要通过详尽的比较找到到这些参考的最小距离(无论如何请注意,您可以省去平方根)。

      如果你觉得它太慢,你可以考虑切换到编译语言。

      对于更多颜色,您可以开始考虑 k-D 树 (k=3),这将稍微加快对最接近颜色的搜索速度。但不要指望奇迹。

      对于近似解决方案,您可以考虑构建一个低分辨率查找表,例如,每个颜色分量有 32 个 bin,以达到 10% 的准确度要求。这使得总共 32³=32768 个 bin,您将使用最接近的颜色的索引对其进行初始化,如上计算。这与填充 32768 像素图像所需的时间相同。当表格可用时,这会将每个像素的处理减少为简单的表格查找。

      如果相同的引用用于多个或更大的图像,您可以考虑使用更准确的查找表。如果您的引用确实是静态的并且您可以负担 24 MB 的存储空间,那么您可以映射整个 RGB 空间。

      最后一点:k-means 过程的每一步都在精确地执行最近邻分配任务,而您的百分比只是一个副产品。如果您手边有一个优化的 k-means,可以访问集群大小和设置初始中心的选项,请使用它并执行单次迭代。

      【讨论】:

      • 这个任务也是高度可并行化的
      • 除非他使用GPU,否则他的手动循环会像opencv的功能一样优化吗?特别是如果 opencv 是使用并行框架构建的?
      猜你喜欢
      • 1970-01-01
      • 2011-04-20
      • 1970-01-01
      • 2011-03-15
      • 2023-01-22
      • 2013-04-10
      • 2013-09-27
      • 2011-08-21
      • 2011-10-22
      相关资源
      最近更新 更多