1. ColorHistogram.h
#if !defined COLHISTOGRAM
#define COLHISTOGRAM
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
class ColorHistogram {
private:
int histSize[3];
float hranges[2];
const float* ranges[3];
int channels[3];
public:
ColorHistogram() {
// Prepare arguments for a color histogram
histSize[0]= histSize[1]= histSize[2]= 256;
hranges[0]= 0.0; // BRG range
hranges[1]= 255.0;
ranges[0]= hranges; // all channels have the same range
ranges[1]= hranges;
ranges[2]= hranges;
channels[0]= 0; // the three channels
channels[1]= 1;
channels[2]= 2;
}
// Computes the histogram.
cv::MatND getHistogram(const cv::Mat &image) {
cv::MatND hist;
// BGR color histogram
hranges[0]= 0.0; // BRG range
hranges[1]= 255.0;
channels[0]= 0; // the three channels
channels[1]= 1;
channels[2]= 2;
// Compute histogram
cv::calcHist(&image,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
// Computes the histogram.
cv::SparseMat getSparseHistogram(const cv::Mat &image) {
cv::SparseMat hist(3,histSize,CV_32F);
// BGR color histogram
hranges[0]= 0.0; // BRG range
hranges[1]= 255.0;
channels[0]= 0; // the three channels
channels[1]= 1;
channels[2]= 2;
// Compute histogram
cv::calcHist(&image,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
3, // it is a 3D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
// Computes the 2D ab histogram.
// BGR source image is converted to Lab
cv::MatND getabHistogram(const cv::Mat &image) {
cv::MatND hist;
// Convert to Lab color space
cv::Mat lab;
cv::cvtColor(image, lab, CV_BGR2Lab);
// Prepare arguments for a 2D color histogram
hranges[0]= -128.0;
hranges[1]= 127.0;
channels[0]= 1; // the two channels used are ab
channels[1]= 2;
// Compute histogram
cv::calcHist(&lab,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
2, // it is a 2D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
// Computes the 1D Hue histogram with a mask.
// BGR source image is converted to HSV
cv::MatND getHueHistogram(const cv::Mat &image) {
cv::MatND hist;
// Convert to Lab color space
cv::Mat hue;
cv::cvtColor(image, hue, CV_BGR2HSV);
// Prepare arguments for a 1D hue histogram
hranges[0]= 0.0;
hranges[1]= 180.0;
channels[0]= 0; // the hue channel
// Compute histogram
cv::calcHist(&hue,
1, // histogram of 1 image only
channels, // the channel used
cv::Mat(), // no mask is used
hist, // the resulting histogram
1, // it is a 1D histogram
histSize, // number of bins
ranges // pixel value range
);
return hist;
}
cv::Mat colorReduce(const cv::Mat &image, int div=64) {
int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
// mask used to round the pixel value
uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
cv::Mat_<cv::Vec3b>::const_iterator it= image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::const_iterator itend= image.end<cv::Vec3b>();
// Set output image (always 1-channel)
cv::Mat result(image.rows,image.cols,image.type());
cv::Mat_<cv::Vec3b>::iterator itr= result.begin<cv::Vec3b>();
for ( ; it!= itend; ++it, ++itr) {
(*itr)[0]= ((*it)[0]&mask) + div/2;
(*itr)[1]= ((*it)[1]&mask) + div/2;
(*itr)[2]= ((*it)[2]&mask) + div/2;
}
return result;
}
};
#endif
2. ObjectFinder.h
#if !defined OFINDER
#define OFINDER
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
class ObjectFinder {
private:
float hranges[2];
const float* ranges[3];
int channels[3];
float threshold;
cv::MatND histogram;
cv::SparseMat shistogram;
bool isSparse;
public:
ObjectFinder() : threshold(0.1f), isSparse(false) {
ranges[0]= hranges; // all channels have the same range
ranges[1]= hranges;
ranges[2]= hranges;
}
// Sets the threshold on histogram values [0,1]
void setThreshold(float t) {
threshold= t;
}
// Gets the threshold
float getThreshold() {
return threshold;
}
// Sets the reference histogram
void setHistogram(const cv::MatND& h) {
isSparse= false;
histogram= h;
cv::normalize(histogram,histogram,1.0);
}
// Sets the reference histogram
void setHistogram(const cv::SparseMat& h) {
isSparse= true;
shistogram= h;
cv::normalize(shistogram,shistogram,1.0,cv::NORM_L2);
}
// Finds the pixels belonging to the histogram
cv::Mat find(const cv::Mat& image) {
cv::Mat result;
hranges[0]= 0.0; // range [0,255]
hranges[1]= 255.0;
channels[0]= 0; // the three channels
channels[1]= 1;
channels[2]= 2;
if (isSparse) { // call the right function based on histogram type
cv::calcBackProject(&image,
1, // one image
channels, // vector specifying what histogram dimensions belong to what image channels
shistogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
} else {
cv::calcBackProject(&image,
1, // one image
channels, // vector specifying what histogram dimensions belong to what image channels
histogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
}
// Threshold back projection to obtain a binary image
if (threshold>0.0)
cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);
return result;
}
cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels, int dim) {
cv::Mat result;
hranges[0]= minValue;
hranges[1]= maxValue;
for (int i=0; i<dim; i++)
this->channels[i]= channels[i];
if (isSparse) { // call the right function based on histogram type
cv::calcBackProject(&image,
1, // we only use one image at a time
channels, // vector specifying what histogram dimensions belong to what image channels
shistogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
} else {
cv::calcBackProject(&image,
1, // we only use one image at a time
channels, // vector specifying what histogram dimensions belong to what image channels
histogram, // the histogram we are using
result, // the resulting back projection image
ranges, // the range of values, for each dimension
255.0 // the scaling factor is chosen such that a histogram value of 1 maps to 255
);
}
// Threshold back projection to obtain a binary image
if (threshold>0.0)
cv::threshold(result, result, 255*threshold, 255, cv::THRESH_BINARY);
return result;
}
};
#endif
3. Finder.cpp
/*------------------------------------------------------------------------------------------*\
This file contains material supporting chapter 4 of the cookbook:
Computer Vision Programming using the OpenCV Library.
by Robert Laganiere, Packt Publishing, 2011.
This program is free software; permission is hereby granted to use, copy, modify,
and distribute this source code, or portions thereof, for any purpose, without fee,
subject to the restriction that the copyright notice may not be removed
or altered from any source or altered source distribution.
The software is released on an as-is basis and without any warranties of any kind.
In particular, the software is not guaranteed to be fault-tolerant or free from failure.
The author disclaims all warranties with regard to this software, any use,
and any consequent failure, is purely the responsibility of the user.
Copyright (C) 2010-2011 Robert Laganiere, www.laganiere.name
\*------------------------------------------------------------------------------------------*/
#include <iostream>
#include <vector>
using namespace std;
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\video\tracking.hpp>
#include "ObjectFinder.h"
#include "ColorHistogram.h"
int main()
{
// Read reference image
cv::Mat image= cv::imread("C:/images/baboon1.jpg");
if (!image.data)
return 0;
// 从image中得到一块ROI区域, 并用红色在image上用红色标出ROI
// Define ROI
cv::Mat imageROI= image(cv::Rect(110,260,35,40));
cv::rectangle(image, cv::Rect(110,260,35,40),cv::Scalar(0,0,255));
// 显示image及其标出的ROI(红色方框)
// Display image
cv::namedWindow("Image");
cv::imshow("Image",image);
// 得到imageROI的色度直方图
// Get the Hue histogram
int minSat=65;
ColorHistogram hc;
// cv::MatND colorhist= hc.getHueHistogram(imageROI, minSat);
cv::MatND colorhist= hc.getHueHistogram(imageROI);
ObjectFinder finder;
finder.setHistogram(colorhist);
finder.setThreshold(0.2f);
// Convert to HSV space
cv::Mat hsv;
cv::cvtColor(image, hsv, CV_BGR2HSV);
// Split the image
vector<cv::Mat> v;
cv::split(hsv,v);
//v[0] - Hue, v[1] - Saturation, v[2] - Value
// Eliminate pixels with low saturation
cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
cv::namedWindow("Saturation");
cv::imshow("Saturation",v[1]);
// Get back-projection of hue histogram
int ch[1]={0};
cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);
cv::namedWindow("Result Hue");
cv::imshow("Result Hue",result);
cv::bitwise_and(result,v[1],result);
cv::namedWindow("Result Hue and");
cv::imshow("Result Hue and",result);
// Second image
image= cv::imread("C:/images/baboon3.jpg");
// Display image
cv::namedWindow("Image 2");
cv::imshow("Image 2",image);
// Convert to HSV space
cv::cvtColor(image, hsv, CV_BGR2HSV);
// Split the image
cv::split(hsv,v);
// Eliminate pixels with low saturation
cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
cv::namedWindow("Saturation");
cv::imshow("Saturation",v[1]);
// Get back-projection of hue histogram
result= finder.find(hsv,0.0f,180.0f,ch,1);
cv::namedWindow("Result Hue");
cv::imshow("Result Hue",result);
// Eliminate low stauration pixels
cv::bitwise_and(result,v[1],result);
cv::namedWindow("Result Hue and");
cv::imshow("Result Hue and",result);
// Get back-projection of hue histogram
finder.setThreshold(-1.0f);
result= finder.find(hsv,0.0f,180.0f,ch,1);
cv::bitwise_and(result,v[1],result);
cv::namedWindow("Result Hue and raw");
cv::imshow("Result Hue and raw",result);
cv::Rect rect(110,260,35,40);
cv::rectangle(image, rect, cv::Scalar(0,0,255));
cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;
cv::rectangle(image, rect, cv::Scalar(0,255,0));
// Display image
cv::namedWindow("Image 2 result");
cv::imshow("Image 2 result",image);
cv::waitKey();
return 0;
}