Получить процентное использование каждого цвета в изображении

#c# #image-processing #colors #bitmap #pixel

#c# #обработка изображений #Цвет #растровое изображение #пиксель

Вопрос:

У меня это работает, но оно чертовски медленно работает с изображениями в формате jpeg, а также нуждается в некоторых изменениях.

Мне нужно знать отдельные цвета в изображении (с допуском /- 1 для RGB) и процент изображения этого цвета.

итак, если изображение было черно-белым, оно говорило бы что-то вроде Белый: 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()   "%)rn"   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;
}
 

Комментарии:

1. Проблема, которую я вижу в вашем алгоритме, заключается в том, что вы обновляете пользовательский интерфейс и загружаете очередь сообщений окна на каждый пиксель, обновляя индикатор выполнения и вызывая DoEvents. Попробуйте делать это каждые n пикселей подряд, например, каждые 1000 пикселей.

2. Каждый шаг (т.Е. Во внешнем цикле) — лучшее место для касания элементов пользовательского интерфейса. Кроме того, вы добавляете новый элемент в словарь для каждого отдельного пикселя — это снизит вашу производительность. Вы должны создать массив с длиной, соответствующей количеству цветов, которые вы хотите обнаружить, и вместо этого увеличить связанные значения. Нет необходимости в словаре, когда ваши ключи являются последовательными целыми числами.

Ответ №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);
}
 

И это должно сделать это, за исключением случаев, когда вы хотите сделать это еще быстрее и использовать блокировочные биты вместо 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);
 

Почему фрукты !? Похоже, вы скопировали это откуда-то.

Комментарии:

1. Получение пикселей — это не проблема, которая выполняется за доли секунды, а обработка данных, которая требует времени

2. lol да, я никогда раньше не использовал LINQ, поэтому просто перепутал то, что мне нужно, чтобы заставить его работать 🙂 сейчас попробую вашу реализацию и отмечу, работает ли она 🙂

3. По сути, вам нужно решить для каждого цвета, с каким другим цветом вы хотели бы его объединить, или если вы хотите объединить оба с новым промежуточным цветом … прямого решения нет. Самым простым способом было бы следовать предложению ComputerLinguist применить сглаживание / квантование к изображениям. Вы можете использовать . NET или библиотека обработки изображений, чтобы уменьшить изображение, например, до 256 цветов или, если это оттенки серого, до 16 оттенков серого. Это статья из уже упомянутого AForge.NET .

4. 1, как в вашем разделе «некоторые другие наблюдения». Ответы, идущие дальше, чем исходные вопросы, являются лучшими для изучения.

Ответ №2:

Похоже, это часть более масштабной цели обработки изображений. Платформа Aforge является идеальным выбором для анализа и обработки изображений в .NET, и она чрезвычайно быстра. Вероятно, в нем уже есть нужный вам код.

Вы упомянули систему допусков — для меня это звучит так, как будто вам нужно квантование — округление цвета.

Получив квантованное растровое изображение, вы можете создать и создать массив с длиной, соответствующей размеру палитры, заблокировать растровое изображение и использовать индекс цвета для каждого пикселя в качестве индекса массива для каждого пикселя для накопления статистики использования.

Не могли бы вы поделиться дополнительной информацией о ваших целях для этого кода? Что именно он должен делать?

Комментарии:

1. Да, в основном требуется проверить плотность цвета в изображении, но вместо того, чтобы возвращаться с 12 оттенками черного, он должен вернуться с одним, так что, например, RGB от 0, 0, 0 до 5, 5, 5 вернется в виде одного цвета и от 200, 200, 200 до 255, 255, 255 вернутся как белые

2. Яркость зависит от цвета — например, лучшим диапазоном для черного будет от 0,0,0 до 13,5,50, если вы ищете воспринимаемые диапазоны яркости. Плотность цвета обычно представлена гистограммой — возможно, вы могли бы предоставить больше информации о том, для чего пользователи будут использовать это?

3. чтобы определить плотность стружки в материале, стружка может быть любого цвета, но необходимо знать общий процент покрытия стружки по сравнению с материалом.

4. Тогда определенно квантовать / постеризировать. Таким образом, вы можете выбрать, до какого количества цветов вы хотите округлить. И вы должны быть в состоянии получить очень хорошую производительность из алгоритма, упомянутого в моем ответе.

Ответ №3:

Мой метод вычисления процентного содержания цвета изображения заключается в следующем, также таким образом мы можем

вычислите любой процент пикселей для любого цвета.

1. Используйте программное обеспечение с именем «ImageJ» в этом месте, оно бесплатное.

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

2. Откройте изображение с помощью этого инструмента

  1. Перейдите в меню Анализ и выберите Гистограмма, откроется окно гистограммы

4.In окно гистограммы, в левом нижнем углу нажмите кнопку «список», откроется окно списка

5.In в окне списка выберите «сохранить как», это позволит сохранить количество пикселей для каждого цвета в диапазоне 0-256

6. Для измерения площади вычислите размер в пикселях по разрешению и умножьте на количество

пикселей. Используйте Excel для любого другого численного анализа, особенно сводных таблиц.

Вот программное обеспечение ImageJ, файл Excel, который я использовал, и скриншот.

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

Комментарии:

1. пожалуйста, разместите скриншот здесь. ссылка на некоторые неизвестные файлы .rar — не лучший способ ответить…

2. Хороший пост, но вы не смогли прочитать исходный вопрос, это зависит от стороннего программного обеспечения, и процедура требовалась для C#