Ask Your Question
0

How to get histogram of a rectangular area (ROI) of an image?

asked 2012-11-05 22:42:00 -0600

SeeVee gravatar image

What I am trying to accomplish is to read an image from a JPEG file, display it, select a rectangular region, and calculate a histogram of the HSV transform of the image. The book "Learning OpenCV" suggests that setting ROI will cause operations to be performed only on that part of the image, but that doesn't seem to be true for the histogram.

I am an OpenCV novice, and would appreciate any suggestions.

Here is my code:

CvRect ROI;
IplImage *m_cvImage = cvLoadImage( (LPCTSTR)ar.m_strFileName ); // read JPEG image from file
ROI.x=0; ROI.y=0; ROI.width=m_cvImage->width; ROI.height=m_cvImage->height;
// Make the histogram of entire image
CvHistogram *hist;
int err=HueSaturationHistogram(m_cvImage, ROI, false/*visualizeHist*/, hist );
...
// Change ROI according to selection with mouse
// Recalculate the image HSV histogram
HueSaturationHistogram(m_cvImage, ROI, false/*visualizeHist*/, hist);

and the function HueSaturationHistogram is

    HueSaturationHistogram( IplImage* src, const CvRect rect, const bool visualizeHist,
 CvHistogram *& hist )
    {
        // Set the part of the image we will be processing
        cvSetImageROI(src,rect);
        CvSize roiSize; roiSize.width=rect.width; roiSize.height=rect.height;
        // Compute the HSV image, and decompose it into separate planes.
        IplImage* hsv = cvCreateImage( roiSize, 8, 3 ); 
        cvCvtColor( src, hsv, CV_BGR2HSV ); // RGB to HSV (hue, saturation, value)

        IplImage* h_plane  = cvCreateImage( roiSize, 8, 1 );
        IplImage* s_plane  = cvCreateImage( roiSize, 8, 1 );
        IplImage* v_plane  = cvCreateImage( roiSize, 8, 1 );
        IplImage* planes[] = { h_plane, s_plane, v_plane };
        cvCvtPixToPlane( hsv, h_plane, s_plane, v_plane, 0 );

        // Build the histogram and compute its contents.
        int nH = 30, nS = 32, nV=30; 
        float  h_ranges[2], s_ranges[2], v_ranges[2];   // H is units of 2 deg
        h_ranges[0] = 0; h_ranges[1] = 180; // hue is [0,180], because 0-360 is divided by 2 (i e, units of 2 deg)
        s_ranges[0] = 0, s_ranges[1] = 255; 
        v_ranges[0] = 0; v_ranges[1] = 255; 
        {
        int hist_size[] = { nH, nS, nV };
        float* ranges[] = { h_ranges, s_ranges, v_ranges };
        hist = cvCreateHist( 
        3, 
        hist_size, 
        CV_HIST_ARRAY, 
        ranges, 
        1 // uniform
        ); 
        }
    cvCalcHist( planes, hist, 0, 0 );
    ...
    }

64-bit Windows 7, Visual Studio 10, C++

edit retag flag offensive close merge delete

3 answers

Sort by ยป oldest newest most voted
4

answered 2012-11-06 01:33:57 -0600

Michael Burdinov gravatar image

updated 2012-11-06 03:15:30 -0600

I am not sure that "Learning OpenCV" is a good book for novice, because it is quite outdated. It was writen for OpenCV 1.0. OpenCV 2.4.3 was reased few days ago. OpenCV 1.0 had very difficult non-intuitive inteface, one of the issues that was fixed in OpenCV 2. And so you are learning how to do things in a complicate way and use functions some of which are already depricated. Don't take it wrong, this is a very good book that was very helpful back then, but I don't think it is good for you now.

As for your question, some of those old functions indeed ignored ROI even if it is was defined. I am not sure but it is possible that cvCalcHist was one of them. New image format of OpenCV (called Mat) don't even has such field as ROI. Instead you can define partial images in convenient way that has same function as ROI (and of course without memory copy). Your code will look like:

Mat whole_image = imread(image_name);
Rect ROI;
...
Mat part_of_image(whole_image, ROI);

And then you provide part_of_image to calcHist and do the rest of the job.

Here you can read more about interface of calcHist and see example of its use.

edit flag offensive delete link more

Comments

Thank you for your answer--this is valuable advice.

The authors of Learning (copyright 2008) claim it is based on OpenCV 2.0, but there is certainly outdated material.

