图像处理-自实现方法(opencv)

void sobeltest()
{	
	for (int i = 1; i <= 10; i++)
	{
		char name[1000];
		char grayname[1000];
		char srcname[1000];
		sprintf(name,"%s%d%s", "C:\\Users\\Administrator\\Desktop\\w",i,".jpg");
		sprintf(grayname, "%s%d%s", "C:\\Users\\Administrator\\Desktop\\gray2\\w", i, "_last.jpg");

		Mat img = imread(name);
		Mat grad_x, grad_y;
		Mat abs_grad_x, abs_grad_y, dst;
		GaussianBlur(img, img, Size(5, 5), 0, 0, BORDER_DEFAULT);
		//求x方向梯度
		Sobel(img, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
		convertScaleAbs(grad_x, abs_grad_x);
		//求y方向梯度
		Sobel(img, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
		convertScaleAbs(grad_y, abs_grad_y);
		//合并梯度
		addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, dst);
		//二值化 整体
		Mat gray;
		cvtColor(dst, gray, COLOR_BGR2GRAY);
		threshold(gray, gray, 0.2 * 255, 255, THRESH_BINARY_INV);
		imwrite(grayname, gray);
	}	
}
//通过传入一行数据 得到黑白间隔数目
int CountBlackWhiteInterval(Mat src)
{
	//先归一化为 0/1 (0白 1黑)
	int interValuNum = 0;
	int* dataArray = (int*)malloc(sizeof(int)*src.cols);
	for (int i = 0; i < src.cols; i++)
	{
		if (src.at<uchar>(0, i) == 0)
		{
			dataArray[i] = 1;
		}
		else 
		{
			dataArray[i] = 0;
		}
	}

	//进行处理 变成黑白对 如果2个黑之间白色过少 则认为是杂色 变为全白
	vector<int> BWVector;
	int firstValue = dataArray[0];
	if (firstValue == 1)		
		BWVector.push_back(0);
	
	for (int i = 0; i < src.cols; i++)
	{
		if (dataArray[i] != firstValue)
		{
			BWVector.push_back(i);
			firstValue = dataArray[i];
		}
	}

	//应该成双 如果是单数 补充最后一位
	if (BWVector.size() % 2 == 1)
	{
		BWVector.push_back(src.cols - 1);
	}

	interValuNum = BWVector.size()/2;	
	if (BWVector.size() >= 4)
	{
		//去除因为太靠近的误差
		for (int i = 0; i < BWVector.size() / 2 - 1; i++)
		{
			if (BWVector[(i + 1) * 2] - BWVector[i * 2 + 1] < 5)
			{
				interValuNum--;
			}
		}
	}
	return interValuNum;
}
//判别黑色图片直方图是否均匀
//true 均匀 false 不均匀
bool CheckImageHist(Mat src)
{
	Mat gray;
	cvtColor(src, gray, COLOR_BGR2GRAY);
	int grayScale[256];
	long totalLight = 0;
	for (int i = 0; i < 256; i++)
	{
		grayScale[i] = 0;
	}
	//计算直方图分布
	for (int i = 0; i < gray.rows; i++)
	{
		for (int j = 0; j < gray.cols; j++)
		{
			grayScale[ gray.at<uchar>(i,j)]++;			
		}
	}

	//求0-30段占总的比例 判别是否过亮 (因为黑色)
	long lightTotal = 0;
	for (int i = 0; i < 30; i++)
	{
		lightTotal += grayScale[i];
	}
	totalLight = gray.rows*gray.cols;
	double lightPer = (double)lightTotal / (double)totalLight;
	//printf("高亮比:%lf -> 值:%ld %ld\n",lightPer,totalLight,lightTotal);
	if (lightPer < 0.1)//10%
	{
		return false;
	}

	return true;
} 
#pragma region 求图像重心
/** 计算二值图像的重心
* @param[in] src  输入的待处理图像
* @param[out] center 重心坐标
* @retval 0  操作成功
* @retval -1 操作失败
* @note 输入图像是二值化图像
* @note xc=M10/M00, yc=M01/M00, 其中 Mx_order,y_order=SUMx,y(I(x,y)*x^x_order*y^y_order)
 */
static int aoiGravityCenter(IplImage *src, CvPoint* center)
{
	double m00, m10, m01;
	CvMoments moment;
	cvMoments(src, &moment, 1);
	m00 = cvGetSpatialMoment(&moment, 0, 0);
	if (m00 == 0)
		return 1;
	m10 = cvGetSpatialMoment(&moment, 1, 0);
	m01 = cvGetSpatialMoment(&moment, 0, 1);
	center->x = (int)(m10 / m00);
	center->y = (int)(m01 / m00);
	return 0;
}

IplImage* binary_image(IplImage* src)
{
	// cvThreshold( src, src, 100, 255, CV_THRESH_BINARY );//100 is the thredhold 
	IplImage* one_channel = cvCreateImage(cvSize(src->width, src->height), IPL_DEPTH_8U, 0);

	for (int y = 0; y < src->height; y++)
	{
		char *ptr = src->imageData + y * src->widthStep;
		char *p_one_channel = one_channel->imageData + y * one_channel->widthStep;
		for (int x = 0; x < src->width; x++)
		{
			int temp = ptr[x];
			if (temp != 0)//不是黑色也就是说不是背景
			{
				p_one_channel[x] = 255;//设置为白色
			}
			else
			{
				p_one_channel[x] = 0;

			}
		}
	}
	return one_channel;
}

//求取二值图像重心
cv::Point GetImageQualityCenter(Mat src)
{
	IplImage * dst;
	dst = &IplImage(src);
	CvPoint xy;
	aoiGravityCenter(binary_image(dst), &xy);
	//cout << xy.x << endl;
	//cout << xy.y << endl;
	return cv::Point(xy.x, xy.y);
	//cvCircle(draw, cvPoint(xy.x, xy.y), 3, CV_RGB(0, 0, 255), 5);
	//cvNamedWindow("重心", 1);
	//cvShowImage("重心", draw);
}
#pragma endregion
void myHoughLinesTest(Mat src)
{	
	Mat midImage, dstImage;
	Canny(src,midImage,128,255,3);
	cvtColor(midImage, dstImage, CV_GRAY2BGR);
	vector<Vec2f> lines;
	HoughLines(midImage, lines, 1, CV_PI / 180, 150, 0, 0);
	cout <<"Hough Result:"<< lines.size() << endl;
	for (size_t i = 0; i < lines.size(); i++)
	{
		float rho = lines[i][0], theta = lines[i][1];
		Point pt1, pt2;
		double a = cos(theta), b = sin(theta);
		double x0 = a * rho, y0 = b * rho;
		pt1.x = cvRound(x0 + 1000 * (-b));
		pt1.y = cvRound(y0 + 1000 * (a));
		pt2.x = cvRound(x0 - 1000 * (-b));
		pt2.y = cvRound(y0 - 1000 * (a));
		line(dstImage, pt1, pt2, Scalar(55,100,195),1,LINE_AA);
	}

	imshow("src", src);
	imshow("mid", midImage);
	imshow("dst", dstImage);
	waitKey();	
}
//获取特定目录下 所有文件夹名字
void GetAllFolder(string path, vector<string>& files)
{
	//文件句柄
	intptr_t hFile = 0;//findnext 返回值是intptr_t 用long会丢失信息
	//文件信息
	struct _finddata_t fileinfo;  //很少用的文件信息读取结构
	string p;  //string类很有意思的一个赋值函数:assign(),有很多重载版本
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
		do {
			if ((fileinfo.attrib & _A_SUBDIR))  //文件夹
			{
				if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
				{
					//files.push_back(p.assign(path).append("\\").append(fileinfo.name));
					files.push_back(p.assign(path).append(fileinfo.name));
				}
			}
			else //文件
			{		}
		} while (_findnext(hFile, &fileinfo) == 0);  //寻找下一个,成功返回0,否则-1
		_findclose(hFile);
	}
}
//获取特定目录下 所有文件名字 不包含子目录
void GetAllFilesForThisFolder(string path,vector<string>& files)
{
	//文件句柄
	intptr_t hFile = 0;//findnext 返回值是intptr_t 用long会丢失信息
	//文件信息
	struct _finddata_t fileinfo;  //很少用的文件信息读取结构
	string p;  //string类很有意思的一个赋值函数:assign(),有很多重载版本
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
		do {
			if ((fileinfo.attrib & _A_SUBDIR))  //文件夹
			{
				
			}
			else //文件
			{
				files.push_back(p.assign(path).append("\\").append(fileinfo.name));
			}
		} while (_findnext(hFile, &fileinfo) == 0);  //寻找下一个,成功返回0,否则-1
		_findclose(hFile);
	}
}
//获取文件夹内所有 文件的名字(包括 子文件夹)
void getFiles(string path, vector<string>& files)
{
	//文件句柄
	intptr_t hFile = 0;//findnext 返回值是intptr_t 用long会丢失信息
	//文件信息
	struct _finddata_t fileinfo;  //很少用的文件信息读取结构
	string p;  //string类很有意思的一个赋值函数:assign(),有很多重载版本
	if ((hFile = _findfirst(p.assign(path).append("\\*").c_str(), &fileinfo)) != -1) {
	     do {
	           if ((fileinfo.attrib & _A_SUBDIR))  //非文件夹
			   {  
	                if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0) 
					{
	                    files.push_back(p.assign(path).append("\\").append(fileinfo.name));
						//跳转遍历 子目录内容
	                    getFiles(p.assign(path).append("\\").append(fileinfo.name), files);					
					}				
				}
			   else //文件夹
			   {
				    files.push_back(p.assign(path).append("\\").append(fileinfo.name));
			   }			
		} while (_findnext(hFile, &fileinfo) == 0);  //寻找下一个,成功返回0,否则-1
			         _findclose(hFile);			
	}
}
//读取路径下 符合条件的 单步识别 组合 并切割
void GetResultImage(string filePath)
{
	vector<string> folders;
	int mytotalNum = 0;
	int mytrueNum = 0;
	//获取该路径下的所有文件夹名字  
	GetAllFolder(filePath, folders);

	printf("遍历所有文件夹名字\n");
	int size = folders.size();
	/*
	for (int i = 0; i < size; i++)
	{
		std::cout << folders[i].c_str() << endl;
	}
	*/
	printf("对每个文件夹进行文件名检索 并判定结果\n");	
	for (int i = 0; i < size; i++)
	{
		vector<string> tempFiles;
		int ResultArray[1024];
		string imagename,markname,modename;
		GetAllFilesForThisFolder(folders[i], tempFiles);
		printf("%d\n",i);
		//判别 得到 三个值 分别是那个
		for (int m = 0; m < tempFiles.size(); m++)
		{
			if ( ( tempFiles[m].find(".jpg") != string::npos 
				|| tempFiles[m].find(".jpeg") != string::npos 
				|| tempFiles[m].find(".JPG") != string::npos 
				|| tempFiles[m].find(".JPEG") != string::npos )
				&&(tempFiles[m].find(".txt") == string::npos))
			{
				imagename = tempFiles[m];
			}
			if (tempFiles[m].find(".mod") != string::npos)
			{
				modename = tempFiles[m];
			}
			if (tempFiles[m].find(".png") != string::npos)
			{
				markname = tempFiles[m];
			}
		}
		char iname[1024];
		char mark[1024];
		char mode[1024];
		int tempTotalNum = 0;
		int tempTrueNum = 0;
		sprintf(iname, "%s", imagename.c_str());
		sprintf(mode, "%s", modename.c_str());
		sprintf(mark, "%s", markname.c_str());		
		SingleImageCheck(iname, mark, mode, ResultArray);
		/*CheckSingleImageCheck(iname, mark, mode, ResultArray,&tempTotalNum,&tempTrueNum);		
		if (tempTrueNum == tempTotalNum) //输出正确的
		{
			cout << "图名:" << iname << endl;// << "模板:" << mode << endl << "马克:" << mark << endl;
			printf("结果 True:%d Total:%d\n", tempTrueNum, tempTotalNum);
		}
		mytotalNum += tempTotalNum;
		mytrueNum += tempTrueNum;*/
	}
	//double successPer = (double)mytrueNum / mytotalNum;
	//printf("成功率:%.4f\n",successPer);
	printf("图片分割完成 请点击回车\n");
}
//计算全局亮度
double getAvg(Mat img)
{
	Mat gray;
	cvtColor(img, gray, CV_RGB2GRAY);
	Scalar scalar = mean(gray);
	return scalar.val[0];
}
//自动色阶算法 处理不好的图
void myAutoColorLevel(Mat src)
{	
	int i = 0, j = 0, k = 0;
	int m_Height = src.rows, m_Width = src.cols;
	double cutParam = 0.2;//0.1; //参照PhotoShop中的自动色阶的裁剪参数,此处设置为为0.1%
	double doubleThreshod = m_Height * m_Width * cutParam * 0.01;  //由于是0.1%,所以再×0.01
	int HistBGR[3][256]; //= new int[3, 256]; //B, G, R
	unsigned char SpeedBGR[3][256];// = new byte[3, 256];
	int Threshold = (int)doubleThreshod;
	int Integral = 0;
	int NewMin = 0;
	int NewMax = 0;

	//cout << src.size() << endl;
	
	//初始化
	for (i = 0; i < 3; i++)
	{		
		for (int m = 0; m < 256; m++)
		{
			HistBGR[i][m] = 0;
			SpeedBGR[i][m] = 0;
		}
	}

	//获得直方图数组
	for (i = 0; i < m_Height; i++)
	{		
		for (j = 0; j < m_Width; j++)
		{
			for (k = 0; k < 3; k++)
			{
				HistBGR[k][ src.at<Vec3b>(i,j)[k] ] += 1;
			}
		}
	}
	/*
	for (i = 0; i < 3; i++)
	{
		printf("通道%d\n" , i);
		for (int m = 0; m < 256; m++)
		{
			printf("%d:%d\n",m,HistBGR[i][m]);
		}
	}*/
	//逐一比对每个像素RGB三种颜色的值,分别从右向左(取NewMax)和从左向右(取NewMin)获取该图片的RGB的最小值和最大值
	for (k = 0; k < 3; k++)
	{
		Integral = 0;
		for (i = 0; i <= 255; i++)
		{
			Integral += HistBGR[k][i];
			if (Integral >= Threshold)
			{
				NewMin = i;
				break;
			}
		}
		Integral = 0;
		for (i = 255; i >= 0; i--)
		{
			Integral += HistBGR[k][i];
			if (Integral > Threshold)
			{
				NewMax = i;
				break;
			}
		}
		//将根据上步获得的最大值和最小值,将当前照片的值进行线性映射,以此获得新的直方图数组
		for (i = 0; i <= 255; i++)
		{
			if (i <= NewMin)
			{
				SpeedBGR[k][i] = 0;
			}
			else if (i >= NewMax)
			{
				SpeedBGR[k][i] = 255;
			}
			else
			{
				SpeedBGR[k][i] = (unsigned char)((i - NewMin) * 255 / (NewMax - NewMin));
			}
		}
	}

	//根据新的直方图对每个像素的RGB的数据进行修改
	for (i = 0; i < m_Height; i++)
	{
		for (j = 0; j < m_Width; j++)
		{
			for (k = 0; k < 3; k++)
			{				
				src.at<Vec3b>(i,j)[k] = SpeedBGR[k][ src.at<Vec3b>(i,j)[k] ];
			}
		}
	}	
}
//测试FAST角点 对压板的应用
void TestFastPlaten(char* imagename)
{
	Mat frame = imread(imagename, IMREAD_COLOR);
	double t = getTickCount();//当前滴答数
	std::vector<KeyPoint> keyPoints;
	int threaholdvalue = 40;
	Ptr<FastFeatureDetector> fast = FastFeatureDetector::create(threaholdvalue);
	fast->detect(frame, keyPoints);

	drawKeypoints(frame, keyPoints, frame, Scalar(0, 0, 255), DrawMatchesFlags::DRAW_OVER_OUTIMG);

	t = ((double)getTickCount() - t) / getTickFrequency();
	cout << "算法用时:" << t << "秒" << endl;

	imshow("FAST特征点", frame);
	cvWaitKey(0);
}
//计算2点之间的距离
double CountTwoPointDistance(cv::Point p1, cv::Point p2)
{
	return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
}
//求2点构成的直线的角度
double CountTwoPointAngel(cv::Point p1, cv::Point p2)
{
	//三种可能 垂直 水平 斜线
	double k = 0;
	double angel = 0;
	if (p1.x == p2.x) angel = 90.0;
	else if (p1.y == p2.y) angel = 0.0;
	else {
		k = (p2.y - p1.y) / (p2.x - p1.x);
		angel = atan(k)*180.0/MyPI;
	}
	return angel;
}
//求2点构成的直线
double CountTwoPointLineEquation(cv::Point p1, cv::Point p2,double* k ,double* c)
{
	//三种可能 垂直 水平 斜线
	//double k = 0;
	double angel = 0;
	if (p1.x == p2.x) 
	{
		angel = 90.0; 
		*k = -1;
	}
	else if (p1.y == p2.y)
	{
		angel = 0.0;
		*k = -2;
	}
	else {
		*k = (p2.y - p1.y) / (p2.x - p1.x);
		angel = atan(*k)*180.0 / MyPI;
		*c = 0;
	}
	return angel;
}
//求有顺序的3点之前的夹角
double CountThreePointAngel(cv::Point p1,cv::Point center,cv::Point p2)
{
	//p1,center / p2,center 组成2条直线
	//求∠A : center
	//假定 p1:B center:A p2:C
	//cos∠A = [ (x2-x1)(x3-x1)+(y2-y1)(y3-y1)]/ ( sqrt[ (x2-x1)*(x2-x1)+(y2-y1)*(y2-y1) ] * sqrt([x3-x1]*[x3-x1]+(y3-y1)*(y3-y1) ) )
	//∠A = acos(cos∠A)
	Point A = center;
	Point B = p1;
	Point C = p2;
	double AB_AC = (B.x - A.x)*(C.x - A.x) + (B.y - A.y)*(C.y - A.y);
	double muAB  = sqrt( (B.x - A.x)*(B.x - A.x) + (B.y - A.y) *( B.y - A.y) );
	double muAC  = sqrt( (C.x - A.x)*(C.x - A.x) + (C.y - A.y) *( C.y - A.y) );	
	double cosA = AB_AC / (muAB *muAC);
	double angel = acos(cosA)/CV_PI*180.0;
	return angel;
}
//求2条直线的夹角
double CountThreePointAngel(cv::Point p1, cv::Point p1_1, cv::Point p2, cv::Point p2_2)
{
	double k1, k2, c1, c2;
	double angel1 = CountTwoPointLineEquation(p1, p1_1, &k1, &c1);
	double angel2 = CountTwoPointLineEquation(p2, p2_2, &k2, &c2);
	double angel = 0;	
	angel = abs(angel1 - angel2); //acos(cosA) / CV_PI * 180.0;
	return angel;
}
//测试凸包构成的多边形 开闭关系
bool TestHullPlaten( Mat src)
{
	Mat testImage(1024, 1024, CV_8UC3);//画布大小600x600	
	//按回车键一直更新	
	testImage = Scalar::all(0);//将画布设置为黑色,每次按键后可更新	
	//绘制凸包 外围多边形
	vector<Point> mypoints;
	bool OpenFlag = false;//判别是否有打开可能性 false是关闭 true是打开
	//求出所有点
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			if (src.at<uchar>(i, j) != 255)
			{
				cv::Point tempP = cv::Point(j, i);
				mypoints.push_back(tempP);
			}
		}
	}	
	vector<int> hull;
	if (mypoints.size() > 0)
	{
		convexHull(Mat(mypoints), hull, true);
		//准备参数
		int hullcount = hull.size();
		Point point0 = mypoints[hull[hullcount - 1]];//连接凸包边的坐标点 最后一个点
		//检测是否存在 很长(>20点)一条 且 类似 \ / 这种斜率的直线 若有则为开
		//1.计算2点之间长度 2.计算直线角度 tan@
		double* hullLength = (double*)malloc(sizeof(double)*hullcount);
		double* hullAngel = (double*)malloc(sizeof(double)*hullcount);
		//绘制凸包边
		for (int i = 0; i < hullcount; i++)
		{
			Point point = mypoints[hull[i]];
			hullLength[i] = CountTwoPointDistance(point0, point);
			hullAngel[i] = CountTwoPointAngel(point0, point);
			line(testImage, point0, point, Scalar(255, 255, 255), 2, LINE_AA);
			point0 = point;
		}
		if (saveFlag)
			imwrite("image\\hullResult.jpg", testImage);

		int LengthLimit = src.cols / 2;
		int LengthLimit2 = src.cols / 3;//稍短的线
		double angelLimit1 = 70.0;//角度条件1
		double angelLimit2 = 20.0;//角度条件2
		double limitLineLength = 0.0;

		if (printFlag)
			printf("凸包数目:%d\n", hullcount);
		//检测是否有超标的 实际上不应该是一条线 固定很大 也可能是多个稍短的线 多段 短的线 长度应当超过高度 才是打开的 否则闭合
		//需要判别是否近似 矩形 而非只是线
		int limitNum = 0;
		for (int i = 0; i < hullcount; i++)
		{
			if (printFlag)
				printf("长度:%.4f - 角度:%.4f\n", hullLength[i], hullAngel[i]);
			if (abs(hullAngel[i]) > angelLimit2 && abs(hullAngel[i]) < angelLimit1)
			{
				limitLineLength += hullLength[i];
			}
			if (limitLineLength >= (double)src.rows*0.5)
			{
				OpenFlag = true;
				break;
			}			
		}
		free(hullLength);
		free(hullAngel);
	}
	//输出
	if (showFlag)
	{
		imshow("凸包绘制检测", testImage);
		waitKey();
	}

	return OpenFlag;
}
//拟合直线 返回角度
double niheTest(Mat dst)
{
	Mat image(1000, 1000, CV_8UC3, Scalar::all(0));
	//Mat dst = imread(filename,IMREAD_GRAYSCALE);
	cv::Vec4f line_para;
	cv::Point point0;

	//获得拟合点
	std::vector<cv::Point> points;
	for (int i = 0; i < dst.rows; i++)
	{
		for (int j = 0; j < dst.cols; j++)
		{
			if (dst.at<uchar>(i, j) == 0)
			{
				cv::Point tP = cv::Point(j, i);
				points.push_back(tP);
			}
		}
	}

	if (points.size() >= 2)
	{
		cv::fitLine(points, line_para, cv::DIST_L2, 0, 1e-2, 1e-2);
		//获取点斜式的点和斜率		
		point0.x = line_para[2];
		point0.y = line_para[3];
		double k = line_para[1] / line_para[0];
		//计算直线的端点(y = k(x - x0) + y0)
		cv::Point point1, point2;
		point1.x = 0;
		point1.y = k * (0 - point0.x) + point0.y;
		point2.x = 128;
		point2.y = k * (128 - point0.x) + point0.y;
		//计算角度
		double angel = atan(k)*180.0 / MyPI;
		if (showFlag)
		{
			line(image, point1, point2, Scalar(255, 0, 0), 3);
			imshow("dst", image);
			waitKey();
		}
		return angel;
	}

	return 0;
}

