OpenCV颜色提取——获取图片上某一区域的颜色平均HSV值——颜色识别

彩色模型

数字图像处理中常用的采用模型是RGB(红,绿,蓝)模型和HSV(色调,饱和度,亮度),RGB广泛应用于彩色监视器和彩色视频摄像机,我们平时的图片一般都是RGB模型。而HSV模型更符合人描述和解释颜色的方式,HSV的彩色描述对人来说是自然且非常直观的。

HSV模型

HSV模型中颜色的参数分别是:色调(H:hue),饱和度(S:saturation),亮度(V:value)。由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。

 

 

用鼠标获取图片上某一点的颜色HSV值

#include <opencv2/opencv.hpp>
#include <iostream>

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<int>( image.at<Vec3b>(y, x)[0]) << ",";
		cout << static_cast<int>(image.at<Vec3b>(y, x)[1]) << ",";
		cout << static_cast<int>(image.at<Vec3b>(y, x)[2]) << endl;
	}
	break;
	}
}

 

 

获取某一区域的颜色平均HSV值

通过双层循环,遍历所有的HSV像素值,相加取平均

#include <opencv2/imgproc/imgproc.hpp> //头文件
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
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<Vec3b>(Point(pointVal))[0]; //H通道灰度值
                hValues += hsvImage.at<Vec3b>(Point(pointVal))[0]; //S通道灰度值
                hValues += hsvImage.at<Vec3b>(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颜色的表格 (下面会用到):
《OpenCV颜色提取——获取图片上某一区域的颜色平均HSV值——颜色识别》

 

首先我们读取一张图片或从视频读取一帧图像,用下面的函数转为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
  • imgHSV:输入图像
  • lowerb:若输入的为三通道hsv图像,则为提取的下限,应填入Scalar(hmin,smin,vmin) 
  • upperb:若输入的为三通道hsv图像,则为提取颜色的上限,应填入Scalar(hmax,smax,vmax)
  • mask:imgThresholded输出为一个二值化的图像,提取出来的部分为255,其余部分为0

通过上面的函数我们就可以得到目标颜色的二值图像,接着我们先对二值图像进行开操作,删除一些零零星星的噪点,再使用闭操作,连接一些连通域,也就是删除一些目标区域的白色的洞。

//开操作 (去除一些噪点)
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(imgThresholded, imgThresholded, MORPH_OPEN, element);
 
//闭操作 (连接一些连通域)
morphologyEx(imgThresholded, imgThresholded, MORPH_CLOSE, element);

整个代码实现

#include <iostream>
#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<Mat> 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 <vector<Point>> contours;
        vector<Vec4i> 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<Rect> boundRect(contours.size());
        //2.2 定义Rect类型的vector容器roRect存放最小外接矩形,初始化大小为contours.size()即轮廓个数  
        vector<RotatedRect> 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 返回说明
*     -<em>false</em> fail
*     -<em>true</em> succeed
*/
void  CTestCvDlg::SortContourPoint(vector<vector<Point>> inputContours, vector<vector<Point>> &outputContours, int sortType, bool sortOrder)
{
	vector<Point> tempContoursPoint;

	//计算轮廓矩 	
	vector<Moments> mu(inputContours.size());
	//计算轮廓的质心 	
	vector<Point2f> mc(inputContours.size());
	//计算轮廓的面积
	double area, area1;
	//2.2 定义Rect类型的vector容器roRect存放最小外接矩形,初始化大小为contours.size()即轮廓个数  
	vector<RotatedRect> 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];
	//}

}

 

 

 

    原文作者:c1learning
    原文地址: https://blog.csdn.net/c1learning/article/details/100130805
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