选择性直方图均衡(仅在图像的指定区域)

时间:2022-01-09 17:06:38

I am developping on Qt creator, with opencv.

我正在开发Qt创建者,使用opencv。

I have to developp a program that does the histogram equalization of an image. My images are 16bits grayscale images so I cannot use the opencv function "equalizeHist" because it only works with 8bit grayscale images.

我必须开发一个程序,对图像进行直方图均衡。我的图像是16位灰度图像,所以我不能使用opencv函数“equalizeHist”,因为它只适用于8位灰度图像。

The code I wrote to do it is the following :

我写的代码如下:

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);
}

It does what I expected, now I want to do the histogram equialization of my image but only on a specified part of the image. I added x1,x2,y1,y2 parameters to my function and changed the bounds of my "for" like this (the lines of code i changed have arrows):

它做了我的预期,现在我想做我的图像的直方图均衡,但只在图像的指定部分。我将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);
}

But it does not work as expected, my image is not equalized, I do not have extrem values (clear white and dark black) on my image. If I try

但它没有按预期工作,我的图像不均衡,我的图像上没有极值(清晰的白色和深黑色)。如果我试试

equalizeHist_16U(&img, 0, 50, 0, 50)

The image i get is very very bright And if I try

我得到的图像是非常非常明亮的,如果我尝试

equalizeHist(&img, 300, 319, 220, 239)

The image I get is very very dark

我得到的图像非常暗

I think I have made a mistake in the loops bounds but I can't find where ! Maybe you have an idea ?

我想我在循环边界上犯了一个错误,但我找不到哪里!也许你有个主意?

Thank you in advance

先感谢您

2 个解决方案

#1


Preliminary:
Did you notice you are not using at all your second version of the cumulative histogram function?

初步:您是否注意到您没有使用累积直方图函数的第二个版本?

void equalizeHist_16U(Mat* img, int x1, int x2, int y1, int y2)

is calling

compute_hist_16U(img, hist, true);

and not:

long compute_hist_16U (Mat* img, long* hist, bool cumul, int x1, int x2, int y1, int y2)

(I suppose you wanted to post the last one, otherwise I wouldn't see why you posted the code :) )

(我想你想发布最后一个,否则我不明白为什么你发布了代码:))


Actual answer:

Everything can become much easier if you use cv::Mat rois, through the operator ().

如果你通过operator()使用cv :: Mat rois,一切都会变得容易多了。

Your function would become as following:

您的功能将变为如下:

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!!

}

And that's it! If this doesn't work, then it means your original function that process the whole image has a bug that you couldn't notice before.

就是这样!如果这不起作用,那么这意味着处理整个图像的原始函数有一个你以前无法注意到的错误。

