opencv calcHist results are not what expected - c++

In openCV, I have a matrix of integers (a 4000x1 Mat). Each time I read different ranges of this matrix: Mat labelsForHist = labels(Range(from,to),Range(0,1));
The size of the ranges is variable. Then I convert the labelsForHist matrix to float(because calcHist doesnt accept int values!) by using:
labelsForHist.convertTo(labelsForHistFloat, CV_32F);
After this I call calcHist with these parameters:
Mat hist;
int histSize = 4000;
float range[] = { 0, 4000 } ;
int channels[] = {0};
const float* histRange = { range };
bool uniform = true; bool accumulate = false;
calcHist(&labelsForHistFloat,1,channels,Mat(),hist,1,&histSize,&histRange,uniform,accumulate);
The results are normalized by using:
normalize(hist,hist,1,0,NORM_L1,-1,Mat());
The problem is that my histograms doesn't look like what I was expecting. Any idea on what I am doing wrong or does the problem come from other part of the code (and not calculation of histograms)?
I expect this sparse histogram:
while I get this flat histogram, for same data:
The first hist was calculated in python, but I want to do the same in c++
There is a clustering process before calculating histograms, so if there is no problem with creating histograms then deffinitly the problem comes from before that in clustering part!

Related

OpenCV Mat Problem: Difference between Histogram and this loop

