I am trying to plot Histogram of lenna here the 8 bit single ch. gray scale image.
But it is not displaying the output correctly, as can be seen in the following output:
void show_histogram_image(Mat img1)
{
int sbins = 256;
int histSize[] = {sbins};
float sranges[] = { 0, 256 };
const float* ranges[] = { sranges };
cv::MatND hist;
int channels[] = {0};
cv::calcHist( &img1, 1, channels, cv::Mat(), // do not use mask
hist, 1, histSize, ranges,
true, // the histogram is uniform
false );
double maxVal=0;
minMaxLoc(hist, 0, &maxVal, 0, 0);
int xscale = 10;
int yscale = 10;
cv::Mat hist_image;
hist_image = cv::Mat::zeros(256, sbins*xscale, CV_8UC1);
for( int s = 0; s < sbins; s++ )
{
float binVal = hist.at<float>(s, 0);
int intensity = cvRound(binVal*255/maxVal);
rectangle( hist_image, cv::Point(s*xscale, 0),
cv::Point( (s+1)*xscale - 1, intensity),
cv::Scalar::all(255),
CV_FILLED );
}
imshow("Image1",hist_image);
waitKey(0);
}
Here is my main();
int main()
{
Mat img1 = imread("lena512.bmp", CV_8UC1);
if (img1.empty()) //check whether the image is valid or not
{
cout << "Error : Image cannot be read..!!" << endl;
system("pause"); //wait for a key press
return -1;
}
show_histogram_image(img1);
}
And here is the output Histogram image:
I tried changing the xscale even then it is not coming correctly.
Update
I made the following changes:
rectangle( hist_image, cv::Point(s*xscale, hist_image.rows),
cv::Point( (s+1)*xscale - 1, hist_image.rows - intensity),
cv::Scalar::all(255), CV_FILLED );
And now the output is:
It is much better , but I need lines and clearly visible bins. And it looks like some part is hidden on the right side.
Update 2
I changed CV_FILLED to '1' and now I have:
since the image origin in opencv is (0,0), and thus the y-axis is pointing downwards,
you will have to invert the y-values for your histogram-drawing:
rectangle( hist_image, cv::Point(s*xscale, hist_image.rows),
cv::Point( (s+1)*xscale - 1, hist_image.rows - intensity),
cv::Scalar::all(255),
CV_FILLED );
Related
My aim is to generate a histogram for a gray-scale image. The code I used is :
Mat img = imread("leeds-castle.jpg",IMREAD_GRAYSCALE);
Mat hst;
int hstsize = 256;
float ranges[] = { 0,256 };
float *hstrange = { ranges };
calcHist( img, 1,0, Mat(), hst, 1, &hstsize,&hstrange,true,false);
int hst_w = 512, hst_h = 400;
int bin_w = cvRound((double)hst_w / 256);
Mat histimg(hst_w, hst_h, CV_8U);
normalize(hst, hst, 0, histimg.rows, NORM_MINMAX, -1, Mat());
for (int i = 1; i < 256; i++)
{
line(histimg, Point(bin_w*(i - 1), hst_h - cvRound(hst.at<float>(i - 1))), Point(bin_w*i, hst_h - cvRound(hst.at<float>(i))), 2, 8, 0);
}
imshow("Histogram", histimg);
The only error is the usage of calcHist() function. Is there anything wrong with it?
See the comment above calcHist to identify the correct usage:
// original image
Mat img = imread("leeds-castle.jpg",IMREAD_GRAYSCALE);
// NOTE: check if img.channels is equal to 1
// histogram
Mat hst;
// number of bins
int hstsize = 256;
float ranges[] = { 0,256 };
float *hstrange = { ranges };
// parameters for histogram calculation
bool uniform = true;
bool accumulate = false;
// calculate histogram
// the '&' was missing here
calcHist( &img, 1,0, Mat(), hst, 1, &hstsize,&hstrange,true,false);
I'm trying to detect this circle
within an image with several other circles .
How would you go about doing something like this? I tried combining both colour ranges but this didn't work.
Here's my current code:
// Threshold for yellow colour on the Drop-off point
int bLowH = 25;
int bHighH = 79;
int bLowS = 0;
int bHighS = 121;
int bLowV = 87;
int bHighV = 196;
// Threshold values for red colour on the Drop-off point
int gLowH = 148;
int gHighH = 180;
int gLowS = 54;
int gHighS = 255;
int gLowV = 96;
int gHighV = 247;
Mat imgHSV;
Mat yellowRange;
Mat redRange;
cvtColor(frame, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV
//Threshold the images.. Only Keep The threshold values for the dropoff point
inRange(imgHSV, Scalar(bLowH, bLowS, bLowV), Scalar(bHighH, bHighS, bHighV), yellowRange);
inRange(imgHSV, Scalar(gLowH, gLowS, gLowV), Scalar(gHighH, gHighS, gHighV), redRange);
// combine both images and slightly blur...
Mat dropoff_image;
addWeighted(yellowRange, 1.0, redRange, 1.0, 0.0, dropoff_image);
GaussianBlur(dropoff_image, dropoff_image, Size(9, 9), 2, 2);
// Hough Transform to detect circle
vector<Vec3f> dropoff;
HoughCircles(dropoff_image, dropoff, CV_HOUGH_GRADIENT, 1, dropoff_image.rows / 8, 100, 20, 0, 0);
if (dropoff.size() == 0)
{
cout << "No dropoff circle found" << endl;
exit(-1);
}
for (size_t current_circle = 0; current_circle < dropoff.size(); ++current_circle)
{
cout << "circle found" << endl;
Point center(round(dropoff[current_circle][0]), round(dropoff[current_circle][1]));
int radius = round(dropoff[current_circle][2]);
circle(frame, center, radius, Scalar(0, 255, 0), 5);
imshow("Gaussian", dropoff_image);
}
I am a beginner in openCV.
I want to plot the intensity profile for R, G and B for the image given below.
I am like to plot R, G and B values w.r.t to pixel location in three different graphs.
So far I have learnt how to read an Image and display. for example using imread();
Mat img = imread("Apple.bmp");
and then showing it on the screen using imshow(" Window", img);.
Now I would like to put all R , G and B values in 3 separate buffers; buf1, buf2, buf3 and plot these values.
Kindly provide me some hint or a sample code snippet to help me understand this.
You can separate R, G and B into separate Mats using cv::split()
std::vector<Mat> planes(3);
cv::split(img, planes);
cv::Mat R = planes[2];
cv::Mat G = planes[1];
cv::Mat B = planes[0];
But you only need to separate them like this if you have code that is expecting a Mat with a single color channnel.
Don't use at<>() as the supposed duplicate suggest - it is really slow if you are sequentially scanning an image (but it is good for random access).
You can scan the image efficiently like this
for(int i = 0; i < img.rows; ++i)
{
// get pointers to each row
cv::Vec3b* row = img.ptr<cv::Vec3b>(i);
// now scan the row
for(int j = 0; j < img.cols; ++j)
{
cv::Vec3b pixel = row[j];
uchar r = pixel[2];
uchar g = pixel[1];
uchar b = pixel[0];
process(r, g, b);
}
}
Lastly if you do want to make a histogram, you can use this code. It is fairly old so I suppose it still works.
void show_histogram_image(cv::Mat src, cv::Mat &hist_image)
{ // based on http://docs.opencv.org/2.4.4/modules/imgproc/doc/histograms.html?highlight=histogram#calchist
int sbins = 256;
int histSize[] = {sbins};
float sranges[] = { 0, 256 };
const float* ranges[] = { sranges };
cv::MatND hist;
int channels[] = {0};
cv::calcHist( &src, 1, channels, cv::Mat(), // do not use mask
hist, 1, histSize, ranges,
true, // the histogram is uniform
false );
double maxVal=0;
minMaxLoc(hist, 0, &maxVal, 0, 0);
int xscale = 10;
int yscale = 10;
//hist_image.create(
hist_image = cv::Mat::zeros(256, sbins*xscale, CV_8UC3);
for( int s = 0; s < sbins; s++ )
{
float binVal = hist.at<float>(s, 0);
int intensity = cvRound(binVal*255/maxVal);
rectangle( hist_image, cv::Point(s*xscale, 0),
cv::Point( (s+1)*xscale - 1, intensity),
cv::Scalar::all(255),
CV_FILLED );
}
}
I calculated H-S Histograms (using opencv) of 100 images for the same object which located in different environment conditions, I need now one average histogram of these 100 histograms!
Thank you in advance
// we compute the histogram from the 0-th and 1-st channels
int channels[] = {0, 1};
calcHist( &hsv, 1, channels, Mat(), // do not use mask
hist, 2, histSize, ranges,
true, // the histogram is uniform
false );
double maxVal=0;
minMaxLoc(hist, 0, &maxVal, 0, 0);
int scale = 10;
Mat histImg = Mat::zeros(sbins*scale, hbins*10, CV_8UC3);
for( int h = 0; h < hbins; h++ )
for( int s = 0; s < sbins; s++ )
{
float binVal = hist.at<float>(h, s);
int intensity = cvRound(binVal*255/maxVal);
rectangle( histImg, Point(h*scale, s*scale),
Point( (h+1)*scale - 1, (s+1)*scale - 1),
Scalar::all(intensity),
CV_FILLED );
}
You could try using addWeighted and go through the array of histograms.
You can set the first weight to 1 and the second one to 1/100.0, and also have your final histogram array use float as the underlaying type.
I did the work by accessing the first bin in hist1, then the first bin in hist2, ....,first bin in hist 100 and find the average of first bins, and so looping for all other 3000 bins
(h_bins*S_bins=3000)
finally I get one average histogram and it works fine for advance proccedures
What I intend to do is as follows:
Fix a colored object. Track it across video frames using histogram-backprojection approach with camshift. I use the following code and it always ends up detecting the skin. I understand I am making some terribly simple mistake. It would be helpful if someone could point it out.
//I have included only the integral parts of code. There are no compilation errors.
int lowerH =80, upperH =100, lowerS =80, upperS =255, lowerV =80, upperV =255;
CvScalar output_min =cvScalar(lowerH, lowerS, lowerV, 0); //Color Track
CvScalar output_max =cvScalar(upperH, upperS, upperV, 0);
CvScalar output_min2 =cvScalar(0, lowerS, lowerV, 0); //Color Track
CvScalar output_max2 =cvScalar(180, upperS, upperV, 0);
while(true){
frame =cvQueryFrame(capture);
cvCvtColor(frame, output, CV_BGR2HSV);
cvInRangeS(output, output_min, output_max, output_mask);
blobs =CBlobResult(output_mask, NULL, 0);
blobs.Filter(blobs, B_EXCLUDE, CBlobGetArea(), B_LESS, 35);
int num_blobs =blobs.GetNumBlobs();
for(int i=0; i<num_blobs;++i){
currentBlob = blobs.GetBlob( i );
sortedBlobs.push_back(currentBlob);
}
if(num_blobs){
sort(sortedBlobs.begin(), sortedBlobs.end(), local::sortBlobs);
CvRect blobRect =sortedBlobs[0].GetBoundingBox();
initX =blobRect.x;
initY =blobRect.y;
initWidth =blobRect.width;
initHeight =blobRect.height;
initFrame =cvCloneImage(frame);
}
int c=cvWaitKey(40);
if((char)c ==27)break;
}
CvRect selection;
selection.x = initX;
selection.y = initY;
selection.width = initWidth;
selection.height = initHeight;
CvHistogram *hist;
int hist_bins = 30;
float hist_range[] = {0, 180};
float* range = hist_range;
hist = cvCreateHist(1, &hist_bins, CV_HIST_ARRAY, &range, 1);
cvCvtColor(initFrame, output, CV_BGR2HSV);
cvInRangeS(output, output_min2, output_max2, output_mask);
cvSplit(output, hue, 0, 0, 0);
cvSetImageROI(hue, selection);
cvSetImageROI(output_mask, selection);
cvCalcHist(&hue, hist, 0, output_mask);
float max_val = 0.f;
cvGetMinMaxHistValue(hist, 0, &max_val, 0, 0 );
cvConvertScale(hist->bins, hist->bins,
max_val ? 255.0/max_val : 0, 0);
cvResetImageROI(hue);
cvResetImageROI(output_mask);
CvBox2D curr_box;
CvRect prev_rect =selection;
CvConnectedComp components;
bool rectFlag =false;
CvPoint Pt =cvPoint(0,0), prevPt =cvPoint(0,0);
int clearCounter =0;
while(true){
frame =cvQueryFrame(capture);
if(!frame)break;
cvCvtColor(frame, output, CV_BGR2HSV);
cvInRangeS(output, output_min2, output_max2, output_mask);
cvSplit(output, hue, 0, 0, 0);
cvCalcBackProject(&hue, prob, hist);
cvAnd(prob, output_mask, prob, 0);
cvCamShift(prob, prev_rect, cvTermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 20, 1), &components, &curr_box);
prev_rect = components.rect;
curr_box.angle = -curr_box.angle;
cvEllipseBox(frame, curr_box, CV_RGB(255,0,0), 3, CV_AA, 0);
int c=cvWaitKey(40);
if((char)c ==27)break;
}
EDIT:
Do check the edited part of code wherein while creating the histogram I am using hue value for the mask in the range 0-180. If I use the reduced range 80-100, I get garbage values in components and curr_box.
UPDATE :
Output images
This is the green blob detected initially and should be tracked throughout the recording.
This is what happens. The green blob is completely blacked out in the mask image and instead the skin is tracked.
First of all, apologies for all the confusion. There is a very silly bug in the code. I am cloning the original frame after placing a filled red rectangle onto it.
CvPoint pt1, pt2;
pt1.x = blobRect.x;
pt1.y = blobRect.y;
pt2.x = blobRect.x + blobRect.width;
pt2.y = blobRect.y + blobRect.height;
cvRectangle( frame, pt1, pt2, cvScalar(0, 0, 255, 0), CV_FILLED, 8, 0 );
initX =blobRect.x;
initY =blobRect.y;
initWidth =blobRect.width;
initHeight =blobRect.height;
initFrame =cvCloneImage(frame);
Hence, the histogram that is created is always for the red color. The solution is trivial.