5.11.1 颜色圆检测
RGB:
RGBA(Alpha),A用于描述三原色的透明度!
单通道:
俗称灰度图,每个像素点只能有一个值表示颜色,它的像素值在0到255之间,0是黑色,255是白色,中间值是一些不同等级的灰色,可以说灰度是黑与白之间的过渡色!
多通道:
多通道也就是RGB三原色,每个像素点有三个字节来表示(RGB),分别最大取值范围是0-255,可以组合成千万种颜色。
所以三通道想要组合成黑白色(灰度)必须三原色值相同。也就是说灰度图不一定是单通道,但是单通道一定是灰度图!
色调、饱和度、亮度(Hue, Saturation, Value):
色调(偏向):
色调决定一个像素点中的颜色更偏向于哪一方(RGB):
饱和度(深浅):
饱和度决定了颜色空间中颜色分量,饱和度越高,说明颜色越深,饱和度越低,说明颜色越浅!
亮度(明暗):
亮度决定颜色空间中颜色的明暗程度!
RGB和HSV关系:
其实色调,饱和度,亮度都是通过特定的算法经过计算修改RGB三色而达到的控制颜色效果!
- 色调(H:hue):用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,品红为300°;
- 饱和度(S:saturation):取值范围为0.0~1.0,值越大,颜色越饱和。
- 亮度(V:value):取值范围为0(黑色)~255(白色)。
参考:
https://blog.csdn.net/bjbz_cxy/article/details/79701006
OpenCV下有个函数可以直接将RGB模型转换为HSV模型,注意的是OpenCV中H∈ [0, 180), S ∈ [0, 255], V ∈ [0, 255]。
HoughCircles()使用比较关键是参数的设置
1 ////////////https://blog.csdn.net/piaoxuezhong/article/details/60757278 2 ////////找出圆绘制圆心和外圆 3 #include <opencv2/opencv.hpp> 4 #include <opencv2/imgproc/imgproc.hpp> 5 6 using namespace cv; 7 using namespace std; 8 9 int main(int argc, char** argv) 10 { 11 12 Mat srcImage = imread("D:\1块硬币.jpg"); 13 Mat midImage, dstImage; 14 imshow("【原始图】", srcImage); 15 16 cvtColor(srcImage, midImage, CV_BGR2GRAY);//转化边缘检测后的图为灰度图 17 GaussianBlur(midImage, midImage, Size(9, 9), 2, 2); 18 vector<Vec3f> circles; 19 HoughCircles(midImage, circles, CV_HOUGH_GRADIENT, 1, midImage.rows / 20, 100, 100, 0, 0); 20 //依次在图中绘制出圆 21 for (size_t i = 0; i < circles.size(); i++) 22 { 23 Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); 24 int radius = cvRound(circles[i][2]); 25 //绘制圆心 26 circle(srcImage, center, 3, Scalar(0, 255, 0), -1, 8, 0); 27 //绘制圆轮廓 28 circle(srcImage, center, radius, Scalar(155, 50, 255), 3, 8, 0); 29 } 30 31 imshow("【效果图】", srcImage); 32 waitKey(0); 33 return 0; 34 }
第1张图片:
第2张图片:
参考:https://blog.csdn.net/piaoxuezhong/article/details/60757278
1 ////////https://blog.csdn.net/linqianbi/article/details/78936612 2 #include<opencv2highguihighgui.hpp> 3 #include<opencv2imgprocimgproc.hpp> 4 #include<iostream> 5 #include<vector> 6 using namespace cv; 7 using namespace std; 8 int main() 9 { 10 Mat srcImage = imread("D:\颜色圆.jpg"); 11 if (!srcImage.data) 12 { 13 printf("could not load image... "); 14 return -1; 15 } 16 imshow("srcImage", srcImage); 17 Mat resultImag = srcImage.clone(); 18 //中值滤波 19 medianBlur(srcImage, srcImage, 3); 20 //转换成HSV颜色空间 21 Mat hsvImage; 22 cvtColor(srcImage, hsvImage, CV_BGR2HSV); 23 imshow("hsvImage", hsvImage); 24 //颜色阈值化处理 25 //定义高低阈值 26 Mat lowMat; 27 Mat upperMat; 28 //红色H的范围是[0,10] [160,180] 29 inRange(hsvImage, Scalar(0, 100, 100), Scalar(10, 255, 255), lowMat); 30 inRange(hsvImage, Scalar(160, 100, 100), Scalar(179, 255, 255), upperMat); 31 imshow("lowMat", lowMat); 32 imshow("upperMat", upperMat); 33 //将高低阈值合并 34 Mat redMat; 35 addWeighted(lowMat, 1, upperMat, 1, 0, redMat); 36 imshow("redMat", redMat); 37 //高斯滤波 38 GaussianBlur(redMat, redMat, Size(9, 9), 2, 2); 39 //霍夫圆加测 40 vector<Vec3f> circles; 41 HoughCircles(redMat, circles, CV_HOUGH_GRADIENT, 1, redMat.rows / 8, 100, 50, 0, 0); 42 //如果没有检测到圆 43 if (circles.size() == 0) 44 return -1; 45 for (int i = 0; i < circles.size(); i++) 46 { 47 //求出圆心的位置和圆半径的大小 48 Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); 49 int radius = cvRound(circles[i][2]); 50 circle(resultImag, center, radius, Scalar(0, 255, 0), 5); 51 } 52 imshow("resultImag", resultImag); 53 waitKey(0); 54 return 0; 55 }
1.将原始图片的BGR颜色空间转换到HSV色彩空间
2.根据红色在HSV中的范围,对图像进行阈值分割,红色的圆被保留(白色),其他的变成黑色
3.对步骤二检测到的高低阈值的颜色圆进行合并,找出属于红色的圆
4.利用霍夫圆检测算法找出步骤3中的圆
拓展:
如果要选中找出蓝色圆:
需要一个取色和调色小工具,这个小工具支持HSV空间的取色。不过不管是这个小工具还是PS中,H的范围都是0~360,S的范围是0到百分之百,V的范围也是0到百度分之百,这个也和百科上的解释是一致的。所以你在具体使用的时候要按比例转化一下哦!比如车牌蓝在标准HSV中是(225,100%,81%)那么转换到OpenCV中就是(225/360*180,255*100%,255*81%)!
参考:https://blog.csdn.net/wenhao_ir/article/details/51850948
下面是用HoughCircles函数进行霍夫变换圆检测的实例。由于HoughCircles函数内是调用Canny函数进行边缘检测,opencv的Canny函数是不包括平滑滤波这一步的,因此为了增强抗干扰能力,在使用HoughCircles函数之前,我们先对原图进行滤波处理,我们使用的是高斯模糊方法。
参考:
https://blog.csdn.net/piaoxuezhong/article/details/60757278
https://blog.csdn.net/linqianbi/article/details/78936612
https://blog.csdn.net/poem_qianmo/article/details/26977557
https://blog.csdn.net/yanxiaolx/article/details/62889943
未研究:https://blog.csdn.net/taily_duan/article/details/51886019
http://www.opencv.org.cn/forum.php?mod=viewthread&tid=34096
5.11.2 车牌区域检测
1 ////////https://blog.csdn.net/z827997640/article/details/80527581 2 3 #include <iostream> 4 #include <vector> 5 #include <stdint.h> 6 #include "opencv2/core/core.hpp" 7 #include "opencv2/highgui/highgui.hpp" 8 #include "opencv2/imgproc/imgproc.hpp" 9 #include "opencv2/features2d/features2d.hpp" 10 // 提取竖直的sobel边缘 11 bool SobelVerEdge(cv::Mat srcImage, cv::Mat& resultImage) 12 { 13 CV_Assert(srcImage.channels() == 1); 14 srcImage.convertTo(srcImage, CV_32FC1); 15 // 水平方向的 Sobel 算子 16 cv::Mat sobelx = (cv::Mat_<float>(3, 3) << -0.125, 0, 0.125, 17 -0.25, 0, 0.25, 18 -0.125, 0, 0.125); 19 cv::Mat ConResMat; 20 // 卷积运算 21 cv::filter2D(srcImage, ConResMat, srcImage.type(), sobelx); 22 // 计算梯度的幅度 23 cv::Mat graMagMat; 24 cv::multiply(ConResMat, ConResMat, graMagMat); 25 // 根据梯度幅度及参数设置阈值 26 int scaleVal = 4; 27 double thresh = scaleVal * cv::mean(graMagMat).val[0]; 28 cv::Mat resultTempMat = cv::Mat::zeros( 29 graMagMat.size(), graMagMat.type()); 30 float* pDataMag = (float*)graMagMat.data; 31 float* pDataRes = (float*)resultTempMat.data; 32 const int nRows = ConResMat.rows; 33 const int nCols = ConResMat.cols; 34 for (int i = 1; i != nRows - 1; ++i) { 35 for (int j = 1; j != nCols - 1; ++j) { 36 // 计算该点梯度与水平或垂直梯度值大小比较结果 37 bool b1 = (pDataMag[i * nCols + j] > pDataMag[i * 38 nCols + j - 1]); 39 bool b2 = (pDataMag[i * nCols + j] > pDataMag[i * 40 nCols + j + 1]); 41 bool b3 = (pDataMag[i * nCols + j] > pDataMag[(i - 1) 42 * nCols + j]); 43 bool b4 = (pDataMag[i * nCols + j] > pDataMag[(i + 1) 44 * nCols + j]); 45 // 判断邻域梯度是否满足大于水平或垂直梯度 46 // 并根据自适应阈值参数进行二值化 47 pDataRes[i * nCols + j] = 255 * ((pDataMag[i * 48 nCols + j] > thresh) && 49 ((b1 && b2) || (b3 && b4))); 50 } 51 } 52 resultTempMat.convertTo(resultTempMat, CV_8UC1); 53 resultImage = resultTempMat.clone(); 54 return true; 55 } 56 // 疑似区域提取 57 cv::Mat getPlateArea(cv::Mat srcImage, cv::Mat sobelMat) 58 { 59 // 转换成hsv 60 cv::Mat img_h, img_s, img_v, imghsv; 61 std::vector<cv::Mat> hsv_vec; 62 cv::cvtColor(srcImage, imghsv, CV_BGR2HSV); 63 cv::imshow("hsv", imghsv); 64 cv::waitKey(0); 65 // 分割hsv通道 66 cv::split(imghsv, hsv_vec); 67 img_h = hsv_vec[0]; 68 img_s = hsv_vec[1]; 69 img_v = hsv_vec[2]; 70 img_h.convertTo(img_h, CV_32F); 71 img_s.convertTo(img_s, CV_32F); 72 img_v.convertTo(img_v, CV_32F); 73 double max_s, max_h, max_v; 74 cv::minMaxIdx(img_h, 0, &max_h); 75 cv::minMaxIdx(img_s, 0, &max_s); 76 cv::minMaxIdx(img_v, 0, &max_v); 77 // 各个通道归一化 78 img_h /= max_h; 79 img_s /= max_s; 80 img_v /= max_v; 81 // hsv 限定范围元素提取 82 cv::Mat bw_blue = ((img_h>0.45) & 83 (img_h<0.75) & 84 (img_s>0.15) & 85 (img_v>0.25)); 86 int height = bw_blue.rows; 87 int width = bw_blue.cols; 88 cv::Mat bw_blue_edge = cv::Mat::zeros(bw_blue.size(), bw_blue.type()); 89 cv::imshow("bw_blue", bw_blue); 90 cv::waitKey(0); 91 // 车牌疑似区域提取 92 for (int k = 1; k != height - 2; ++k) 93 { 94 for (int l = 1; l != width - 2; ++l) 95 { 96 cv::Rect rct; 97 rct.x = l - 1; 98 rct.y = k - 1; 99 rct.height = 3; 100 rct.width = 3; 101 if ((sobelMat.at<uchar>(k, l) == 255) && (cv::countNonZero(bw_blue(rct)) >= 1)) 102 bw_blue_edge.at<uchar>(k, l) = 255; 103 } 104 } 105 // 形态学闭操作 106 cv::Mat morph; 107 cv::morphologyEx(bw_blue_edge, morph, cv::MORPH_CLOSE, 108 cv::Mat::ones(2, 25, CV_8UC1)); 109 cv::Mat imshow5; 110 cv::resize(bw_blue_edge, imshow5, cv::Size(), 1, 1); 111 cv::imshow("morphology_bw_blue_edge", imshow5); 112 cv::waitKey(0); 113 // 连通区域提取 114 cv::imshow("morph", morph); 115 std::vector<std::vector<cv::Point> > region_contours; 116 cv::findContours(morph.clone(), region_contours, 117 CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0)); 118 std::vector<cv::Rect> candidates; 119 std::vector<cv::Mat> candidates_img; 120 cv::Mat result; 121 for (size_t n = 0; n != region_contours.size(); ++n) 122 { 123 // 去除高度宽度不符合条件区域 124 cv::Rect rect = cv::boundingRect(region_contours[n]); 125 int sub = cv::countNonZero(morph(rect)); 126 double ratio = double(sub) / rect.area(); 127 double wh_ratio = double(rect.width) / rect.height; 128 if (ratio > 0.5 && wh_ratio > 2 && wh_ratio < 5 && 129 rect.height > 12 && rect.width > 60) 130 { 131 cv::Mat small = bw_blue_edge(rect); 132 result = srcImage(rect); 133 cv::imshow("rect", srcImage(rect)); 134 cv::waitKey(0); 135 } 136 } 137 return result; 138 } 139 int main() 140 { 141 cv::Mat srcImage = cv::imread("D:\车辆4.jpg"); 142 if (!srcImage.data) 143 return 1; 144 cv::Mat srcGray; 145 cv::cvtColor(srcImage, srcGray, CV_RGB2GRAY); 146 cv::imshow("srcImage", srcImage); 147 // sobel 提取边缘 148 cv::Mat sobelMat; 149 SobelVerEdge(srcGray, sobelMat); 150 cv::imshow("Sobel", sobelMat); 151 // 疑似区域提取 152 cv::Mat result = getPlateArea(srcImage, sobelMat); 153 cv::imshow("result", result); 154 return 0; 155 }
1 ////////https://blog.csdn.net/hust_bochu_xuchao/article/details/52230694 2 ////////第4章图片“车辆4”才识别出车牌 3 #include "opencv2/highgui/highgui.hpp" 4 #include "opencv2/features2d.hpp" 5 #include "opencv2/imgproc/imgproc.hpp" 6 #include <iostream> 7 // Mser车牌目标检测 8 std::vector<cv::Rect> mserGetPlate(cv::Mat srcImage) 9 { 10 // HSV空间转换 11 cv::Mat gray, gray_neg; 12 cv::Mat hsi; 13 cv::cvtColor(srcImage, hsi, CV_BGR2HSV); 14 // 通道分离 15 std::vector<cv::Mat> channels; 16 cv::split(hsi, channels); 17 // 提取h通道 18 gray = channels[1]; 19 cv::imshow("提取h通道", gray); 20 // 灰度转换 21 cv::cvtColor(srcImage, gray, CV_BGR2GRAY); 22 // 取反值灰度 23 gray_neg = 255 - gray; 24 std::vector<std::vector<cv::Point> > regContours; 25 std::vector<std::vector<cv::Point> > charContours; 26 27 // 创建MSER对象 28 cv::Ptr<cv::MSER> mesr1 = cv::MSER::create(2, 10, 5000, 0.5, 0.3); 29 cv::Ptr<cv::MSER> mesr2 = cv::MSER::create(2, 2, 400, 0.1, 0.3); 30 31 32 std::vector<cv::Rect> bboxes1; 33 std::vector<cv::Rect> bboxes2; 34 // MSER+ 检测 35 mesr1->detectRegions(gray, regContours, bboxes1); 36 // MSER-操作 37 mesr2->detectRegions(gray_neg, charContours, bboxes2); 38 39 cv::Mat mserMapMat = cv::Mat::zeros(srcImage.size(), CV_8UC1); 40 cv::Mat mserNegMapMat = cv::Mat::zeros(srcImage.size(), CV_8UC1); 41 42 for (int i = (int)regContours.size() - 1; i >= 0; i--) 43 { 44 // 根据检测区域点生成mser+结果 45 const std::vector<cv::Point>& r = regContours[i]; 46 for (int j = 0; j < (int)r.size(); j++) 47 { 48 cv::Point pt = r[j]; 49 mserMapMat.at<unsigned char>(pt) = 255; 50 } 51 } 52 // MSER- 检测 53 for (int i = (int)charContours.size() - 1; i >= 0; i--) 54 { 55 // 根据检测区域点生成mser-结果 56 const std::vector<cv::Point>& r = charContours[i]; 57 for (int j = 0; j < (int)r.size(); j++) 58 { 59 cv::Point pt = r[j]; 60 mserNegMapMat.at<unsigned char>(pt) = 255; 61 } 62 } 63 // mser结果输出 64 cv::Mat mserResMat; 65 // mser+与mser-位与操作 66 mserResMat = mserMapMat & mserNegMapMat; 67 cv::imshow("mserMapMat", mserMapMat); 68 cv::imshow("mserNegMapMat", mserNegMapMat); 69 cv::imshow("mserResMat", mserResMat); 70 // 闭操作连接缝隙 71 cv::Mat mserClosedMat; 72 cv::morphologyEx(mserResMat, mserClosedMat, 73 cv::MORPH_CLOSE, cv::Mat::ones(1, 20, CV_8UC1)); 74 cv::imshow("mserClosedMat", mserClosedMat); 75 // 寻找外部轮廓 76 std::vector<std::vector<cv::Point> > plate_contours; 77 cv::findContours(mserClosedMat, plate_contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cv::Point(0, 0)); 78 // 候选车牌区域判断输出 79 std::vector<cv::Rect> candidates; 80 for (size_t i = 0; i != plate_contours.size(); ++i) 81 { 82 // 求解最小外界矩形 83 cv::Rect rect = cv::boundingRect(plate_contours[i]); 84 // 宽高比例 85 double wh_ratio = rect.width / double(rect.height); 86 // 不符合尺寸条件判断 87 if (rect.height > 20 && wh_ratio > 4 && wh_ratio < 7) 88 candidates.push_back(rect); 89 } 90 return candidates; 91 } 92 int main() 93 { 94 cv::Mat srcImage = cv::imread("D:\车辆4.jpg"); 95 if (srcImage.empty()) 96 return-1; 97 cv::imshow("src Image", srcImage); 98 // 候选车牌区域检测 99 std::vector<cv::Rect> candidates; 100 candidates = mserGetPlate(srcImage); 101 // 车牌区域显示 102 for (int i = 0; i < candidates.size(); ++i) 103 { 104 cv::imshow("rect", srcImage(candidates[i])); 105 cv::waitKey(); 106 } 107 cv::waitKey(0); 108 return 0; 109 }