/**
 * @brief expandEdge 扩展边界函数
 * @param img:输入图像,单通道二值图,深度为8
 * @param edge  边界数组,存放4条边界值
 * @param edgeID 当前边界号
 * @return 布尔值 确定当前边界是否可以扩展
 */
bool expandEdge(const Mat & img, int edge[], const int edgeID)
{
	//[1] --初始化参数
	int nc = img.cols;
	int nr = img.rows;

	switch (edgeID) {
	case 0:
		if (edge[0] > nr)
			return false;
		for (int i = edge[3]; i <= edge[1]; ++i)
		{
			if (img.at<uchar>(edge[0], i) == 0)
				return false;
		}
		edge[0]++;
		return true;
		break;
	case 1:
		if (edge[1] > nc)
			return false;
		for (int i = edge[2]; i <= edge[0]; ++i)
		{
			if (img.at<uchar>(i, edge[1]) == 0)
				return false;
		}
		edge[1]++;
		return true;
		break;
	case 2:
		if (edge[2] < 0)
			return false;
		for (int i = edge[3]; i <= edge[1]; ++i)
		{
			if (img.at<uchar>(edge[2], i) == 0)
				return false;
		}
		edge[2]--;
		return true;
		break;
	case 3:
		if (edge[3] < 0)
			return false;
		for (int i = edge[2]; i <= edge[0]; ++i)
		{
			if (img.at<uchar>(i, edge[3]) == 0)
				return false;
		}
		edge[3]--;
		return true;
		break;
	default:
		return false;
		break;
	}
}

