选择性直方图均衡化(仅在图像的指定区域)
Selective histogram equalization (only on a specified area of the image)
我正在使用 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)
我得到的图像非常非常暗
我想我在循环边界上犯了一个错误,但我找不到哪里!
也许你有想法?
提前致谢
初步:
您是否注意到您根本没有使用累积直方图函数的第二个版本?
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)
(我想你想 post 最后一个,否则我不明白你为什么 post 编辑了代码:))
实际答案:
如果您使用 cv::Mat
rois,通过 operator ()
.
,一切都会变得容易得多
你的函数将变成如下:
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!!
}
就是这样!如果这不起作用,则意味着您处理整个图像的原始函数存在您之前没有注意到的错误。
当我有时间的时候,我会 post 一些建议来让你的函数更快(例如,你应该避免使用 .at
,你应该计算最大值在直方图计算结束时的直方图中,您应该创建 short
的 table 查找,将直方图乘以比率,以便应用直方图变得更快;而不是 ratio
导致浮点转换的变量,你可以简单地将你的直方图元素除以常量 (img->width*img->height)
)并且更整洁(你应该通过引用传递 Mat
,而不是使用指针,那是 C-风格,不是 C++)
此外:
- 为什么 return 来自
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;
}
感谢您的回答。
初步
我想我在粘贴我的代码时犯了一个错误,我忘记更改你注意到的那一行,这一行应该是你写的最后一行。
实际回答
您的技术非常有效,无论 selected 区域如何,我的像素(清晰的白色和深黑色)都有极值。
唯一的问题(我没有在我的问题中提到它,所以你不可能知道)是它只保留 selected 区域,图像的其余部分没有变化。
事实上,我想对图像的指定部分进行直方图计算,并将该直方图应用于我的所有图像。
我也把compute_hist_16U
的返回值去掉,把long hist[65535]
改成了long hist[65536]
我改变了传递图像的方式并删除了变量ratio
。
我使用 .at
是因为它是访问文档中描述的像素值的方式,以及当我搜索 "how to acces pixel value opencv mat" 时在 Whosebug 上描述的方式
而且我从未见过如何创建查找 table 所以当我的程序完全正常运行时我可能会研究这个
我的新功能是:
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);
}
我认为它有效,如果我 select 我的图像的一个明亮部分,我在这部分有极值(亮白色和深黑色)并且图像的其余部分非常暗。例如,如果我select右边的建筑物:http://www.noelshack.com/2015-20-1431349774-result-ok.png
但有时结果很奇怪,例如如果我 select 最黑的云 : http://www.noelshack.com/2015-20-1431349774-result-nok.png
我正在使用 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)
我得到的图像非常非常暗
我想我在循环边界上犯了一个错误,但我找不到哪里! 也许你有想法?
提前致谢
初步:
您是否注意到您根本没有使用累积直方图函数的第二个版本?
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)
(我想你想 post 最后一个,否则我不明白你为什么 post 编辑了代码:))
实际答案:
如果您使用 cv::Mat
rois,通过 operator ()
.
你的函数将变成如下:
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!!
}
就是这样!如果这不起作用,则意味着您处理整个图像的原始函数存在您之前没有注意到的错误。
当我有时间的时候,我会 post 一些建议来让你的函数更快(例如,你应该避免使用 .at
,你应该计算最大值在直方图计算结束时的直方图中,您应该创建 short
的 table 查找,将直方图乘以比率,以便应用直方图变得更快;而不是 ratio
导致浮点转换的变量,你可以简单地将你的直方图元素除以常量 (img->width*img->height)
)并且更整洁(你应该通过引用传递 Mat
,而不是使用指针,那是 C-风格,不是 C++)
此外:
- 为什么 return 来自
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; }
感谢您的回答。
初步 我想我在粘贴我的代码时犯了一个错误,我忘记更改你注意到的那一行,这一行应该是你写的最后一行。
实际回答 您的技术非常有效,无论 selected 区域如何,我的像素(清晰的白色和深黑色)都有极值。 唯一的问题(我没有在我的问题中提到它,所以你不可能知道)是它只保留 selected 区域,图像的其余部分没有变化。 事实上,我想对图像的指定部分进行直方图计算,并将该直方图应用于我的所有图像。
我也把compute_hist_16U
的返回值去掉,把long hist[65535]
改成了long hist[65536]
我改变了传递图像的方式并删除了变量ratio
。
我使用 .at
是因为它是访问文档中描述的像素值的方式,以及当我搜索 "how to acces pixel value opencv mat" 时在 Whosebug 上描述的方式
而且我从未见过如何创建查找 table 所以当我的程序完全正常运行时我可能会研究这个
我的新功能是:
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);
}
我认为它有效,如果我 select 我的图像的一个明亮部分,我在这部分有极值(亮白色和深黑色)并且图像的其余部分非常暗。例如,如果我select右边的建筑物:http://www.noelshack.com/2015-20-1431349774-result-ok.png
但有时结果很奇怪,例如如果我 select 最黑的云 : http://www.noelshack.com/2015-20-1431349774-result-nok.png