二值化+连通域+提取轮廓小记

1.颜色空间-RGB

在这里插入图片描述

//颜色通道
Mat-rgb
0-r,1-g,2-b

Mat-bgr
0-b,1-g,2-r

//通道分离:
vector<Mat>channels_;
split(img, channels_);

生成随机颜色:

随机颜色范围:0.0~255.0
RNG rng;
b=rng.uniform(0.0, 255.0);
g=rng.uniform(0.0, 255.0);
r=rng.uniform(0.0, 255.0);
Scalar color_ = Scalar(b,g,r);

2.颜色空间-HSV

倒锥形模型:
在这里插入图片描述

HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间。这个模型就是按色彩、深浅、明暗来描述的。

色调H
用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°;
色调图谱:
在这里插入图片描述
饱和度S
饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,值越大,颜色越饱和。
明度V
明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白)。
RGB和CMY颜色模型都是面向硬件的,而HSV(Hue Saturation Value)颜色模型是面向用户的。
HSV模型的三维表示从RGB立方体演化而来。设想从RGB沿立方体对角线的白色顶点向黑色顶点观察,就可以看到立方体的六边形外形。六边形边界表示色彩,水平轴表示纯度,明度沿垂直轴测量。
相互转换:
RGB->HSV:
在这里插入图片描述
HSV->RGB:
在这里插入图片描述
实现:
参考:色彩转换系列之RGB格式与HSV格式互转原理及实现

Mat RGB2HSV(Mat src) {
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_32FC3);
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			float b = src.at<Vec3b>(i, j)[0] / 255.0;
			float g = src.at<Vec3b>(i, j)[1] / 255.0;
			float r = src.at<Vec3b>(i, j)[2] / 255.0;
			float minn = min(r, min(g, b));
			float maxx = max(r, max(g, b));
			dst.at<Vec3f>(i, j)[2] = maxx; //V
			float delta = maxx - minn;
			float h, s;
			if (maxx != 0) {
				s = delta / maxx;
			}
			else {
				s = 0;
			}
			if (r == maxx) {
				h = (g - b) / delta;
			}
			else if (g == maxx) {
				h = 2 + (b - r) / delta;
			}
			else if (b==maxx) {
				h = 4 + (r - g) / delta;
			}
			else{
				h = 0;
			}
			h *= 60;
			if (h < 0)
				h +=  360;
			dst.at<Vec3f>(i, j)[0] = h;
			dst.at<Vec3f>(i, j)[1] = s;
		}
	}
 
	return dst;
}
 
Mat HSV2RGB(Mat src) {
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC3);
	float r, g, b, h, s, v;
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			h = src.at<Vec3f>(i, j)[0];
			s = src.at<Vec3f>(i, j)[1];
			v = src.at<Vec3f>(i, j)[2];
			if (s == 0) {
				r = g = b = v;
			}
			else {
				h /= 60;
				int offset = floor(h);
				float f = h - offset;
				float p = v * (1 - s);
				float q = v * (1 - s * f);
				float t = v * (1 - s * (1 - f));
				switch (offset)
				{
				case 0: r = v; g = t; b = p; break;
				case 1: r = q; g = v; b = p; break;
				case 2: r = p; g = v; b = t; break;
				case 3: r = p; g = q; b = v; break;
				case 4: r = t; g = p; b = v; break;
				case 5: r = v; g = p; b = q; break;
				default:
					break;
				}
			}
			dst.at<Vec3b>(i, j)[0] = int(b * 255);
			dst.at<Vec3b>(i, j)[1] = int(g * 255);
			dst.at<Vec3b>(i, j)[2] = int(r * 255);
		}
	}
	return dst;
}

3.图像中绘制填充背景色的矩形

void draw_(const cv::Mat&img)
{
	cv::Mat dst = img.clone();
	cv::Point p1 = cv::Point(50, 30);
	cv::Point p2 = cv::Point(100, 70);
	cv::Rect rect_0(p1, p2);
	cv::Mat mask(dst.size(), dst.type(), cv::Scalar::all(0));
	cv::Mat roi_0(rect_0.size(), dst.type(), cv::Scalar::all(0));

	cv::rectangle(mask, rect_0, cv::Scalar(180, 238, 120), -1);
	cv::addWeighted(dst(rect_0), 0.7, mask(rect_0), 0.3, 0, roi_0);
	roi_0.copyTo(dst(rect_0));

	cv::namedWindow("mask", cv::WINDOW_NORMAL);
	cv::imshow("mask", dst);
	cv::waitKey(0);
}

在这里插入图片描述

4.二值化:

输入应为灰度图

//大津阈值
threshold(gray, bw, -1, 255,THRESH_OTSU);//

//可调节阈值T
threshold(gray, bw, T, 255,THRESH_BINARY_INV);//

或者:
Mat bw0(img.size(), CV_8UC1, Scalar::all(0));
bw0.setTo(255, gray == 255);

对连通域进行分析
Mat label, stats, cens;
connectedComponentsWithStats(bw0, label, stats, cens, 8);
//统计面积
Mat bw1(gray.size(), CV_8UC1, Scalar(0));
int area_T = 0.;
vector<int>indexes;
for (int i = 0; i < stats.rows; ++i)
{
	int* ptr = stats.ptr<int>(i);
	if (ptr[4] >=10 && ptr[4] < 150)
	{
		indexes.push_back(i);
		bw1.setTo(255, label == i);
		//cout << i << "    "<< stats.at<int>(i, CC_STAT_AREA)<< endl;
	}
}

5.轮廓查找

vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(bw1, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point());
Mat bw2(gray.size(), CV_8UC1, Scalar(0));
for (int i = 0; i < contours.size(); ++i)
{
	RotatedRect rect = minAreaRect(contours[i]);
	if (contourArea(contours[i]) / rect.size.area() > 0.8)
	{
		drawContours(bw2, contours, i, Scalar(255), -1);
	}
    cv::Point2f vertex[4];
	rect.points(vertex);
	for (int i = 0; i < 4; i++) {
		cv::circle(mask, points[i], 4, cv::Scalar(0, 255, 0), -1);
		cv::line(mask, vertex[i], vertex[(i + 1) % 4],cv::Scalar(100, 200, 211), 1);
	}
	//计算长度
	int len=cv::arcLength(contours[i],1);
}

//轮廓按照面积排序
std::sort(contour.begin(),contour.end(),[](const std::vector<Point> &s1,
              const std::vector<Point> &s2){
        double a1=contourArea(s1);
        double a2=contourArea(s2);
        return a1>a2;
    });

//轮廓按照大小排序
std::sort(contour.begin(),contour.end(),[](const std::vector<Point> &s1,
              const std::vector<Point> &s2){return s1.size()>s2.size();});

//对点集按照坐标x排序
sort(points_.begin(), points_.end(), [=](cv::Point2f& a, cv::Point2f& b) 
		{return a.x < b.x; });

//对点集按照坐标y排序
sort(points_.begin(), points_.end(), [=](cv::Point2f& a, cv::Point2f& b)
		{return a.y < b.y; });
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月醉窗台

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值