/**
 * @brief 求取连通区域内接矩
 * @param img:输入图像,单通道二值图,深度为8
 * @param center:最小外接矩的中心
 * @return  最大内接矩形
 * 基于中心扩展算法
 */
cv::Rect InSquare(Mat &img, const Point center)
{
	// --[1]参数检测
	if (img.empty() ||
		img.channels() > 1
		|| img.depth() > 8)
		return Rect();
	//[1]

	// --[2] 初始化变量
	int edge[4];
	edge[0] = center.y + 1;//top
	edge[1] = center.x + 1;//right
	edge[2] = center.y - 1;//bottom
	edge[3] = center.x - 1;//left
	//[2]

	// --[3]边界扩展(中心扩散法)
	bool EXPAND[4] = { 1,1,1,1 };//扩展标记位
	int n = 0;
	while (EXPAND[0] || EXPAND[1] || EXPAND[2] || EXPAND[3])
	{
		int edgeID = n % 4;
		EXPAND[edgeID] = expandEdge(img, edge, edgeID);

		n++;
	}
	Point tl = Point(edge[3], edge[0]);
	Point br = Point(edge[1], edge[2]);
	return Rect(tl, br);
}

//去除小块干扰 
void ridSmallInterfere(cv::Mat& finalMat)
{
	//边缘检测 结合最小外包矩形
	//把原图 放到更大的一张白图上 进行检测
	vector< vector<Point> > contours;
	vector< Vec4i > hierarchy;

	//判别类别
	if (finalMat.type() == CV_8UC3)
	{
		cvtColor(finalMat, finalMat, COLOR_BGR2GRAY);
	}

	Mat whiteMat(finalMat.rows + 30 * 2, finalMat.cols + 30 * 2, finalMat.type(), Scalar::all(255));
	Mat whiteSaveMat;
	for (int i = 0; i < finalMat.rows; i++)
	{
		for (int j = 0; j < finalMat.cols; j++)
		{
			whiteMat.at<uchar>(i + 30, j + 30) = finalMat.at<uchar>(i, j);
		}
	}
	if (saveFlag)
		imwrite("image\\BigWhite.jpg", whiteMat);
	whiteSaveMat = whiteMat.clone();

	//先高斯平滑 再膨胀一下 去除小的干扰点
	GaussianBlur(whiteMat,whiteMat,Size(3,3),0);
	Mat element = getStructuringElement(MORPH_RECT,Size(3,3));
	dilate(whiteMat, whiteMat, element);

	//先边缘检测一下 再进行轮廓检测
	Canny(whiteMat, whiteMat, 128, 255);
	if (saveFlag)
		imwrite("image\\CannyMat.jpg", whiteMat);

	findContours(whiteMat, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
	int ridSize = finalMat.cols > 60 ? 30 : finalMat.cols / 2;
	if (printFlag)
		printf("去除块尺寸:%d\n", ridSize);
	//遍历 去除面积过小的 有时候会把识别部分的清除掉了 因为存在重复问题 所以先取出来不重复的rect
	//判别 条件 1:2个 一个很大 一个很小 去除小的 2:3个 一个小 2个差不多 3:>3个
	for (int i = 0; i < contours.size(); i++)
	{
		Rect rect = boundingRect(contours[i]);
		//判断面积 < 30*30
		if (rect.width * rect.height < ridSize * ridSize / 3 || rect.width < 15 || rect.height < 15)
		{
			if (printFlag)
				cout << rect << endl;
			for (int m = rect.y; m < rect.y + rect.height; m++)
			{
				for (int n = rect.x; n < rect.x + rect.width; n++)
				{
					whiteSaveMat.at<uchar>(m, n) = 255;
				}
			}
		}
	}
	if (saveFlag)
		imwrite("image\\BigWhiteSave.jpg", whiteSaveMat);
	//重新赋值回去
	for (int i = 0; i < finalMat.rows; i++)
	{
		for (int j = 0; j < finalMat.cols; j++)
		{
			finalMat.at<uchar>(i, j) = whiteSaveMat.at<uchar>(i + 30, j + 30);
		}
	}	
	if (saveFlag)
		imwrite("image\\FinalMatLast.jpg", whiteSaveMat);
}
//判别是否是梯形,并传递收缩范围
bool CheckEchelonAndCountArea(Mat src,int* x1,int* x2)
{	
	Mat testImage(1024, 1024, CV_8UC3);//画布大小600x600	
	//按回车键一直更新	
	testImage = Scalar::all(0);//将画布设置为黑色,每次按键后可更新	
	//绘制凸包 外围多边形
	vector<Point> mypoints;
	bool OpenFlag = false;//判别是否有打开可能性 false是关闭 true是打开
	if (src.rows > 0 && src.cols > 0)
	{
		//求出所有点
		for (int i = 0; i < src.rows; i++)
		{
			for (int j = 0; j < src.cols; j++)
			{
				if (src.at<uchar>(i, j) != 255)
				{
					cv::Point tempP = cv::Point(j, i);
					mypoints.push_back(tempP);
				}
			}
		}
		vector<int> hull;
		if (mypoints.size() > 0)
		{
			convexHull(Mat(mypoints), hull, true);
			//准备参数
			int hullcount = hull.size();
			Point point0 = mypoints[hull[hullcount - 1]];//连接凸包边的坐标点 最后一个点
			//检测是否存在 很长(>20点)一条 且 类似 \ / 这种斜率的直线 若有则为开
			//1.计算2点之间长度 2.计算直线角度 tan@
			double* hullLength = (double*)malloc(sizeof(double)*hullcount);
			double* hullAngel = (double*)malloc(sizeof(double)*hullcount);
			//绘制凸包边
			for (int i = 0; i < hullcount; i++)
			{
				Point point = mypoints[hull[i]];
				hullLength[i] = CountTwoPointDistance(point0, point);
				hullAngel[i] = CountTwoPointAngel(point0, point);
				line(testImage, point0, point, Scalar(255, 255, 255), 2, LINE_AA);
				point0 = point;
			}
			if (saveFlag)
				imwrite("image\\echelonResult.jpg", testImage);

			int LengthLimit = src.cols / 2;
			int LengthLimit2 = src.cols / 3;//稍短的线				

			//检测是否有超标的 实际上不应该是一条线 固定很大 也可能是多个稍短的线 多段 短的线 长度应当超过高度 才是打开的 否则闭合
			//需要判别是否近似 矩形 而非只是线
			int limitNum = 0;
			bool leftF = false, rightF = false;
			for (int i = 0; i < hullcount; i++)
			{
				if (printFlag)
					printf("长度:%.4f - 角度:%.4f\n", hullLength[i], hullAngel[i]);
				//判别是否存在一个梯形 也就是左右2边是否有2条长斜边
				if (hullAngel[i] > 30.0  && hullAngel[i] <80.0 && hullLength[i]>LengthLimit2) //左边 30-70
				{
					leftF = true;
				}
				else if (hullAngel[i] < -30.0  && hullAngel[i] > -80.0 && hullLength[i] > LengthLimit2)//右边 -70 - -30
				{
					rightF = true;
				}
				else {}

				if (leftF && rightF) { OpenFlag = true; break; }
			}

			//寻找最上方的平行范围
			for (int i = 0; i < hullcount; i++)
			{
				if (hullLength[i] < LengthLimit &&
					((hullAngel[i] >= 0 && hullAngel[i] < 30.0) || (hullAngel[i] >= 0 && hullAngel[i] < 30.0))
					)
				{
					//判别对应线段点 是否在上方 0时对应的点是 [last,0] 后面均是[i-1,i]
					Point p1, p2;
					if (i == 0)
					{
						p1 = mypoints[hull[i]];
						p2 = mypoints[hull[hullcount - 1]];
					}
					else
					{
						p1 = mypoints[hull[i]];
						p2 = mypoints[hull[i - 1]];
					}
					if (p1.y < (double)src.rows*0.2 && p2.y < (double)src.rows*0.2)
					{
						//范围*3
						*x1 = MIN(p1.x, p2.x) - hullLength[i] > 0 ? MIN(p1.x, p2.x) - hullLength[i] : 0;
						*x2 = MAX(p1.x, p2.x) + hullLength[i] < src.cols ? MAX(p1.x, p2.x) + hullLength[i] : src.cols - 1;
						break;
					}
				}
			}
			free(hullLength);
			free(hullAngel);
		}
	}
	return OpenFlag;
}

//去除标题误差
bool ridTitleError(Mat src,Mat& dst)
{
	//1.先判别是否需要 进行标题去除 (长宽比 1.0左右 浮动 30%)
	double LWper = (double)MIN(src.rows, src.cols) / (double)MAX(src.rows, src.cols);
	//2. 下方是否有大量黑色积攒 没有也就是 小圆干扰 也不需要标题 上方较少 下方较多(10行)
	int topbNum = 0, botbNum = 0;
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			if (src.at<Vec3b>(i, j)[0] == 0)
			{
				topbNum++;
			}
		}
	}
	for (int i = src.rows-10; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			if (src.at<Vec3b>(i, j)[0] == 0)
			{
				botbNum++;
			}
		}
	}
	//标题上下10行 黑色累积值 若有标题误差 应当倍数>1.7
	double top_botPer = (double)MAX(topbNum, botbNum) / (double)MIN(topbNum, botbNum);

	if (LWper > 0.5 && top_botPer>1.7)
	{
		if (printFlag)
			printf("存在标题误差,需要去干扰\n");

		//因为存在同色标题问题 多在上下部分 因此需要先上下切割 有2种可能 一种独立于下方 一种连到了一起
		//求取有几块
		Mat finalMat;
		cvtColor(src, finalMat, COLOR_BGR2GRAY);
		vector< vector<Point> > contours;
		vector< Vec4i > hierarchy;
		Mat whiteMat(finalMat.rows + 30 * 2, finalMat.cols + 30 * 2, finalMat.type(), Scalar::all(255));
		Mat whiteSaveMat;
		for (int i = 0; i < finalMat.rows; i++)
		{
			for (int j = 0; j < finalMat.cols; j++)
			{
				whiteMat.at<uchar>(i + 30, j + 30) = finalMat.at<uchar>(i, j);
			}
		}	
		whiteSaveMat = whiteMat.clone();
		//先边缘检测一下 再进行轮廓检测
		Canny(whiteMat, whiteMat, 128, 255);
		findContours(whiteMat, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);	
		//寻找是否有细长段 对于此全覆盖行 进行去除
		for (int i = 0; i < contours.size(); i++)
		{
			Rect rect = boundingRect(contours[i]);
			//判断是否是细长型
			double linePer = (double)rect.width / (double)rect.height;
			if (linePer>2)
			{				
				for (int m = rect.y; m < rect.y + rect.height; m++)
				{
					for (int n = 0; n < whiteSaveMat.cols; n++)
					{
						whiteSaveMat.at<uchar>(m, n) = 255;
					}
				}
			}
		}		
		//把值赋值回去
		for (int i = 0; i < finalMat.rows; i++)
		{
			for (int j = 0; j < finalMat.cols; j++)
			{
				finalMat.at<uchar>(i, j) = whiteSaveMat.at<uchar>(i + 30, j + 30);
			}
		}
		if (saveFlag)
			imwrite("image\\Title1.jpg",finalMat);

		//实际上如果连在一起 还需要进行二次去除 根据百分比去除(下方5行平均占比 就是清除的比例)
		//判别是否构成梯形 若是 则全部收缩到 最上方的范围

		int x1, x2;
		bool checkEch = CheckEchelonAndCountArea(finalMat,&x1,&x2);		
		if (finalMat.rows > 0 && finalMat.cols > 0)
		{			
			if (checkEch)
			{
				if (printFlag)
					printf("梯形-切割\n");
				Mat cut1;
				finalMat.colRange(x1, x2).copyTo(cut1);
				if (saveFlag)
					imwrite("image\\titlePlaten.jpg", cut1);

				//第二道 下方检测 是否存在 瘦长段
				dst = cut1.clone();
			}
			else
			{
				if (printFlag)
					printf("非-梯形\n");
				dst = finalMat.clone();
			}
			return true;
		}
		else 
		{
			return false;
		}
	}
	else 
	{
		if (printFlag)
		{
			printf("不存在去除标题干扰-> 长宽比:%.3f  上下黑色误差比:%.3f\n", LWper, top_botPer);
		}
		dst = src.clone();
		return true;
	}	
}

void GetEquationFromAngel(int angel,double* k,double* c)
{
	if (angel % 90 != 0)
	{
		*k = tan(angel);
		*c = 0;
	}
}
//直方图 挪移
void HistMove(cv::Mat& src)
{
	//1.计算直方图重心位置 (累加到50%)
	int histArray[256];
	long histTotal = 0;

	for (int i = 0; i < 256; i++)
	{
		histArray[i] = 0;
	}

	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			int histValue = src.at<uchar>(i, j);
			histTotal += 1;
			histArray[histValue]++;
		}
	}
	if (printFlag)
		printf("perTotal:%ld \n", histTotal);

	long per50 = histTotal / 2;
	int per1 = (int)((double)histTotal *0.015);
	int lastIndex = 0;
	//求黑的开始 和 白的开始 第一个>1%
	int blackBeginIndex = 0;
	int whiteBeginIndex = 0;

	if (printFlag)
		printf("per1:%ld \n", per1);
	long totalB = 0;
	for (int i = 0; i < 256; i++)
	{
		totalB += histArray[i];
		if (totalB > per1)
		{
			blackBeginIndex = i;
			break;
		}
	}
	totalB = 0;
	for (int i = 255; i >= 0; i--)
	{
		totalB += histArray[i];
		if (totalB > per1)
		{
			whiteBeginIndex = i;
			break;
		}
	}
	if (printFlag)
		printf("BlackBegin:%d White:%d\n", blackBeginIndex, whiteBeginIndex);

	//计算比例
	double perBW = (double)255 / (whiteBeginIndex - blackBeginIndex);
	//对所有点进行补差
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			int lastValue = src.at<uchar>(i, j);
			if (lastValue < blackBeginIndex)
			{
				lastValue = 0;
			}
			else if (lastValue > whiteBeginIndex)
			{
				lastValue = 255;
			}
			else
			{
				lastValue = (int)((double)(lastValue - blackBeginIndex) *perBW);
			}
			src.at<uchar>(i, j) = lastValue;
		}
	}
}
//已知圆心 半径 角度 求圆上任一点
cv::Point GetEquationPoint(double angel, int radius, cv::Point centerP)
{
	//已知圆心 半径 角度 求圆上任一点
	//x1 = x0 + r * cos(angel   *   PI / 180) 
	//y1 = y0 + r * sin(angel   *   PI / 180)
	int x1 = (int)(centerP.x + radius * cos(angel * MyPI / 180.0 ));
	int y1 = (int)(centerP.y + radius * sin(angel * MyPI / 180.0 ));

	return Point(x1, y1);
}
//去除四周的环境干扰 从头到尾的黑线
void ReduceEnvironmentError(cv::Mat& src)
{
	//默认不会超过一半都是 扫描范围 W:[0,1/3] -> [ 2/3-1] H:[0,1/3] -> [ 2/3-1]	
	vector<int> ridV; vector<int> ridH;
	for (int i = 0; i < src.rows; i++)
	{
		int blackNum = 0;
		for (int j = 0; j < src.cols; j++)
		{
			if (src.at<uchar>(i, j) == 0)
			{
				blackNum++;
			}
		}
		if (blackNum > (double)src.cols*0.9)
		{
			ridV.push_back(i);
		}
	}
	for (int i = 0; i < src.cols; i++)
	{
		int blackNum = 0;
		for (int j = 0; j < src.rows; j++)
		{
			if (src.at<uchar>(j,i) == 0)
			{
				blackNum++;
			}
		}
		if (blackNum > (double)src.rows*0.9)
		{
			ridH.push_back(i);
		}
	}
	
	for (int m = 0; m < ridV.size(); m++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			src.at<uchar>(ridV[m], j) = 255;			
		}
	}
	for (int m = 0; m < ridH.size(); m++)
	{
		for (int j = 0; j < src.rows; j++)
		{
			src.at<uchar>(j,ridH[m]) = 255;
		}
	}
}
//硬聚类 返回最大颜色部分块
void GetMaxColorForKmeans(cv::Mat& src)
{
	//Kmeans 运算需要 32F类型
	Mat samples(src.rows*src.cols, 3, CV_32F);
	//赋值过去
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			for (int z = 0; z < 3; z++)
			{
				samples.at<float>(i + j * src.rows, z) = src.at<Vec3b>(i, j)[z];
			}
		}
	}

	int clusterCount = 3;//分几类
	Mat labels;//存放样本结果 最好的一次 的位置
	int attempts = 5;//遍历深度
	Mat centers;//存放最好的结果 聚类中心
	kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 0.0001, 10000), attempts, KMEANS_PP_CENTERS, centers);
	//绘制回去
	Mat new_image(src.size(), src.type());
	//求三个颜色里面最深的那个 灰度最低的那个
	Vec3f c1 = Vec3f(centers.at<float>(0, 0), centers.at<float>(0, 1), centers.at<float>(0, 2));
	Vec3f c2 = Vec3f(centers.at<float>(1, 0), centers.at<float>(1, 1), centers.at<float>(1, 2));
	Vec3f c3 = Vec3f(centers.at<float>(2, 0), centers.at<float>(2, 1), centers.at<float>(2, 2));
	Vec3f lastColor;
	float g1 = c1[0] * 0.3 + c1[1] * 0.59 + c1[2] * 0.11;
	float g2 = c2[0] * 0.3 + c2[1] * 0.59 + c2[2] * 0.11;
	float g3 = c3[0] * 0.3 + c3[1] * 0.59 + c3[2] * 0.11;
	float minG = MIN(MIN(g1, g2), g3);
	if (minG == g1)
	{
		lastColor = c1;
	}
	else if (minG == g2)
	{
		lastColor = c2;
	}
	else
	{
		lastColor = c3;
	}

	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			int cluster_idx = labels.at<int>(i + j * src.rows, 0);
			//比对下 是否符合要求
			if (centers.at<float>(cluster_idx, 0) == lastColor[0] && centers.at<float>(cluster_idx, 1) == lastColor[1] && centers.at<float>(cluster_idx, 2) == lastColor[2])
			{
				new_image.at<Vec3b>(i, j)[0] = 0;
				new_image.at<Vec3b>(i, j)[1] = 0;
				new_image.at<Vec3b>(i, j)[2] = 0;
				//new_image.at<Vec3b>(i, j)[0] = centers.at<float>(cluster_idx, 0);
				//new_image.at<Vec3b>(i, j)[1] = centers.at<float>(cluster_idx, 1);
				//new_image.at<Vec3b>(i, j)[2] = centers.at<float>(cluster_idx, 2);
			}
			else 
			{
				new_image.at<Vec3b>(i, j)[0] = 255;
				new_image.at<Vec3b>(i, j)[1] = 255;
				new_image.at<Vec3b>(i, j)[2] = 255;
			}
		}
	}
	src = new_image.clone();
	//printf("C1:[%.3f,%.3f,%.3f]\n", centers.at<float>(0, 0), centers.at<float>(0, 1), centers.at<float>(0, 2));
	//printf("C2:[%.3f,%.3f,%.3f]\n", centers.at<float>(1, 0), centers.at<float>(1, 1), centers.at<float>(1, 2));
	//printf("C3:[%.3f,%.3f,%.3f]\n", centers.at<float>(2, 0), centers.at<float>(2, 1), centers.at<float>(2, 2));
	//imshow("CImage", new_image);
	//waitKey(0);
}
//用于判别 转换开关中大头小柱子类型的(BlackRect)类型的结果
//辅助识别 LeftBot/RightBot/Mid(Y型)
int CheckBlackRect(cv::Mat src,bool blackFlag)
{
	//中心切割(1/3) 若差不多 比重 说明可能是Y型 否则那边重 那边就是方向所在(大圆头)
	int leftQuality = 0, rightQuality = 0;
	
	if (blackFlag)
	{
		//从上下扫描 80%部分
		int black80per = (double)src.cols*0.8;
		for (int i = 0; i < src.rows; i++)
		{
			int blackNum = 0;
			for (int j = 0; j < src.cols; j++)
			{
				if (src.at<uchar>(i, j) == 0)
				{
					blackNum++;
				}
			}
			if (blackNum > black80per)
			{
				break;
			}
			else 
			{
				for (int j = 0; j < src.cols; j++)
				{
					src.at<uchar>(i, j) = 255;					
				}
			}
		}
		for (int i = src.rows-1; i >= 0; i--)
		{
			int blackNum = 0;
			for (int j = 0; j < src.cols; j++)
			{
				if (src.at<uchar>(i, j) == 0)
				{
					blackNum++;
				}
			}
			if (blackNum > black80per)
			{
				break;
			}
			else
			{
				for (int j = 0; j < src.cols; j++)
				{
					src.at<uchar>(i, j) = 255;
				}
			}
		}
		if (saveFlag)
			imwrite("image\\cut80.jpg",src);
	}

	for (int j = 0; j < src.rows; j++)
	{
		for (int i = 0; i < src.cols / 2; i++)
		{
			if (src.at<uchar>(j, i) == 0)
			{
				leftQuality++;
			}
		}
		for (int i = src.cols / 2; i < src.cols; i++)
		{
			if (src.at<uchar>(j, i) == 0)
			{
				rightQuality++;
			}
		}
	}	

	double LRper = (double)MIN(leftQuality, rightQuality) / (double)MAX(leftQuality, rightQuality);
	if (printFlag)
		printf("BlackQuality:%.3f ->L:%d R:%d\n",LRper,leftQuality,rightQuality);
	//if (LRper > 0.9) //近似比重90 Y 可能性很大
	//{	
	//	return ConvertSwitchMid;
	//}
	//else 
	//{
	
	if (blackFlag)
	{
		if (leftQuality < rightQuality)
		{
			return ConvertSwitchLeftBot;
		}
		else
		{
			return ConvertSwitchRightBot;
		}
	}
	else 
	{
		if (leftQuality > rightQuality)
		{
			return ConvertSwitchLeftBot;
		}
		else
		{
			return ConvertSwitchRightBot;
		}
	}
	//}
}
//转换_去除小块干扰 
void CS_ridSmallInterfere(cv::Mat& finalMat,int type)
{
	//边缘检测 结合最小外包矩形
	//把原图 放到更大的一张白图上 进行检测
	vector< vector<Point> > contours;
	vector< Vec4i > hierarchy;

	//判别类别
	if (finalMat.type() == CV_8UC3)
	{
		cvtColor(finalMat, finalMat, COLOR_BGR2GRAY);
	}

	Mat whiteMat(finalMat.rows + 30 * 2, finalMat.cols + 30 * 2, finalMat.type(), Scalar::all(255));
	Mat whiteSaveMat;
	for (int i = 0; i < finalMat.rows; i++)
	{
		for (int j = 0; j < finalMat.cols; j++)
		{
			whiteMat.at<uchar>(i + 30, j + 30) = finalMat.at<uchar>(i, j);
		}
	}
	if (saveFlag)
		imwrite("image\\BigWhite.jpg", whiteMat);
	whiteSaveMat = whiteMat.clone();	

	//若是这种类型 则去除白边 不切取最大部分	
	//先高斯平滑 再膨胀一下 去除小的干扰点
	GaussianBlur(whiteMat, whiteMat, Size(3, 3), 0);
	Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
	dilate(whiteMat, whiteMat, element);
	//先边缘检测一下 再进行轮廓检测
	Canny(whiteMat, whiteMat, 128, 255);
	if (saveFlag)
		imwrite("image\\CannyMat.jpg", whiteMat);

	findContours(whiteMat, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
	int ridSize = finalMat.cols > 60 ? 30 : finalMat.cols / 2;
	if (printFlag)
		printf("去除块尺寸:%d\n", ridSize);
	//过滤 非最大块 内部的小块
	Rect maxRect;
	int maxArea = 0;
	for (int i = 0; i < contours.size(); i++)
	{
		Rect rect = boundingRect(contours[i]);
		//计算黑点数目
		int blackNumT = 0;
		for (int p = rect.y; p < rect.y + rect.height; p++)
		{
			for (int q = rect.x; q < rect.x + rect.width; q++)
			{
				if (whiteSaveMat.at<uchar>(p, q) == 0)
				{
					blackNumT++;
				}
			}
		}
		if (blackNumT > maxArea)
		{
			maxArea = blackNumT;
			maxRect = rect;
		}
	}

	for (int i = 0; i < contours.size(); i++)
	{
		Rect rect = boundingRect(contours[i]);
		//判断面积 < 30*30
		if (rect.width * rect.height < ridSize * ridSize / 3 || rect.width < 10 || rect.height < 10)
		{
			if (printFlag)
				cout << rect << endl;
			//判别下 是不是在大块的内部 不在内部再切 (起点是否再内部)
			//if (!(rect.x >= maxRect.x && rect.x < maxRect.x + maxRect.width && rect.y >= maxRect.y && rect.y < maxRect.y + maxRect.height))
			{
				for (int m = rect.y-1; m < rect.y + rect.height+1; m++)
				{
					for (int n = rect.x-1; n < rect.x + rect.width+1; n++)
					{
						whiteSaveMat.at<uchar>(m, n) = 255;
					}
				}
			}
		}
		else
		{
			char tFilename[1024];
			sprintf(tFilename, "%s%d%s", "image\\cut\\", i, ".jpg");
			Mat tempS(whiteSaveMat, rect);
			if (saveFlag)
				imwrite(tFilename, tempS);
		}
	}
	if (saveFlag)
		imwrite("image\\whiteSaveMat.jpg", whiteSaveMat);
	
	//四维切白的
	if (type == ConvertSwitchBackgroudBlackCircle)
	{		
		GaussianBlur(whiteSaveMat, whiteSaveMat, Size(3, 3), 0, 0);

		//1.四维切白		
		int checkLineTBValue = 3;//检测点的要求
		int left = 0, top = 0, right = whiteSaveMat.cols - 1, bottom = whiteSaveMat.rows - 1;
		int checkWhiteNum = 10;
		//left
		for (int m = 0; m < whiteSaveMat.cols - 1; m++)
		{
			int noWhitenum = 0;
			for (int n = 0; n < whiteSaveMat.rows; n++)
			{
				if (whiteSaveMat.at<uchar>(n, m) == 0)
				{
					noWhitenum++;
				}
			}
			if (noWhitenum > checkWhiteNum) {
				left = m; break;
			}
		}
		//right
		for (int m = whiteSaveMat.cols - 1; m > 1; m--)
		{
			int noWhitenum = 0;
			for (int n = 0; n < whiteSaveMat.rows; n++)
			{
				if (whiteSaveMat.at<uchar>(n, m) == 0)
				{
					noWhitenum++;
				}
			}
			if (noWhitenum > checkWhiteNum) {
				right = m; break;
			}
		}
		//top
		for (int m = 0; m < whiteSaveMat.rows - 1; m++)
		{
			int noWhitenum = 0;
			for (int n = 0; n < whiteSaveMat.cols; n++)
			{
				if (whiteSaveMat.at<uchar>(m, n) == 0)
				{
					noWhitenum++;
				}
			}
			if (noWhitenum > checkWhiteNum) {
				top = m; break;
			}
		}
		//bottom
		for (int m = whiteSaveMat.rows - 1; m > 1; m--)
		{
			int noWhitenum = 0;
			for (int n = 0; n < whiteSaveMat.cols; n++)
			{
				if (whiteSaveMat.at<uchar>(m, n) == 0)
				{
					noWhitenum++;
				}
			}
			if (noWhitenum > checkWhiteNum) {
				bottom = m; break;
			}
		}
		Mat tempM(whiteSaveMat, Rect(left,top,right-left,bottom-top));
		//Mat tempM(whiteSaveMat, Rect(whiteSaveMat.cols/2-40, whiteSaveMat.rows/2-40, 80,80));
		finalMat = tempM;
		if (saveFlag)
			imwrite("image\\FinalMatLast.jpg", tempM);
		return;
	}
	//最后切除最大的那块
	Mat lastMax(whiteSaveMat, maxRect);
	finalMat = lastMax.clone();		
	if (saveFlag)
		imwrite("image\\lastMax.jpg", lastMax);
	if (saveFlag)
		imwrite("image\\FinalMatLast.jpg", whiteSaveMat);
	
}
//去四维白色部分 限二值图
void GetNoWhiteBorard(cv::Mat& src, int checkNum)
{
	//1.四维切白		
	int checkLineTBValue = 3;//检测点的要求
	int left = 0, top = 0, right = src.cols - 1, bottom = src.rows - 1;
	int checkWhiteNum = checkNum;
	//left
	for (int m = 0; m < src.cols - 1; m++)
	{
		int noWhitenum = 0;
		for (int n = 0; n < src.rows; n++)
		{
			if (src.at<uchar>(n, m) == 0)
			{
				noWhitenum++;
			}
		}
		if (noWhitenum > checkWhiteNum) {
			left = m; break;
		}
	}
	//right
	for (int m = src.cols - 1; m > 1; m--)
	{
		int noWhitenum = 0;
		for (int n = 0; n < src.rows; n++)
		{
			if (src.at<uchar>(n, m) == 0)
			{
				noWhitenum++;
			}
		}
		if (noWhitenum > checkWhiteNum) {
			right = m; break;
		}
	}
	//top
	for (int m = 0; m < src.rows - 1; m++)
	{
		int noWhitenum = 0;
		for (int n = 0; n < src.cols; n++)
		{
			if (src.at<uchar>(m, n) == 0)
			{
				noWhitenum++;
			}
		}
		if (noWhitenum > checkWhiteNum) {
			top = m; break;
		}
	}
	//bottom
	for (int m = src.rows - 1; m > 1; m--)
	{
		int noWhitenum = 0;
		for (int n = 0; n < src.cols; n++)
		{
			if (src.at<uchar>(m, n) == 0)
			{
				noWhitenum++;
			}
		}
		if (noWhitenum > checkWhiteNum) {
			bottom = m; break;
		}
	}

	if (printFlag)
	{
		cout << src.size() << endl;
		printf("[%d,%d] -> [%d,%d]\n", left, top, right, bottom);
	}

	Mat tempM(src, Rect(left, top, right - left, bottom - top));
	src = tempM.clone();
}
//去四维白色部分 限二值图
void CutWhiteBorard(cv::Mat& src,int type)
{
	//1.四维切白		
	int checkLineTBValue = 3;//检测点的要求
	int left = 0, top = 0, right = src.cols - 1, bottom = src.rows - 1;
	int checkWhiteNum = 10;
	//left
	for (int m = 0; m < src.cols - 1; m++)
	{
		int noWhitenum = 0;
		for (int n = 0; n < src.rows; n++)
		{
			if (src.at<uchar>(n, m) == 0)
			{
				noWhitenum++;
			}
		}
		if (noWhitenum > checkWhiteNum) {
			left = m; break;
		}
	}
	//right
	for (int m = src.cols - 1; m > 1; m--)
	{
		int noWhitenum = 0;
		for (int n = 0; n < src.rows; n++)
		{
			if (src.at<uchar>(n, m) == 0)
			{
				noWhitenum++;
			}
		}
		if (noWhitenum > checkWhiteNum) {
			right = m; break;
		}
	}
	//top
	for (int m = 0; m < src.rows - 1; m++)
	{
		int noWhitenum = 0;
		for (int n = 0; n < src.cols; n++)
		{
			if (src.at<uchar>(m, n) == 0)
			{
				noWhitenum++;
			}
		}
		if (noWhitenum > checkWhiteNum) {
			top = m; break;
		}
	}
	//bottom
	for (int m = src.rows - 1 ; m > 1; m--)
	{
		int noWhitenum = 0;
		for (int n = 0; n < src.cols; n++)
		{
			if (src.at<uchar>(m, n) == 0)
			{
				noWhitenum++;
			}
		}
		if (noWhitenum > checkWhiteNum) {
			bottom = m; break;
		}
	}

	if (printFlag)
	{
		cout << src.size() << endl;
		printf("[%d,%d] -> [%d,%d]\n", left, top, right, bottom);
	}
	
	Mat tempM(src, Rect(left, top, right - left, bottom - top));
	if (printFlag)
	{
		cout <<" TempM Size: "<< tempM.size() << endl;
	}
	Mat tempSrc = tempM.clone();
	if (saveFlag)
		imwrite("image\\RidWhite.jpg", tempSrc);

	//膨胀腐蚀
	if (type == ConvertSwitchBackgroudBlackRect || type == ConvertSwitchBlack)
	{
		Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
		erode(tempM, tempM, element);
		dilate(tempM, tempM, element);
		if (saveFlag)
			imwrite("image\\RidWhiteDE.jpg", tempSrc);
	}
	//2.左右方向 去掉白色间隔过多的部分 取最大黑 部分 (去除阴影干扰)
	int* BWArray = (int*)malloc(sizeof(int)*tempM.cols);
	vector<int> BWSegmentV;
	for (int i = 0; i < tempM.cols; i++)
	{
		int Bnum = 0;
		for (int j = 0; j < tempM.rows; j++)
		{
			if (tempM.at<uchar>(j, i) == 0) Bnum++;
		}
		BWArray[i] = Bnum > 5 ? 1 : 0;
	}
	int firstValue = BWArray[0];
	if (firstValue == 1) BWSegmentV.push_back(0);
	for (int i = 0; i < tempM.cols; i++)
	{
		int tempValue = BWArray[i];
		if (tempValue != firstValue)
		{
			firstValue = tempValue;
			BWSegmentV.push_back(i);
		}
	}
	if (BWSegmentV.size() % 2 == 1)
	{
		BWSegmentV.push_back(tempM.cols-1);
	}
	int maxSegmentIndex = 0;
	int maxSegmentLength = 0;
	for (int i = 0; i < BWSegmentV.size() / 2; i++)
	{
		int length = BWSegmentV[i * 2 + 1] - BWSegmentV[i * 2];
		if (length > maxSegmentLength)
		{
			maxSegmentLength = length;
			maxSegmentIndex = i;
		}
	}	
	
	if (BWSegmentV.size() > 1)
	{
		Mat LRtempM ;		
		tempM.colRange(BWSegmentV[maxSegmentIndex * 2], BWSegmentV[maxSegmentIndex * 2 + 1]).copyTo(LRtempM);		
		if (saveFlag)
			imwrite("image\\LRtempM.jpg", LRtempM);
		//3.四维去除小细条 上下 <5的
		int minLineNum = 5;
		int* BWArrayLR = (int*)malloc(sizeof(int)*LRtempM.rows);
		vector<int> BWSegmentVLR;
		for (int i = 0; i < LRtempM.rows; i++)
		{
			int Bnum = 0;
			for (int j = 0; j < LRtempM.cols; j++)
			{
				if (LRtempM.at<uchar>(i, j) == 0) Bnum++;
			}
			BWArrayLR[i] = Bnum > 5 ? 1 : 0;
		}
		firstValue = BWArrayLR[0];
		if (firstValue == 1) BWSegmentVLR.push_back(0);
		for (int i = 0; i < LRtempM.rows; i++)
		{
			int tempValue = BWArrayLR[i];
			if (tempValue != firstValue)
			{
				firstValue = tempValue;
				BWSegmentVLR.push_back(i);
			}
		}
		if (BWSegmentVLR.size() % 2 == 1)
		{
			BWSegmentVLR.push_back(LRtempM.cols - 1);
		}
		maxSegmentIndex = 0;
		maxSegmentLength = 0;
		for (int i = 0; i < BWSegmentVLR.size() / 2; i++)
		{
			int length = BWSegmentVLR[i * 2 + 1] - BWSegmentVLR[i * 2];
			if (length < minLineNum)
			{
				//全白区域
				for (int m = BWSegmentVLR[i * 2]; m < BWSegmentVLR[i * 2 + 1]; m++)
				{
					for (int n = 0; n < LRtempM.cols; n++)
					{
						LRtempM.at<uchar>(m, n) = 255;
					}
				}
			}
		}
		if (saveFlag)
			imwrite("image\\NoSmallRidImage.jpg",LRtempM);
		
		CS_ridSmallInterfere(LRtempM,type);		
		if (LRtempM.rows > 0 && LRtempM.cols > 0 && type!=ConvertSwitchBackgroudBlackCircle)
		{
			//4.四维去杂块
			left = 0; top = 0; right = LRtempM.cols - 1; bottom = LRtempM.rows - 1;
			int checkLineWidthValue = (int)((double)LRtempM.cols*0.2);//检测点的要求 20%
			int checkLineHeightValue = (int)((double)LRtempM.rows*0.2);//检测点的要求 20%
			//left
			for (int m = 0; m < LRtempM.cols; m++)
			{
				int noWhitenum = 0;
				for (int n = 0; n < LRtempM.rows; n++)
				{
					if (LRtempM.at<uchar>(n, m) == 0)
					{
						noWhitenum++;
					}
				}
				if (noWhitenum > checkLineHeightValue) {
					left = m; break;
				}
			}
			//right
			for (int m = LRtempM.cols - 1; m > 1; m--)
			{
				int noWhitenum = 0;
				for (int n = 0; n < LRtempM.rows; n++)
				{
					if (LRtempM.at<uchar>(n, m) == 0)
					{
						noWhitenum++;
					}
				}
				if (noWhitenum > checkLineHeightValue) {
					right = m; break;
				}
			}
			//top
			for (int m = 0; m < LRtempM.rows; m++)
			{
				int noWhitenum = 0;
				for (int n = 0; n < LRtempM.cols; n++)
				{
					if (LRtempM.at<uchar>(m, n) == 0)
					{
						noWhitenum++;
					}
				}
				if (noWhitenum > checkLineWidthValue) {
					top = m; break;
				}
			}
			//bottom
			for (int m = LRtempM.rows - 1; m > 1; m--)
			{
				int noWhitenum = 0;
				for (int n = 0; n < LRtempM.cols; n++)
				{
					if (LRtempM.at<uchar>(m, n) == 0)
					{
						noWhitenum++;
					}
				}
				if (noWhitenum > checkLineWidthValue) {
					bottom = m; break;
				}
			}

			Mat tempLast(LRtempM, Rect(left, top, right - left, bottom - top));
			if (printFlag)
			{
				cout << LRtempM.size() << endl;
				printf("四维去杂块: [%d,%d] -> [%d,%d]\n", left, top, right, bottom);
			}
			src = tempLast.clone();
		}
		else 
		{
			src = LRtempM.clone();
		}
	}
	else 
	{
		src = tempM.clone();
	}
}
//转换开关 去边框函数
void CutRidCW(cv::Mat src, cv::Mat& dst,int type)
{
	//1.四维切白
	Mat tempM = src.clone();
	CutWhiteBorard(tempM,type);
	if (saveFlag)
		imwrite("image\\cutrid.jpg", tempM);

	//2.判别是否存在标题干扰 (若存在 则左右必定存在一段 少于30%的黑色段 >10段 在中间 左右)
	int titleLeft = 0, titleRight = 0;
	bool titleLFlag = false, titleRFlag = false;
	int checkSegmentNum = 10;
	int whiteSegmentLimit = (int)((double)tempM.rows * 0.3);
	for (int i = tempM.cols / 2; i > checkSegmentNum && i >= 0; i--)
	{
		int nowhiteNum = 0;
		for (int j = 0; j < tempM.rows; j++)
		{
			if (tempM.at<uchar>(j, i) == 0)
			{
				nowhiteNum++;
			}
		}
		if (nowhiteNum < whiteSegmentLimit)
		{
			//检测左边10段是否全符合要求			
			titleLeft = 0;
			for (int m = i - checkSegmentNum; m < i; m++)
			{
				int noTwhiteNum = 0;
				for (int j = 0; j < tempM.rows; j++)
				{
					if (tempM.at<uchar>(j, i) == 0)
					{
						noTwhiteNum++;
					}
				}
				if (noTwhiteNum < whiteSegmentLimit)
				{
					titleLeft++;
				}
			}
			if (titleLeft > (double)checkSegmentNum*0.8)
			{
				titleLFlag = true; break;
			}
		}
	}
	for (int i = tempM.cols / 2; i < tempM.cols - checkSegmentNum; i++)
	{
		int nowhiteNum = 0;
		for (int j = 0; j < tempM.rows; j++)
		{
			if (tempM.at<uchar>(j, i) == 0)
			{
				nowhiteNum++;
			}
		}
		if (nowhiteNum < whiteSegmentLimit)
		{
			//检测右边10段是否全符合要求			
			titleRight = 0;
			for (int m = i; m < i + checkSegmentNum; m++)
			{
				int noTwhiteNum = 0;
				for (int j = 0; j < tempM.rows; j++)
				{
					if (tempM.at<uchar>(j, i) == 0)
					{
						noTwhiteNum++;
					}
				}
				if (noTwhiteNum < whiteSegmentLimit)
				{
					titleRight++;
				}
			}
			if (titleRight > (double)checkSegmentNum*0.8)
			{
				titleRFlag = true; break;
			}
		}
	}

	if (titleLFlag || titleRFlag)
	{
		if (printFlag)
			printf("去除边框干扰\n");
		//3.四维切除超过60%的部分(标题) 先要判别 是否有标题
		int cutLeft = 0, cutRight = tempM.cols - 1;
		int cutTop = 0, cutBot = tempM.rows - 1;
		//因为上下段 多比较细 所以要求更小 采取块扫描方式
		int cutWidth  = (int)((double)tempM.cols*0.6);
		int cutHeight = (int)((double)tempM.rows*0.6);
		int checkPiece = 5;//5行一段 
		//先去除左右 再去除上下
		//left
		for (int m = 0; m < tempM.cols/2/checkPiece; m++)
		{
			int tempSegNum = 0;
			for (int p = m * checkPiece; p < (m + 1)*checkPiece && p < tempM.cols; p++)
			{
				int noWhitenum = 0;
				for (int n = 0; n < tempM.rows; n++)
				{
					if (tempM.at<uchar>(n, p) == 0)
					{
						noWhitenum++;
					}
				}
				if (noWhitenum < cutHeight) {
					tempSegNum++;					
				}
			}
			if (tempSegNum == checkPiece)
			{
				cutLeft = m* checkPiece; break;
			}
		}
		//right
		for (int m = tempM.cols/checkPiece - 1; m > tempM.cols /2 / checkPiece; m--)
		{
			int tempSegNum = 0;
			for (int p = m * checkPiece; p < (m + 1)*checkPiece && p < tempM.cols; p++)
			{
				int noWhitenum = 0;
				for (int n = 0; n < tempM.rows; n++)
				{
					if (tempM.at<uchar>(n, p) == 0)
					{
						noWhitenum++;
					}
				}
				if (noWhitenum < cutHeight) {
					tempSegNum++;
				}
			}
			if (tempSegNum == checkPiece)
			{
				cutRight = m* checkPiece; break;
			}
		}
		Mat cutLR;
		tempM.colRange(cutLeft, cutRight).copyTo(cutLR);
		if (saveFlag)
			imwrite("image\\cutLR.jpg", cutLR);
		//top
		for (int m = 0; m < cutLR.rows / 2 / checkPiece; m++)
		{
			int tempSegNum = 0;
			for (int p = m * checkPiece; p < (m + 1)*checkPiece && p < cutLR.rows; p++)
			{
				int noWhitenum = 0;
				for (int n = 0; n < cutLR.cols; n++)
				{
					if (cutLR.at<uchar>(p, n) == 0)
					{
						noWhitenum++;
					}
				}
				if (noWhitenum < cutWidth) {
					tempSegNum++;
				}
			}
			if (tempSegNum == checkPiece)
			{
				cutTop = m* checkPiece; break;
			}
		}
		//bottom
		for (int m = cutLR.rows / checkPiece - 1; m > cutLR.rows / 2 / checkPiece; m--)
		{
			int tempSegNum = 0;
			for (int p = m * checkPiece; p < (m + 1)*checkPiece && p < cutLR.rows; p++)
			{
				int noWhitenum = 0;
				for (int n = 0; n < cutLR.cols; n++)
				{
					if (cutLR.at<uchar>(p,n) == 0)
					{
						noWhitenum++;
					}
				}
				if (noWhitenum < cutWidth) {
					tempSegNum++;
				}
			}
			if (tempSegNum == checkPiece)
			{
				cutBot = m* checkPiece; break;
			}
		}
		Mat cutLast;
		cutLR.rowRange(cutTop, cutBot).copyTo(cutLast);
		dst = cutLast.clone();
		if (printFlag)
			printf("[%d,%d] -> [%d,%d]\n",cutLeft,cutTop,cutRight,cutBot);
		if (saveFlag)
			imwrite("image\\cutLast.jpg", cutLast);
	}
	else 
	{
		dst = tempM.clone();
	}
}
//环形切割 根据2个不同半径
void CirqueCutImage(cv::Mat& src,cv::Point center, int radius1,int radius2)
{
	int myradius1 = radius1;
	int myradius2 = radius2;
	if (center.x > 0 && center.y > 0
		&& center.x + radius1 < src.cols && center.y + radius1 < src.rows
		&& center.x + radius2 < src.cols && center.y + radius2 < src.rows
		&& radius1 < radius2 && radius1> 0
		)
	{
		
	}
	else 
	{	
		//若超过 则减少radius2
		//printf("环形切割参数错误:IS:[%d,%d] Cut:[%d,%d]:%d - %d\n",src.cols,src.rows,center.x,center.y,radius1,radius2);
		myradius2 = MIN(src.rows - center.y, src.cols - center.x);
	}

	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			Point tempP = Point(j, i);
			double CLength = CountTwoPointDistance(tempP, center);
			if (!(CLength >= myradius1 && CLength <= myradius2))
			{
				src.at<uchar>(i, j) = 255;
			}
		}
	}
}

