选择性直方图均衡(仅在图像的指定区域)
Posted
技术标签:
【中文标题】选择性直方图均衡(仅在图像的指定区域)【英文标题】:Selective histogram equalization (only on a specified area of the image) 【发布时间】:2015-05-07 16:42:35 【问题描述】:我正在使用 opencv 开发 Qt creator。
我必须开发一个对图像进行直方图均衡的程序。 我的图像是 16 位灰度图像,所以我不能使用 opencv 函数“equalizeHist”,因为它只适用于 8 位灰度图像。
我为此编写的代码如下:
void equalizeHist_16U(Mat* img)
long hist[65535] = 0;
double ratio;
int i, j;
assert(img->channels() == 1);
assert(img->type() == CV_16U);
ratio = 65535.0 / (img->cols*img->rows);
//Cumulative histogram calculation
compute_hist_16U(img, hist, true);
for(i=0 ; i<img->cols ; i++)
for(j=0 ; j<img->rows ; j++)
img->at<unsigned short>(j,i) = ratio*hist[img->at<unsigned short>(j,i)];
long compute_hist_16U (Mat* img, long* hist, bool cumul)
unsigned short i, j, k;
long* b;
long max = 0;
//is the image 16bits grayscale ?
assert(img->channels() == 1);
assert(CV_16U == img->type());
//histogram calculation
for(i=0 ; i<img->cols ; i++)
for(j=0 ; j<img->rows ; j++)
hist[img->at<unsigned short>(j,i)]++;
if(hist[img->at<unsigned short>(j,i)] > max)
max = hist[img->at<unsigned short>(j,i)];
//Cumulative histogram calculation (if cumul=true)
if(cumul)
for(b=hist ; b<hist+65535 ; b++)
*(b+1) += *b;
return (cumul ? hist[65535] : max);
它达到了我的预期,现在我想对我的图像进行直方图均衡化,但只在图像的指定部分进行。 我将 x1,x2,y1,y2 参数添加到我的函数中,并像这样更改了“for”的边界(我更改的代码行有箭头):
---->void equalizeHist_16U(Mat* img, int x1, int x2, int y1, int y2)
long hist[65535] = 0;
double ratio;
int i, j;
assert(img->channels() == 1);
assert(img->type() == CV_16U);
ratio = 65535.0 / (img->cols*img->rows);
//Cumulative histogram calculation
compute_hist_16U(img, hist, true);
---->for(i=x1 ; i<=x2 ; i++)
---->for(j=y1 ; j<=y2 ; j++)
img->at<unsigned short>(j,i) = ratio*hist[img->at<unsigned short>(j,i)];
---->long compute_hist_16U (Mat* img, long* hist, bool cumul, int x1, int x2, int y1, int y2)
unsigned short i, j, k;
long* b;
long max = 0;
//is the image 16bits grayscale ?
assert(img->channels() == 1);
assert(CV_16U == img->type());
//histogram calculation
---->for(i=x1 ; i<=x2 ; i++)
---->for(j=y1 ; j<=y2 ; j++)
hist[img->at<unsigned short>(j,i)]++;
if(hist[img->at<unsigned short>(j,i)] > max)
max = hist[img->at<unsigned short>(j,i)];
//Cumulative histogram calculation (if cumul=true)
if(cumul)
for(b=hist ; b<hist+65535 ; b++)
*(b+1) += *b;
return (cumul ? hist[65535] : max);
但它没有按预期工作,我的图像没有均衡,我的图像上没有极值(清晰的白色和深黑色)。 如果我尝试
equalizeHist_16U(&img, 0, 50, 0, 50)
我得到的图像非常非常明亮 如果我尝试
equalizeHist(&img, 300, 319, 220, 239)
我得到的图像非常非常暗
我想我在循环边界中犯了一个错误,但我找不到在哪里! 也许你有一个想法?
提前谢谢你
【问题讨论】:
【参考方案1】:初步: 您是否注意到您根本没有使用累积直方图函数的第二个版本?
void equalizeHist_16U(Mat* img, int x1, int x2, int y1, int y2)
正在打电话
compute_hist_16U(img, hist, true);
而不是:
long compute_hist_16U (Mat* img, long* hist, bool cumul, int x1, int x2, int y1, int y2)
(我想你想发布最后一个,否则我看不出你为什么发布代码:))
实际答案:
如果您通过operator ()
使用cv::Mat
rois,一切都会变得容易得多。
你的函数会变成如下:
void equalizeHist_16U(Mat* img, int x1, int x2, int y1, int y2)
//Here you should check you have x2 > x1 and y2 > y1 and y1,x1 >0 and x2 <= img->width and y2 <= img->height
cv::Rect roi(x1,y1,x2-x1,y2-y1);
//To reproduce exactly the behaviour you seem to target,
//it should be x2-x2+1 and y2-y1+1.
//But you should get used on the fact that extremes are,
//as a convention, excluded
cv::Mat& temp = *img; //Otherwise using operator() is complicated
cv::Mat roiMat = temp(roi); //This doesn't do any memory copy, just creates a new header!
void equalizeHist_16U(&roiMat); //Your original function!!
就是这样!如果这不起作用,则意味着您处理整个图像的原始函数存在您以前无法注意到的错误。
当我有一点时间时,我会发布一些建议以使您的函数更快(例如,您应该避免使用 .at
,您应该在结束时计算直方图中的最大值直方图计算,您应该创建一个short
的查找表,在其中将直方图乘以比率,以便应用直方图变得更快;而不是导致浮点转换的ratio
变量,您可以简单地将您的由常量(img->width*img->height)
组成的直方图元素)更简洁(你应该通过引用传递Mat
,而不是使用指针,这是 C 风格,而不是 C++)
此外:
为什么要从compute_hist_16U
返回值?
long hist[65535]
应该是long hist[65536]
,这样索引65535
才有效。首先,65535
是您图像中的白色值。此外,当您拥有b+1
和b=hist+65534
(最后一个循环)时,您可以在循环中使用它
for(b=hist ; b<hist+65535 ; b++)
*(b+1) += *b;
【讨论】:
【参考方案2】:感谢您的回答。
初步 我想我在粘贴代码时犯了一个错误,我忘记更改你注意到的那一行,这行应该是你写的最后一行。
实际答案 您的技术效果很好,无论所选区域如何,我的像素(清晰的白色和深黑色)都有极值。 唯一的问题(我没有在我的问题中提到它,所以你不可能知道)是它只保留选定的区域,图像的其余部分保持不变。 事实上,我想对图像的指定部分进行直方图计算,并将该直方图应用于我的所有图像。
我还从compute_hist_16U
中删除了返回值并将long hist[65535]
更改为long hist[65536]
我改变了传递图像的方式并删除了变量ratio
。
我使用了.at
,因为当我搜索“如何访问像素值opencv mat”时,它是访问文档和*** 中描述的像素值的方法
而且我从未见过如何创建查找表,所以当我的程序完全正常运行时,我可能会研究这个
我的新功能是:
void compute_hist_16U (Mat &img, long* hist)
unsigned short i, j;
long* b;
assert(img.channels() == 1);
assert(CV_16U == img.type());
for(i=0 ; i<=img.cols-1 ; i++)
for(j=0 ; j<=img.rows-1 ; j++)
hist[img.at<unsigned short>(j,i)]++;
//Calcul de l'histogramme cumulé
for(b=hist ; b<hist+65535 ; b++)
*(b+1) += *b;
void equalizeHist_16U(Mat &img, int x1, int x2, int y1, int y2)
long hist[65536] = 0;
double ratio;
int i,j;
assert(img.channels() == 1);
assert(img.type() == CV_16U);
assert(x1>=0 && y1>=0 && x2>x1 && y2>y1);
assert(y2<img.rows && x2<img.cols);
cv::Rect roi(x1,y1,x2-x1+1,y2-y1+1);
cv::Mat& temp = img;
cv::Mat roiMat = temp(roi);
compute_hist_16U(roiMat, hist);
for(i=0 ; i<=img.cols-1 ; i++)
for(j=0 ; j<=img.rows-1 ; j++)
img.at<unsigned short>(j,i) = 65536.0*hist[img.at<unsigned short>(j,i)] / (roiMat.cols*roiMat.rows);
void equalizeHist_16U(Mat &img)
equalizeHist_16U(img, 0, img.cols-1, 0, img.rows-1);
我认为它有效,如果我选择图像的明亮部分,我在这部分有极值(亮白色和深黑色),而图像的其余部分很暗。例如,如果我选择右边的建筑物:http://www.noelshack.com/2015-20-1431349774-result-ok.png
但有时结果很奇怪,例如如果我选择最黑的云:http://www.noelshack.com/2015-20-1431349774-result-nok.png
【讨论】:
我刚刚发现为什么我选择了一个黑暗的区域时结果很奇怪。在双循环中的函数equalizeHist_16U(...)
中,我将累积直方图乘以65536.0
而不是65535.0
现在它就像一个魅力,如果我选择一个黑暗的区域,其余的都非常清晰,如果我选择一个清晰的其余区域为深黑色。
你只接受你自己的答案吗? :) 请注意,您的问题是now I want to do the histogram equialization of my image but only on a specified part of the image
。这就是我的回答,正如你所说,我提供的解决方案有效。
对不起,我检查答案太快了,我现在有点调整! :) 我改变了我的看法,并将您的答案标记为问题的解决方案。问候以上是关于选择性直方图均衡(仅在图像的指定区域)的主要内容,如果未能解决你的问题,请参考以下文章