选择性直方图均衡(仅在图像的指定区域)
Selective histogram equalization (only on a specified area of the image)
我正在qt创建者上发展,
我必须开发一个程序,该程序执行图像的直方图均衡。我的图像是16位灰度图像,因此我无法使用"均衡史"的OpenCV函数,因为它仅适用于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)
(我想您想发布最后一个,否则我不会明白您为什么发布代码:))
实际答案:
如果您使用cv::Mat
ROI,通过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!!
}
就是这样!如果这不起作用,则意味着您的原始功能该处理整个图像具有您以前看不到的错误。
当我有一些时间时,我将发布一些建议以使您的功能更快(例如,您应该避免使用.at
,应在直方图计算结束时计算直方图中的最大值,您应该创建一个查找short
的查找表,其中您将直方图乘以乘比率,以便应用直方图变得更快;而不是导致浮点转换的ratio
变量,您可以简单地将直方图元素除以常数(img->width*img->height)
)和更整洁的(您应该通过参考来传递Mat
,而不是使用指针,那是C风格,而不是C )
此外:
- 为什么您从
compute_hist_16U
返回值? long hist[65535]
应该long hist[65536]
,以便索引65535
有效。首先,65535
是图像中的白色值。此外,当您使用b=hist+65534
(最后一个周期)的b+1
时,您可以在周期中使用它for(b=hist ; b<hist+65535 ; b++) { *(b+1) += *b; }
感谢您的回答。
初步我想我在粘贴代码时犯了一个错误,我忘了更改您注意到的行,这一行应该是您写的最后一行。
实际答案您的技术效果很好,无论选定区域如何,我的像素(透明白色和深色黑色)确实具有极端值。唯一的问题(我没有在问题中提及它,所以您不知道)是它只能保留所选区域,其余图像没有变化。实际上,我想对图像的指定部分进行直方图计算,并将该直方图应用于我的所有图像。
我还从compute_hist_16U
删除了返回的值,并将long hist[65535]
更改为long hist[65536]
我更改了传递图像的方式,并删除了变量ratio
。我使用了.at
,因为它是访问文档中描述的像素值的方法,当我搜索"如何加入Pixel value 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-20-1431349774-Result-nok.png
- 在图像上的矩形区域之外填充黑色
- 在绘制信号上保留绘图区域"图像"
- 忽略父 Qt 快速图像的透明区域的鼠标区域单击事件
- 使用C 中的WXWIDGET使用可拖动矩形从图像中选择区域
- 选择性直方图均衡(仅在图像的指定区域)
- Qt图像中的可点击区域
- 用于在黑白图像上查找未连接区域(岛屿)的算法
- 如何从图像中提取ROI区域并将其显示在窗口中
- 检测图像中的区域.QT中的重叠小部件是可能的
- OpenCV 标签连接和计算图像区域的特征测量值
- 用于删除图像中的非文本区域的开源代码
- 如何为视口应用程序重新采样图像的子区域
- 如何在MFC对话框的静态区域中绘制图像
- 将图像划分为区域,并为其指定标签?使用指针
- 在Leptonica中获得Pix类型图像的感兴趣区域(ROI)
- 在opencvc++中设置图像区域为零的最佳方法
- GTKMM::在GTk::绘图区域使用单个Gdk::PixBuf绘制多个图像
- 在雷达图像上绘制区域而不是点
- 如何确定感兴趣的区域,然后使用opencvc++裁剪图像
- c++计算图像中每个区域的2 3x3协方差矩阵