i am working on image processing project that i want to implement it on cuda with opencv (opencv 4.0 with cuda suport)and i am not good at c++.
for color correction between two images, i am using code from this link: (https://answers.opencv.org/question/178127/matching-colors-between-two-pictures-in-opencv/)
my goal is to implement this code on GPU. for that i tried to rewrite that code. i faced two problems:
1- Is there any Cuda implemented library for this purpose? (Same Functionality)
2- in rewriting function ((do1ChnHist)), it seams that this loop calculates 1D histogram (Is that true?) :
for (size_t p = 0; p<img.total(); p++)
{
if (mask(p) > 0)
{
uchar c = img(p);
h(c) += 1.0;
}
}
but i can't replace it with :
int histSize = 256;
float range[] = { 0, 256 }; //the upper boundary is exclusive
const float* histRange = { range };
bool uniform = false, accumulate = false;
calcHist(&img, 1, 0, Mat(), h, 1, &histSize, &histRange, uniform, accumulate);
or rewrite it with this loop (For changing Mat >> GpuMat in future. unfortunately Opencv_cuda does not support GpuMat_<>, due to that i tried to rewrite loop with Mat):
Mat h;
h = Mat::zeros(cv::Size(256, 1), CV_16U);
uchar x;
for (size_t m = 0; m < img.size().width; m++)
{
for (size_t n = 0; n < img.size().width; n++)
{
x = img.at<int>(Point(m, n));
h.at<int>(Point(int(x),0)) += 1;
}
}
because ether of two options return different answer from main loop in do1ChnHist function...
thanks...
Opencv has all the function u want
virtual void cv::cuda::TemplateMatching::match ( InputArray image,
InputArray templ,
OutputArray result,
Stream & stream = Stream::Null()
)
void cv::cuda::calcHist (InputArray src, OutputArray hist, Stream &stream=Stream::Null())
Calculates histogram for one channel 8-bit image. More...
void cv::cuda::calcHist (InputArray src, InputArray mask, OutputArray hist, Stream &stream=Stream::Null())
Calculates histogram for one channel 8-bit image confined in given mask. More...
depends, could be 1D array, and could be 2D array, depends on color. You should learn some basic image processing principle first.

Mapping pixels/elements to percentiles (C++/OpenCV)

I am using OpenCV and I have the following problem I wish to solve on an image. However, my question doesn't have to be restricted to OpenCV if it is an issue, it could simply be more general c++ on, say, vectors.
The problem
I want to map the pixel values of an input image (or elements of an input vector) into an output image (or vector) of same size. Each element of the output should contain the percentile* of the corresponding element in the input.
My question is
How would I code an implementation of this problem? (In c++ or OpenCV)
Example
I will show an example of a one-dimensional image, just for simplicity.
Input:
(1, 2, 10, 3, 4, 5, 6, 12, 7)
Output*: (0.11, 0.22, 0.89, 0.33, 0.44, 0.56, 0.67, 1.00, 0.78)
Performance?
I'm writing code for an analysis of images that may be a few hundred by a few hundred. I assume that my problem is possible in O(n log n), but I don't think even O(n^2) would be an issue (where n is the total number of elements in the image/vector). However, if precise percentiles cause too much issues with the complexity, I'm okay with having a certain number of bins instead.
(*) I know that there are a few different ways of conceptualizing percentile, whether you round up or down et cetera. This is of no importance to me. Whatever way works.
I am not sure it this is what you're looking for but this a naive implementation of percentile for image pixel values.
cv::Mat image = cv::imread("image.jpg",cv::IMREAD_UNCHANGED);
// convert image to gray
cv::Mat gray;
cv::cvtColor(image, gray, cv::COLOR_BGR2GRAY);
// calculate histogram for every pixel value (i.e [0 - 255])
cv::Mat hist;
int histSize = 256;
float range[] = { 0, 256 } ;
const float* histRange = { range };
bool uniform = true; bool accumulate = false;
cv::calcHist( &gray, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange, uniform, accumulate );
// total pixels in image
float totalPixels = gray.cols * gray.rows;
// calculate percentage of every histogram bin (i.e: pixel value [0 - 255])
// the 'bins' variable holds pairs of (int pixelValue, float percentage)
std::vector<std::pair<int, float>> bins;
float percentage;
for(int i = 0; i < 256; ++i)
{
percentage = (hist.at<float>(i,0)*100.0)/totalPixels;
bins.push_back(std::make_pair(i, percentage));
}
// sort the bins according to percentage
sort(bins.begin(), bins.end(),comparator());
// compute percentile for a pixel value
int pixel = 185;
float sum = 0;
for (auto b : bins)
{
if(b.first != pixel)
sum += b.second;
else
{
sum += b.second/2;
break;
}
}
std::cout<<"Percentile for pixel("<<pixel<<"): "<<sum<<std::endl;

C++ OpenCV use vector<Point> as index of a matrix

I have a matrix img (480*640 pixel, float 64 bits) on which I apply a complex mask. After this, I need to multiply my matrix by a value but in order to win time I want to do this multiplication only on the non-zero elements because for now the multiplication is too long because I have to iterate the operation 2000 times on 2000 different matrix but with the same mask. So I found the index (on x/y axes) of the nonzero pixels which I keep in a vector of Point. But I don't succeed to use this vector to do the multplication only on the pixels indexed in this same vector.
Here is an example (with a simple mask) to understand my problem :
Mat img_temp(480, 640, CV_64FC1);
Mat img = img_temp.clone();
Mat mask = Mat::ones(img.size(), CV_8UC1);
double value = 3.56;
// Apply mask
img_temp.copyTo(img, mask);
// Finding non zero elements
vector<Point> nonZero;
findNonZero(img, nonZero);
// Previous multiplication (long because on all pixels)
Mat result = img.clone()*value;
// What I wish to do : multiplication only on non-zero pixels (not functional)
Mat result = Mat::zeros(img.size(), CV_64FC1);
result.at<int>(nonZero) = img.at(nonZero).clone() * value
What is tricky is that my pixels are not on a range (for example pixels 3, 4 and 50, 51 on a line).
Thank you in advance.
I would suggest using Mat.convertTo.
Basically, for the parameter alpha, which is the scaling factor, use the value of the mask (3.56 in your case). Make sure that the Mat is of type CV_32 or CV_64.
This will be faster than finding all non-zero pixels, saving their coordinates in a Vector and iterating (it was faster for me in Java).
Hope it helps!
Constructing vector of points will also increase computation time. I think you should consider iterating over all pixels and multiply if the pixel is not equal to zero.
Iterating will be faster if you have the matrix as raw data.
If you do
Mat result = img*value;
Instead of
Mat result = img.clone()*value;
The speed will be almost 10 times as fast
I have also tested your suggestion with vector but this is even slower than your first solution.
Below the code I used to test your firs suggestion
cv::Mat multMask(cv::Mat &img, std::vector<cv::Point> mask, double fact)
{
if (img.type() != CV_64FC1) throw "invalid format";
cv::Mat res = cv::Mat::zeros(img.size(), img.type());
int iLen = (int)mask.size();
for (int i = 0; i < iLen; i++)
{
cv::Point &p = mask[i];
((double*)(res.data + res.step.p[0] * p.y))[p.x] = ((double*)(img.data + img.step.p[0] * p.y))[p.x] * fact;
}
return res;
}

Why do these histogram functions differ, and why is one nondeterministic?

NOTE: This is a homework problem and the professor explicitly forbids soliciting answers from StackOverflow, so please limit your response to the specific question I have asked and do not attempt to provide a working solution.
I am asked to implement a function that computes the histogram of a single-channel 8-bit image represented as an OpenCV Mat with type CV_U8.
In this case, the histogram uses 256 uniformly-distributed buckets. This is the reference we are intended to replicate (using OpenCV 3.4):
Mat reference;
/// Establish the number of bins
int histSize = 256;
/// Set the ranges ( for B,G,R) )
float range[] = { 0, 256 } ;
const float* histRange = { range };
bool uniform = true;
bool accumulate = false;
cv::calcHist(&bgr_planes[0], 1, 0, Mat(), reference, 1, &histSize, &histRange,
uniform, accumulate);
// reference now contains the canonical histogram of the input image's
// blue channel
I wrote the following function to calculate the histogram, which produces the correct results 45-69% of the time (p<0.05, n=66). Once when it failed, I examined the results and found no discernable pattern. All trials were conducted on the same test image.
Mat myCalcHist(const Mat& input) {
assert(input.isContinuous());
Mat res(256, 1, CV_32F);
for (const uint8_t* it = input.datastart; it != input.dataend; ++it) {
++res.at<float>(*it);
}
return res;
}
The following function, on the other hand, more closely matches OpenCV's internal implementation in that it uses the idiomatic accessors and converts the float result from an int work matrix, but in n=66 trials it did not produce the correct result a single time. Again, I found no discernable pattern in the data.
Mat myCalcHist(const Mat& input) {
Mat ires(256, 1, CV_32S);
for (int i = 0; i < input.total(); ++i) {
++ires.at<int>(input.at<uint8_t>(i));
}
Mat res(256, 1, CV_32F);
ires.convertTo(res, CV_32F);
return res;
}
Why are the results for my first implementation different than those from my second implementation, and where is nondeterminism introduced to the first implementation?
initializing the histogram matrix should work:
Mat myCalcHist(const Mat& input)
{
Mat ires = cv::Mat::zeros(256, 1, CV_32S);
for (int i = 0; i < input.total(); ++i)
{
++ires.at<int>(input.at<uint8_t>(i));
}
Mat res(256, 1, CV_32F);
ires.convertTo(res, CV_32F);
return res;
}

Similarity Measurement between Color Image (OpenCV)

I'm working with a CBIR (Content-based Image Retrieval) project which will draw RGB histogram of images and also calculate the distance between other images with query image.
I'm using VS 2008 - MFC and OpenCV Library. The method I wanted to use for calculating the distance is Euclidean Distance(ED), but somehow I failed to work it out.
I found a function - cvCalcEMD2() that can help me calculate the distance between two histogram.
To use this function, i need to create signature for my histogram.
Here is an example for creating signature that I found
in the For loop, there is a line where I need to pass in my histogram:
float bin_val = cvQueryHistValue_2D( hist1, h, s );
and in my function for histogram don't have something like the variable h_bins and s_bins
In my program, I calculate/draw my histogram into R, G and B.
means, each image I've 3 histogram.
eg: CvHistogram *hist_red, *hist_green, *hist_blue;
How do I use my histogram to create signature?
*the link to my drawHistogram function is on my comment below
This is my code to create RGB hist signature in my project:
In my case I needed the signature tu be an array of floats.
void makeColorSign(const IplImage* img,float** colorSign) {
unsigned int* N = Params::colorSignSize;
float* sign = (float*)malloc(N[0]*N[1]*3*sizeof(float));
IplImage* s = cvCreateImage(cvSize(N[0],N[1]),img->depth,img->nChannels);
cvResize(img,s,CV_INTER_NN);
RgbImage rgb(s);
for(unsigned int y=0; y<N[1]; ++y) {
for(unsigned int x=0; x<N[0]; ++x) {
unsigned int coord = (y*N[1]+x)*3;
sign[coord] = rgb[y][x].r;
sign[coord+1] = rgb[y][x].g;
sign[coord+2] = rgb[y][x].b;
}
}
*colorSign = sign;
cvReleaseImage(&s);
}