How to use OpenCV's histogram? - c++

I'm struggling with taking the histogram of floating point data in OpenCV:
cv::ocl::setUseOpenCL(true);
auto rows = 2048;
auto cols = 2064;
auto input_d = cv::UMat(rows, cols, CV_32F, cv::USAGE_ALLOCATE_DEVICE_MEMORY);
cv::UMat hist_d;
cv::randn(input_d, 0, 0.5);
std::vector<int> channels = { 0 };
std::vector<int> histSize = { 256 };
std::vector<float> ranges = { 0, 1 };//run the histogram to track values 0 to 1
cv::calcHist(input_d, channels, cv::UMat(), hist_d, histSize, ranges, false);
I'm getting an error like:
OpenCV Error: Assertion failed (0 <= _rowRange.start && _rowRange.start <= _rowRange.end && _rowRange.end <= m.rows) in cv::Mat::Mat, file src\matrix.cpp, line 452
Anybody know how to use this function?
The following code works, but the computation doesn't happen on the GPU
auto rows = 2048;
auto cols = 2064;
auto input_d = cv::Mat(rows, cols, CV_32F);
cv::MatND hist_d;
cv::randn(input_d, 0, 0.5);
int histSize[1] = { 256 };
float hranges[2] = { 0.0, 256.0 };
const float* range[1] = { hranges };
int channels[1] = { 0 };
cv::calcHist(&input_d, 1, channels, cv::Mat(), hist_d, 1, histSize, range);
I suspect that foo should not be size zero, but I don't understand whats going on.
cv::InputArray& foo = input_d;
cv::calcHist(foo, channels, cv::UMat(), hist_d, histSize, ranges, false);

Looks like it needs to be wrapped.
std::vector<cv::UMat> foo = { input_d };// should ref count, and avoid a deep copy?
cv::calcHist(foo, channels, cv::UMat(), hist_d, histSize, ranges, false);

Here's how I've been able to do it with cv::UMat:
std::vector<int> channels = { 0 };
std::vector<int> histSize = { 256 };
std::vector<float> range = { 0, 256 };
std::vector<cv::UMat> foo = { oInputMat };
cv::calcHist(foo, channels, cv::UMat(), oHistogram, histSize, range, false);

Related

OpenCV Histogram Mat to Bitmap for Picturebox

I use a FLIR Camera (Grasshopper3) and the SDK (Spinnaker) to take an image (Mono8). After converting the image, I wuold like to compute the Histogram and display it in my GUI in a picturebox (C++ CLR/CLI .net environment). For this, I need to convert it, but I guess there is a mistake in the color conversion or the BitMap creation.
Here is the code:
Spinnaker::ImagePtr convertedImage_MONO = Grasshopper3.pResultImage_MONO->Convert(Spinnaker::PixelFormat_Mono8, Spinnaker::NO_COLOR_PROCESSING); // Raw image is converted to Mono8
unsigned int XPadding = convertedImage_MONO->GetXPadding();
unsigned int YPadding = convertedImage_MONO->GetYPadding();
unsigned int rowsize = convertedImage_MONO->GetWidth();
unsigned int colsize = convertedImage_MONO->GetHeight();
//image data contains padding. When allocating Mat container size, you need to account for the X,Y image data padding.
cv::Mat cvimg_Mono = cv::Mat(colsize + YPadding, rowsize + XPadding, CV_8UC1, convertedImage_MONO->GetData(), convertedImage_MONO->GetStride());
cvtColor(cvimg_Mono, cvimg_Mono, cv::COLOR_BGR2BGRA);
// Histogram
int bins = 256;
int histSize[] = { bins };
// Set ranges for histogram bins
float lranges[] = { 0, 256 };
const float* ranges[] = { lranges };
// create matrix for histogram
cv::Mat hist;
int channels[] = { 0 };
// create matrix for histogram visualization
int const hist_height = 256;
cv::Mat3b hist_image = cv::Mat3b::zeros(hist_height, bins);
cv::calcHist(&cvimg_Mono, 1, channels, cv::Mat(), hist, 1, histSize, ranges, true, false);
double max_val = 0;
minMaxLoc(hist, 0, &max_val);
// visualize each bin
for (int b = 0; b < bins; b++)
{
float const binVal = hist.at<float>(b);
int const height = cvRound(binVal*hist_height / max_val);
cv::line(hist_image, cv::Point(b, hist_height - height), cv::Point(b, hist_height), cv::Scalar::all(255));
}
cv::Mat Histogram_Mono = hist_image;
cv::resize(Histogram_Mono, Histogram_Mono, cv::Size(pictureBox_Mono->Width, pictureBox_Mono->Height), cv::INTER_AREA);
hBit_Mono = CreateBitmap(Histogram_Mono.cols, Histogram_Mono.rows, 1, 32, Histogram_Mono.data); // hBit_Mono was created global
bmp_Mono = Bitmap::FromHbitmap((IntPtr)hBit_Mono); // bmp_Mono was created as a global Bitmap
pictureBox_Mono->Image = bmp_Mono;

Improper usage of calcHist

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

How to set the parameters of OpenCV3 calcHist() using vectors?

