Similarity Measurement between Color Image (OpenCV) - c++

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

Related

How to crop a part of image in the different matrix, but the matrix doesn't change its size in openCV.

I'm doing a project using openCV 2.4 ver. C++
I want to crop a part of image and save it in a different Matrix. Instead of getting a new single cropped image every loop, the cropped_image keeps previous images, and keeps building on next to the previous image. I wasn't sure what I did wrong..
Also this loops stops when n = 64 and m = 240. I also don't understand why..
Can anyone help me?
openCV 2.4v C++
using namespace cv;
using namespace std;
original_image = imread("image.jpg",1);
int n, m, cols_ss, rows_ss;
int cols = 640;
int rows = 480;
cols_ss = 64 // arbitrary number;
rows_ss = 48 // arbitrary number;
Mat cropped_image;
for (n = 0; n < cols - cols_ss; n = n + cols_ss) {
for (m = 0; m < rows - rows_ss; m = m + rows_ss) {
// initialize cropped_image as zeros.
Mat cropped_image(cols_ss, rows_ss, CV_8UC1, Scalar::all(0));
// Crop a small part of an original_image to cropped_image.
cropped_image = original_image(Rect(n, m, n + cols_ss, m + rows_ss));
}
}
Based on OpenCV docs, the constructor for Rect are Rect(x, y, width, height). Are you sure you don't mean cropped_image = original_image(Rect(n, m, cols_ss, rows_ss));?
ROI function gives 1:1 mapping to an area in image via pointer. Another way to make sure cropped_image is as intended, use:
Mat cropped_image(original_image, Rect(n, m, cols_ss, rows_ss));
This would make sure cropped_image's size is determined by ROI's Rect.

opencv calcHist results are not what expected

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!

Finding all objects in an image based on color

I am looking for a way to take an image and get masks of all objects in it by color. My goal is to be able to separate similarly colored objects into layers so I can further examine each layer. The plan is to use each mask against the original image to create a histogram of the colors in each object and determine the similarity with other objects in the image. If something is similar enough it will be combined with other objects to form a layer.
The problem is that I can not find a function in opencv to find all objects in an image based on color contiguity. I am sure such an algorithm exists, but it seems to be evading me. Does anyone know of an algorithm or function like this?
The best method that I have found is K-means Clustering. This separates the image into different layers based on color. It uses a k-neighbors algorithm to do so. With this I am able to effectively split the image into several layers that are of similar color.
#define numClusters 7
cv::Mat src = cv::imread("img0.png");
cv::Mat kMeansSrc(src.rows * src.cols, 3, CV_32F);
//resize the image to src.rows*src.cols x 3
//cv::kmeans expects an image that is in rows with 3 channel columns
//this rearranges the image into (rows * columns, numChannels)
for( int y = 0; y < src.rows; y++ )
{
for( int x = 0; x < src.cols; x++ )
{
for( int z = 0; z < 3; z++)
kMeansSrc.at<float>(y + x*src.rows, z) = src.at<Vec3b>(y,x)[z];
}
}
cv::Mat labels;
cv::Mat centers;
int attempts = 2;
//perform kmeans on kMeansSrc where numClusters is defined previously as 7
//end either when desired accuracy is met or the maximum number of iterations is reached
cv::kmeans(kMeansSrc, numClusters, labels, cv::TermCriteria( CV_TERMCRIT_EPS+CV_TERMCRIT_ITER, 8, 1), attempts, KMEANS_PP_CENTERS, centers );
//create an array of numClusters colors
int colors[numClusters];
for(int i = 0; i < numClusters; i++) {
colors[i] = 255/(i+1);
}
std::vector<cv::Mat> layers;
for(int i = 0; i < numClusters; i++)
{
layers.push_back(cv::Mat::zeros(src.rows,src.cols,CV_32F));
}
//use the labels to draw the layers
//using the array of colors, draw the pixels onto each label image
for( int y = 0; y < src.rows; y++ )
{
for( int x = 0; x < src.cols; x++ )
{
int cluster_idx = labels.at<int>(y + x*src.rows,0);
layers[cluster_idx].at<float>(y, x) = (float)(colors[cluster_idx]);;
}
}
std::vector<cv::Mat> srcLayers;
//each layer to mask a portion of the original image
//this leaves us with sections of similar color from the original image
for(int i = 0; i < numClusters; i++)
{
layers[i].convertTo(layers[i], CV_8UC1);
srcLayers.push_back(cv::Mat());
src.copyTo(srcLayers[i], layers[i]);
}
I suggest you convert the image to the HSV-space (Hue-Saturation-Value). Then make a histogram based on the Hue value to find thresholds online, or define them before (depends if this is a general problem or a given one).
Crate one-channel images for each layer you want to form. (set them as black)
Then then use the HSV-image and mark a layer based on the threshold values. You might want to add some constant thresholds for value and saturation too (to avoid dark and light areas)
Does this make sense to you?
I think that you should proceed in the following proceess:
Smooth you image if it has too much details.
find edges
Find all contours
Try to find the color of each contour..lets say you want to keep all contours which are red. So, keep only those contours which are red.
Once you find the contours which you want to keep, then create a mask image based upon the contours you want to keep.
Using mask image, extract the required objects from the original image.

