OpenCV рисование 2D гистограммы

Мне интересно, как построить 2-мерную гистограмму HSV Mat в opencv C++. Мой текущий код пытается отобразить его с треском. Я посмотрел вокруг, как построить гистограммы, и все, что я нашел, были те, которые изображали их как независимые 1d гистограммы.

Вот мой текущий вывод с количеством баков оттенка, равным 30, и бинов насыщения, равным 32:

Вот еще один вывод с количеством бункеров оттенка, равным 7, и бункеров насыщения, равным 5:

Я хотел бы, чтобы это выглядело больше как результат здесь

http://docs.opencv.org/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html

Я также заметил, что всякий раз, когда я делаю cout << Hist.size, это дает мне 50x50. Должен ли я понять, что это просто означает, что первое измерение массива имеет размер 250?

Кроме того, как можно отсортировать гистограмму от самой высокой к самой низкой (или наоборот) частоте значений? Это еще одна проблема, которую я пытаюсь решить.

Моя текущая функция заключается в следующем.

void Perform_Hist(Mat& MeanShift, Mat& Pyramid_Result, Mat& BackProj){  

  Mat HSV, Hist;

  int histSize[] = {hbins, sbins};
  int channels[] = {0, 1};

  float hranges[] = {0, 180};
  float sranges[] = {0, 256};

  const float* ranges[] = {hranges, sranges};

  cvtColor(MeanShift, HSV, CV_BGR2HSV);

  Mat PyrGray = Pyramid_Result.clone();

  calcHist(&HSV, 1, channels, Mat(), Hist, 2, histSize, ranges, true, false); 
  normalize(Hist, Hist, 0, 255, NORM_MINMAX, -1, Mat());  
  invert(Hist, Hist, 1);  
  calcBackProject(&PyrGray, 1, channels, Hist, BackProj, ranges, 1, true);

  double maxVal = 0; minMaxLoc(Hist, 0, &maxVal, 0, 0);

  int scale = 10;
  Mat histImage = Mat::zeros(sbins*scale, hbins*10, CV_8UC3);

  for(int i = 1; i < hbins * sbins; i++){
     line(histImage,
     Point(hbins*sbins*(i-1), sbins - cvRound(Hist.at<float>(i-1))),
     Point(hbins*sbins*(i-1), sbins - cvRound(Hist.at<float>(i))),
     Scalar(255,0,0), 2, 8, 0);
  }
  imshow (HISTOGRAM, histImage);
}

1 ответ

Решение

Вы имели в виду что-то подобное?

Гистограмма ВПГ как трехмерный график

  • это гистограмма ВПГ, показанная в виде 3D-графика
  • V игнорируется, чтобы добраться до 3D (иначе это будет 4D график...)

если да, то вот как это сделать (я не использую OpenCV, поэтому настройте его под свои нужды):

  1. конвертировать исходное изображение в HSV
  2. вычислить гистограмму, игнорируя значение V
    • все цвета с одинаковыми H,S считаются одним цветом, независимо от того, что V
    • Вы можете игнорировать любой другой, но параметр V выглядит как лучший выбор
  3. нарисовать график

    • эллипс первого рисования с более темным цветом (базовый диск HSV)
    • затем для каждой точки возьмите соответствующее значение гистограммы и нарисуйте вертикальную линию более ярким цветом. Размер линии пропорционален значению гистограммы

Вот код C++, с которым я это сделал:

picture pic0,pic1,pic2,zed;

int his[65536];
DWORD w;

int h,s,v,x,y,z,i,n;
double r,a;
color c;

// compute histogram (ignore v)
pic2=pic0;                      // copy input image pic0 to pic2
pic2.rgb2hsv();                 // convert to HSV
for (x=0;x<65536;x++) his[x]=0; // clear histogram
for (y=0;y<pic2.ys;y++)         // compute it
 for (x=0;x<pic2.xs;x++)
    {
    c=pic2.p[y][x];
    h=c.db[picture::_h];
    s=c.db[picture::_s];
    w=h+(s<<8);                 // form 16 bit number from 24bit HSV color
    his[w]++;                   // update color usage count ...
    }
