opencv绘制二维直方图
opencv drawing a 2d histogram
我想知道如何在 opencv c++ 中绘制 HSV Mat 的二维直方图。我当前试图显示它的代码惨遭失败。我查看了如何绘制直方图,我发现的所有这些都是将它们绘制为独立的一维直方图。
这是我当前的输出,色相箱数为 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);
}
你的意思是这样的吗?
- 它是显示为 3D 图的 HSV 直方图
V
被忽略到3D(否则就是4D图形...)
如果是,那么这就是如何做到的(我不使用 OpenCV,因此请根据您的需要进行调整):
- 将源图像转换为 HSV
- 计算直方图忽略 V 值
- 无论 V 是什么,具有相同 H,S 的所有颜色都被视为单一颜色
- 您可以忽略任何其他参数,但 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
是用于 3D 显示的 Zed 缓冲区,避免 Z 排序 ...
我使用自己的图片 class 作为图片,所以一些成员是:
xs,ys
图像大小(以像素为单位)
p[y][x].dd
是 (x,y) 位置的像素为 32 位整数类型
clear(color)
- 清除整个图像
resize(xs,ys)
- 将图像调整为新分辨率
rgb2hsv()
和 hsv2rgb()
...猜猜它的作用:)
[edit1] 你的二维直方图
您似乎已将颜色编码到二维数组中。一轴是 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 图形、3D、映射...)。对于未排序的二维图绘制图,其中:
x=i+maj*i
y=HSV[i][j]
color=(H,S,V=200);
如果你想对它进行排序,那么只需以不同的方式计算 x 轴,或者按排序顺序循环二维数组,x 只是递增
[edit2] 更新代码和一些图片
我已经修复了上面的 C++ 代码(错误的 Z 值符号,更改了 Z 缓冲区条件并添加了更大的点以获得更好的输出)。您的二维数组颜色可以是这样的:
其中一个axis/index是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);
}
这是递增的方式。您可以从 H,S
计算 x
而不是实现不同的排序或交换 fors
(x++
必须在内部循环中)
如果您想要 RGB 直方图,请参阅:
我想知道如何在 opencv c++ 中绘制 HSV Mat 的二维直方图。我当前试图显示它的代码惨遭失败。我查看了如何绘制直方图,我发现的所有这些都是将它们绘制为独立的一维直方图。
这是我当前的输出,色相箱数为 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);
}
你的意思是这样的吗?
- 它是显示为 3D 图的 HSV 直方图
V
被忽略到3D(否则就是4D图形...)
如果是,那么这就是如何做到的(我不使用 OpenCV,因此请根据您的需要进行调整):
- 将源图像转换为 HSV
- 计算直方图忽略 V 值
- 无论 V 是什么,具有相同 H,S 的所有颜色都被视为单一颜色
- 您可以忽略任何其他参数,但 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
是用于 3D 显示的 Zed 缓冲区,避免 Z 排序 ...
我使用自己的图片 class 作为图片,所以一些成员是:
xs,ys
图像大小(以像素为单位)p[y][x].dd
是 (x,y) 位置的像素为 32 位整数类型clear(color)
- 清除整个图像resize(xs,ys)
- 将图像调整为新分辨率rgb2hsv()
和hsv2rgb()
...猜猜它的作用:)
[edit1] 你的二维直方图
您似乎已将颜色编码到二维数组中。一轴是 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 图形、3D、映射...)。对于未排序的二维图绘制图,其中:
x=i+maj*i
y=HSV[i][j]
color=(H,S,V=200);
如果你想对它进行排序,那么只需以不同的方式计算 x 轴,或者按排序顺序循环二维数组,x 只是递增
[edit2] 更新代码和一些图片
我已经修复了上面的 C++ 代码(错误的 Z 值符号,更改了 Z 缓冲区条件并添加了更大的点以获得更好的输出)。您的二维数组颜色可以是这样的:
其中一个axis/index是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);
}
这是递增的方式。您可以从 H,S
计算 x
而不是实现不同的排序或交换 fors
(x++
必须在内部循环中)
如果您想要 RGB 直方图,请参阅: