关于胶囊检测的思考-代码实现

**

关于胶囊检测的思考-代码实现

作者:Simon  Song

**
先看两张图。我们要实现对生产胶囊的快速检测,有两种方案,一种是DNN方式,一种是openCV方法。为避开大量样本集的问题,我选择的是openCV方式实现胶囊检测。
关于胶囊检测的思考-代码实现_第1张图片关于胶囊检测的思考-代码实现_第2张图片
检测规则如下:
一、三个胶囊不全为空,且检测结果均为好品,该幅图判断为好品。三个胶囊不全为空,有至少一个胶囊为坏品,该幅图判断为坏品。

二、每个胶囊分别检测,顶部蓝色数字代表每个胶囊检测结果:
-1:空
0:好品
其他大于1的数字代表不同缺陷种类,请自行编号。

三、左侧显示检测参数,例如:W宽,H高,DA脏点面积。可按实际需要显示。

四、好品胶囊标注为绿色。坏品胶囊标注为红色,相应错误参数也标注为红色。
代码环境:
VS2005+openCV341+C++
先给出我的实现代码:

#include
#include

using namespace std;
using namespace cv;
//定义所需文件路径
string sample_path = "G:/opencv_trainning/vs2015/myself_project/Capsule_Picture/";//样本路径
string good_example = "G:/opencv_trainning/vs2015/myself_project/Capsule_Picture/good_example/goodmodel.bmp";//好的模板
//定义数据结构,用于返回数据方便操作
typedef struct mydata{
	char classification;//分类,-1:坏品,0:好品,1:异形,2:大小端,3:气泡,4:黑点, 5:空位置
	short width;//宽
	short height;//高
	double area;//面积
	//其他再添加
} Mydata;//起个别名方便调用
vector<string>  filelist;//文件列表
//定义变量和判断函数
vector<vector<Point>> model_contours;//模型轮廓
vector<Vec4i> model_hierarchy;//模型拓扑结构
int max_model_contours;//模型最大轮廓变量
void model_reader(void);//模型读取器
Mat processing(Mat& capsule,Mat& black_mask,Mat& small_mask);//胶囊图片处理
bool is_empty_func(Mat& mask);//判空函数
bool is_alien_func(Mat& mask,Mydata& data);//判变异函数
bool is_big_and_small_func(Mat& mask, Mydata& data);//判断大小头函数
bool is_bobble_and_blackP_func(Mat& capsule,Mat& mask, Mydata& data);//判气泡和黑点函数,绘制问题位置
Mydata capsule_processing_funciton(Mat& capsule,Mat& mask,Mat&small_mask);//胶囊处理函数,返回胶囊所需数据
void drawCapsule_function(Mat& capsule,Mat&blk_mask,Mat& mask,Mat& small_mask,Mydata& data);//绘制胶囊掩码
void putText_function(Mat& src,Rect&pos,Mydata& data);//在图片上写文本

void drawDashContours(InputOutputArray image, InputArrayOfArrays contours,
	int contourIdx, const Scalar& color, int thinkness, int lineType,int gap_threshold);//自定义函数,实现画虚线轮廓

/*
	此文件用于胶囊检测验证,目录中已提供了样本图
	说明:
*/
int main(int argc, char** argv) {
	//读取文件列表
	ifstream file((sample_path+"file.txt").c_str());//定义文件流
	if (!file.is_open()) {//打开文件
		cout << "open fault" << endl;
		return -1;
	}
	string line;//定义字符串
	while (!file.eof()) {//当文件可读时
		//读取一行,参数:(文件流,字符串)
		getline(file, line);
		//printf("%s\n",line.c_str());//打印提示
		//写入文件列表,转为c字符串保存
		filelist.push_back(line.c_str());
	}
	//读取好的模型
	model_reader();
	//循环处理图片
	int index = 0;//索引位置
	Mat src;//定义图矩阵
	while (index < filelist.size()){//当位置有效时
		cout << "file_path:" << filelist[index] << endl;;//显示文件名
		//读取图片,参数:(文件名),位置后加1
		src= imread(filelist[index++]);
		if (src.empty()) {//判空处理
			cout << "open fault" << endl;
			return -1;
		}
		//显示
		imshow("src", src);
		//发现黑色框
		Mat black_mask;
		inRange(src, Scalar(0, 0, 0), Scalar(180, 255, 46), black_mask);//获取黑色区域
		bitwise_not(black_mask, black_mask);//反色处理,得到胶囊的黑框部分
		//图形学-闭操作,使得边缘更好
		//参数:(形状,核尺寸,锚点)
		Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));//定义结构元素
		//参数:(图,目标图,操作标记,结构元素,锚点,迭代次数),	其他参数默认
		morphologyEx(black_mask, black_mask, MORPH_CLOSE, kernel, Point(-1, -1), 8);//闭操作处理
		//腐蚀-让边缘收进去一部分
		//参数:(图,目标图,操作标记,结构元素,锚点,迭代次数),	其他参数默认
		morphologyEx(black_mask, black_mask, MORPH_ERODE, kernel, Point(-1, -1),5);//腐蚀操作处理,8
		//获得掩码图,用于显示
		Mat black_mask_img;
		src.copyTo(black_mask_img,black_mask);//获得胶囊以外的图
		//imshow("black_mask_img",black_mask_img);//显示
		bitwise_not(black_mask,black_mask);//反色操作,得到胶囊位置为黑色
		//imshow("black_mask", black_mask);//显示
		//0.0转为灰度图
		Mat gray;//定义灰度图
		cvtColor(src,gray,CV_BGR2GRAY);
		//imshow("gray",gray);
		//GaussianBlur(gray,gray);
		//0.1二值化图
		Mat binary;//二值化图
		//参数:(灰度图,目标图,低阈值,最大值,阈值模式)
		threshold(gray,binary,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU);//多峰自动阈值
		//imshow("binary", binary);
		//0.2图形学-闭操作,去除内部干扰
		//参数:(形状宏定义,核尺寸,锚点)
		Mat element = getStructuringElement(MORPH_RECT,Size(10,10),Point(-1,-1));//定义结构元素
		//参数:(图,目标图,操作宏定义,结构元素),其他参数默认
		morphologyEx(binary,binary,MORPH_CLOSE,element,Point(-1,-1));//闭操作
		//1.获得每个一个胶囊的位置图片
		//1.1发现轮廓,只找最外层的轮廓
		vector<vector<Point>> contours;//轮廓向量
		vector<Vec4i> hierarchy;//拓扑结构变量,单个结构数据含义为[后一个标记位置,前一个标记位置,子轮廓标记位置,父轮廓标记位置]
		//参数:(二值化图,轮廓向量,检测方法,发现方法,偏移点),偏移点用于大图还原,此处未用到
		findContours(binary,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point(0,0));
		//1.2获取每个胶囊图
		Mat contoursImg=src.clone();//克隆图为了整体显示
		vector<Mat> capsules;//胶囊向量
		vector<Mat> blk_mask;//胶囊掩码向量
		vector<Mat> small_mask;//胶囊的小掩码
		vector<Rect> good_pos;//好的胶囊位置
		for (int c = 0; c < contours.size(); c++) {//循环轮廓位置
			//计算面积,参数:(单个轮廓)
			double area = contourArea(contours[c]);
			if (area/100 < 600)continue;//当面积太小,继续下一次
			cout << "area/100=" << area / 100 << endl;//打印提示
			//获得框位置,参数:单个轮廓,返回矩形变量
			Rect pos = boundingRect(contours[c]);
			//截取图片局部
			capsules.push_back(src(pos));
			//截取掩码局部
			blk_mask.push_back(black_mask(pos));
			//保存好的矩形位置信息,后面使用
			good_pos.push_back(pos);
			//保存全黑图到小胶囊掩码,后面使用
			small_mask.push_back(Mat::zeros(pos.height,pos.width,CV_8UC1));
			//绘制到图上,参数:(图,rect变量,颜色,线宽)
			rectangle(contoursImg,pos,Scalar(0,0,255),1);
			//显示每个胶囊
			//imshow(format("capsules[%d]", capsules.size() - 1).c_str(),capsules[capsules.size()-1]);
		}
		cout << "--------" << endl;//中断打印提示
		//imshow("contoursImg",contoursImg);
		//Mat processing_board = src.clone();//克隆一张图给处理图,后面用于显示
		vector<char> class_group;//分类组变量,用于统计各种综合情况的变量
		//2.检测胶囊内的几种情况:空,异型,气泡,黑点等
		for (int c = 0; c < capsules.size(); c++) {//循环胶囊
			//显示胶囊
			//imshow("org_capsule",capsules[c]);
			//处理图片为纯胶囊掩码图,方便后面操作,参数:(胶囊原图,胶囊位置掩码,胶囊小掩码图)
			Mat mask = processing(capsules[c],blk_mask[c], small_mask[c]);
			//胶囊处理,获得胶囊数据,参数:(胶囊原图,胶囊小掩码图)
			Mydata data = capsule_processing_funciton(capsules[c],mask,small_mask[c]);
			//绘制胶囊线,参数:(胶囊原图,胶囊位置掩码,纯胶囊掩码,胶囊小掩码,胶囊数据)
			drawCapsule_function(capsules[c],blk_mask[c],mask,small_mask[c],data);
			//绘制文本,参数:(图,胶囊位置信息(rect),胶囊数据)
			putText_function(src, good_pos[c],data);
			//保存分类号
			class_group.push_back(data.classification);
		}
		//判断整体好品情况
		if ((class_group[0]==5&&class_group[1]==5&&class_group[3]==5)////当全为空(5)时
			||(class_group[0] != 0|| class_group[1] != 0|| class_group[2] != 0)) {//或当有不为好(0)时,为坏品
			//放入文字-坏品
			//参数:(图,文本,原点,字体,缩放比,颜色,线宽,线型,底左对齐(倒过来的效果))
			putText(src,"Bad",Point(0,25),CV_FONT_NORMAL,1.0,Scalar(0,0,255),1,8,false);
		}else {//否则为好品
			//放入文字-好品
			//参数:(图,文本,原点,字体,缩放比,颜色,线宽,线型,底左对齐(倒过来的效果))
			putText(src, "Good", Point(0, 20), CV_FONT_NORMAL, 1.0, Scalar(0, 255, 0), 1, 8, false);
		}
		imshow("processing_board", src);
		cv::waitKey(4000);//显示等待
	}
	cv::waitKey(0);//按键等待
	return 0;
}

void model_reader(void) {//模型读取器
	//读取模板图
	Mat good_model = imread(good_example.c_str(), IMREAD_GRAYSCALE);//灰度图
	if (good_model.empty()) { //判空处理
		cout << "read model fault" << endl;
		return;
	}
	//imshow("model_gray",good_model);//显示
	//二值化图
	Mat model_bianry;//定义二值化图
	//参数:(灰度图,二值化画图,阈值,最大值,二值化标记)
	threshold(good_model, model_bianry, 230, 255, CV_THRESH_BINARY | CV_THRESH_TRIANGLE);//单峰自动
	//imshow("model_bianry", model_bianry);
	//图形学-闭操作,去掉外围噪点
	//定义元素结构,参数:(形状,核尺寸,锚点)
	Mat  element = getStructuringElement(MORPH_RECT,Size(3,3),Point(-1,-1));
	//开操作,参数:(图,目标图,操作标记,结构元素,锚点,迭代次数),其他参数默认
	morphologyEx(model_bianry,model_bianry,MORPH_CLOSE,element,Point(-1,-1),1);
	//反色,获得黑底白面图 参数:(原图,目标图)
	bitwise_not(model_bianry,model_bianry);
	//imshow("model_morphologyEx", model_bianry);//显示
	//模型轮廓发现,参数:(二值化画图,模型轮廓,模型拓扑结构,检测方法,发现方法,偏移量)
	findContours(model_bianry, model_contours, model_hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(-1, -1));
	//发现最大的那个轮廓
	double max_area = 0;//最大面积变量
	int max_pos = -1;//最大位置变量
	for (int c = 0; c < model_contours.size(); c++) {//循环模型轮廓位置
		double area = contourArea(model_contours[c]);//计算面积
		if (area > max_area) {//当前轮廓面积大于最大面积时
			max_area = area;//赋值最大值
			max_pos = c;//保存最大位置
		}
	}
	max_model_contours = max_pos;//赋值到全局变量
	cout << "max_model_contours=" << max_model_contours << endl;//打印测试
	//显示模型轮廓图
	Mat model_contours_img = Mat::zeros(good_model.size(),good_model.type());//定义显示图
	//参数:(图,轮廓向量,轮廓位置下标,颜色,线宽,线型,拓扑结构变量)
	drawContours(model_contours_img,model_contours, max_model_contours,Scalar(255),-1,8,model_hierarchy);//绘制最大轮廓
	//imshow("model_contours_img", model_contours_img);//显示
	//清除向量,重新发现轮廓,确保掩码的一致性
	model_contours.clear();//轮廓清除
	model_hierarchy.clear();//拓扑结构变量清除
	max_area = 0;//最大面积变量
	max_pos = -1;//最大位置变量
	//模型再次轮廓发现,参数:(二值化画图,模型轮廓,模型拓扑结构,检测方法,发现方法,偏移量)
	findContours(model_contours_img, model_contours, model_hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(-1, -1));
	//发现最大的那个轮廓
	for (int c = 0; c < model_contours.size(); c++) {//循环轮廓位置
		double area = contourArea(model_contours[c]);//计算面积
		if (area > max_area) {//当前轮廓面积大于最大面积时
			max_area = area;//赋值最大值
			max_pos = c;//保存最大位置
		}
	}
}

