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
Related
I have been trying to find matched image from sample image using histogram matching. for most of the cases my code is working fine. The range of used method, Bhattacharyya, is 0 <= method <= 1.
normally using Bhattacharyya method the output result will close to 0, in case of matched cases. but i have come to a case where both images are almost similar, though there could be some contrast difference.
which is why this procedure is giving higher result...
can anyone help me why this comparison is giving so much bigger value?
src image and test image
int main(){
src_base = imread("images/src.jpg",-1);
src_test1 = imread("images/test.png",-1);
double base_test1 = hsvToHist(src_base, src_test1,3);
cout<< " Bhattacharyya template Base-Test(1) : "<< base_test1<<endl;
return 0;
}
double hsvToHist( Mat src_base, Mat src_test1, int method){
Mat hsv_base, hsv_test1;
cvtColor( src_base, hsv_base, COLOR_BGR2HSV );
cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV );
/// initialization to calculate histograms (Using 50 bins for hue, 60 for saturation)
int h_bins = 50; int s_bins = 60;
int histSize[] = { h_bins, s_bins };
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0, 1 };
/// Histograms
Mat hist_base, hist_test1;
/// Calculate the histograms for the HSV images
calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false );
normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false );
normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() );
///'3' for Bhattacharyya
double base_test1 = compareHist( hist_base, hist_test1, method );
return base_test1;
}
The PNG and JPEG images will have different histograms even though they appear the same, because the JPEG is compressed which means information has been removed and the histogram has been essentially filtered and smoothed. Also, the PNG will have a larger range of values than the JPEG. You may get better results with different bin sizes, but it's hard to tell without testing.
The Bhattacharyya distance has an N^2 term in the denominator where N is the number of pixels. In general, this allows similar values for different sizes of images. However, for the icons that you are comparing, the divisor is much smaller. You could scale the metric by a factor related to the image size.
Alternately, you could use the HISTCMP_CORREL method, which produces lower absolute values if the differences between pixels are less significant. This method produces larger values if more pixels are compared.
When you want similar results independent of differences in image size you could compute both metrics and consider the images equal if one of them passes a tight threshold for similarity. Actual thresholds will vary depending on whether you are comparing color or grayscale images, and whether you have pre-processed the images using histogram equalization (see cv::equalizeHist).
I want to find dominant color on an image. For this, I know that I should use image histogram. But I am not sure of image format. Which one of rgb, hsv or gray image, should be used?
After the histogram is calculated, I should find max value on histogram. For this, should I find below maximum binVal value for hsv image? Why my result image contains only black color?
float binVal = hist.at<float>(h, s);
EDIT :
I have tried the below code. I draw h-s histogram. And my result images are here. I don't find anything after binary threshold. Maybe I find max histogram value incorrectly.
cvtColor(src, hsv, CV_BGR2HSV);
// Quantize the hue to 30 levels
// and the saturation to 32 levels
int hbins = 20, sbins = 22;
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 };
MatND hist;
// 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);
int maxIntensity = -100;
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 );
if(intensity > maxIntensity)
maxIntensity = intensity;
}
}
std::cout << "max Intensity " << maxVal << std::endl;
Mat dst;
cv::threshold(src, dst, maxIntensity, 255, cv::THRESH_BINARY);
namedWindow( "Dest", 1 );
imshow( "Dest", dst );
namedWindow( "Source", 1 );
imshow( "Source", src );
namedWindow( "H-S Histogram", 1 );
imshow( "H-S Histogram", histImg );
Alternatively you could try a k-means approach. Calculate k clusters with k ~ 2..5 and take the centroid of the biggest group as your dominant color.
The python docu of OpenCv has an illustrated example that gets the dominant color(s) pretty well:
The solution
Find H-S histogram
Find peak H value(using minmaxLoc function)
Split image 3 channel(h,s,v)
Apply to threshold.
Create image by merge 3 channel
Here's a Python approach using K-Means Clustering to determine the dominant colors in an image with sklearn.cluster.KMeans()
Input image
Results
With n_clusters=5, here are the most dominant colors and percentage distribution
[14.69488554 34.23074345 41.48107857] 13.67%
[141.44980073 207.52576948 236.30722987] 15.69%
[ 31.75790423 77.52713644 114.33328324] 18.77%
[ 48.41205713 118.34814452 176.43411287] 25.19%
[ 84.04820266 161.6848298 217.14045211] 26.69%
Visualization of each color cluster
Similarity with n_clusters=10,
[ 55.09073171 113.28271003 74.97528455] 3.25%
[ 85.36889668 145.80759374 174.59846237] 5.24%
[164.17201088 223.34258123 241.81929254] 6.60%
[ 9.97315932 22.79468111 22.01822211] 7.16%
[19.96940211 47.8375841 72.83728002] 9.27%
[ 26.73510467 70.5847759 124.79314278] 10.52%
[118.44741779 190.98204701 230.66728334] 13.55%
[ 51.61750364 130.59930047 198.76335878] 13.82%
[ 41.10232129 104.89923271 160.54431333] 14.53%
[ 81.70930412 161.823664 221.10258949] 16.04%
import cv2, numpy as np
from sklearn.cluster import KMeans
def visualize_colors(cluster, centroids):
# Get the number of different clusters, create histogram, and normalize
labels = np.arange(0, len(np.unique(cluster.labels_)) + 1)
(hist, _) = np.histogram(cluster.labels_, bins = labels)
hist = hist.astype("float")
hist /= hist.sum()
# Create frequency rect and iterate through each cluster's color and percentage
rect = np.zeros((50, 300, 3), dtype=np.uint8)
colors = sorted([(percent, color) for (percent, color) in zip(hist, centroids)])
start = 0
for (percent, color) in colors:
print(color, "{:0.2f}%".format(percent * 100))
end = start + (percent * 300)
cv2.rectangle(rect, (int(start), 0), (int(end), 50), \
color.astype("uint8").tolist(), -1)
start = end
return rect
# Load image and convert to a list of pixels
image = cv2.imread('1.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
reshape = image.reshape((image.shape[0] * image.shape[1], 3))
# Find and display most dominant colors
cluster = KMeans(n_clusters=5).fit(reshape)
visualize = visualize_colors(cluster, cluster.cluster_centers_)
visualize = cv2.cvtColor(visualize, cv2.COLOR_RGB2BGR)
cv2.imshow('visualize', visualize)
cv2.waitKey()
Here are some suggestions to get you started.
All 3 channels in RGB contribute to the color, so you'd have to
somehow figure out where three different histograms are all at maximum. (Or their sum is maximum, or whatever.)
HSV has all of the color (well, Hue) information in one channel, so
you only have to consider one histogram.
Grayscale throws away all color information so is pretty much useless for
finding color.
Try converting to HSV, then calculate the histogram on the H channel.
As you say, you want to find the max value in the histogram. But:
You might want to consider a range of values instead of just one, say
from 20-40 instead of just 30. Try different range sizes.
Remember that Hue is circular, so H=0 and H=360 are the same.
Try plotting the histogram following this:
http://docs.opencv.org/doc/tutorials/imgproc/histograms/histogram_calculation/histogram_calculation.html
to see if your results make sense.
If you're using a range of Hues and you find a range that is maximum, you can either just use the middle of that range as your dominant color, or you can find the mean of the colors within that range and use that.
I used openCV library to get the similarity percentage in images . I used compareHist function of openCv library which returns double value, there different method name (int value) are passed in this function and got different- different result for every Mehod .Now how to take decision on these double values????
Mat src_base, hsv_base;
Mat src_test1, hsv_test1;
// Mat src_test2, hsv_test2;
Mat hsv_half_down;
String baseImgPath = [baseImagePath UTF8String];
String firstCmpImgPath = [firstCmpImagePath UTF8String];//compare image path
src_base = imread( baseImgPath, 1 ); read source image
src_test1 = imread(firstCmpImgPath, 1 ); read compared image
// src_test2 = imread(secondCmpImgPath, 1 );
if( !src_base.data || !src_test1.data /*||!src_test2.data*/)
{
return nil;
}
cvtColor( src_base, hsv_base, COLOR_BGR2HSV );
cvtColor( src_test1, hsv_test1, COLOR_BGR2HSV );
//cvtColor( src_test2, hsv_test2, COLOR_BGR2HSV );
hsv_half_down = hsv_base( Range( hsv_base.rows/2, hsv_base.rows - 1 ), Range( 0, hsv_base.cols - 1 ) );
/// Using 50 bins for hue and 60 for saturation
int h_bins = 50; int s_bins = 60;
int histSize[] = { h_bins, s_bins };
// hue varies from 0 to 179, saturation from 0 to 255
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
// Use the o-th and 1-st channels
int channels[] = { 0, 1 };
/// Histograms
MatND hist_base;
MatND hist_half_down;
MatND hist_test1;
MatND hist_test2;
/// Calculate the histograms for the HSV images
calcHist( &hsv_base, 1, channels, Mat(), hist_base, 2, histSize, ranges, true, false );
normalize( hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsv_half_down, 1, channels, Mat(), hist_half_down, 2, histSize, ranges, true, false );
normalize( hist_half_down, hist_half_down, 0, 1, NORM_MINMAX, -1, Mat() );
calcHist( &hsv_test1, 1, channels, Mat(), hist_test1, 2, histSize, ranges, true, false );
normalize( hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat() );
for( int i = 0; i < 4; i++ )
{
int compare_method = i;
double base_test1 = compareHist( hist_base, hist_test1, compare_method );
}
compare method are CV_COMP_CORREL, CV_COMP_CHISQR , CV_COMP_INTERSECT , CV_COMP_BHATTACHARYYA
Reference link http://docs.opencv.org/doc/tutorials/imgproc/histograms/histogram_comparison/histogram_comparison.html
For a bitmap it makes sense to define a similarity metric that computes the percentage of pixels in an image that are different from a target image.
However when you are using the histogram of a bitmap/image this metric looses sense because you already made a statistic on that image (or extracted a feature). From this point, to compute the similarity, you compare the features of the 2 images, in your case with compareHist.
A higher distance means a more different image and 0 distance could mean that the images are 100% identical. Now it depends if the algorithm can actually output 0. However a 0.5 distance does not mean that the images are identical 50%.
However you can artificially create a similarity degree measured in percentage. You can consider the following:
The 2 images with the lowest similarity degree between them (maximum distance) have 0% similarity; You can even compute this distance using one pure black image and one pure white image :)
Distance 0 is similarity 100%.
Based on these assumptions, you can extract the similarity measured in percentage, based on your computeHist distance.
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 );
I am using the normalise function in the code below. My understanding was that normalising the histogram would result in the bins summing to one? But when I add them all up I keep getting a result higher then one. I dont know if I am doing something wrong or have misunderstood what the function does?
//read in image
Mat img = imread("image.jpg",1);
vector<Mat> planes;
split(img, planes);
//calculate hist
MatND hist;
int nbins = 256; // hold 256 levels
int hsize[] = { nbins }; // one dimension
float range[] = { 0, 255 };
const float *ranges[] = { range };
int chnls[] = {0};
calcHist(&planes[0], 1, chnls, Mat(), hist,1,hsize,ranges);
//normalise
normalize(hist,hist,1);
float tot = 0;
for( int n = 0;n < nbins; n++ )
{
float binVal = hist.at<float>(n);
tot+=binVal;
}
cout<<tot;
Normalized array doesn't sum to 1, but square root of sum of squares of components equals 1, F.e. in vector:
It is normalized, when:
sqrt(x^2 + y^2 + z^2) = 1
*This applies for vectors
in OpenCV - histogram - normalize is described here OpenCV histogram normalize, it should be clear (after reading the specs) that it doesn't have to sum to 1