for (n=0,x=0;x<65536;x++) if (n<his[x]) n=his[x];   // max probability

// draw the colored HSV base plane and histogram
zed =pic1; zed .clear(999); // zed buffer for 3D
           pic1.clear(0);   // image of histogram
for (h=0;h<255;h++)
 for (s=0;s<255;s++)
    {
    c.db[picture::_h]=h;
    c.db[picture::_s]=s;
    c.db[picture::_v]=100;      // HSV base darker
    c.db[picture::_a]=0;
    x=pic1.xs>>1;               // HSV base disc position centers on the bottom
    y=pic1.ys-100;
    a=2.0*M_PI*double(h)/256.0; // disc -> x,y
    r=double(s)/256.0;
    x+=120.0*r*cos(a);          // elipse for 3D ilusion
    y+= 50.0*r*sin(a);
    z=-y;
    if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } x++;
    if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } y++;
    if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } x--;
    if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } y--;

    w=h+(s<<8);                 // get histogram index for this color
    i=((pic1.ys-150)*his[w])/n;
    c.db[picture::_v]=255;      // histogram brighter
    for (;(i>0)&&(y>0);i--,y--)
        {
        if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } x++;
        if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } y++;
        if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } x--;
        if (zed.p[y][x].dd>=z){ pic1.p[y][x]=c; zed.p[y][x].dd=z; } y--;
        }
    }
pic1.hsv2rgb();                 // convert to RGB to see correct colors
  • входное изображение pic0 (роза), выходное изображение pic1 (график гистограммы)
  • pic2 это pic0 преобразован в HSV для вычисления гистограммы
  • zed это буфер Zed для отображения 3D, избегающий сортировки по Z...

Я использую свой собственный класс изображений для изображений, поэтому некоторые участники:

  • xs,ys размер изображения в пикселях
  • p[y][x].dd является пикселем в положении (x,y) как 32-битный целочисленный тип
  • clear(color) - очищает все изображение
  • resize(xs,ys) - изменяет размер изображения до нового разрешения
  • rgb2hsv() а также hsv2rgb()... угадай, что он делает:)

[edit1] Ваша 2D гистограмма

Похоже, у вас есть цветовой код в 2D массив. Одна ось H и второй S, Так что вам нужно рассчитать H,S значение из адреса массива. Если он линейный, то для HSV[i][j]:

  • H=h0+(h1-h0)*i/maxi
  • S=s0+(s1-s0)*j/maxj
  • или же i,j обратная
  • h0,h1,s0,s1 цветовые диапазоны
  • maxi,maxj размер массива

Как вы видите, вы также выбросить V как я, так что теперь у вас есть H,S для каждой ячейки гистограммы 2D массив. Где вероятность - это значение ячейки. Теперь, если вы хотите нарисовать изображение, вам нужно знать, как вывести его (в виде 2D-графика, 3D, отображения,...). Для несортированного 2D-графика нарисуйте график где:

  • x=i+maj*i
  • y=HSV[i][j]
  • color=(H,S,V=200);

Если вы хотите отсортировать его, просто вычислите ось x по-другому или заведите двумерный массив в порядке сортировки, а x просто увеличьте

[edit2] обновление кода и некоторых изображений

Я исправил приведенный выше код C++ (неправильный знак значения Z, изменил условие буфера Z и добавил больше точек для лучшего вывода). Ваши цвета 2D-массива могут быть такими:

HS цвета

Где одна ось / индекс H, другой S а также Value исправлено (я выбираю 200). Если ваши оси меняются местами, просто отразите их y=x Я думаю...

Сортировка цветов - это просто порядок, в котором вы выбираете все цвета из массива. например:

v=200; x=0;
for (h=0;h<256;h++)
 for (s=0;s<256;s++,x++)
 {
 y=HSV[h][s];
 // here draw line (x,0)->(x,y) by color hsv2rgb(h,s,v); 
 }

Это увеличивающийся путь. Вы можете вычислить x от H,S вместо этого, чтобы добиться другой сортировки или поменять местами fors (x++ должен быть во внутренней петле)

Если вы хотите построить график гистограммы RGB, смотрите:

Другие вопросы по тегам