//定义判断函数:颜色掩码+二值化掩码=实际掩码
Mat processing(Mat& capsule, Mat& black_mask,Mat& small_mask) {//胶囊图片处理
	//显示原图
	//imshow("capsule",capsule);
	//获得反的掩码图(黑底白面)
	Mat ref_black_mask;//定义反色掩码图
	bitwise_not(black_mask,ref_black_mask);//反色处理
	//imshow("ref_black_mask", ref_black_mask);
	//按颜色范围获得黄颜色掩码
	//转为hsv
	Mat hsv;//定义HSV图
	cvtColor(capsule,hsv,CV_BGR2HSV);//参数:(图1,图2,转换标记)
	//提取黄色
	Mat mask_yellow;//定义掩码图
	inRange(hsv,Scalar(26,43,46),Scalar(34,255,255),mask_yellow);//获取掩码,参数:(图,低值,高值,掩码图)
	//掩码处理
	Mat new_mask_yellow;//定义新图
	mask_yellow.copyTo(new_mask_yellow,ref_black_mask);//掩码拷贝胶囊,使其在胶囊范围内
	//图形学-开操作,去掉外面的噪点
	Mat element_yellow = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));//参数:(形状,核尺寸,锚点)
	morphologyEx(new_mask_yellow, new_mask_yellow, MORPH_OPEN, element_yellow, Point(-1, -1), 4);//参数:(原图,目标图,操作标记,元素矩阵,锚点,迭代次数),其他参数默认
	//imshow("inRange", new_mask_yellow);
	//使用二值化获得黑白图
	//灰度图
	Mat gray;//定义灰度图
	cvtColor(capsule,gray,CV_BGR2GRAY);//参数:(原图,目标图,转换标记)
	//二值化处理
	Mat binary;//定义二值化图
	threshold(gray,binary,230,255,THRESH_BINARY);//参数:(图,二值化图,阈值,最大值,阈值标记)
	//反色处理
	bitwise_not(binary,binary);
	//获得掩码图
	Mat new_binary;
	binary.copyTo(new_binary,ref_black_mask);//获得胶囊的框内掩码图,参数:(新图,掩码)
	//图像学-开操作
	Mat element = getStructuringElement(MORPH_RECT,Size(3,3),Point(-1,-1));//定义核元素,参数:(形状,核尺寸,锚点)
	morphologyEx(new_binary,new_binary,MORPH_OPEN,element,Point(-1,-1),4);//开操作,参数:(图,目标图,操作标记,核元素,锚点,迭代次数),其他参数默认
	//imshow("threshold_mask", new_binary);
	//合并掩码图
	Mat mask;//定义总掩码图
	bitwise_or(new_mask_yellow,new_binary,mask);//或处理,得到完成掩码,参数:(图1,图2,掩码图)
	//图形学-闭操作
	Mat element_mask = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));//定义核元素,参数:(形状,核尺寸,锚点)
	morphologyEx(mask, mask, MORPH_CLOSE, element_mask, Point(-1, -1), 2);//闭操作,参数:(图,目标图,操作标记,核元素,锚点,迭代次数),其他参数默认
	//imshow("mask",mask);
	//单掩码处理
	//1.发现轮廓
	vector<vector<Point>> capsule_contours;//轮廓向量
	vector<Vec4i> capsule_hierarchy;//拓扑结构变量
	//参数:(二值化图,轮廓向量,拓扑结构变量,检测方法,发现方法,偏移量)
	findContours(mask, capsule_contours, capsule_hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(-1, -1));
	//当没有轮廓时,返回全黑图
	if (capsule_contours.size()<1) {
		return Mat::zeros(mask.size(), mask.type());
	}
	//发现最大的那个轮廓
	double max_area = 0;//最大面积变量
	int max_pos = -1;//最大位置变量
	for (int c = 0; c < capsule_contours.size(); c++) {
		double area = contourArea(capsule_contours[c]);//计算面积
		if (area > max_area) {//当前轮廓面积大于最大面积时
			max_area = area;//赋值最大面积
			max_pos = c;//保存最大位置
		}
	}
	//当面积太小时,返回全黑
	double area = contourArea(capsule_contours[max_pos])/100;//计算面积,/100方便计算,参数:(单个轮廓)
	cout << "area=" << area << endl;
	if (area<10) {
		return Mat::zeros(mask.size(),mask.type());
	}
	//绘制轮廓
	Mat contours_mask = Mat::zeros(mask.size(), mask.type());//定义图
	drawContours(contours_mask, capsule_contours, max_pos, Scalar(255), -1, 8, capsule_hierarchy);//绘制轮廓
	//imshow("contours_mask", contours_mask);//显示
	//获取原图内容
	Mat capsule_small_mask;//定义胶囊小图
	gray.copyTo(capsule_small_mask,contours_mask);//获得灰度图的胶囊部分,参数:(新图,掩码)
	//imshow("capsule_small_mask", capsule_small_mask);
	//二值化处理
	Mat binary_small_mask;//定义二值化图
	threshold(capsule_small_mask, binary_small_mask,200,255,THRESH_BINARY);//参数:(图,二值化图,阈值,最大值,二值化操作标记)
	//图像学-闭操作,去内噪点,此处不需要,故注掉
	//Mat element_test = getStructuringElement(MORPH_RECT,Size(3,3),Point(0,0));//定义核元素,参数:(形状,核尺寸,锚点)
	//morphologyEx(binary_small_mask,binary_small_mask,MORPH_CLOSE,element,Point(-1,-1),1);//闭操作,参数:(图,目标图,操作标记,核元素,锚点,迭代次数),其他参数默认
	//发现轮廓
	vector<vector<Point>> small_mask_contours;//轮廓向量
	vector<Vec4i> small_mask_hierarchy;//拓扑结果向量
	//参数:(二值化图,轮廓向量,拓扑结构变量,检测方法,发现方法,偏移量)
	findContours(binary_small_mask,small_mask_contours,small_mask_hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));
	//循环查找面积最大的那个
	double small_mask_max_area=-1;//定义小轮廓最大面积
	int small_mask_max_area_pos=-1;//定义最大面积的位置
	for (int i = 0; i < small_mask_contours.size();i++) {//循环轮廓位置
		double area = contourArea(small_mask_contours[i]);//计算面积
		if (area > small_mask_max_area) {//当前面积大于最大面积时,
			small_mask_max_area_pos = i;//记录位置
			small_mask_max_area = area;//更改最大面积
		}
	}
	//发现凸包
	vector<vector<Point>> convex_contours(small_mask_contours.size());//定义凸包个数为一致大小
	for (int i = 0; i < small_mask_contours.size(); i++) {//循环轮廓位置
		//发现凸包, 参数:(原轮廓向量,目标轮廓向量,顺时针方向状态,返回点状态)
		convexHull(small_mask_contours[i], convex_contours[i], false, true);//获得凸包
		//逼近多边形,参数:(原轮廓向量,目标轮廓向量,最小量,闭包状态),放在此处不合适
		//approxPolyDP(small_mask_contours[i],convex_contours[i],5,true);
	}
	//绘制小轮廓
	drawContours(small_mask, convex_contours, small_mask_max_area_pos,Scalar(255),-1);//绘制轮廓
	//imshow("small_mask", small_mask);
	return contours_mask;//返回
}

