【问题标题】:Get the percentage usage of every colour in an image获取图像中每种颜色的使用百分比
【发布时间】:2011-12-14 00:54:48
【问题描述】:

我有这个工作,但它在 jpeg 图像上太慢了,还需要一些改变。

我需要知道图像中的各个颜色(RGB 的容差为 +/- 1)以及该颜色在图像中所占的百分比。

所以如果图像是黑白的,它会说类似 白色:74% 黑色:26%

下面的代码就像我说的那样工作,但我还需要添加一个容差系统,我不知道该怎么做。

private Dictionary<string, string> getPixelData(Bitmap image)
{
    Dictionary<string, string> pixelData = new Dictionary<string, string>();
    //int col, row;
    //int r, g, b;
    Color pixel;

    double offset = 0.000001;
    int hmm = (image.Height * image.Width);
    double current = 0;
    offset = 100 / double.Parse(hmm.ToString());// 0.01;// 100 / (image.Height * image.Width) * 10000;

    try
    {
        for (int i = 0; i < image.Height; i++)
        {
            for (int j = 0; j < image.Width; j++)
            {
                current = current + offset;
                pixel = image.GetPixel(i, j);                        
                pixelData.Add(i + "," + j, (pixel.R.ToString() + " " + pixel.G.ToString() + " " + pixel.B.ToString()));
                pBarprocess.Value = int.Parse(Math.Floor(current).ToString());
                pBarprocess.Update();
                Application.DoEvents();
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Unable to parse image " + ex);
    }

    return pixelData;
}

还有其他功能

private void btnProcess_Click(object sender, EventArgs e)
{
    pBarprocess.Value = 0;
    pBarprocess.Enabled = false;
    Bitmap foo = Bitmap.FromFile(@txtFileName.Text) as Bitmap;
    Dictionary<string, string> pixelData = new Dictionary<string, string>();

    lblProcess.Text = "Processing pixel map";
    pixelData = getPixelData(foo);

    lblProcess.Text = "Calculating Density";
    lblProcess.Update();

    var distinctList = pixelData.Values.Distinct().ToList();

    Console.WriteLine("DL = " + distinctList.Count);
    double offset = 100 / double.Parse(distinctList.Count.ToString());
    double current = 0;

    foreach (var value in distinctList)
    {
        IEnumerable<string> query = pixelData.Values.Where(fruit => fruit == value);
        double perc = (double.Parse(query.Count().ToString()) / double.Parse(pixelData.Count.ToString())) * 100;
        Console.WriteLine(value + " = " + query.Count() + "(" + perc + "%)");
        txtAnalysis.Text = "Colour " + value + " : " + query.Count() + " (" + perc.ToString() + "%)\r\n" + txtAnalysis.Text;
        txtAnalysis.Update();
        pBarprocess.Value = int.Parse(Math.Floor(current).ToString());
        pBarprocess.Update();
        Application.DoEvents();
    }

    lblProcess.Text = "Finished.";
    pBarprocess.Value = 0;
    pBarprocess.Enabled = false;
}

【问题讨论】:

  • 我可以看到您的算法的一个问题是您正在更新 UI 并通过更新进度条和调用 DoEvents 在每个像素上抽取窗口消息队列。尝试每隔 n 个像素执行一次,例如每 1000 像素。
  • 每一步(即在外循环中)都是触摸 UI 元素的更好位置。此外,您正在为每个像素添加一个新项目到字典中 - 这会影响您的性能。您应该创建一个长度与您要检测的颜色数量相对应的数组,并改为增加关联的值。当您的键是连续整数时,不需要字典。

标签: c# image-processing colors bitmap pixel


【解决方案1】:

GetPixel 并不是真正快速访问图像数据的方法。使用LockBits 方法。

编辑:

嗯,你用字符串做了很多事情。以这种方式构建 pixelData 字典是毫无用处的,为什么不立即处理不同的颜色呢? Color 是一个不可变的结构,所以它已经是我们字典的好键了。

Dictionary<Color, int> frequency = new Dictionary<Color, int>();
for (int i = 0; i < image.Height; i++) {
  for (int j = 0; j < image.Width; j++) {
    pixel = image.GetPixel(i, j);
    if (frequency.ContainsKey(pixel)) frequency[pixel]++;
    else frequency.Add(pixel, 1);
  }
}

// and finally
int totalPixels = image.Width * image.Height;
foreach (var kvp in frequency) {
  Console.WriteLine("Color (R={0},G={1},B={2}): {3}", kvp.Key.R, kvp.Key.G, kvp.Key.B, kvp.Value / (double)totalPixels);
}

应该这样做,除非您想让它更快并使用 LockBits 而不是 GetPixel。

其他一些观察:

int hmm = (image.Height * image.Width);
double offset = 100 / double.Parse(hmm.ToString());

您正在使用一种非常奇怪且缓慢的从 int 转换为 double 的方式。你可以只写double offset = 100 / (double)hmm;,它是一样的(你也可以写100.0而不是100,编译器会为你创建一个double,所以你不需要转换hmm)。

这让我笑了:

IEnumerable<string> query = pixelData.Values.Where(fruit => fruit == value);

为什么是水果!?好像你是从某个地方复制的。

【讨论】:

  • 获取像素不是在几分之一秒内完成的问题,而是需要时间的数据处理
  • 哈哈,是的,我之前从未使用过 LINQ,所以只是弄乱了我必须让它工作的东西:) 现在将尝试你的实现,并标记它是否有效:)
  • 基本上,您必须为每种颜色确定要将其与哪种其他颜色合并,或者是否要将两者与新的中间色合并...没有直接的解决方案。最简单的方法是按照 ComputerLinguist 的建议对图像应用抖动/量化。您可以使用 .NET 或图像处理库将图像缩小到例如256 种颜色,或者如果它是 16 种灰度的灰度。这是已经提到的AForge.NET的文章。
  • +1,就像您的“其他一些观察”部分。比原始问题更深入的答案是最好的学习方法。
