基于hsv的图像分割与裁剪

原图:

1. RGB转化为HSV

cvtColor(srcImage, hsv, CV_BGR2HSV);

2. 得到红色区域掩膜

//设置HSV色彩空间各个分量的范围,Hue:色调,Saturation:饱和度,Value:明度
double low_H = 0; //156
double low_S = 43;
double low_V = 46;
double high_H = 10; //180
double high_S = 255;
double high_V = 255;
//使用inrange函数,将在这个范围的颜色提取出来,得到mask矩阵
cv::inRange(hsv, Scalar(low_H, low_S, low_V), Scalar(high_H, high_S, high_V), mask);

hsv色彩空间颜色范围: 

3. 对掩膜进行膨胀与腐蚀操作

//得到结构元素(内核矩阵),MORPH_RECT表示内核形状为矩形,Size(30, 30)表示内核的尺寸
Mat kernel = getStructuringElement(MORPH_RECT, Size(30, 30));
//对掩码进行膨胀,MORPH_DILATE表示进行膨胀操作
morphologyEx(mask, mask, MORPH_DILATE, kernel);
//对掩膜进行腐蚀,MORPH_ERODE表示进行腐蚀操作
morphologyEx(mask, mask, MORPH_ERODE, kernel);

 膨胀:

腐蚀:

4. 分割图像

Mat divmask = mask;
divmask = divmask / 255;
Mat channels[3];
//分离色彩通道
split(srcImage, channels);  
//分别乘以1或0
channels[0] = channels[0].mul(mask);
channels[1] = channels[1].mul(mask);
channels[2] = channels[2].mul(mask);
//将背景区域填成白色
for (int i = 0; i<channels[0].rows; i++)
{
	for (int j = 0; j<channels[0].cols; j++)
	{
		if ((channels[0].at<int8_t >(i, j) == 0) && (channels[1].at<int8_t >(i, j) == 0) && (channels[2].at<int8_t >(i, j) == 0))
		{
			channels[0].at<int8_t >(i, j) = 255;
			channels[1].at<int8_t >(i, j) = 255;
			channels[2].at<int8_t >(i, j) = 255;
		}
	}
}
Mat divImage;
//合成RGB
merge(channels, 3, divImage);

 5. 寻找轮廓

//向量contours内每个元素保存了一组由连续的Point点构成的点的集合的向量,在这里用来保存轮廓
vector<vector<Point> > contours;	
//findContours用于寻找轮廓,单通道二值图
//RETR_EXTERNAL表示只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
//CHAIN_APPROX_NONE表示保存物体边界上所有连续的轮廓点到contours向量;CHAIN_APPROX_SIMPLE表示仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量
findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
//绘制轮廓,第3个参数代表contours向量的下标,为0表示绘制第一个轮廓;最后一个参数-1代表绘制时填充轮廓,若大于0则指轮廓厚度
drawContours(srcImage, contours, 0, Scalar(255, 0, 0), 3);

6. 得到最小面积矩形 ,该矩形与图像边界不平行

//minAreaRect函数用于求点集最小面积的矩形,这个矩形是可以有偏转角度的,即可以与图像的边界不平行
//RotatedRect是一个存储旋转矩形的类,minAreaRect函数返回的最小面积矩形存储在rect对象中
RotatedRect rect = minAreaRect(contours[0]);
Point2f vertices[4];
//得到矩形的4个顶点
rect.points(vertices);
for (int i = 0; i < 4; i++)
	line(srcImage, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0));

7.  得到包含旋转矩形的最小矩形 ,该矩形与边界平行

//得到包含旋转矩形的最小矩形
Rect brect = rect.boundingRect();
rectangle(srcImage, brect, Scalar(0, 0, 255));

8. 裁剪 

imwrite("9_裁剪.jpg", srcImage(brect));


9.  完整代码