bool is_empty_func(Mat& mask) {//判空函数
	//1.发现轮廓
	vector<vector<Point>> capsule_contours;//轮廓向量
	vector<Vec4i> capsule_hierarchy;//拓扑结构变量
	//参数:(二值化图,轮廓向量,拓扑结构变量,检测方法,发现方法,偏移量)
	findContours(mask,capsule_contours,capsule_hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point(-1,-1));
	if (capsule_contours.size()<1) {//当没有轮廓时
		return true;//返回真,表示没有胶囊
	}
	//计算总面积
	double area = 0;//白区面积
	for (int c = 0; c < capsule_contours.size(); c++) {//循环胶囊轮廓
		//cout << "hierarchy=" << capsule_hierarchy[c][0] << "," << capsule_hierarchy[c][1] << "," << capsule_hierarchy[c][2] << "," << capsule_hierarchy[c][3] << endl;
		area += contourArea(capsule_contours[c]) / 100;//计算面积并累加,/100是为了方便计算
	}
	cout << "area/100=" << area << endl;//打印提示
	//判断返回
	if (area<300) {//空处理,按照白面积计算判断
		return true;//返回真
	}else{//否则
		return false;//返回假
	}
}

bool is_alien_func(Mat& mask, Mydata& data) {//判变异函数
	//1.发现轮廓
	vector<vector<Point>> capsule_contours;//轮廓向量
	vector<Vec4i> capsule_hierarchy;//拓扑结构变量
	//参数:(二值化图,轮廓向量,拓扑结构变量,检测方法,发现方法,偏移量)
	findContours(mask,capsule_contours,capsule_hierarchy,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point(-1,-1));
	//发现最大的那个轮廓
	double max_area = 0;//最大面积变量
	int max_pos = -1;//最大位置变量
	RotatedRect max_rrect;//定义最大斜矩阵
	for (int c = 0; c < capsule_contours.size(); c++) {
		double area = contourArea(capsule_contours[c]);//计算面积
		if (area > max_area) {//当前轮廓面积大于最大面积时
			max_area = area;//赋值最大面积
			max_pos = c;//保存最大位置
			max_rrect = minAreaRect(capsule_contours[c]);//获得斜矩阵
		}
	}
	//给定数据,为何防止出现宽高显示不对的情况,人为调整一下
	Mydata local_data;//定义本地结构变量
	local_data.height = (max_rrect.size.height>max_rrect.size.width? max_rrect.size.height: max_rrect.size.width);//给定高
	local_data.width = (max_rrect.size.height>max_rrect.size.width ? max_rrect.size.width : max_rrect.size.height);//给定宽
	local_data.area = max_area;//给定面积
	//绘制轮廓,为了显示
	Mat contours_img = Mat::zeros(mask.size(), mask.type());//定义图
	drawContours(contours_img,capsule_contours,max_pos,Scalar(255),-1,8,capsule_hierarchy);//绘制轮廓
	//imshow("is_alien_func_contours", contours_img);//显示
	/*//从点数量上无法判别异形的特点,故注掉
	//2.使用最小多边形点位置,判断是否为异型
	//2.1胶囊的最小轮廓点
	vector> min_contours(1);//轮廓数量与轮廓向量一致,此处1个就行
	//基于RDP获得最小轮廓点 (多变型轮廓点数量),参数:(原轮廓,目标轮廓,最小量,封闭状态)
	approxPolyDP(capsule_contours[max_pos],min_contours[0],5,true);
	printf("capsule min_contours[%d].size()=%d\n",0, min_contours[0].size());
	//2.2模型的最小轮廓
	vector> model_min_contours(1);//轮廓数量与轮廓向量一致,此处1个就行
	//基于RDP获得最小轮廓点 (多变型轮廓点数量),参数:(原轮廓,目标轮廓,最小量,封闭状态)
	approxPolyDP(model_contours[max_model_contours],model_min_contours[0],5,true);
	printf("model min_contours[%d].size()=%d\n", 0, model_min_contours[0].size());
	//绘制,
	Mat compare_contours_img = Mat::zeros(mask.size(),CV_8UC3);
	drawContours(compare_contours_img, min_contours,0,Scalar(0,0,255),1);
	drawContours(compare_contours_img, model_min_contours, 0, Scalar(0, 255, 0), 1);
	imshow("compare_contours_img", compare_contours_img);
	//2.3形状比较,完全相同返回0,最大值为1,参数:(轮廓1,轮廓2,轮廓匹配表示(CV_CONTOURS_MATCH_I1/I2/I3),0)
	double score = matchShapes(model_min_contours[0], min_contours[0], CV_CONTOURS_MATCH_I1, 0);
	cout << "score=" << score << endl;//打印提示
	//判断得分
	if (score > 0.10) {//大于10%返回true
		putText(compare_contours_img, "alien", Point(20, 20), CV_FONT_NORMAL, 0.5, Scalar(0, 0, 255), 1);//添加文字显示
		imshow("compare_contours_img", compare_contours_img);
		return true;//返回真
	}
	else {//否则返回假
		putText(compare_contours_img, "normal", Point(20, 20), CV_FONT_NORMAL, 0.5, Scalar(0, 255, 0), 1);//添加文字显示
		imshow("compare_contours_img", compare_contours_img);
		return false;//返回假
	}
	*/
	//2.使用凸包测试
	//2.1胶囊的最大轮廓点
	vector<vector<Point>> convex_contours(1);//轮廓数量与轮廓向量一致,此处1个就行
	//获得凸包,参数:(原轮廓,目标轮廓,顺时针方向状态,返回点状态)
	convexHull(capsule_contours[max_pos], convex_contours[0],false,true);
	//printf("capsule min_contours[%d].size()=%d\n", 0, convex_contours[0].size());
	//2.2模型的最大轮廓
	vector<vector<Point>> model_convex_contours(1);//轮廓数量与轮廓向量一致,此处1个就行
	//获得凸包,参数:(原轮廓,目标轮廓,顺时针方向状态,返回点状态)
	convexHull(model_contours[max_model_contours], model_convex_contours[0],false,true);
	//printf("model min_contours[%d].size()=%d\n", 0, model_convex_contours[0].size());
	//绘制轮廓,方便观察
	Mat compare_contours_img = Mat::zeros(mask.size(), CV_8UC3);//定义比较图
	//参数:(图,轮廓,轮廓下标位置,颜色,线宽)
	drawContours(compare_contours_img, convex_contours, 0, Scalar(0, 0, 255), 1);
	drawContours(compare_contours_img, model_convex_contours, 0, Scalar(0, 255, 0), 1);
	//imshow("compare_contours_img", compare_contours_img);
	//2.3形状比较,完全相同返回0,最大值为1,参数:(轮廓1,轮廓2,轮廓匹配表示(CV_CONTOURS_MATCH_I1/I2/I3),0)
	double score = matchShapes(model_convex_contours[0], convex_contours[0], CV_CONTOURS_MATCH_I1, 0);
	cout << "score=" << score << endl;//打印提示
	//判断得分
	if (score > 0.10) {//大于10%返回true
		//putText(compare_contours_img,"alien",Point(20,20),CV_FONT_NORMAL,0.5,Scalar(0,0,255),1);//添加文字显示
		//imshow("compare_contours_img", compare_contours_img);
		//给定值
		data.classification = 1;//给定异形类别
		data.height = local_data.height;//高
		data.width = local_data.width;//宽
		data.area = local_data.area;//面积
		return true;//返回真
	}
	else {//否则返回假
		//putText(compare_contours_img, "normal", Point(20, 20), CV_FONT_NORMAL, 0.5, Scalar(0, 255, 0), 1);//添加文字显示
		//imshow("compare_contours_img", compare_contours_img);
		return false;//返回假
	}
}

