Opencv直方图反投影检测颜色

本文按照OpenCV2计算机视觉编程手册书写代码。
对于书中一些地方,为了实现更好的效果有改动。
首先是第一个类ColorHistogram

#pragma once
#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() {


		histSize[0] = histSize[1] = histSize[2] = 256;
		hranges[0] = 0.0;
		hranges[1] = 256.0;
		ranges[0] = hranges;
		ranges[1] = hranges;
		ranges[2] = hranges;
		channels[0] = 0;
		channels[1] = 1;
		channels[2] = 2;
	}


	void setSize(int size) {


		histSize[0] = histSize[1] = histSize[2] = size;
	}


	cv::Mat getHistogram(const cv::Mat &image) {

		cv::Mat hist;


		hranges[0] = 0.0;
		hranges[1] = 256.0;
		channels[0] = 0;
		channels[1] = 1;
		channels[2] = 2;


		cv::calcHist(&image,
			1,
			channels,
			cv::Mat(),
			hist,
			3,
			histSize,
			ranges
		);
		return hist;
	}


	cv::SparseMat getSparseHistogram(const cv::Mat &image) {

		cv::SparseMat hist(3,
			histSize,
			CV_32F);


		hranges[0] = 0.0;
		hranges[1] = 256.0;
		channels[0] = 0;
		channels[1] = 1;
		channels[2] = 2;


		cv::calcHist(&image,
			1,
			channels,
			cv::Mat(),
			hist,
			3,
			histSize,
			ranges
		);
		return hist;
	}


	cv::Mat getHueHistogram(const cv::Mat &image,
		int minSaturation = 0) {
		cv::Mat hist;


		cv::Mat hsv;
		cv::cvtColor(image, hsv, CV_BGR2HSV);


		cv::Mat mask;

		if (minSaturation>0) {

			std::vector<cv::Mat> v;
			cv::split(hsv, v);


			cv::threshold(v[1], mask, minSaturation, 255,
				cv::THRESH_BINARY);
		}


		hranges[0] = 0.0;
		hranges[1] = 180.0;
		channels[0] = 0;


		cv::calcHist(&hsv,
			1,
			channels,
			mask,
			hist,
			1,
			histSize,
			ranges
		);
		return hist;
	}

	// Computes the 2D ab histogram.
	// BGR source image is converted to Lab
	cv::Mat getabHistogram(const cv::Mat &image) {

		cv::Mat hist;

		// Convert to Lab color space
		cv::Mat lab;
		cv::cvtColor(image, lab, CV_BGR2Lab);

		// Prepare arguments for a 2D color histogram
		hranges[0] = 0;
		hranges[1] = 256.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;
	}
};
#endif


第二个类ContentFinder

#pragma once
#if !defined OFINDER
#define OFINDER

#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>

class ContentFinder {

private:

	// histogram parameters
	float hranges[2];
	const float* ranges[3];
	int channels[3];

	float threshold;           // decision threshold
	cv::Mat histogram;         // histogram can be sparse 输入直方图
	cv::SparseMat shistogram;  // or not
	bool isSparse;

public:

	ContentFinder() : threshold(0.1f), isSparse(false) {

		// in this class,
		// all channels have the same range
		ranges[0] = hranges;
		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::Mat& h) {

		isSparse = false;
		cv::normalize(h, histogram, 1.0);
	}

	// Sets the reference histogram
	void setHistogram(const cv::SparseMat& h) {

		isSparse = true;
		cv::normalize(h, shistogram, 1.0, cv::NORM_L2);
	}

	// All channels used, with range [0,256]
	cv::Mat find(const cv::Mat& image) {

		cv::Mat result;

		hranges[0] = 0.0;	// default range [0,256]
		hranges[1] = 256.0;
		channels[0] = 0;		// the three channels 
		channels[1] = 1;
		channels[2] = 2;

		return find(image, hranges[0], hranges[1], channels);
	}