【解决方案2】:

这似乎是更大的图像处理目标的一部分。 Aforge framework 是 .NET 中图像分析和处理的首选,而且速度非常快。很可能它已经有你需要的代码了。

你提到了一个容忍系统——对我来说,这听起来你需要quantization - color rounding

一旦你有一个量化的位图,你可以制作一个长度与调色板大小相匹配的数组,LockBits 位图,并使用每个像素的颜色索引作为每个像素的数组索引来累积使用统计信息。

您能否分享有关此代码目标的更多信息?它到底应该做什么?

【讨论】:

  • 是的,基本上它需要检查图像中的颜色密度,但不是返回 12 种黑色,而是应该返回一种,例如 RGB 0、0、0 到 5, 5、5 会以单色返回,200、200、200 到 255、255、255 会以白色返回
  • 每种颜色的亮度不同 - 例如,如果您正在寻找感知的亮度范围,黑色的更好范围是 0,0,0 到 13,5,50。颜色密度通常由直方图表示 - 也许您可以提供更多关于用户将使用它的信息?
  • 要检测材料中的切屑密度,切屑可以是任何颜色,但需要知道与材料相比,切屑的总体覆盖率。
  • 然后肯定量化/后处理。这样,您可以选择要四舍五入的颜色数。而且您应该能够从我的回答中提到的算法中获得非常好的性能。
【解决方案3】:

我计算图片颜色百分比的方法如下,也可以这样

计算任何颜色的任何像素百分比。

1.在这个位置使用名为“ImageJ”的软件,是免费的。

http://rsb.info.nih.gov/ij/download.html

2.用这个工具打开一张图片

  1. 进入分析菜单并选择直方图,它将打开直方图窗口

4.在直方图窗口左下方点击“列表”按钮,打开列表窗口

5.在列表窗口中选择“另存为”,这将保存0-256之间的每种颜色的像素数

6.对于面积测量,从分辨率计算像素尺寸并乘以数量

像素。使用 excel 进行任何其他数值分析,尤其是数据透视表。

这是ImageJ软件,我用的excel文件,还有截图。

https://www.mediafire.com/?htwk83rwgio4zds

【讨论】:

  • 请把截图放在这里。链接到一些未知的 .rar 文件并不是最好的回答方式……
  • 不错的帖子,但您没有阅读原始问题,这依赖于第 3 方软件,并且 C# 需要该过程
猜你喜欢
  • 1970-01-01
  • 2020-05-12
  • 1970-01-01
  • 1970-01-01
  • 2015-11-02
  • 1970-01-01
  • 2017-08-27
  • 2013-09-16
  • 2011-05-23
相关资源
最近更新 更多