bool is_big_and_small_func(Mat& mask,Mydata& data) {//判断大小头函数
	//imshow("is_big_and_small_func",mask);
	//发现轮廓
	vector<vector<Point>> contours;//轮廓向量
	vector<Vec4i> hierarchy;//拓扑结构向量
	//参数:(二值化图,轮廓向量,拓扑结构向量,发现方法,检测方法,偏移量点)
	findContours(mask,contours,hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point(0,0));
	//获得斜矩阵
	RotatedRect rotaterect = minAreaRect(contours[0]);
	//给定本地数据,宽高认为调整一下,以便显示正常
	Mydata Local_data;//定义本地数据
	Local_data.height = (rotaterect.size.height>rotaterect.size.width? rotaterect.size.height: rotaterect.size.width);//赋值高
	Local_data.width = (rotaterect.size.height>rotaterect.size.width ? rotaterect.size.width : rotaterect.size.height);//赋值宽
	Local_data.area = contourArea(contours[0]);//赋值面积
	//更改斜矩阵大小,获得25%和75%的直线
	int height = rotaterect.size.height;//获得高
	int width= rotaterect.size.width;//获得宽
	//cout << "width=" << width << endl;
	//cout << "height=" << height << endl;
	RotatedRect rrect_25_75 = rotaterect;//25%,75%
	RotatedRect rrect = rotaterect;//50%
	Point2f rrect25_75_points[4];//定义点信息
	Point2f rrect_points[4];//定义点信息
	if (width>height) {//宽大,调宽
		//调整25%和75%
		rrect_25_75.size.width = width / 2;//高处理
		rrect_25_75.points(rrect25_75_points);//获得四个点信息
		//50%处理
		rrect.size.width = 1;//高处理
		rrect.points(rrect_points);//获得四个点信息
	}
	else {//否则,高大,调高
		//调整25%和75%
		rrect_25_75.size.height = height / 2;//高处理
		rrect_25_75.points(rrect25_75_points);//获得四个点信息
		//50%处理
		rrect.size.height = 1;//高处理
		rrect.points(rrect_points);//获得四个点信息
	}
	//在新的掩码图中绘制直线
	Mat new_mask=mask.clone();//定义图片;
	for (int i = 0; i < 4;i++) {//循环点位置
		//绘制线,25_75
		//line(new_mask, rrect25_75_points[i], rrect25_75_points[(i+1)%4],Scalar(0),1,LINE_AA);
		//绘制线,50
		line(new_mask,rrect_points[i], rrect_points[(i+1)%4],Scalar(0),5,LINE_AA);
	}
	//imshow("new_mask", new_mask);
	/*//换个思路,先注掉,此思路只对明显大小头有效
	//反色获得直线图,并掩码只剩下白色直线
	Mat ref_new_mask;
	bitwise_not(new_mask,ref_new_mask);//非操作,得到反色图
	Mat and_ref_new_mask;
	bitwise_and(ref_new_mask,mask,and_ref_new_mask);//与掩码图像,得到纯值线图
	//图形学-开操作,取出外侧的零散边缘
	Mat element = getStructuringElement(MORPH_RECT,Size(3,3),Point(-1,-1));
	morphologyEx(and_ref_new_mask, and_ref_new_mask,MORPH_OPEN,element,Point(-1,-1),1);
	imshow("ref_new_mask", and_ref_new_mask);
	//霍夫直线获得直线坐标
	vector lines;//定义直线向量
	//参数:(8位灰度图,vec4f的向量,像素扫描步长,角度扫描步长,交点阈值,最小直线长度,最大长度间隔)
	HoughLinesP(and_ref_new_mask,lines,1,CV_PI/180,30,mask.cols/2,10);
	//搜索合适的直线
	Point2f pointlines[3][2] = {0,0,0,0,0,0};//用于保存最长直线位置,方便后续操作
	float lengths[3] = {0,0,0};//用于保存长度的数组,[0]为上线,[1]位置为中线,[2]位置为下线
	cout << "lines.size()=" << lines.size() << endl;
	Mat hough_img = Mat::zeros(mask.size(),CV_8UC3);
	for (int c = 0; c < lines.size();c++) {//循环线
		Vec4f hline = lines[c];//获得点前向量
		float length = abs(hline[0] - hline[2]) + abs(hline[1] - hline[3]);//计算长度
		//判断,hline[1]表示y位置
		if (hline[1]>100&& hline[1]<200&&length>lengths[0]) {//上线,且大于最大长度
			//保存点向量
			pointlines[0][0]=Point(hline[0], hline[1]);//点1
			pointlines[0][1] = Point(hline[2], hline[3]);//点2
			//保存长度
			lengths[0] = length;
		}else if (hline[1]>200 && hline[1]<300&&length>lengths[1]) {//中线,且大于最大长度
			//保存点向量
			pointlines[1][0] = Point(hline[0], hline[1]);//点1
			pointlines[1][1] = Point(hline[2], hline[3]);//点2
			//保存长度
			lengths[1] = length;
		}else if (hline[1]>300&&length>lengths[2]) {//下线,且大于最大长度
			//保存点向量
			pointlines[2][0] = Point(hline[0], hline[1]);//点1
			pointlines[2][1] = Point(hline[2], hline[3]);//点2
			//保存长度
			lengths[2] = length;
		}
	}
	//绘制显示
	//画线
	line(hough_img, pointlines[0][0], pointlines[0][1], Scalar(0, 0, 255), 1);//上线
	line(hough_img, pointlines[1][0], pointlines[1][1], Scalar(0, 0, 255), 1);//中线
	line(hough_img, pointlines[2][0], pointlines[2][1], Scalar(0, 0, 255), 1);//下线
	printf("Point(%f,%f),Point(%f,%f),length=%f\n", pointlines[0][0].x, pointlines[0][0].y, pointlines[0][1].x, pointlines[0][1].y, lengths[0]);//打印提示,上线
	printf("Point(%f,%f),Point(%f,%f),length=%f\n", pointlines[1][0].x, pointlines[1][0].y, pointlines[1][1].x, pointlines[1][1].y, lengths[1]);//打印提示,上线
	printf("Point(%f,%f),Point(%f,%f),length=%f\n", pointlines[2][0].x, pointlines[2][0].y, pointlines[2][1].x, pointlines[2][1].y, lengths[2]);//打印提示,上线
	imshow("hough_img", hough_img);
	//筛选并计算直线长度,c^2=|x1-x2|^2+|y1-y2|^2
	//比较长度,当大于N个点时,应给大小头,否则为正常范围内的胶囊
	
	//    -----   1
		
	//	-----   2

	//	-----   3
	//	大小端的全部条件
	//	线1与线3的差值绝对值较大
	//	线1与线2的差值绝对值较大
	//	线2与线3的差值绝对值较大
	
	*/
	//发现轮廓,后面比较上下两部分
	vector<vector<Point>> half_contours;//轮廓向量
	vector<Vec4i> half_hierarchy;//拓扑结构
	//参数:(二值化图,轮廓向量,拓扑结构向量,发现方法,检测方法,偏移量点)
	findContours(new_mask,half_contours,half_hierarchy,RETR_LIST,CHAIN_APPROX_NONE,Point(0,0));
	cout << "half_contours.size()=" <<half_contours.size()<< endl;
	//形状比较, 完全相同返回0,最大值为1,参数:(轮廓1,轮廓2,轮廓匹配表示(CV_CONTOURS_MATCH_I1 / I2 / I3), 0)
	double score = matchShapes(half_contours[0],half_contours[1],CV_CONTOURS_MATCH_I3,0);
	cout << "is_big_and_small_func:score="<<score<< endl;
	//判断
	if (score > 0.085) {//当得分大于0.085时,返回真,表示大小头,此值需要按实际调整
		//给定数据
		data.classification = 2;//赋值分类器,2为大小端
		data.height = Local_data.height;//赋值高
		data.width = Local_data.width;//赋值宽
		data.area = Local_data.area;//赋值面积
		return true;
	}
	else {//否则
		return false;//返回假
	}
}