	// Finds the pixels belonging to the histogram
	cv::Mat find(const cv::Mat& image, float minValue, float maxValue, int *channels) {

		cv::Mat result;

		hranges[0] = minValue;
		hranges[1] = maxValue;

		if (isSparse) { // call the right function based on histogram type

			for (int i = 0; i<shistogram.dims(); i++)
				this->channels[i] = channels[i];

			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 {

			for (int i = 0; i<histogram.dims; i++)
				this->channels[i] = channels[i];
			//某对象的this指针,指向被调用函数所在的对象,此处对象为ContentFinder类
			//this->channels[i]即ContentFinder类的私有成员channels[3]
			//对ContentFinder类各成员的访问均通过this进行
			cv::calcBackProject(&image,
				1,            // we only use one image at a time
				channels,     // 向量表示哪个直方图维度属于哪个图像通道
				histogram,    // 用到的直方图
				result,       // 反向投影的图像
				ranges,       // 每个维度值的范围
				255.0         // 选用的换算系数
			);
		}
		// Threshold back projection to obtain a binary image阈值分割反向投影图像得到二值图
		if (threshold>0.0)// 设置的阈值>0时,才进行阈值分割
			cv::threshold(result, result, 255.0*threshold, 255.0, cv::THRESH_BINARY);
		return result;
	}
};
#endif

还有一个Histogram1D

#pragma once
#if !defined HISTOGRAM
#define HISTOGRAM

#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>

// To create histograms of gray-level images
class Histogram1D { 
private:
	int histSize[1];         
	float hranges[2];    
	const float* ranges[1];  
	int channels[1];        
public:
	Histogram1D() {

		histSize[0] = 256;  
		hranges[0] = 0.0;    
		hranges[1] = 256.0;  
		ranges[0] = hranges;
		channels[0] = 0;    
	}

	
	void setChannel(int c) {

		channels[0] = c;
	}

	
	int getChannel() {

		return channels[0];
	}

	
	void setRange(float minValue, float maxValue) {

		hranges[0] = minValue;
		hranges[1] = maxValue;
	}

	// Gets the min pixel value.
	float getMinValue() {

		return hranges[0];
	}

	// Gets the max pixel value.
	float getMaxValue() {

		return hranges[1];
	}

	
	void setNBins(int nbins) {

		histSize[0] = nbins;
	}

	
	int getNBins() {

		return histSize[0];
	}

	
	cv::Mat getHistogram(const cv::Mat &image) {

		cv::Mat hist;

		
		cv::calcHist(&image,
			1,		
			channels,	
			cv::Mat(),	
			hist,		
			1,		
			histSize,	
			ranges		
		);

		return hist;
	}

	
	cv::Mat getHistogramImage(const cv::Mat &image, int zoom = 1) {

		// Compute histogram first
		cv::Mat hist = getHistogram(image);

		// Creates image
		return Histogram1D::getImageOfHistogram(hist, zoom);
	}

	// Stretches the source image using min number of count in bins.
	cv::Mat stretch(const cv::Mat &image, int minValue = 0) {

		// Compute histogram first
		cv::Mat hist = getHistogram(image);

		// find left extremity of the histogram
		int imin = 0;
		for (; imin < histSize[0]; imin++) {
			// ignore bins with less than minValue entries
			if (hist.at<float>(imin) > minValue)
				break;
		}

		// find right extremity of the histogram
		int imax = histSize[0] - 1;
		for (; imax >= 0; imax--) {

			// ignore bins with less than minValue entries
			if (hist.at<float>(imax) > minValue)
				break;
		}

		// Create lookup table
		int dims[1] = { 256 };
		cv::Mat lookup(1, dims, CV_8U);

		for (int i = 0; i<256; i++) {

			if (i < imin) lookup.at<uchar>(i) = 0;
			else if (i > imax) lookup.at<uchar>(i) = 255;
			else lookup.at<uchar>(i) = cvRound(255.0*(i - imin) / (imax - imin));
		}

		// Apply lookup table
		cv::Mat result;
		result = applyLookUp(image, lookup);

		return result;
	}

	// Stretches the source image using percentile.
	cv::Mat stretch(const cv::Mat &image, float percentile) {

		// number of pixels in percentile
		float number = image.total()*percentile;

		// Compute histogram first
		cv::Mat hist = getHistogram(image);

		// find left extremity of the histogram
		int imin = 0;
		for (float count = 0.0; imin < histSize[0]; imin++) {
			// number of pixel at imin and below must be > number
			if ((count += hist.at<float>(imin)) >= number)
				break;
		}

		// find right extremity of the histogram
		int imax = histSize[0] - 1;
		for (float count = 0.0; imax >= 0; imax--) {
			// number of pixel at imax and below must be > number
			if ((count += hist.at<float>(imax)) >= number)
				break;
		}

		// Create lookup table
		int dims[1] = { 256 };
		cv::Mat lookup(1, dims, CV_8U);

		for (int i = 0; i<256; i++) {

			if (i < imin) lookup.at<uchar>(i) = 0;
			else if (i > imax) lookup.at<uchar>(i) = 255;
			else lookup.at<uchar>(i) = cvRound(255.0*(i - imin) / (imax - imin));
		}

		// Apply lookup table
		cv::Mat result;
		result = applyLookUp(image, lookup);

		return result;
	}

	// static methods

	// Create an image representing a histogram
	static cv::Mat getImageOfHistogram(const cv::Mat &hist, int zoom) {

		// Get min and max bin values
		double maxVal = 0;
		double minVal = 0;
		cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);

		// get histogram size
		int histSize = hist.rows;

		// Square image on which to display histogram
		cv::Mat histImg(histSize*zoom, histSize*zoom, CV_8U, cv::Scalar(255));

		// set highest point at 90% of nbins (i.e. image height)
		int hpt = static_cast<int>(0.9*histSize);

		// Draw vertical line for each bin
		for (int h = 0; h < histSize; h++) {

			float binVal = hist.at<float>(h);
			if (binVal>0) {
				int intensity = static_cast<int>(binVal*hpt / maxVal);
				cv::line(histImg, cv::Point(h*zoom, histSize*zoom),
					cv::Point(h*zoom, (histSize - intensity)*zoom), cv::Scalar(0), zoom);
			}
		}

		return histImg;
	}

	// Equalizes the source image.
	static cv::Mat equalize(const cv::Mat &image) {

		cv::Mat result;
		cv::equalizeHist(image, result);

		return result;
	}

	// Applies a lookup table transforming an input image into a 1-channel image
	static cv::Mat applyLookUp(const cv::Mat& image, // input image
		const cv::Mat& lookup) { // 1x256 uchar matrix

								 // the output image
		cv::Mat result;

		// apply lookup table
		cv::LUT(image, lookup, result);

		return result;
	}
};

#endif


主程序(main):

#include <cv.h>
#include <highgui.h>
#include <opencv2\opencv.hpp>
#include "ColorHistogram.h"
#include "ContentFinder.h"
using namespace std;
using namespace cv;

/// 全局变量的声明与初始化
const int alpha_slider_max = 99;
int alpha_slider;

cv::Mat image;
cv::Mat imageROI;
ContentFinder finder;
cv::Mat result1;
ColorHistogram hc;
VideoCapture cap(1);
Mat color;

void on_trackbar(int, void*)
{

	float a = float(alpha_slider / 100.0);
	finder.setThreshold(a);
	while (true) {

		cap >> color;
		namedWindow("1");
		imshow("1", color);
		result1 = finder.find(color);
		Mat res;
		Mat element5(3, 3, CV_8U, Scalar(1));
		morphologyEx(result1, res, MORPH_CLOSE, element5);
		blur(res, res, Size(2, 2));
		cv::namedWindow("Color Detection Result");
		cv::imshow("Color Detection Result", res);
		char c = waitKey(33);
		if (c == 27) break;
	}
	return;
}

int main(int argc, char** argv)
{

	image = cv::imread("E:\\ProgramProject\\1.png");
	if (!image.data)
		return 0;
	//cv::Mat imageROI;
	imageROI = image(cv::Rect(0, 0, 30, 24));
	hc.setSize(4);
	cv::Mat shist = hc.getHistogram(imageROI);
	finder.setHistogram(shist);
	/// 初始化为零
	alpha_slider = 9;

	/// 创建窗体
	namedWindow("Rate");

	/// 在创建的窗体中创建一个滑动条控件
	char TrackbarName[50];
	sprintf(TrackbarName, "Alpha x %d", alpha_slider);

	createTrackbar(TrackbarName, "Rate", &alpha_slider, alpha_slider_max, on_trackbar);

	/// 结果在回调函数中显示
	on_trackbar(alpha_slider, 0);
	return 0;
}

书中的histSize[0] = histSize[1] = histSize[2] = 256;
并没有设置修改方法,这里设置了

void setSize(int size) {


		histSize[0] = histSize[1] = histSize[2] = size;
	}

这一方法用于修改bin值,一举解决了按照书上代码有时无法显示图像的问题。
另外,通过滑动条可以动态调整识别阈值。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值