//寻找图中是否存在一个符合要求的小三角 若有 则返回坐标点
bool SearchSmallArrow(cv::Mat src,cv::Point& resultP,bool redType = false)
{
	//面积在一定范围内 且有3条边(HoughLinesP)
	vector< vector<Point> > contours;
	vector< Vec4i > hierarchy;
	Mat whiteMat;
	Mat whiteSaveMat;
	if (src.type() != IMREAD_GRAYSCALE)
	{
		cvtColor(src, whiteMat, COLOR_BGR2GRAY);
	}
	else 
	{
		whiteMat = src.clone();
	}
	GaussianBlur(whiteMat, whiteMat, Size(3, 3), 0);	
	whiteSaveMat = whiteMat.clone();
	Canny(whiteMat, whiteMat, 128, 255);
	findContours(whiteMat, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);	
	//三角面积很小 20*8 但也不小
	for (int m = 0; m < contours.size(); m++)
	{
		Rect minR = boundingRect(contours[m]);
		int outMeaArea = minR.width*minR.height;
		double conMeaArea = contourArea(contours[m]);		
		if ( conMeaArea < 200  )
		{
			//拟合出三条直线 比对相交关系
			Mat tempM(whiteSaveMat, minR);
			//vector<Vec4i> lines;
			//HoughLinesP(tempM, lines, 1, CV_PI / 180.0, 80, 5, 30);
			vector<Point> approx;
			//构建拟合多边形
			approxPolyDP(Mat(contours[m]), approx, arcLength(Mat(contours[m]), true)*0.02, true);
			if (approx.size() >= 3)
			{
				resultP = Point(minR.x + minR.width / 2, minR.y + minR.height / 2);
				return true;
			}
			else{	}
		}
		char filename[1024];
		Mat tempM(whiteMat, minR);
		sprintf(filename, "%s%d%s", "C", m, ".jpg");
		imwrite(filename, tempM);
	}
	return false;
}
//计算圆度
double CountCircleDegree(std::vector<cv::Point> contours)
{
	//计算周长 和 面积 
	double MeasureArea = contourArea(contours);
	double Perimeter = arcLength(contours,true);
	if(printFlag)
		cout << "周长:" << Perimeter << " 面积:" << MeasureArea << endl;
	//计算R
	double R1 = Perimeter / MyPI / 2; //周长=2*Π*R
	double R2 = sqrt(MeasureArea / MyPI);//面积=Π*R*R
	double perCD = R1/R2;
	if (printFlag)
	{
		cout << "R1:" << R1 << "  R2:" << R2 << endl;
		cout << "圆度:" << perCD << endl;
	}
	return perCD;
}
//判定矩形 角度符合 且占黑比>90%
bool CheckRectShape(vector<Point> contours,cv::Mat src)
{
	vector<Point> approx;
	//构建拟合多边形
	approxPolyDP(Mat(contours), approx,arcLength(Mat(contours), true)*0.02, true);
	//判别是否有4条以上轮廓 同时 轮廓是凸包 且面积符合一定条件 20*6/3*18?
	int RectMesArea1 = 40;
	int RectMesArea2 = 220;
	double Carea = fabs(contourArea(Mat(approx)));//计算轮廓面积
	double blackPer;//轮廓的占黑比
	int myBlackNum = 0;
	//printf("轮廓面积:%.2f\n",Carea);
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			if (src.at<uchar>(i, j) < 128)
			{
				myBlackNum++;
			}
		}
	}
	blackPer = (double)myBlackNum / Carea;
	//多边形 多于3条边
	if (approx.size() >= 4 &&
		Carea > RectMesArea1 &&
		Carea < RectMesArea2 &&
		isContourConvex(Mat(approx))
		)
	{
		/*
		//判别是否角度都符合要求
		int maxRectTrueNum = 0;
		for (int j = 2; j < 5; j++)
		{
			//循环比对 每2条边的角度
			double cosine = fabs(CountThreePointAngel(approx[j % 4], approx[j - 2], approx[j - 1]));
			if (cosine > 85.0 && cosine < 95.0)
			{
				maxRectTrueNum++;
			}
		}
		//至少3条边
		if (maxRectTrueNum > 2)
		{
			return true;
		}
		else { return false; }
		*/
		return true;
	}
	else
	{
		return false;
	}	
}
//拟合直线 返回角度
bool myAngelForFitLine(cv::Mat src,double* angel)
{
	if (src.type() == IMREAD_GRAYSCALE)
	{
		cv::Vec4f line_para;
		cv::Point point0;		
		//获得拟合点
		std::vector<cv::Point> points;
		for (int i = 0; i < src.rows; i++)
		{
			for (int j = 0; j < src.cols; j++)
			{
				if (src.at<uchar>(i, j) == 0)
				{
					cv::Point tP = cv::Point(j, i);
					points.push_back(tP);
				}
			}
		}

		if (points.size() >= 2)
		{
			cv::fitLine(points, line_para, cv::DIST_L2, 0, 1e-2, 1e-2);
			//获取点斜式的点和斜率		
			point0.x = line_para[2];
			point0.y = line_para[3];
			double k = line_para[1] / line_para[0];
			//计算直线的端点(y = k(x - x0) + y0)
			cv::Point point1, point2;
			point1.x = 0;
			point1.y = k * (0 - point0.x) + point0.y;
			point2.x = 128;
			point2.y = k * (128 - point0.x) + point0.y;
			*angel = atan(k)*180.0 / MyPI;
			return true;
		}
		else 
		{
			printf("拟合点不足,无法拟合直线\n");
			return false;
		}
	}
	else 
	{
		printf("非灰度图,无法拟合\n");
		return false;
	}
}

