数字图像处理中常用的采用模型是RGB(红,绿,蓝)模型和HSV(色调,饱和度,亮度),RGB广泛应用于彩色监视器和彩色视频摄像机,我们平时的图片一般都是RGB模型。而HSV模型更符合人描述和解释颜色的方式,HSV的彩色描述对人来说是自然且非常直观的。
HSV模型中颜色的参数分别是:色调(H:hue),饱和度(S:saturation),亮度(V:value)。由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。
#include
#include
using namespace cv;
using namespace std;
#define WINDOW_NAME "【效果图窗口】" //为窗口标题定义的宏
void pickHighLightFire(Mat& inputFrame, Mat& outputFrame);
void on_MouseHandle(int event, int x, int y, int flags, void* param);
int main()
{
int multiple = 1;//图片的放大倍数
Mat inputImage = imread("ld//53.jpg");//这里放置自己的文件路径。
Mat outputImage;
resize(inputImage, inputImage, Size(multiple * inputImage.cols, multiple * inputImage.rows));
cvtColor(inputImage, outputImage, COLOR_BGR2HSV);
//设置鼠标操作回调函数
namedWindow(WINDOW_NAME);
setMouseCallback(WINDOW_NAME, on_MouseHandle, (void*)&outputImage);
imshow(WINDOW_NAME, inputImage);
while (1)
{
if (waitKey(10) == 27) break;//按下ESC键,程序退出
}
waitKey();
return 0;
}
void on_MouseHandle(int event, int x, int y, int flags, void* param)
{
Mat& image = *(cv::Mat*) param;
switch (event)
{
//左键按下消息
case EVENT_LBUTTONDOWN:
{
cout << static_cast( image.at(y, x)[0]) << ",";
cout << static_cast(image.at(y, x)[1]) << ",";
cout << static_cast(image.at(y, x)[2]) << endl;
}
break;
}
}
通过双层循环,遍历所有的HSV像素值,相加取平均
#include //头文件
#include
#include
#include
using namespace cv; //包含cv命名空间
using namespace std;
int main()
{
// 【1】读入一张图片
Mat srcImage = imread("1.jpg",0),dstImage,imagePart,hsvImage;
dstImage = srcImage.clone();
//区域,可用鼠标事件来框选获得
Rect rect;
int x=50;
int y=50;
int width=100;
int height=100;
rect=Rect(x,y,width,height);
imagePart=srcImage(rect);//抠图
int channels = imagePart.channels();
if(3==channels)
{
cvtColor(imagePart, hsvImage, COLOR_BGR2HSV); //将RGB转成HSV
int rowNumber = hsvImage.rows; //行数
int colNumber = hsvImage.cols; //列数
CvPoint pointVal;
int hValues,sValues,vValuses;
for(int i = 0; i < rowNumber; i++) //行循环,可根据需要换成rowNumber
{
for(int j = 0; j < colNumber; j++) //列循环,同理
{
pointVal.x=j;
pointVal.y=i;
hValues += hsvImage.at(Point(pointVal))[0]; //H通道灰度值
hValues += hsvImage.at(Point(pointVal))[0]; //S通道灰度值
hValues += hsvImage.at(Point(pointVal))[0]; //V通道灰度值
//cout << intensity << " " ;
}
cout << endl;
}
int meanH=hValues/(rowNumber*colNumber) //区域图像的平均H值
int meanS=sValues/(rowNumber*colNumber) //区域图像的平均S值
int meanV=vValues/(rowNumber*colNumber) //区域图像的平均V值
}
//cvtColor(srcImage,dstImage,CV_BGR2GRAY);
// 【2】在窗口中显示载入的图片
//imshow("效果图",dstImage);
// 【3】等待按任意键窗口自动关闭
waitKey();
return 0;
}
根据提取的颜色HSV模板值做颜色识别
Opencv中,颜色提取的一种方式是将BGR空间下的图像转换为HSV空间下,然后利用opencv自带函数inRange,设置需提取的HSV各分量上下限,从而进行提取
下给出HSV对应BGR颜色的表格 (下面会用到):
首先我们读取一张图片或从视频读取一帧图像,用下面的函数转为HSV模型
cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV);
然后我们对彩色图像做直方图均衡化
//因为我们读取的是彩色图,直方图均衡化需要在HSV空间做
split(imgHSV, hsvSplit);
equalizeHist(hsvSplit[2],hsvSplit[2]);
merge(hsvSplit,imgHSV);
接着就是进行颜色检测,我们用void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst);函数进行颜色检测,这个函数的作用就是检测src图像的每一个像素是不是在lowerb和upperb之间,如果是,这个像素就设置为255,并保存在dst图像中,否则为0。
inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), imgThresholded); //Threshold the image
通过上面的函数我们就可以得到目标颜色的二值图像,接着我们先对二值图像进行开操作,删除一些零零星星的噪点,再使用闭操作,连接一些连通域,也就是删除一些目标区域的白色的洞。
//开操作 (去除一些噪点)
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(imgThresholded, imgThresholded, MORPH_OPEN, element);
//闭操作 (连接一些连通域)
morphologyEx(imgThresholded, imgThresholded, MORPH_CLOSE, element);
整个代码实现
#include
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;
int main( int argc, char** argv )
{
VideoCapture cap(0); //capture the video from web cam
if ( !cap.isOpened() ) // if not success, exit program
{
cout << "Cannot open the web cam" << endl;
return -1;
}
namedWindow("Control", CV_WINDOW_AUTOSIZE); //create a window called "Control"
/////蓝色的HSV区间。也可换成上面提取的HSV颜色模板来识别颜色
int iLowH = 100;
int iHighH = 140;
int iLowS = 90;
int iHighS = 255;
int iLowV = 90;
int iHighV = 255;
//Create trackbars in "Control" window
cvCreateTrackbar("LowH", "Control", &iLowH, 179); //Hue (0 - 179)
cvCreateTrackbar("HighH", "Control", &iHighH, 179);
cvCreateTrackbar("LowS", "Control", &iLowS, 255); //Saturation (0 - 255)
cvCreateTrackbar("HighS", "Control", &iHighS, 255);
cvCreateTrackbar("LowV", "Control", &iLowV, 255); //Value (0 - 255)
cvCreateTrackbar("HighV", "Control", &iHighV, 255);
while (true)
{
Mat imgOriginal;
bool bSuccess = cap.read(imgOriginal); // read a new frame from video
if (!bSuccess) //if not success, break loop
{
cout << "Cannot read a frame from video stream" << endl;
break;
}
//进行了两次滤波
//medianBlur(imgOriginal, imgOriginal, 5);
//GaussianBlur(imgOriginal, imgOriginal, Size(3, 3), 0, 0);
Mat imgHSV;
vector hsvSplit;
cvtColor(imgOriginal, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV
//因为我们读取的是彩色图,直方图均衡化需要在HSV空间做
split(imgHSV, hsvSplit);
equalizeHist(hsvSplit[2],hsvSplit[2]);
merge(hsvSplit,imgHSV);
Mat imgThresholded;
inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), imgThresholded); //Threshold the image
//开操作 (去除一些噪点)
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(imgThresholded, imgThresholded, MORPH_OPEN, element);
//闭操作 (连接一些连通域)
morphologyEx(imgThresholded, imgThresholded, MORPH_CLOSE, element);
imshow("Thresholded Image", imgThresholded); //show the thresholded image
imshow("Original", imgOriginal); //show the original image
/////轮廓最小外接矩形的绘制 和 粗略计算物体像素长宽/////
vector > contours;
vector hierarcy;//没用到
//1.2查找轮廓
findContours(imgThresholded, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//最外层轮廓
//排序
SortContourPoint(contours, contours, 0, true);
//1.3绘制所有轮廓
drawContours(imgOriginal, contours, -1, Scalar(0, 255, 0), 1, 8);
//2.由轮廓确定正外接矩形及最小外接矩形
//2.1 定义Rect类型的vector容器boundRect存放正外接矩形,初始化大小为contours.size()即轮廓个数
vector boundRect(contours.size());
//2.2 定义Rect类型的vector容器roRect存放最小外接矩形,初始化大小为contours.size()即轮廓个数
vector roRect(contours.size());
//2.3 遍历每个轮廓
for (int i = 0; i < contours.size(); i++)
{
//2.4 由轮廓(点集)确定出正外接矩形并绘制
boundRect[i] = boundingRect(Mat(contours[i]));
//2.4.1获得正外接矩形的左上角坐标及宽高
int width = boundRect[i].width;
int height = boundRect[i].height;
int x = boundRect[i].x;
int y = boundRect[i].y;
//2.4.2用画矩形方法绘制正外接矩形
rectangle(imgOriginal, Rect(x, y, width, height), Scalar(255, 0, 0), 2, 8);
//2.5 由轮廓(点集)确定出最小外接矩形并绘制
//旋转矩形主要成员有center、size、 angle、points()
roRect[i] = minAreaRect(Mat(contours[i]));
//2.5.1 旋转矩形类RotatedRect中有Point()方法,参数Point2f* pts,将旋转矩形的四个端点存储进pts.
Point2f pts[4] = { 0 };
roRect[i].points(pts); //把最小外接矩形四个端点复制给pts数组
//2.5.2 用line方法,根据旋转矩形的四个角点画出最小外接矩形每条边
line(imgOriginal, pts[0], pts[1], Scalar(0, 0, 255), 2, 8);
line(imgOriginal, pts[0], pts[3], Scalar(0, 0, 255), 2, 8);
line(imgOriginal, pts[2], pts[1], Scalar(0, 0, 255), 2, 8);
line(imgOriginal, pts[2], pts[3], Scalar(0, 0, 255), 2, 8);
//for(int j=0; j<4; j++)
//{
//line(imgOriginal, pts[j], pts[(j+1)%4], Scalar(0, 0, 255), 2, 8); //绘制最小外接矩形每条边
//}
//*2.5.3 由旋转矩形的center成员得出中心点
Point center = roRect[i].center;
//*2.5.4 用circle方法画出中心点center
circle(imgOriginal, center, 5, Scalar(0, 255, 0), -1, 8);
//*2.5.5 由旋转矩形的size成员得出宽和高
double widthRotated = roRect[i].size.width;
double heightRotated = roRect[i].size.height;
char widthStr[20] = { 0 };
char heightStr[20] = { 0 };
sprintf(widthStr, "width:%.2f", widthRotated);
sprintf(heightStr, "height:%.2f", heightRotated);
//*2.5.6 用putText方法将最小外接矩形的宽高显示在图像上
putText(imgOriginal, widthStr, Point(center.x,center.y+50),CV_FONT_HERSHEY_PLAIN,1,Scalar(64,64,255),1,8);
putText(imgOriginal, heightStr, Point(center.x, center.y + 80), CV_FONT_HERSHEY_PLAIN, 1, Scalar(64,64,255), 1, 8);
char sortIndex[20] = { 0 };
sprintf(sortIndex, "%d", i+1);
putText(imageSrc, sortIndex, Point(center.x-5, center.y + 20), CV_FONT_HERSHEY_PLAIN, 1, Scalar(64, 255, 64), 1, 8);
}
imshow("粗略计算物体像素长宽", imgOriginal);
char key = (char) waitKey(300);
if(key == 27)
break;
}
return 0;
}
/**
* @brief 排序
* @param inputContours 输入轮廓
* @param outputContours 输出轮廓
* @param sortType 排序类型:0-按x排序,1-按y排序,2-按面积排序
* @param sortOrder 排序方式:true-升序,false-降序
*
* @return 返回说明
* -false fail
* -true succeed
*/
void CTestCvDlg::SortContourPoint(vector> inputContours, vector> &outputContours, int sortType, bool sortOrder)
{
vector tempContoursPoint;
//计算轮廓矩
vector mu(inputContours.size());
//计算轮廓的质心
vector mc(inputContours.size());
//计算轮廓的面积
double area, area1;
//2.2 定义Rect类型的vector容器roRect存放最小外接矩形,初始化大小为contours.size()即轮廓个数
vector roRect(inputContours.size());
//计算轮廓最小外接矩形中心
Point center, center1;
for (int i = 0; i < inputContours.size(); i++)
{
tempContoursPoint.clear(); //每次循环注意清空
mu[i] = moments(inputContours[i], false); //轮廓矩
mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00); //轮廓质心
area = inputContours[i].size(); //轮廓点数
area = contourArea(inputContours[i], false); //轮廓的面积
//旋转矩形主要成员有center、size、 angle、points()
roRect[i] = minAreaRect(Mat(inputContours[i]));
center = roRect[i].center; //轮廓最小外接矩形中心
//outputContours[0] = inputContours[1];
}
//////////冒泡法
for (int i = 0; i < inputContours.size() - 1; i++) //n个数要进行n-1趟比较
{
for (int j = 0; j < (inputContours.size() - 1) - i; j++) //每趟比较n-i次
{
mu[j] = moments(inputContours[j], false); //轮廓矩
mu[j+1] = moments(inputContours[j+1], false); //轮廓矩
mc[j] = Point2d(mu[j].m10 / mu[j].m00, mu[j].m01 / mu[j].m00); //轮廓质心
mc[j+1] = Point2d(mu[j+1].m10 / mu[j+1].m00, mu[j+1].m01 / mu[j+1].m00); //轮廓质心
area = inputContours[j].size(); //轮廓点数
area = contourArea(inputContours[j], false); //轮廓的面积
area1 = inputContours[j+1].size(); //轮廓点数
area1 = contourArea(inputContours[j+1], false); //轮廓的面积
//旋转矩形主要成员有center、size、 angle、points()
roRect[j] = minAreaRect(Mat(inputContours[j]));
center = roRect[j].center; //轮廓最小外接矩形中心
roRect[j+1] = minAreaRect(Mat(inputContours[j+1]));
center1 = roRect[j+1].center; //轮廓最小外接矩形中心
if (mc[j].x > mc[j + 1].x) //依次比较两个相邻的数,将小数放在前面,大数放在后面:升序
{
tempContoursPoint = inputContours[j]; //temp是局部变量
inputContours[j] = inputContours[j + 1];
inputContours[j + 1] = tempContoursPoint;
}
}
}
outputContours = inputContours;
//for (int i = 0; i < inputContours.size(); i++)
//{
// tempContoursPoint.clear(); //每次循环注意清空
// mu[i] = moments(inputContours[i], false); //轮廓矩
// mc[i] = Point2d(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00); //轮廓质心
// area = inputContours[i].size(); //轮廓点数
// area = contourArea(inputContours[i], false); //轮廓的面积
// //旋转矩形主要成员有center、size、 angle、points()
// roRect[i] = minAreaRect(Mat(inputContours[i]));
// center = roRect[i].center; //轮廓最小外接矩形中心
// //outputContours[0] = inputContours[1];
//}
}