#include <iostream>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
using namespace std;
//根据面积对轮廓进行排序所需要的用于比较的函数
bool ContoursSortFun(vector<cv::Point> contour1, vector<cv::Point> contour2){
	return (cv::contourArea(contour1) > cv::contourArea(contour2));
}
int main()
{
        Mat srcImage;
	Mat hsv;
	Mat mask;

	//加载图像
	srcImage = imread("src.jpg");    
	if (srcImage.empty()) {
		cout << "could not load image..." << endl;
		return -1;
	}
	else cout << "load succsessful." << endl;
	//imshow("原图src", srcImage);

	//转化为 hsv
	cvtColor(srcImage, hsv, CV_BGR2HSV);
	//imshow("1_hsv", hsv);
	imwrite("1_hsv.jpg", hsv);

	//设置HSV色彩空间各个分量的范围,Hue:色调,Saturation:饱和度,Value:明度
	double low_H = 0; //156
	double low_S = 43;
	double low_V = 46;
	double high_H = 10; //180
	double high_S = 255;
	double high_V = 255;
	//使用inrange函数,将在这个范围的颜色提取出来,得到mask矩阵
	inRange(hsv, Scalar(low_H, low_S, low_V), Scalar(high_H, high_S, high_V), mask);
	//imshow("2_mask", mask);
	imwrite("2_mask.jpg", mask);

	//得到结构元素(内核矩阵),MORPH_RECT表示内核形状为矩形,Size(30, 30)表示内核的尺寸
	Mat kernel = getStructuringElement(MORPH_RECT, Size(30, 30));
	//对掩码进行膨胀,MORPH_DILATE表示进行膨胀操作
	morphologyEx(mask, mask, MORPH_DILATE, kernel);
	//imshow("3_mask_dilate", mask);
	imwrite("3_mask_dilate.jpg", mask);

	//对掩膜进行腐蚀,MORPH_ERODE表示进行腐蚀操作
	morphologyEx(mask, mask, MORPH_ERODE, kernel);
	//imshow("3_mask_erode", mask);
	imwrite("3_mask_erode.jpg", mask);
	
	Mat divmask = mask;
	divmask = divmask / 255;
	Mat channels[3];
	//分离色彩通道
	split(srcImage, channels);  
	//分别乘以1或0
	channels[0] = channels[0].mul(mask);
	channels[1] = channels[1].mul(mask);
	channels[2] = channels[2].mul(mask);
	//将背景区域填成白色
	for (int i = 0; i<channels[0].rows; i++){
		for (int j = 0; j<channels[0].cols; j++){
			if ((channels[0].at<int8_t >(i, j) == 0) && (channels[1].at<int8_t >(i, j) == 0) && (channels[2].at<int8_t >(i, j) == 0)){
				channels[0].at<int8_t >(i, j) = 255;
				channels[1].at<int8_t >(i, j) = 255;
				channels[2].at<int8_t >(i, j) = 255;
			}
		}
	}
	Mat divImage;
	//合成RGB
	merge(channels, 3, divImage);
	//imshow("4_divImage", divImage);
	imwrite("4_divImage.jpg", divImage);

	//向量contours内每个元素保存了一组由连续的Point点构成的点的集合的向量,在这里用来保存轮廓
	vector<vector<Point> > contours;
	//findContours用于在单通道二值图中寻找轮廓
	//RETR_EXTERNAL表示只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
	//CHAIN_APPROX_NONE表示保存物体边界上所有连续的轮廓点到contours向量;CHAIN_APPROX_SIMPLE表示仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量
	findContours(mask, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
	//cout << contours.size();
	//对所得到的轮廓按面积大小排序,由于只得到了最外侧的一个轮廓,因此不需要排序
	//sort(contours.begin(), contours.end(), ContoursSortFun);

	//绘制轮廓,第3个参数代表contours向量的下标,为0表示绘制第一个轮廓;最后一个参数-1代表绘制时填充轮廓,若大于0则指轮廓厚度
	drawContours(srcImage, contours, 0, Scalar(255, 0, 0), 3);
	imshow("5_绘制轮廓", srcImage);
	imwrite("5_绘制轮廓.jpg", srcImage);


	//minAreaRect函数用于求点集最小面积的矩形,这个矩形是可以有偏转角度的,即可以与图像的边界不平行
	//RotatedRect是一个存储旋转矩形的类,minAreaRect函数返回的最小面积矩形存储在rect对象中
	RotatedRect rect = minAreaRect(contours[0]);

	Point2f vertices[4];
	//得到矩形的4个顶点
	rect.points(vertices);
	for (int i = 0; i < 4; i++)
		line(srcImage, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0));
	//imshow("6_最小面积的矩形", srcImage);
	imwrite("6_最小面积的矩形.jpg", srcImage);
	
	//得到包含旋转矩形的最小矩形
	Rect brect = rect.boundingRect();
	rectangle(srcImage, brect, Scalar(0, 0, 255));
	//imshow("7_最小矩形", srcImage);
	imwrite("7_最小矩形.jpg", srcImage);

	//imshow("8_裁剪", srcImage(brect));
	imwrite("8_裁剪.jpg", srcImage(brect));

	waitKey();
	return 0;
}

若只要裁剪图片,则不需要进行图像分割,当然绘制轮廓也是不需要的,可自行根据需求修改。

参考:

使用Python和OpenCV检测图像中的物体并将物体裁剪下来:https://blog.csdn.net/liqiancao/article/details/55670749

基于颜色hsv的图像分割:https://blog.csdn.net/Mr_Dec/article/details/103160537

OpenCV中HSV颜色模型及颜色分量范围:https://www.cnblogs.com/wangyblzu/p/5710715.html

关于findContours的一些知识点:https://blog.csdn.net/m0_37350758/article/details/82016820

findContours函数参数详解:https://blog.csdn.net/dcrmg/article/details/51987348

minAreaRect函数:https://blog.csdn.net/qq_18343569/article/details/48000179

RotatedRect类详解:https://blog.csdn.net/u011574296/article/details/71405239

  • 16
    点赞
  • 143
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值