I am calculating a histogram from a greyscale image using the above code; it is working fine.
cv::Mat imgSrc = cv::imread("Image.jpg", cv::IMREAD_UNCHANGED);
cv::Mat histogram; //Array for the histogram
int channels[] = {0};
int binNumArray[] = {256};
float intensityRanges[] = { 0, 256 };
const float* ranges[] = { intensityRanges };
cv::calcHist( &imgSrc, 1, channels, cv::noArray(), histogram, 1, binNumArray, ranges, true, false) ;
In the book of Kaehler & Bradski they refer to this as the "old-fashioned C-style arrays" and they say the new style would use STL vector templates, and the arrays of images from which to calculate the histogram are to be given using cv::InputArrayOfArrays. However, if I try to replace for example the channel array by:
std::vector channels {0};
Gives compilation error.So my questions are these:
1. How can I define the arrays of the 'channels', 'binNumArray', 'intensityRanges' using vectors?
2. How can I define the array of input images using cv::InputArrayOfArrays?
This example show both the "old" approach and the "new" approach, so you can appreciate the difference. It's based on the example found in the OpenCV documentation.
The "new" approach is just a convenience wrapper that internally calls the "old" one.
#include <opencv2\opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
int main()
{
Mat3b src = imread("path_to_image");
Mat3b hsv;
cvtColor(src, hsv, CV_BGR2HSV);
Mat hist;
Mat hist2;
{
// Quantize the hue to 30 levels
// and the saturation to 32 levels
int hbins = 30, sbins = 32;
int histSize[] = { hbins, sbins };
// hue varies from 0 to 179, see cvtColor
float hranges[] = { 0, 180 };
// saturation varies from 0 (black-gray-white) to
// 255 (pure spectrum color)
float sranges[] = { 0, 256 };
const float* ranges[] = { hranges, sranges };
// 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);
}
{
// Quantize the hue to 30 levels
// and the saturation to 32 levels
vector<int> histSize = { 30, 32 };
// hue varies from 0 to 179, see cvtColor
// saturation varies from 0 (black-gray-white) to
// 255 (pure spectrum color)
vector<float> ranges = { 0, 180, 0, 256 };
// we compute the histogram from the 0-th and 1-st channels
vector<int> channels = { 0, 1 };
vector<Mat> mats = { hsv };
calcHist(mats, channels, Mat(), // do not use mask
hist2, histSize, ranges,
/*true, // the histogram is uniform, this is ALWAYS true*/
false);
}
return 0;
}

Drawing intensity profile for RGB using openCV

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

OpenCV - Confusion using calcHist

I've read the documentation for calcHist() many times, but I think my inexperience with OpenCV and rusty programming skills are completely precluding me from understanding it.
I'm looking to count pixels in one channel of an HSV image (Hue, or channel[0]) for segmentation purposes using 10 bins that closely approximate color according to something like (let's use this as an example, I stole the ranges off the web - fwiw, it seems erroneous to omit purple-red):
Red: 0-19 & 330-360
Red-Yellow (RY): 20-49
Yellow: 50-69
YG: 70-84
Green: 85-170
GB: 171-191
Blue: 192-264
BP: 265-289
Purple: 290-329
And so on...
So how do I do this with calcHist?
I'm as far as:
#include <opencv2/opencv.hpp>
#include <vector>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
Mat scene, sceneHSV, dest, histo;
int numImages = 1, histChannel[] = {0}, dims = 1, histSize[] = {10};
float redRange[] = {0, 10};
float roRange[] = {10, 25};
float orangeRange[] = {25, 35};
float oyRange[] = {35, 42};
float yellowRange[] = {42, 85};
float ygRange[] = {85, 96};
float greenRange[] = {96, 132};
float gbRange[] = {132, 145};
float blueRange[] = {145, 160};
float bpRange[] = {160, 165};
float purpleRange[] = {165, 180};
const float* ranges[] = {redRange, roRange, orangeRange, oyRange, yellowRange, ygRange, greenRange, gbRange, blueRange, bpRange, purpleRange};
vector<Mat> channels;
scene = imread("Apple.jpg", 1);
if (scene.data == NULL)
{
cout<<"FAIL"<<endl;
cin.get();
}
cvtColor(scene, sceneHSV, CV_BGR2HSV);
dilate(sceneHSV, sceneHSV, Mat(), Point(-1, -1), 1, BORDER_CONSTANT, 1);
pyrMeanShiftFiltering(sceneHSV, dest, 2, 50, 3);
split(sceneHSV, channels);
calcHist(&scene, 1, histChannel, Mat(), histo, dims, histSize, ranges, false, false);
cout<<histo<<endl;
waitKey(0);
return 0;
}
Now what? What would the arguments to calcHist look like in this case, and what does the output histogram look like? Simply a 1x9 array full of ints?
Thanks very much.
I modified the code from here
You might also want to take a look at the documentation of cvtColor here
Note that I have not tried to compile or run this code so I do not guarantee that it will work. But nonetheless, it might be useful as a reference.
Mat hist;
int nimages = 1; // Only 1 image, that is the Mat scene.
int channels[] = {0} // Index for hue channel
int dims = 1 // Only 1 channel, the hue channel
int histSize[] = {9} // 9 bins, 1 each for Red, RY, Yellow, YG etc.
float hranges[] = { 0, 180 }; // hue varies from 0 to 179, see cvtColor
const float *ranges[] = {hranges};
// Compute the histogram.
calcHist(&scene,
nimages,
channels,
Mat(), // No mask
hist, dims, histSize, ranges, uniform=true)
// Now hist will contain the counts in each bin.
// Lets just print out the values. Note that you can output Mat using std::cout
cout << "Histogram: " << endl << hist << endl;
// To access the individual bins, you can either iterate over them
// or use hist.at<uchar>(i, j); Note that one of the index should be 0
// because hist is 1D histogram. Print out hist.rows and hist.cols to see if hist is a N x 1 or 1 x N matrix.
/*
MatIterator_<uchar> it, end;
int binIndex = 0;
for( it = hist.begin<uchar>(), end = hist.end<uchar>(); it != end; ++it)
{
printf("Count in %d bin: %d\n", binIndex, *it);
++binIndex;
}
*/