When I'll have a bit of time I'll post a couple of suggestions to make your function faster (e.g., you should avoid using .at, you should compute the max value in your histogram at the end of the histogram computation, you should create a look up table of short where you multiply your histogram by ratio, so that applying the histogram becomes much faster; and instead of the ratio variable which causes floating point conversion, you could simply divide your histogram elements by the constant (img->width*img->height)) and more neat (you should pass Mat by reference, not using a pointer, that's C-style, not C++)

当我有一点时间我会发布一些建议来使你的功能更快(例如,你应该避免使用.at,你应该在直方图计算结束时计算直方图中的最大值,你应该创建一个short的查找表,你将直方图乘以比率,这样应用直方图变得更快;而不是导致浮点转换的比率变量,你可以简单地将直方图元素除以常数(img- > width * img-> height))更整洁(你应该通过引用传递Mat,而不是使用指针,那是C风格,而不是C ++)

Furthermore:

  • Why do you return a value from compute_hist_16U?
  • 为什么从compute_hist_16U返回一个值?

  • long hist[65535] should long hist[65536], so that index 65535 is valid. First of all, 65535 is the white value in your image. Furthermore, you use it in your cycle, when you have b+1 with b=hist+65534 (the last cycle)

    long hist [65535]应该长[65536],因此索引65535有效。首先,65535是图像中的白色值。此外,你在你的周期中使用它,当你有b + 1而b = hist + 65534(最后一个周期)

    for(b=hist ; b<hist+65535 ; b++)
    {
        *(b+1) += *b;
    }
    

#2


thanks for your answer.

感谢您的回答。

Preliminary I think I have made a mistake when pasting my code, I forgot to change the line you're noticed, this line should be the last one you wrote.

初步我认为在粘贴代码时我犯了一个错误,我忘了改变你注意到的那一行,这一行应该是你写的最后一行。

Actual answer Your technique works very well, I do have extrem values for my pixels (clear white and dark black), regardless of the selected area. The only problem (I did not mention it in my question so you couldn't have known) is that it only keeps the selected area, the rest of the image is unchanged. In fact I want to do the histogram calculation on a specified part of the image, and apply that histogram to all my image.

实际答案您的技术非常有效,我的像素(清晰的白色和深黑色)都有极值,无论选择的区域如何。唯一的问题(我没有在我的问题中提及它,所以你不可能知道)是它只保留选定的区域,图像的其余部分保持不变。实际上我想在图像的指定部分进行直方图计算,并将该直方图应用于我的所有图像。

I also removed the returned value from compute_hist_16U and changed long hist[65535] to long hist[65536] I changed the way I pass the image and deleted the variable ratio. I used .at because it is the way to access pixel values that is described in the documentation and on * when I searched "how to acces pixel value opencv mat" And I have never seen how to create a lookup table so I may look into this when my program will be totally functionnal

我还从compute_hist_16U中删除了返回的值,并将long hist [65535]更改为long hist [65536]我改变了传递图像的方式并删除了变量比率。我使用.at是因为它是访问文档和*中描述的像素值的方式,当我搜索“如何访问像素值opencv mat”时我从未见过如何创建查找表,所以我可能会看一下这时我的程序将完全功能化

My new functions are :

我的新功能是:

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);
}

I think it works, if I select a bright part of my image, I have extrem values on this part (bright white and dark black) and the rest of the image is pretty dark. For example, if I select the building on the right : http://www.noelshack.com/2015-20-1431349774-result-ok.png

我认为它有效,如果我选择了我的图像的明亮部分,我有这个部分的极值(亮白色和深黑色),图像的其余部分非常暗。例如,如果我选择右侧的建筑物:http://www.noelshack.com/2015-20-1431349774-result-ok.png

But the result is weird sometimes, for example if I select the blackest clouds : http://www.noelshack.com/2015-20-1431349774-result-nok.png

但结果有时候很奇怪,例如,如果我选择最黑的云:http://www.noelshack.com/2015-20-1431349774-result-nok.png

#1


Preliminary:
Did you notice you are not using at all your second version of the cumulative histogram function?

初步:您是否注意到您没有使用累积直方图函数的第二个版本?

void equalizeHist_16U(Mat* img, int x1, int x2, int y1, int y2)

is calling

compute_hist_16U(img, hist, true);

and not:

long compute_hist_16U (Mat* img, long* hist, bool cumul, int x1, int x2, int y1, int y2)

(I suppose you wanted to post the last one, otherwise I wouldn't see why you posted the code :) )

(我想你想发布最后一个,否则我不明白为什么你发布了代码:))


Actual answer:

Everything can become much easier if you use cv::Mat rois, through the operator ().

如果你通过operator()使用cv :: Mat rois,一切都会变得容易多了。

Your function would become as following:

您的功能将变为如下:

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!!

}

And that's it! If this doesn't work, then it means your original function that process the whole image has a bug that you couldn't notice before.

就是这样!如果这不起作用,那么这意味着处理整个图像的原始函数有一个你以前无法注意到的错误。

