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;
}
}