bool is_bobble_and_blackP_func(Mat& capsule,Mat& mask,Mydata& data) {//判气泡和黑点函数,绘制问题位置
	//获得轮廓原图
	Mat capsule_org;//定义胶囊图
	capsule.copyTo(capsule_org, mask);//掩码拷贝图
	//imshow("is_bobble_and_blackP_func:capsule_org", capsule_org);
	//灰度图
	Mat gray;
	cvtColor(capsule_org,gray,CV_BGR2GRAY);
	//二值化
	Mat binary;//二值化图
	//threshold(gray,binary,130,255,THRESH_BINARY_INV);//二值化
	Canny(capsule,binary,150,230,3,true);//canny
	//imshow("is_bobble_and_blackP_func:binary",binary);
	//掩码图片
	Mat new_binary;
	binary.copyTo(new_binary,mask);
	//膨胀处理,使位置更明显
	Mat element = getStructuringElement(MORPH_RECT,Size(3,3),Point(-1,-1));//定义结构元素,参数:(形状,核尺寸,锚点)
	morphologyEx(new_binary,new_binary,MORPH_DILATE,element,Point(-1,-1),1);//膨胀处理,参数:(图,目标图,操作标记,核元素,锚点,迭代次数)
	//imshow("binary_mask",new_binary);
	//1.发现轮廓
	vector<vector<Point>> capsule_contours;//轮廓向量
	vector<Vec4i> capsule_hierarchy;//拓扑结构变量
	//参数:(二值化图,轮廓向量,拓扑结构变量,检测方法,发现方法,偏移量)
	findContours(new_binary, capsule_contours, capsule_hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(-1, -1));
	//计算总面积
	double total_area = 0;//白区面积
	vector<vector<Point>> capsule_save;//胶囊子轮廓保存
	int bubble_num=0;//气泡数量变量
	int black_num = 0;//黑点数量变量
	for (int c = 0; c < capsule_contours.size(); c++) {//循环胶囊轮廓
		double area = contourArea(capsule_contours[c])/10;//计算面积
		//cout << "area/10=" <
		if (area > 7) {//气泡
			bubble_num++;//气泡数量变量加1
			//绘制轮廓
			drawContours(capsule, capsule_contours, c, Scalar(0,0, 255), 1);
			//面积累加
			total_area += area;
		}else if(area>=3){//噪点
			black_num ++;//黑点数量变量
			//绘制轮廓
			drawContours(capsule, capsule_contours, c, Scalar(255, 0, 255), 1);
			//面积累加
			total_area += area;
		}
	}
	//imshow("contours_img", capsule);
	//判断返回
	if (bubble_num>=1 || black_num >=1) {//当子轮廓数量有时
		//给定数据
		data.classification = (bubble_num > 0 ? 3 : 4);//当气泡数量大于0时,为气泡,否则为黑点
		data.area = total_area;//赋值面积
		return true;//返回真
	}else {//否则
		return false;//返回假
	}
}

