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