//从固定中心 向 8个方向 做等长射线段 通过判断得到白_转换_方向 成功返回true 失败返回false
//src: 源图像
//centerP:中心点
//segmentNum:检测段数
//checkLength:检测长度
//xPox : 与圆心之间的X轴偏差量
//yPox : 与圆心之间的Y轴偏差量
//result : 存放结果的返回值
bool myWhitePassagewayRotation(cv::Mat src, cv::Point centerP,int segmentNum,int checkLength,int xPox,int yPox, int* result,bool showPrintFlag=false)
{
	//8个方向 0(180) 45(225) 90(270) 135(315)
	//方程:   y=0    y=x	 x=0     y=-x
	*result = CHECK_FAIL;
	//判别是否超界
	if (   centerP.x - checkLength >= 0 && centerP.x + checkLength < src.cols 
		&& centerP.y - checkLength >= 0 && centerP.y + checkLength < src.rows
		&& centerP.x - segmentNum / 2 >= 0 && centerP.x + segmentNum / 2 < src.cols
		&& centerP.y - segmentNum / 2 >= 0 && centerP.y + segmentNum / 2 < src.rows
		)
	{
		//存放8个方向 每段结果 0,180,45,225,90,270,135,315
		int* blackNumArray = (int*)malloc(sizeof(int)*segmentNum * 8);
		int segment2Per = segmentNum / 2;
		int blackLimit = 0;//一条多于4个点才算是有黑色

		for (int m = 0; m < segmentNum * 8; m++)
		{
			blackNumArray[m] = 0;
		}
		//0(180)
		for (int i = 0; i < checkLength; i++)
		{
			for (int m = 0; m < segmentNum; m++)
			{
				//0 x+
				Point point1 = Point(centerP.x + i + xPox, centerP.y + m - segment2Per );
				//180 x-
				Point point2 = Point(centerP.x - i - xPox, centerP.y + m - segment2Per );
				if (src.at<uchar>(point1) < 128)
				{
					blackNumArray[m] ++;
				}
				if (src.at<uchar>(point2) < 128)
				{
					blackNumArray[m+8] ++;
				}
			}
		}
		//45(225)
		for (int i = 0; i < checkLength; i++)
		{
			for (int m = 0; m < segmentNum; m++)
			{
				//45 x+ y-
				Point point1 = Point(centerP.x + i + xPox, centerP.y + m - segment2Per - i - yPox);
				//225 x- y+
				Point point2 = Point(centerP.x - i - xPox, centerP.y + m - segment2Per + i + yPox);
				if (src.at<uchar>(point1) < 128)
				{
					blackNumArray[m + 8*2] ++;
				}
				if (src.at<uchar>(point2) < 128)
				{
					blackNumArray[m + 8*3] ++;
				}
			}
		}
		//90(270)
		for (int i = 0; i < checkLength; i++)
		{
			for (int m = 0; m < segmentNum; m++)
			{
				//90 y-
				Point point1 = Point(centerP.x + m - segmentNum, centerP.y - i - yPox);
				//270 y+
				Point point2 = Point(centerP.x + m - segmentNum, centerP.y + i + yPox);
				if (src.at<uchar>(point1) < 128)
				{
					blackNumArray[m + 8*4] ++;
				}
				if (src.at<uchar>(point2) < 128)
				{
					blackNumArray[m + 8*5] ++;
				}
			}
		}
		//135(315)
		for (int i = 0; i < checkLength; i++)
		{
			for (int m = 0; m < segmentNum; m++)
			{
				//135 x- y-
				Point point1 = Point(centerP.x - i - xPox, centerP.y + m - segment2Per - i - yPox);
				//315 x+ y+
				Point point2 = Point(centerP.x + i + xPox, centerP.y + m - segment2Per + i + yPox);
				if (src.at<uchar>(point1) < 128)
				{
					blackNumArray[m + 8*6] ++;
				}
				if (src.at<uchar>(point2) < 128)
				{
					blackNumArray[m + 8*7] ++;
				}
			}
		}

		/*
		唯一情况:
		1.1 若仅有270 有全白段,其他方向均有黑色段,则说明必定中间
		1.2 若仅有0   有全白段,其他方向均有黑色段,则说明必定左下
		1.3 若仅有180 有全白段,其他方向均有黑色段,则说明必定右下
		1.4 若仅有315 有全白段,其他方向均有黑色段,则说明必定左上
		1.5 若仅有225 有全白段,其他方向均有黑色段,则说明必定左上
		1.6 若均有黑色段,则默认中间。
		其余情况(多条全白段,存在参照性)
		1.7 因为全白段的,对应段(0-180/45-225/90-270/135-315),假定是对的,那么应该没有多少黑点,因为就一个小三角,若有则对,若是一堆黑色散块,则说明非此段结果。
		1.8 若存在干扰性,多个符合要求的段,则取最大可能三角形所在段为结果。
		*/
		//先判别是否是唯一情况
		int allwhiteNum = 0;
		int minSegmentNum = 0;
		int* allwhiteArray = (int*)malloc(sizeof(int) * 8);	//记录当前方向有几段黑	
		for (int i = 0; i < 8; i++)
		{
			int tempBn = 0;
			for (int j = 0; j < segmentNum; j++)
			{
				if (blackNumArray[i * 8 + j] > blackLimit)
				{
					tempBn++;
				}
			}
			allwhiteArray[i] = tempBn;
			if (tempBn == 0)
			{				
				allwhiteNum++;
			}
		}
		if (showPrintFlag)
		{
			for (int p = 0; p < 8; p++)
			{
				printf("Segment:%d ->", p);
				for (int q = 0; q < segmentNum; q++)
				{
					printf(" %d", blackNumArray[p * 8 + q]);
				}
				printf("\n");
			}
			printf("Segnum:%d CL:%d AW:%d\n", segmentNum, checkLength, allwhiteNum);
		}
		if (allwhiteNum == 0)
		{
			//printf("8段均有黑色:\n");			
			//都有黑色块 ,默认选 中间
			*result = ConvertSwitchMid;
		}
		else if (allwhiteNum==1)
		{
			//判定是第几段符合要求
			int allwhiteIndex = 0;
			for (int i = 0; i < 8; i++)
			{
				if (allwhiteArray[i] == 0)
				{
					allwhiteIndex = i;
					break;
				}
			}
			//五种情况,其他对应相反结果
			switch (allwhiteIndex)
			{
				case 0://0度
					*result = ConvertSwitchLeftBot;
					break;
				case 1://180度
					*result = ConvertSwitchRightBot;
					break;
				case 2://45度
					*result = ConvertSwitchRight;
					break;
				case 3://225度
					*result = ConvertSwitchLeft;
					break;
				case 4://90度
					*result = ConvertSwitchMid;
					break;
				case 5://270度
					*result = ConvertSwitchMid;
					break;
				case 6://135度
					*result = ConvertSwitchLeft;
					break;
				case 7://315度
					*result = ConvertSwitchLeft;
					break;
				default:break;
			}
		}
		else 
		{
			int lastIndex = -1;
			//得到全白段 - 验证 若有全白段 那么对应段 应当是存在黑色小块的 	也就是说对应2段全白的结果是错误的
			for (int i = 0; i < 4; i++)
			{
				//留下黑白组合 判定黑的那段 数目
				if ( allwhiteArray[i * 2] > 0 && allwhiteArray[i * 2 + 1] == 0 )
				{
					//判别是否只是小污块占有段数 >3
					int blackSegmentNum = 0;
					for (int m = 0; m < segmentNum; m++)
					{
						if (blackNumArray[(i * 2) * 8 + m] > 0)
						{
							blackSegmentNum++;
						}
					}
					if (blackSegmentNum > minSegmentNum)
					{
						lastIndex = i * 2;
						break;
					}
				}
				else if( allwhiteArray[i * 2] == 0 && allwhiteArray[i * 2 + 1] > 0 ) 
				{
					//判别是否只是小污块占有段数 >3
					int blackSegmentNum = 0;
					for (int m = 0; m < segmentNum; m++)
					{
						if (blackNumArray[(i * 2 + 1) * 8 + m] > 0)
						{
							blackSegmentNum++;
						}
					}
					if (blackSegmentNum > minSegmentNum)
					{
						lastIndex = i * 2 + 1;
						break;
					}
				}
				else 
				{
					//跳过
					continue;
				}
			}
			if (lastIndex == -1)
			{
				return false;
			}
			else 
			{
				switch (lastIndex)
				{
					case 0://0度
					*result = ConvertSwitchLeftBot;
					break;
					case 1://180度
					*result = ConvertSwitchRightBot;
					break;
					case 2://45度
					*result = ConvertSwitchRight;
					break;
					case 3://225度
					*result = ConvertSwitchLeft;
					break;
					case 4://90度
					*result = ConvertSwitchMid;
					break;
					case 5://270度
					*result = ConvertSwitchMid;
					break;
					case 6://135度
					*result = ConvertSwitchLeft;
					break;
					case 7://315度
					*result = ConvertSwitchLeft;
					break;
					default:break;
				}
				return true;
			}
		}
	}
	else 
	{
		printf("白_转换_射线法 参数错误\n");
	}

	return false;
}
//从 上下 左右 1/3部分 得到可能的白色通道所在位置
void mySearchWhitePassageway(cv::Mat src,cv::Point centerP,int* result )
{
	//先判别 左右是否存在一段 中间被白色切开的黑色 (1/3) 记录白色段
	int* LeftArray   = (int*)malloc(sizeof(int)*src.rows);
	int* RightArray  = (int*)malloc(sizeof(int)*src.rows);
	int* TopArray    = (int*)malloc(sizeof(int)*src.cols);
	int* BottomArray = (int*)malloc(sizeof(int)*src.cols);
	int BlackLimit = 5;//一行超过5个点算是黑的
	int myresult = CHECK_SUCCESS;
	int myresultArray[4] = { 0,0,0,0 };

	//左右
	for (int i = 0; i < src.rows; i++)
	{
		int leftBN = 0, rightBN = 0;
		for (int m = 0; m < src.cols/3; m++)
		{
			if (src.at<uchar>(i, m) < 128)
			{
				leftBN++;
			}
		}

		for (int n = src.cols * 2 / 3; n < src.cols; n++)
		{
			if (src.at<uchar>(i, n) < 128)
			{
				rightBN++;
			}
		}

		LeftArray[i]  = leftBN < BlackLimit ? 1 : 0;
		RightArray[i] = rightBN < BlackLimit ? 1 : 0;
	}
	//上下
	for (int i = 0; i < src.cols; i++)
	{
		int topBN = 0, botBN = 0;
		for (int m = 0; m < src.rows / 3; m++)
		{
			if (src.at<uchar>(m, i) < 128)
			{
				topBN++;
			}
		}

		for (int n = src.rows * 2 / 3; n < src.rows; n++)
		{
			if (src.at<uchar>(n,i) < 128)
			{
				botBN++;
			}
		}

		TopArray[i] = topBN < BlackLimit ? 1 : 0;
		BottomArray[i] = botBN < BlackLimit ? 1 : 0;
	}

	vector<int> leftV; vector<int> rightV; vector<int> topV; vector<int> botV;
	if (LeftArray[0] == 0) leftV.push_back(0);
	if (RightArray[0] == 0) rightV.push_back(0);
	if (TopArray[0] == 0) topV.push_back(0);
	if (BottomArray[0] == 0) botV.push_back(0);

	int firstLeft = LeftArray[0], firstRight = RightArray[0],firstTop = TopArray[0],firstBottom = BottomArray[0];
	//左右分段
	for (int i = 0; i < src.rows; i++)
	{
		int nowLeft = LeftArray[i];
		int nowRight = RightArray[i];

		if (nowLeft != firstLeft)
		{
			firstLeft = nowLeft;
			leftV.push_back(i);
		}

		if (nowRight != firstRight)
		{
			firstRight = nowRight;
			rightV.push_back(i);
		}
	}

	//上下分段
	for (int i = 0; i < src.cols; i++)
	{
		int nowTop = TopArray[i];
		int nowBot = BottomArray[i];

		if (nowTop != firstTop)
		{
			firstTop = nowTop;
			topV.push_back(i);
		}

		if (nowBot != firstBottom)
		{
			firstBottom = nowBot;
			botV.push_back(i);
		}
	}

	//补差值
	if (leftV.size() % 2 == 1) leftV.push_back(src.rows - 1);
	if (rightV.size() % 2 == 1) rightV.push_back(src.rows - 1);
	if (topV.size() % 2 == 1) topV.push_back(src.cols - 1);
	if (botV.size() % 2 == 1) botV.push_back(src.cols - 1);
	//先判定左右 是否存在一段白的 在中间 且跨度在 15-30之间
	for (int i = 0; i < leftV.size() / 2; i++)
	{
		int length = leftV[i * 2 + 1] - leftV[i * 2];
		if (length > 15 && length < 30)
		{
			//判断位置关系 中心点 与 中心偏差 小于1/5
			int midY = leftV[i * 2] + length / 2;
			int centerY = src.rows / 2;
			int perY = src.rows / 10;
			if (abs(midY - centerY) <perY)
			{
				myresultArray[0] = ConvertSwitchLeftBot;
				break;
			}
		}
	}
	for (int i = 0; i < rightV.size() / 2; i++)
	{
		int length = rightV[i * 2 + 1] - rightV[i * 2];
		if (length > 15 && length < 30)
		{
			//判断位置关系 中心点 与 中心偏差 小于1/5
			int midY = rightV[i * 2] + length / 2;
			int centerY = src.rows / 2;
			int perY = src.rows / 10;
			if (abs(midY - centerY) < perY)
			{					
				myresultArray[1] = ConvertSwitchRightBot;
				break;
			}
		}
	}
	//再判定上下 是否存在一段白的 在中间 且跨度在 15-30之间
	for (int i = 0; i < topV.size() / 2; i++)
	{
		int length = topV[i * 2 + 1] - topV[i * 2];
		if (length > 15 && length < 30)
		{
			//判断位置关系 中心点 与 中心偏差 小于1/5
			int midX = topV[i * 2] + length / 2;
			int centerX = src.cols / 2;
			int perX = src.cols / 10;
			if (abs(midX - centerX) < perX)
			{
				myresultArray[2] = ConvertSwitchMid;
				break;
			}
		}
	}
	for (int i = 0; i < botV.size() / 2; i++)
	{
		int length = botV[i * 2 + 1] - botV[i * 2];
		if (length > 15 && length < 30)
		{
			//判断位置关系 中心点 与 中心偏差 小于1/5
			int midX = botV[i * 2] + length / 2;
			int centerX = src.cols / 2;
			int perX = src.cols / 10;
			if (abs(midX - centerX) < perX)
			{
				myresultArray[3] = ConvertSwitchMid;
				break;
			}
		}
	}

	//进行综合结果判定
	//一个都不符合 说明需要调节阈值
	if ( myresultArray[0] == 0 && myresultArray[1] == 0 && myresultArray[2] == 0 && myresultArray[3] == 0 )
	{
		myresult = CHECK_FAIL;
	}
	else
	{
		//左下又或者右下
		if (
			(myresultArray[0] != 0 && myresultArray[1] != 0 && myresultArray[2] == 0 && myresultArray[3] == 0) //左下右下均有
			||
			(myresultArray[0] != 0 && myresultArray[1] == 0 && myresultArray[2] == 0 && myresultArray[3] == 0) //单左下
			||
			(myresultArray[0] == 0 && myresultArray[1] != 0 && myresultArray[2] == 0 && myresultArray[3] == 0) //单右下
			)
		{
			cv::Point myP;
			bool ReB = SearchSmallArrow(src, myP);
			if (ReB)
			{
				if (myP.x < src.cols / 2)
				{
					myresult = ConvertSwitchLeftBot;
				}
				else
				{
					myresult = ConvertSwitchRightBot;
				}
			}
			else
			{
				//无小三角 判定默认左下
				myresult = ConvertSwitchLeftBot;
			}
		}
		//中间
		else if (
			(myresultArray[0] == 0 && myresultArray[1] == 0 && myresultArray[2] != 0 && myresultArray[3] != 0) //上下均有
			||
			(myresultArray[0] == 0 && myresultArray[1] == 0 && myresultArray[2] != 0 && myresultArray[3] == 0) //单上
			||
			(myresultArray[0] == 0 && myresultArray[1] == 0 && myresultArray[2] == 0 && myresultArray[3] != 0) //单下
			)
		{
			myresult = ConvertSwitchMid;
		}		
		else 
		{
			//其他的22组合 默认都有可能是 左上/右上 比较 左下1/3 与 右下1/3 那边白色多 那边就是反方向
			int leftWnum = 0, rightWnum = 0;
			for (int i = 0; i < src.rows / 3; i++)
			{
				for (int j = 0; j < src.cols / 3; j++)
				{
					if (src.at<uchar>(i, j) > 128)
					{
						leftWnum++;
					}
				}
			}
			for (int i = src.rows * 2 / 3; i < src.rows; i++)
			{
				for (int j = src.cols * 2 / 3; j < src.cols; j++)
				{
					if (src.at<uchar>(i, j) > 128)
					{
						rightWnum++;
					}
				}
			}

			if (leftWnum > rightWnum)
			{
				myresult = ConvertSwitchRight;
			}
			else 
			{
				myresult = ConvertSwitchLeft;
			}
		}
	}

	*result = myresult;
}