When I'll have a bit of time I'll post a couple of suggestions to make your function faster (e.g., you should avoid using .at, you should compute the max value in your histogram at the end of the histogram computation, you should create a look up table of short where you multiply your histogram by ratio, so that applying the histogram becomes much faster; and instead of the ratio variable which causes floating point conversion, you could simply divide your histogram elements by the constant (img->width*img->height)) and more neat (you should pass Mat by reference, not using a pointer, that's C-style, not C++)

当我有一点时间我会发布一些建议来使你的功能更快(例如,你应该避免使用.at,你应该在直方图计算结束时计算直方图中的最大值,你应该创建一个short的查找表,你将直方图乘以比率,这样应用直方图变得更快;而不是导致浮点转换的比率变量,你可以简单地将直方图元素除以常数(img- > width * img-> height))更整洁(你应该通过引用传递Mat,而不是使用指针,那是C风格,而不是C ++)

Furthermore:

  • Why do you return a value from compute_hist_16U?
  • 为什么从compute_hist_16U返回一个值?

  • long hist[65535] should long hist[65536], so that index 65535 is valid. First of all, 65535 is the white value in your image. Furthermore, you use it in your cycle, when you have b+1 with b=hist+65534 (the last cycle)

    long hist [65535]应该长[65536],因此索引65535有效。首先,65535是图像中的白色值。此外,你在你的周期中使用它,当你有b + 1而b = hist + 65534(最后一个周期)

    for(b=hist ; b<hist+65535 ; b++)
    {
        *(b+1) += *b;
    }
    

#2


thanks for your answer.

感谢您的回答。

Preliminary I think I have made a mistake when pasting my code, I forgot to change the line you're noticed, this line should be the last one you wrote.

初步我认为在粘贴代码时我犯了一个错误,我忘了改变你注意到的那一行,这一行应该是你写的最后一行。

Actual answer Your technique works very well, I do have extrem values for my pixels (clear white and dark black), regardless of the selected area. The only problem (I did not mention it in my question so you couldn't have known) is that it only keeps the selected area, the rest of the image is unchanged. In fact I want to do the histogram calculation on a specified part of the image, and apply that histogram to all my image.

实际答案您的技术非常有效,我的像素(清晰的白色和深黑色)都有极值,无论选择的区域如何。唯一的问题(我没有在我的问题中提及它,所以你不可能知道)是它只保留选定的区域,图像的其余部分保持不变。实际上我想在图像的指定部分进行直方图计算,并将该直方图应用于我的所有图像。

I also removed the returned value from compute_hist_16U and changed long hist[65535] to long hist[65536] I changed the way I pass the image and deleted the variable ratio. I used .at because it is the way to access pixel values that is described in the documentation and on * when I searched "how to acces pixel value opencv mat" And I have never seen how to create a lookup table so I may look into this when my program will be totally functionnal

我还从compute_hist_16U中删除了返回的值,并将long hist [65535]更改为long hist [65536]我改变了传递图像的方式并删除了变量比率。我使用.at是因为它是访问文档和*中描述的像素值的方式,当我搜索“如何访问像素值opencv mat”时我从未见过如何创建查找表,所以我可能会看一下这时我的程序将完全功能化

My new functions are :

我的新功能是:

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);
}

I think it works, if I select a bright part of my image, I have extrem values on this part (bright white and dark black) and the rest of the image is pretty dark. For example, if I select the building on the right : http://www.noelshack.com/2015-20-1431349774-result-ok.png

我认为它有效,如果我选择了我的图像的明亮部分,我有这个部分的极值(亮白色和深黑色),图像的其余部分非常暗。例如,如果我选择右侧的建筑物:http://www.noelshack.com/2015-20-1431349774-result-ok.png

But the result is weird sometimes, for example if I select the blackest clouds : http://www.noelshack.com/2015-20-1431349774-result-nok.png

但结果有时候很奇怪,例如,如果我选择最黑的云:http://www.noelshack.com/2015-20-1431349774-result-nok.png