C #: поиск быстрой структуры данных для добавления пикселей к секционированной гистограмме HSB

#c# #data-structures #image-processing #histogram #buckets

#c# #структуры данных #обработка изображений #гистограмма #сегменты

Вопрос:

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

  • Оттенок: 18 секций, в результате чего интервалы в 20 от 0 до 360
  • Насыщенность: 3 секционирования, в результате чего получаются интервалы 0,33 от 0 … 1
  • Яркость: 3 секции, в результате чего интервалы 0,33 от 0 … 1

Итак, моя гистограмма имеет в общей сложности 18*3*3 = 162 раздела (ячейки), которые состоят из нижних границ интервала для каждого канала:

  • Bin1: [0, 0, 0]
  • Bin2: [0, 0, 0.33]
  • Bin3: [0, 0, 0.66]
  • Bin4: [0, 0.33, 0]
  • Bin5: [0, 0.33, 0.33]
  • Bin162: [340, 0.66, 0.66]

Я реализовал это, представив, что каждая ячейка сама по себе будет иметь цвет HSB. Итак, я вычислил границы интервала bin, создал экземпляры HsbColor на основе этих значений и поместил цвета (завернутые в класс HsbHistogramBin) в простой список. При добавлении нового HsbColor к моей гистограмме я использую следующий код, чтобы определить, какую ячейку мне нужно увеличить:

 private HsbHistogramBin FindBin(HsbColor color)
{
    HsbHistogramBin bin = null;
    bool foundBin = false;
    for (int i = Bins.Count - 1; i >= 0; i--)
    {
        bin = Bins[i];
        if (bin.Color.Hue > color.Hue)
            continue;
        if (bin.Color.Saturation > color.Saturation)
            continue;
        if (bin.Color.Brightness > color.Brightness)
            continue;
        foundBin = true;
        break;
    }
    return foundBin ? bin : null;
}

public void AddColor(HsbColor color)
{
    FindBin(color).Value  ;
}
  

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

Мой вопрос: как я могу ускорить эту структуру данных, чтобы я мог сразу найти нужную ячейку для своих пикселей? Может сработать простой массив длиной 162, но как мне вычислить правильный индекс ячейки для данного пикселя, который еще не уменьшен до указанных разделов и может содержать значения типа [259.234, 0.5634, 0.90534]?

Ответ №1:

Почему бы просто не использовать трехмерный массив? Вот так:

 int[,,] histogram = new int[18, 3, 3];

// initialize to 0
for(int h = 0; h < 18; h  ) {
  for(int s = 0; s < 3; s  ) {
    for(int b = 0; b < 3; b  ) {
      histogram[h, s, b] = 0;
    }
  }
}

// foreach pixel...
HsbColor c = ... // color of pixel
int h = (int)(c.Hue / 20);
int s = (int)(c.Saturation * 3);
int b = (int)(c.Brighthess * 3);

// take care of boundary cases (Hue, Saturation or Brightness maxed out)
if(h >= 18) h = 17;
if(s >= 3) s = 2;
if(b >= 3) b = 2;

histogram[h, s, b]  ;
  

ПРИМЕЧАНИЕ: Здесь я предполагаю, что ваше общее количество пикселей (точнее, максимальное количество пикселей, которые попадут в 1 ячейку) не превысит int.MaxValue . В противном случае рассмотрите возможность использования long типа данных для гистограммы вместо int .

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

1. Спасибо, это чистое и простое решение, и оно достаточно быстрое для моих нужд!

2. ПРИМЕЧАНИЕ: на самом деле имеет больше смысла использовать uint (или ulong ) вместо int , поскольку ячейки никогда не будут содержать отрицательное количество пикселей. Это также удваивает количество пикселей, которое гистограмма может обрабатывать без максимального использования.

Ответ №2:

Вы можете преобразовать свой номер HSV в один беззнаковый long вот так:

 ulong colorLookupValue = (color.Hue/18) * 9   (ulong)((color.Saturation*3) * 3)   (ulong)(color.Brightness * 3) 
  

Это ваш индекс ячейки.