Mydata capsule_processing_funciton(Mat& capsule, Mat& mask, Mat&small_mask) {//胶囊处理函数,返回胶囊所需数据
	//定义自定义结构体变量
	Mydata data;
	//初始化结构体变量
	data.classification = -1;
	data.height = -1;
	data.width = -1;
	data.area = -1;
	//1判空
	if (is_empty_func(mask)) {
		data.classification = 5;//分类赋值,5为空
		data.height = 0;//高为0
		data.width = 0;//宽为0
	}else{//2.否则
		//判断
		if (is_alien_func(mask,data)) {//2.1判异形
			//打印提示
			cout << "capsule_processing_funciton:alien shape"<< endl;
		}else if (is_big_and_small_func(mask,data)) {//2.2判大小端
			//打印提示
			cout << "capsule_processing_funciton:big and small shape" << endl;
		}else if (is_bobble_and_blackP_func(capsule, small_mask,data)) {//2.3判气泡或黑点,如果是,那么问题位置由函数绘制上去
			cout << "capsule_processing_funciton:bobble or blackPoint" << endl;
			//计算掩码的宽高
			//发现轮廓
			vector<vector<Point>> contours;//轮廓
			vector<Vec4i> hierarchy;//拓扑结构变量
			//参数:(二值化图,轮廓向量,检测方法,发现方法,偏移点),偏移点用于大图还原,此处未用到
			findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));//发现轮廓
			//获得最小斜矩形
			RotatedRect rrect = minAreaRect(contours[0]);
			//赋值数据,为何防止出现宽高显示不对的情况,人为调整一下
			data.height = (rrect.size.height>rrect.size.width? rrect.size.height:rrect.size.width);//高
			data.width = (rrect.size.height>rrect.size.width ? rrect.size.width :rrect.size.height);//宽
		}else {//否则为正常胶囊
			cout << "capsule_processing_funciton:normal capsule" << endl;
			//计算掩码的宽高
			//发现轮廓
			vector<vector<Point>> contours;//轮廓
			vector<Vec4i> hierarchy;//拓扑结构变量
			//参数:(二值化图,轮廓向量,检测方法,发现方法,偏移点),偏移点用于大图还原,此处未用到
			findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0));//发现轮廓
			//获得最小斜矩形
			RotatedRect rrect = minAreaRect(contours[0]);
			//赋值数据,为何防止出现宽高显示不对的情况,人为调整一下
			data.height = (rrect.size.height>rrect.size.width ? rrect.size.height : rrect.size.width);//高
			data.width = (rrect.size.height>rrect.size.width ? rrect.size.width : rrect.size.height);//宽
			data.classification = 0;//赋值类型,0为好品
			data.area = 0;//面积给0
		}
	}
	return data;//返回数据
}

void drawCapsule_function(Mat& capsule,Mat&blk_mask, Mat& mask, Mat& small_mask,Mydata& data) {//绘制胶囊掩码
	//发现大轮廓
	vector<vector<Point>> big_contours;//大轮廓向量
	vector<Vec4i> big_hierarchy;//大拓扑结构向量
	//参数:(二值化图,轮廓向量,检测方法,发现方法,偏移点),偏移点用于大图还原,此处未用到
	findContours(mask,big_contours,big_hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point(-1,-1));
	//发现小轮廓
	vector<vector<Point>> small_contours;//小轮廓向量
	vector<Vec4i> small_hierarchy;//小拓扑结构向量
	//参数:(二值化图,轮廓向量,检测方法,发现方法,偏移点),偏移点用于大图还原,此处未用到
	findContours(small_mask,small_contours,big_hierarchy,RETR_EXTERNAL,CHAIN_APPROX_NONE,Point(-1,-1));
	//根据分类,绘制内轮廓和外轮廓
	if (data.classification >= 0 && data.classification<=4) {//好品(0)获得其他1-4的情况
		//绘制轮廓,好品(0)绘制绿色,1-4情况绘制红色
		drawContours(capsule,big_contours,-1,(data.classification==0? Scalar(0, 255, 0): Scalar(0, 0, 255)),1,LINE_AA,big_hierarchy);
	}else if (data.classification == 5) {//空(5) 
		//反色掩码处理,方便获得轮廓
		Mat ref_blk_mask;
		bitwise_not(blk_mask,ref_blk_mask);
		//发现大轮廓
		vector<vector<Point>> ref_blk_mask_contours;//大轮廓向量
		vector<Vec4i> ref_blk_mask_hierarchy;//大拓扑结构向量
		//参数:(二值化图,轮廓向量,检测方法,发现方法,偏移点),偏移点用于大图还原,此处未用到
		findContours(ref_blk_mask, ref_blk_mask_contours, ref_blk_mask_hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(-1, -1));
		//绘制轮廓,白色
		drawContours(capsule, ref_blk_mask_contours, -1, Scalar(255, 255, 255), 1, LINE_AA, ref_blk_mask_hierarchy);
	}else {//坏品(-1)
		//绘制轮廓,红色
		drawContours(capsule, big_contours, -1, Scalar(0, 0, 255), 1, LINE_AA, big_hierarchy);
	}
	//绘制内轮廓
	if (data.classification!=5) {//判断不为空
		//绘制内轮廓,蓝色(直线)
		//drawContours(capsule,small_contours,-1,Scalar(255,0,0),1,LINE_AA,small_hierarchy);
		//画虚线(自定义函数)
		drawDashContours(capsule, small_contours, 0, Scalar(255, 0, 0),1,LINE_AA,5);
	}
}