I should have said I am using OpenCV 2.4.2. I also have "OpenCV 2 Computer Vision Application Programming Cookbook", which I have used less (I didn't understand there was a different interface).

I also attempted to look at the source code for calcHist to see if it used the ROI, but there doesn't seem to be any guide to or documentation of the source, and it is huge.

The construction of part_of_image that you show is just what I need. I wanted to avoid memory copying, because the histogram will be updated in response to user input, and so must be quick.

SeeVee gravatar imageSeeVee ( 2012-11-06 10:43:53 -0600 )edit
0

answered 2012-11-06 04:38:14 -0600

updated 2012-11-06 05:09:18 -0600

In the following code you have to call bhDisplayHSVHistogram for display HSI channels histogram.

IplImage* bhGet8Depth(const IplImage* srcImage,int zeroFlag)
{
    IplImage* resultImg = cvCreateImage(cvGetSize(srcImage),8,1);
    if (zeroFlag == 1)
        cvZero(resultImg);
    else if (zeroFlag == 2)
    {
        if (srcImage->depth == 8 )
        {
            if ( srcImage->nChannels == 3)
              cvCvtColor(srcImage,resultImg,CV_BGR2GRAY);
            else if( srcImage->nChannels == 1)
                cvCopyImage(srcImage,resultImg);

        }
        else if (srcImage->depth == IPL_DEPTH_32F && srcImage->nChannels ==1)
        {
            cvNormalize(srcImage,resultImg,0,255,CV_MINMAX);
        }
    }
    return  resultImg;
}

void bhGet1DHistogram(IplImage* srcImage ,CvHistogram** hist, int size, IplImage* mask)
{
    int sizes[] = {size};
    float ranges1[] = {0,float(size)};
    float* ranges[] = {ranges1};
    *hist = cvCreateHist(1,sizes,CV_HIST_ARRAY,ranges,1);
    if (mask != 0)
       cvCalcHist(&srcImage,*hist,0,mask);
    else cvCalcHist(&srcImage,*hist,0);


}

void bhShow1DHistogram( CvHistogram* hist,IplImage** histImage,int height)
{
    *histImage = cvCreateImage(cvSize(hist->mat.dim[0].size,height),8,1);
    cvZero(*histImage);
    float min_value, max_value;
    cvGetMinMaxHistValue(hist,&min_value,&max_value);
    float* pHist = hist->mat.data.fl;
    for (int j=0; j < hist->mat.dim[0].size; j++)
    {
        int value = cvRound( pHist[j] / max_value * height);
        cvLine(*histImage,cvPoint(j,height),cvPoint(j,height- value),cvScalarAll(255))   ;
    }

}

void bhDisplayHSVHistogram(IplImage* srcImage)
{
    IplImage* hsvImg = cvCreateImage(cvGetSize(srcImage),8,3);
    cvCvtColor(srcImage,hsvImg,CV_BGR2HSV);

    IplImage* hImg = bhGet8Depth(srcImage);
    IplImage* sImg = bhGet8Depth(srcImage);
    IplImage* vImg = bhGet8Depth(srcImage);

    cvSplit(hsvImg,hImg,sImg,vImg,0);

CvRect ROI = cvGetImageROI(srcImage);

maskImage = bhGet8Depth(srcImage,1);
cvRectangleR(maskImage,ROI,cvScalarAll(255),-1);

    CvHistogram *histH,*histS,*histI;
    bhGet1DHistogram(hImg,&histH,256,maskImage);
    bhGet1DHistogram(sImg,&histS,256,maskImage);
    bhGet1DHistogram(vImg,&histI,256,maskImage);

    cvReleaseImage(&maskImage);
    IplImage *histImgH ,*histImgS,*histImgI;
    bhShow1DHistogram(histH,&histImgH,100);
    bhShow1DHistogram(histS,&histImgS,100);
    bhShow1DHistogram(histI,&histImgI,100);



    cvShowImage("Hue Histogram",histImgH);
    cvShowImage("Saturation Histogram",histImgS);
    cvShowImage("Intensity Histogram",histImgI);

    cvWaitKey(0);


    cvDestroyWindow("Hue Histogram");
    cvDestroyWindow("Saturation Histogram");
    cvDestroyWindow("Intensity Histogram");

    cvReleaseHist(&histH);
    cvReleaseHist(&histS);
    cvReleaseHist(&histI);

    cvReleaseImage(&histImgH);
    cvReleaseImage(&histImgS);
    cvReleaseImage(&histImgI);

    cvReleaseImage(&hImg);
    cvReleaseImage(&sImg);
    cvReleaseImage(&vImg);

    cvReleaseImage(&hsvImg);

}
edit flag offensive delete link more

Comments

I am unsure what your code does, or how it relates to my question. My code successfully constructs a histogram, and I am able to use the histogram (in code which I did not show); it's just that I want the histogram for a rectangular part of the original image, without having to copy the image part to a new image every time the ROI is changed.

SeeVee gravatar imageSeeVee ( 2012-11-06 10:46:46 -0600 )edit
0

answered 2012-12-29 11:14:31 -0600

paj006@gmail.com gravatar image

Your code is fine, but just do it in OpenCV 2.0.

edit flag offensive delete link more

Question Tools

Stats

Asked: 2012-11-05 22:42:00 -0600

Seen: 7,111 times

Last updated: Dec 29 '12