How to use clustering with opencv c++ to classify the connected component based on the area and height

Hi, with opencv c++, I want to do clustering to classify the connected components based on the area and height.
I do understand the concept of the clustering but i have hard time to implement it in opencv c++.
In the opencv
http://docs.opencv.org/modules/core/doc/clustering.html
There is a clustering methods kmeans
Most of the website I searched, they just explain the concept and parameters of the kmeans function in opencv c++ and most of them were copied from the opencv document website.
double kmeans(InputArray data, int K, InputOutputArray bestLabels, TermCriteria criteria, int attempts, int flags, OutputArray centers=noArray() )
There is also good example here but it was implemented in Python
http://docs.opencv.org/trunk/doc/py_tutorials/py_ml/py_kmeans/py_kmeans_opencv/py_kmeans_opencv.html?highlight=kmeans
As i mentioned above, I have all the connected components and i can calculate areas and height of each connect components.
I want to use clustering to distinguish between connected components.
For instance, with k-means methods i would use k=2.
Thank..
I am posting the snippet, Hope this will help you....
The Height and Area of component can be used as a feature for kmean. Now here for each feature kmean will give you centre. i.e. 1 center for Area and 1 center for height of component.
Mat labels;
int attempts = 11;
Mat centers;
int no_of_features = 2;//(i.e. height, area)
Mat samples(no_of_connected_components, no_of_features, CV_32F);
int no_of_sub_classes = 1; // vary for more sub classes
for (int j = 0; j < no_of_connected_components; j++)
{
for (int x = 0; x < no_of_features; x++)
{
samples.at<float>(j, x) = connected_component_values[j,x];
//fill the values(height, area) of connected component labelling
}
}
cv::kmeans(samples, no_of_sub_classes, labels, TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10000, 0.001), attempts, KMEANS_PP_CENTERS, centers);
for (size_t si_i = 0; si_i < no_of_sub_classes ; si_i++)
{
for (size_t si_j = 0; si_j < no_of_features; si_j++)
{
KmeanTable[si_i*no_of_sub_classes + si_i][si_j] = centers.at<float>(si_i, si_j);
}
}
Here I am storing the center in kmeanTable 2D array you can use yours. Now for each connected component you can calculate the euclidean distance from centers.
The lower difference features qualify for classification.
Check this out.
Except instead of iterating over x,y, and z you'll iterate over component, and property (area, and height).

PCA project and backproject in Opencv

I am working in Ubuntu Opencv.I am trying to do PCA analysis of a single image.I take the 3 channel image and change it to a single channel image with 3 columns and r*c number of rows.r and c being the rows and columns of the original image.When I try to display the reconstructed image after doing the backprojection on the PCA it gives me a green image.Here is my code
Mat pcaset=cvCreateMat(image->height*image->width,image->nChannels,CV_8UC1);
for(int i=0;i<image->height;i++)
{
for(int j=0;j<image->width;j++)
{
for(int k=0;k<image->nChannels;k++)
(ptrpcaset+i*pcaset.step)[k]=((ptrimage+i*image->widthStep)[3*j+k]);
}
}
int nEigens=3;
Mat databackprojected;
PCA pca(pcaset,Mat(),CV_PCA_DATA_AS_ROW,nEigens);
Mat dataprojected(pcaset.rows,nEigens,CV_8UC1);
pca.project(pcaset,dataprojected);
pca.backProject(dataprojected,databackprojected);
Mat backprojectnorm;//(databackprojected.rows,nEigens,CV_8UC1);
normalize(databackprojected,backprojectnorm,0,255,NORM_MINMAX,-1);
Mat finaldataafterreshaping(image->height,image->width,CV_8UC3);
uchar* finalptr=(uchar*)finaldataafterreshaping.data;
uchar* ptrnorm=(uchar*)backprojectnorm.data;
int x=0,y=0,i=0;
while(i<backprojectnorm.rows)
{
while(x<image->height)
{
while(y<image->width)
{
for(int k=0;k<image->nChannels;k++)
{
(finalptr+x*finaldataafterreshaping.step)[3*y+k]=(ptrnorm+i*backprojectnorm.step)[k];
}
y=y+1;i=i+1;
}
x=x+1;y=0;
}
}
imshow("Reconstructed data",finaldataafterreshaping);
You need to make the following changes:
(ptrpcaset+(j + i*image->width)*pcaset.step)[k]=((ptrimage+i*image->widthStep)[3*j+k]);
because you are not taking the j coordinate into account when you transform your data so that at the end you only save the last line of your image in the new matrix.
When you reshape your data, you need to do something like this:
float* val = (float*)&(ptrnorm+i*backprojectnorm.step)[(k*4)];
(finalptr+x*finaldataafterreshaping.step)[3*y+k]=*val;
because the matrix you get as a result is of type float and not uchar. So you need to some kind of conversion. I am not sure, if it is a good idea to do it this way, but it works. I would suggest that you have a look at the C++ API of OpenCV 2, which can handle this things in a much nicer way.
Also, the whole while(i<backprojectnrom.rows) loop is not needed.