OpenCV рисование 2D гистограммы
Мне интересно, как построить 2-мерную гистограмму HSV Mat в opencv C++. Мой текущий код пытается отобразить его с треском. Я посмотрел вокруг, как построить гистограммы, и все, что я нашел, были те, которые изображали их как независимые 1d гистограммы.
Вот мой текущий вывод с количеством баков оттенка, равным 30, и бинов насыщения, равным 32:
Вот еще один вывод с количеством бункеров оттенка, равным 7, и бункеров насыщения, равным 5:
Я хотел бы, чтобы это выглядело больше как результат здесь
Я также заметил, что всякий раз, когда я делаю 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, поэтому настройте его под свои нужды):
- конвертировать исходное изображение в HSV
- вычислить гистограмму, игнорируя значение V
- все цвета с одинаковыми H,S считаются одним цветом, независимо от того, что V
- Вы можете игнорировать любой другой, но параметр V выглядит как лучший выбор
нарисовать график
- эллипс первого рисования с более темным цветом (базовый диск 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-массива могут быть такими:
Где одна ось / индекс 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, смотрите: