本文按照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值,一举解决了按照书上代码有时无法显示图像的问题。
另外,通过滑动条可以动态调整识别阈值。