//判别 从 圆外 固定长度部分 是否存在全白区域
bool CheckOutSideCircle(cv::Mat src,cv::Point centerP,int inRadius,int checkLength)
{
	//也就是判别从圆心处  2个半径之间的区域 是否全白 容忍范围 <10个黑点
	//构建最小外包矩形
	int yMin = centerP.y - inRadius - checkLength;
	int yMax = centerP.y + inRadius + checkLength;
	int xMin = centerP.x - inRadius - checkLength;
	int xMax = centerP.x + inRadius + checkLength;
	int myradius1 = inRadius;
	int myradius2 = inRadius + checkLength;
	int errorNum = 0;

	//黑点数目少于 外环面积的10%
	int maxBlackNum = (int)(CV_PI*(myradius2*myradius2 - myradius1 * myradius1)*0.1);

	for (int i = yMin; i < yMax; i++)
	{
		for (int j = xMin; j < xMax; j++)
		{
			Point tempP = Point(j, i);
			double CLength = CountTwoPointDistance(tempP, centerP);
			if (CLength > myradius1 && CLength <= myradius2)
			{
				if (src.at<uchar>(tempP) < 128)
				{
					errorNum++;
				}
			}
		}
	}
	if (errorNum > maxBlackNum)
	{
		return false;
	}
	else 
	{
		return true;
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值