opencv2用反投影实现标定颜色的识别

来源:互联网 发布:基金怎么玩 知乎 编辑:程序博客网 时间:2024/05/27 00:48

还是需要说明一下,希望各位博友转载的时候和注明文章出处,谢谢!!!

颜色识别一般的方法是用已知的三基色与原图进行比对,对待检测的图像的每一个像素进行遍历,这种方法因为需要知道已知色,而且还要确定容忍度,对于颜色差别较小的物体来说是很难检测的,主要就是颜色的通道值非常难确定,我之前也做过很多这种方法的优化工作,就算用一般的算法优化的再彻底,效果也不敬人意,最后用直方图的反投影实现了颜色识别

这是先用鼠标进行标定的一个颜色检测的方法,直观的说就是先用鼠标标定一下你要检测的物体的颜色,然后用键盘事件去保存你要检测的颜色,然后再通过键盘事件去检测你要检测的物体

代码如下:

#include<opencv2/imgproc/imgproc.hpp>#include<opencv2/imgproc/types_c.h>#include<opencv2/highgui/highgui.hpp>#include<opencv2/core/core.hpp>#include<iostream>#include<vector>#include<opencv2/nonfree/features2d.hpp>#include<opencv2/legacy/legacy.hpp>#include<opencv2/features2d/features2d.hpp>#include<opencv2/calib3d/calib3d.hpp>#include<opencv2/video/tracking.hpp>#include<sstream>using namespace cv;using namespace std;class Package{private:public:MatND ComterBGRHistogram2(const Mat &InImage, const Rect &rectROI);Mat CalcBack_Color2(const Mat &InImage, MatND &hist);Mat colorReduce(const Mat &InImage, int div);/*这个是显示函数,只是为了方便使用加入的,可以不用在意*/void show(String name, Mat &img);};void Package::show(String name, Mat &img){namedWindow(name);imshow(name, img);}Mat Package::colorReduce(const Mat &InImage, int div){Mat OutImage(InImage.rows, InImage.cols, InImage.type());int x = InImage.rows;int y = InImage.cols*InImage.channels();for (int i = 0; i < x; i++){const uchar* data = InImage.ptr<const uchar>(i);uchar* data_out = OutImage.ptr<uchar>(i);for (int j = 0; j < y; j++){data_out[j] = data[j] / div*div + div / 2;}}return OutImage;}MatND Package::ComterBGRHistogram2(const Mat &InImage, const Rect &rectROI){Mat OutImage = InImage;//先初始化一些变量,在函数中用做中间参数使用float hranges[2];    //最小值和最大值const float *ranges[3];int channels[3];int histSize[3];ranges[0] = hranges;ranges[1] = hranges;ranges[2] = hranges;hranges[0] = 0.0;hranges[1] = 255.0;channels[0] = 0;channels[1] = 1;channels[2] = 2;histSize[0] = 256;histSize[1] = 256;histSize[2] = 256;OutImage = colorReduce(OutImage, 32);Mat OutImageROI = OutImage(rectROI);MatND hist;calcHist(&OutImageROI, 1, channels, Mat(), hist, 3, histSize, ranges);normalize(hist, hist, 1.0);/*Color_Hist2_OutImage = OutImage;*/return hist;}Mat Package::CalcBack_Color2(const Mat &InImage, MatND &hist){Mat OutImage = InImage;//先初始化一些变量,在函数中用做中间参数使用float hranges[2];    //最小值和最大值const float *ranges[3];int channels[3];int histSize[3];ranges[0] = hranges;ranges[1] = hranges;ranges[2] = hranges;hranges[0] = 0.0;hranges[1] = 255.0;channels[0] = 0;channels[1] = 1;channels[2] = 2;histSize[0] = 256;histSize[1] = 256;histSize[2] = 256;////使用颜色减缩函数OutImage = colorReduce(OutImage, 32);//Mat OutImageROI = OutImage(rectROI);///*MatND hist;//calcHist(&OutImageROI, 1, channels, Mat(), hist, 3, histSize, ranges);*///normalize(hist, hist, 1.0);Mat result;calcBackProject(&OutImage, 1, channels, hist, result, ranges, 255.0);return result;}Package P;Point coord;//储存初始坐标Rect sqart;//储存矩形框的起始坐标以及长度和宽度bool draw;bool flag = 0;//这个标志位是用在如果要将矩形标定的部分单独显示在一个窗口时使用的Mat frame, frame_org;Mat dst;//感兴趣区域图像Mat calc_B, calc_G, calc_R, calc_Y;Rect prerect;int start = 0;void onMouse(int event, int x, int y, int flags, void *param){//显示鼠标的当前坐标/*cout << "Event:" << event << endl;cout << "x=" << x << "     " << "y=" << y << endl;cout << "flags:" << endl;cout << "param" << param << endl;*///这个if必须放在switch之前if (draw){//用MIN得到左上点作为矩形框的其实坐标,如果不加这个,画矩形时只能向一个方向进行sqart.x = MIN(x, coord.x);sqart.y = MIN(y, coord.y);sqart.width = abs(coord.x - x);sqart.height = abs(coord.y - y);//防止矩形区域超出图像的范围sqart &= Rect(0, 0, frame.cols, frame.rows);}switch (event){case CV_EVENT_LBUTTONDOWN:coord = Point(x, y);sqart = Rect(x, y, 0, 0);draw = true;break;case CV_EVENT_LBUTTONUP:draw = false;flag = 1;break;}}int main(){char c;VideoCapture capture(0);namedWindow("Mouse", 1);MatND hist_B, hist_G, hist_R, hist_Y;double sum_B = 0, sum_G = 0, sum_R = 0, sum_Y = 0;/*calc_B = imread("F:\\color.jpg");calc_B.copyTo(calc_G);calc_B.copyTo(calc_R);calc_B.copyTo(calc_Y);*/setMouseCallback("Mouse", onMouse, 0);//由于视频序列的每一帧都在跟新,所以不会出现连环嵌套的状况while (1){capture >> frame;if (frame.empty())return -1;//将矩形框得到矩形区域用另一个窗口显示if ((flag == 1) && sqart.height > 0 && sqart.width > 0){dst = frame(Rect(sqart.x, sqart.y, sqart.width, sqart.height));namedWindow("dst");imshow("dst", dst);frame.copyTo(frame_org);prerect = Rect(sqart.x, sqart.y, sqart.width, sqart.height);flag = 0;}rectangle(frame, sqart, Scalar(0, 0, 255), 3);imshow("Mouse", frame);c = waitKey(20);if (c == 27)break;//键盘输入的是b的时候是进行蓝颜色的标定if (c == 98){hist_B = P.ComterBGRHistogram2(frame_org, prerect);}//键盘输入的是g的时候是进行蓝颜色的标定if (c == 103){hist_G = P.ComterBGRHistogram2(frame_org, prerect);}//键盘输入的是r的时候是进行蓝颜色的标定if (c == 114){hist_R = P.ComterBGRHistogram2(frame_org, prerect);}if (c == 121){hist_Y = P.ComterBGRHistogram2(frame_org, prerect);}//键盘输入的是a的时候是得到的是运行结果if (c == 97){Mat frame_B, frame_G, frame_R, frame_Y;frame_B = frame;frame_G = frame;frame_R = frame;frame_Y = frame;calc_B = P.CalcBack_Color2(frame_B, hist_B);threshold(calc_B, calc_B, 60, 255, THRESH_BINARY);P.show("calc_B", calc_B);Mat_<uchar>::const_iterator it_B = calc_B.begin<uchar>();while (it_B != calc_B.end<uchar>()){if (*it_B)sum_B++;++it_B;}calc_G = P.CalcBack_Color2(frame_G, hist_G);threshold(calc_G, calc_G, 60, 255, THRESH_BINARY);P.show("calc_G", calc_G);Mat_<uchar>::const_iterator it_G = calc_G.begin<uchar>();while (it_G != calc_G.end<uchar>()){if (*it_G)sum_G++;++it_G;}calc_R = P.CalcBack_Color2(frame_R, hist_R);threshold(calc_R, calc_R, 60, 255, THRESH_BINARY);P.show("calc_R", calc_R);Mat_<uchar>::const_iterator it_R = calc_R.begin<uchar>();while (it_R != calc_R.end<uchar>()){if (*it_R)sum_R++;++it_R;}calc_Y = P.CalcBack_Color2(frame_Y, hist_Y);threshold(calc_Y, calc_Y, 60, 255, THRESH_BINARY);P.show("calc_Y", calc_Y);Mat_<uchar>::const_iterator it_Y = calc_Y.begin<uchar>();while (it_Y != calc_Y.end<uchar>()){if (*it_Y)sum_Y++;++it_Y;}cout << sum_B << endl;cout << sum_G << endl;cout << sum_R << endl;cout << sum_Y << endl;if (sum_B > 1000)cout << "blue" << endl;if (sum_G > 1000)cout << "green" << endl;if (sum_R > 1000)cout << "red" << endl;if (sum_Y > 1000)cout << "yellow" << endl;sum_G = 0;sum_B = 0;sum_R = 0;sum_Y = 0;}}return 0;}

因为是用反投影做的,所以必须要有反投影的函数,为了使用方便,我把上次的函数改了一下

这个函数用于检测四种颜色,所以就需要先标定四次,这段代码只要掌握了反投影的使用很容易理解的,如果不懂可以把我的出现copy过去,因为我写的出现会出现很多中间窗口,比如说四个反投影得到的窗口,运行一下,稍加思考就能理解了

主要看看我的程序的使用步骤:

先标定:


用键盘按下小写的b进行标定



用键盘按下小写的g进行标定



用键盘按下小写的r进行标定



用键盘按下小写的y进行标定


然后就是检测了

先把别把刚刚标定的任何物体放入摄像头视线内,然后用用键盘输入小写的a,打印出来的是:



下面开始放刚刚标定的物体,顺序当然是随便的

下面放刚刚标定的蓝色的物体,用键盘按下a进行检测:



再放刚刚标定的绿色的物体,按下键盘的小写的a进行检测:



接着放红色的物体,按下键盘的a进行检测:



最后放入黄色的物体,按下键盘的小写的a进行检测:



这样就检测完了,记住如果按照我的程序,必须要标定四种颜色,不然会出错!!!

0 0
原创粉丝点击