void putText_function(Mat& src, Rect&pos, Mydata& data) {//在图片上写文本
	//判空
	if (src.empty()) {//图为空
		return;//返回
	}
	//定义对应类别数组
	char* class_name[] = {"bad","good","alien","big_and_small","bubble","black point","empty"};
	//放入文字-类别
	//参数:(图,文本,原点,字体,缩放比,颜色,线宽,线型,底左对齐(倒过来的效果))
	putText(src,format("%d(%s)",data.classification,class_name[data.classification+1]),Point(pos.x+pos.width/2,pos.y),CV_FONT_NORMAL,0.5,Scalar(255,0,0),1,LINE_AA,false);
	if(data.classification!=5){//当分类不等于空(5)时,绘制文字
		//放入文字-宽
		putText(src,format("Width:%d",data.width),Point(pos.x-pos.width/5,pos.y+20),CV_FONT_NORMAL,0.5,Scalar(0,255,0),1,LINE_AA,false);
		//放入文字-高
		putText(src,format("Height:%d",data.height),Point(pos.x-pos.width/5,pos.y+40),CV_FONT_NORMAL,0.5,Scalar(0,255,0),1,LINE_AA,false);
		//放入文字-面积
		putText(src,format("Area:%.2f",data.area),Point(pos.x-pos.width/5,pos.y+60),CV_FONT_NORMAL,0.5,((data.area>0)?Scalar(0,0,255): Scalar(0, 255, 0)),1,LINE_AA,false);
	}
}

void drawDashContours(InputOutputArray image, InputArrayOfArrays contours,
	int contourIdx, const Scalar& color,int thinkness=1,int lineType=8, int gap_threshold=10) {//自定义函数,实现画虚线轮廓
	//获得图像(引用关系)
	Mat img = image.getMat();
	//定义并获取轮廓总个数
	int ncontours = (int)contours.total();
	//当轮廓总数量为0或指定轮廓数字大于(总轮廓数-1)时直接返回
	if (ncontours==0|| contourIdx>(ncontours-1)) {
		return;
	}
	//定义存储区
	AutoBuffer<Point*> _ptsptr(ncontours);//用于存储点的双指针,大小与轮廓数一致
	AutoBuffer<int> _npts(ncontours);//用于存储每个轮廓点的个数的单指针,大小与轮廓数一致
	Point** ptsptr= _ptsptr;//获得双指针,方便后面添加数据
	int* npts= _npts;//获得单指针,方便后面添加数据
	//判断轮廓标记
	if (contourIdx <0) {//当轮廓号为-1时,循环处理
		for (int i = 0; i < ncontours; i++) {//循环轮廓位置
			Mat p = contours.getMat(i);//循环获取矩阵(引用关系)
			CV_Assert(p.checkVector(2,CV_32S)>=0);//检查向量个数,有条件为假,断言判断报错,checkVector参数:(通道数,数据深度)返回向量个数,CV_Assert参数:(条件)
			ptsptr[i]=p.ptr<Point>();//赋值当前轮廓,获得点指针
			npts[i] = p.rows*p.cols*p.channels() / 2;//赋值点数量,行*列*通道数/2
		}
	}else {//当轮廓不为-1时,指定位置处理
		Mat p = contours.getMat(contourIdx);
		CV_Assert(p.checkVector(2, CV_32S) >= 0);//检查向量个数,有条件为假,断言判断报错,checkVector参数:(通道数,数据深度),CV_Assert参数:(条件)
		ptsptr[contourIdx] = p.ptr<Point>();//赋值当前轮廓,获得点指针
		npts[contourIdx] = p.rows*p.cols*p.channels() / 2;//赋值点数量,行*列*通道数/2
	}
	//循环轮廓数组指针位置和每个轮廓的点个数
	int threshold = (gap_threshold<0||gap_threshold>15)?10:gap_threshold;//定义间隔阈值,当阈值在0-15之间时使用阈值,否则给固定阈值10
	int counter_threshold[2] = {0,0};//阈值计数器,用于统计当前是否转化状态,[0]画线统计,[1]空统计
	for (int i = 0; i < sizeof(ptsptr);i++ ) {//循环外层指针位置
		for (int j = 0; j <npts[i];j++) {//循环此层的点个数
			//判断间隔
			if(counter_threshold[0]<threshold){//当[0]位置统计数小于阈值时,画线
				//画线,参数:(图,点1,点2,颜色,线宽,线型)
				line(img, ptsptr[i][j], ptsptr[i][(j+1)% npts[i]], color,thinkness,lineType);//%取余为了确保数据范围
				counter_threshold[0]++;//当前状态阈值加1
				counter_threshold[1] = 0;//空阈值为0
			}else if(counter_threshold[1]<threshold){//当[1]位置统计数小于阈值时,计数
				counter_threshold[1]++;//空阈值阈值加1
			}else {//否则[0]位置计数统计归0,继续画线
				counter_threshold[0]=0;//当前状态阈值为0
				//画线,参数:(图,点1,点2,颜色,线宽,线型)
				line(img, ptsptr[i][j], ptsptr[i][(j + 1) % npts[i]], color,thinkness,lineType);//%取余为了确保数据范围
				counter_threshold[0]++;//当前状态阈值加1
				counter_threshold[1] = 0;//空阈值为0
			}
		}
	}
}

好的样本模型抠图(goodmodel.bmp)
关于胶囊检测的思考-代码实现_第3张图片
实现部分效果:
关于胶囊检测的思考-代码实现_第4张图片关于胶囊检测的思考-代码实现_第5张图片
主要思想:
1.获得胶囊本身。(重点)
2.对胶囊进行各种检测。(重点)
3.对每组胶囊的状态进行组合,判断给出结果。
4.给后台或显示结果。
细节说明:
1.对于胶囊提取:我们需要使用掩码、颜色范围、二值化三个部分完成操作。原因很简单,单个算法是无法完成总胶囊提取的。
2.对于胶囊检测来说,按照不同检测内容,使用不同的方法。其中异型使用凸包+形状比较得分处理,大小端使用轮廓斜矩阵切半+上下两部分形状比较得分处理,气泡和黑点使用Canny(边缘提取)+MorphologyEx(图像学)+findContours(轮廓)完成不同面积的抽取,再进行筛选。
3.对于胶囊外部的虚线如何实现:使用轮廓向量(多个点形成的完整轮廓),自己实现绘制轮廓功能,因为openCV没有给出画虚线的直接方法。实现思路就是间隔几个点绘制线,再间隔几个点不绘线,虚线就形成了。
以上程序和说明都是实际测试过的。
需要再优化的内容:
1.对于胶囊边缘的提取,如何更好的去噪。
2.对于检测点的提取,如何更好。

我是Simon, 在这里期待与您的交流。

你可能感兴趣的:(关于胶囊检测的思考-代码实现)