You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iec104/src/HTTestOpencv.cpp

1849 lines
57 KiB
C++

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/****************************************************************************
** File name : HTOpencvImg.cpp
** Description : define 104 worker thread group
** Create date : 2018.09.01
** Auther by : Liuyx
** Version info : V1.0.01
** Copyrigth By: xi'an huatek, Inc Co., Ltd
** Update record:
** DATE AUTHER DESC
** -------------------------------------------------------------------------
** 2018.09.01 Liuyx first build
****************************************************************************/
#include "HTGlobal.h"
#include "HTTestOpencv.h"
using namespace cv;
static const char *_FILE_ = "HTTestOpencv.cpp";
#ifndef ERROR
#define ERROR -1
#endif
string hehe[100] = { "time_0.jpg", "time_1.jpg", "time_2.jpg", "time_3.jpg", "time_4.jpg",
"time_5.jpg", "time_6.jpg", "time_7.jpg", "time_8.jpg", "time_9.jpg", };
//-----------------------------------------------------------------------------
//平面几何相关函数http://www.cnblogs.com/zjutlitao/p/3243883.html
//-----------------------------------------------------------------------------
// file exist
bool isExistFile(const char *filename)
{
FILE *fp = NULL;
fp = fopen(filename, "rb");
if (fp == NULL) return false;
fclose(fp);
return true;
}
////求向量的夹角
//double Angle(Point A, Point B)
//{
// return acos(Dot(A, B) / Length(A) / Length(B));
//}
//
////点到直线的距离
//double DistanceToLine(Point P, Point A, Point B)
//{
// Point v1 = B - A, v2 = P - A;
// return fabs(Cross(v1, v2)) / Length(v1);//如果不加绝对值是带有方向的距离
//}
//度数转换
double DegreeTrans(double theta)
{
double res = theta / CV_PI * 180;
return res;
}
//逆时针旋转图像degree角度原尺寸
void rotateImage(Mat src, Mat& img_rotate, double degree)
{
//旋转中心为图像中心
Point2f center;
center.x = float(src.cols / 2.0);
center.y = float(src.rows / 2.0);
int length = 0;
length = (int)sqrt(src.cols*src.cols + src.rows*src.rows);
//计算二维旋转的仿射变换矩阵
Mat M = getRotationMatrix2D(center, degree, 1);
warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色
showImg("旋转后:", img_rotate);
}
//通过霍夫变换计算角度
//
double CalcDegree(const Mat &srcImage, Mat &dst)
{
Mat midImage, dstImage;
Canny(srcImage, midImage, 50, 200, 3);
cvtColor(midImage, dstImage, CV_GRAY2BGR);
//通过霍夫变换检测直线
vector<Vec2f> lines;
HoughLines(midImage, lines, 1, CV_PI / 180, 300, 0, 0);//第5个参数就是阈值阈值越大检测精度越高
//cout << lines.size() << endl;
//由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢
//所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。
if (!lines.size())
{
HoughLines(midImage, lines, 1, CV_PI / 180, 200, 0, 0);
}
//cout << lines.size() << endl;
if (!lines.size())
{
HoughLines(midImage, lines, 1, CV_PI / 180, 150, 0, 0);
}
//cout << lines.size() << endl;
if (!lines.size())
{
cout << "没有检测到直线!" << endl;
return -1;
}
float sum = 0;
//依次画出每条线段
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0];
float theta = lines[i][1];
Point pt1, pt2;
//cout << theta << endl;
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));
//只选角度最小的作为旋转角度
sum += theta;
line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, CV_AA); //Scalar函数用于调节线段颜色
showImg("直线探测效果图", dstImage);
}
float average = sum / lines.size(); //对所有角度求平均,这样做旋转效果会更好
cout << "average theta:" << average << endl;
double angle = DegreeTrans(average) - 90;
rotateImage(dstImage, dst, angle);
showImg("直线探测效果图2", dstImage);
return angle;
}
// 图片矫正
void ImageRecify(const char* pInFileName)
{
double degree;
Mat src = imread(pInFileName);
showImg("原始图", src);
int srcWidth, srcHight;
srcWidth = src.cols;
srcHight = src.rows;
cout << srcWidth << " " << srcHight << endl;
Mat dst;
src.copyTo(dst);
//倾斜角度矫正
degree = CalcDegree(src, dst);
if (degree == ERROR)
{
cout << "矫正失败!" << endl;
return;
}
rotateImage(src, dst, degree);
cout << "angle:" << degree << endl;
showImg("旋转调整后", dst);
Mat resulyImage = dst(Rect(0, 0, srcWidth, srcHight));
showImg("裁剪之后", resulyImage);
//imwrite("recified.jpg", resulyImage);
}
/****************倾斜校正子程序*****************/
//函数名称IplImage *Rotate(IplImage *RowImage
//功能:对每行数字进行倾斜校正
//入口参数行图像RowImage
//出口参数旋转后的图像RotateRow
//https://blog.csdn.net/ZhtSunday/article/details/52094745
/********************************************/
void ImgRotate(const char *img_file)
{
IplImage * RowImage = cvLoadImage(img_file);
//建立储存边缘检测结果图像canImage
IplImage *canImage = cvCreateImage(cvGetSize(RowImage), IPL_DEPTH_8U, 1);
//进行边缘检测
cvCanny(RowImage, canImage, 30, 200, 3);
//进行hough变换
CvMemStorage *storage = cvCreateMemStorage();
CvSeq *lines = NULL;
lines = cvHoughLines2(canImage, storage, CV_HOUGH_STANDARD, 1, CV_PI / 180, 20, 0, 0);
//统计与竖直夹角<30度的直线个数以及其夹角和
int numLine = 0;
float sumAng = 0.0;
for (int i = 0; i < lines->total; i++)
{
float *line = (float *)cvGetSeqElem(lines, i);
float theta = line[1]; //获取角度 为弧度制
if (theta < 30 * CV_PI / 180 || (CV_PI - theta) < 30 * CV_PI / 180)
{
numLine++;
sumAng = sumAng + theta;
}
}
//计算出平均倾斜角anAng为角度制
double avAng = ((sumAng / numLine) * 180) / CV_PI;
//获取二维旋转的仿射变换矩阵
CvPoint2D32f center;
center.x = float(RowImage->width / 2.0);
center.y = float(RowImage->height / 2.0);
float m[6];
CvMat M = cvMat(2, 3, CV_32F, m);
cv2DRotationMatrix(center, avAng, 1, &M);
//建立输出图像RotateRow
double a = sin(sumAng / numLine);
double b = cos(sumAng / numLine);
int width_rotate = int(RowImage->height*fabs(a) + RowImage->width*fabs(b));
int height_rotate = int(RowImage->width*fabs(a) + RowImage->height*fabs(b));
IplImage *RotateRow = cvCreateImage(cvSize(width_rotate, height_rotate), IPL_DEPTH_8U, 1);
//变换图像,并用黑色填充其余值
m[2] += (width_rotate - RowImage->width) / 2;
m[5] += (height_rotate - RowImage->height) / 2;
cvWarpAffine(RowImage, RotateRow, &M, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); // 这有异常报出
//释放
cvReleaseImage(&canImage);
cvReleaseMemStorage(&storage);
cvNamedWindow("Roter:");
cvShowImage("Roter", RotateRow);
return; // RotateRow;
}
//旋转图像内容不变,尺寸相应变大
//https ://blog.csdn.net/xiaowei_cqu/article/details/7616044
IplImage* rotateImage1(IplImage* img, int degree) {
double angle = degree * CV_PI / 180.; // 弧度
double a = sin(angle), b = cos(angle);
int width = img->width;
int height = img->height;
int width_rotate = int(height * fabs(a) + width * fabs(b));
int height_rotate = int(width * fabs(a) + height * fabs(b));
//旋转数组map
// [ m0 m1 m2 ] ===> [ A11 A12 b1 ]
// [ m3 m4 m5 ] ===> [ A21 A22 b2 ]
float map[6];
CvMat map_matrix = cvMat(2, 3, CV_32F, map);
// 旋转中心
CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);
cv2DRotationMatrix(center, degree, 1.0, &map_matrix);
map[2] += (width_rotate - width) / 2;
map[5] += (height_rotate - height) / 2;
IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3);
//对图像做仿射变换
//CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。
//如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval.
//CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换,
cvWarpAffine(img, img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0));
return img_rotate;
}
//旋转图像内容不变,尺寸相应变大
//https ://blog.csdn.net/xiaowei_cqu/article/details/7616044
IplImage* rotateImage2(IplImage* img, int degree)
{
double angle = degree * CV_PI / 180.;
double a = sin(angle), b = cos(angle);
int width = img->width, height = img->height;
//旋转后的新图尺寸
int width_rotate = int(height * fabs(a) + width * fabs(b));
int height_rotate = int(width * fabs(a) + height * fabs(b));
IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels);
cvZero(img_rotate);
//保证原图可以任意角度旋转的最小尺寸
int tempLength = (int)(sqrt((double)width * width + (double)height *height) + 10);
int tempX = (tempLength + 1) / 2 - width / 2;
int tempY = (tempLength + 1) / 2 - height / 2;
IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels);
cvZero(temp);
//将原图复制到临时图像tmp中心
cvSetImageROI(temp, cvRect(tempX, tempY, width, height));
cvCopy(img, temp, NULL);
cvResetImageROI(temp);
//旋转数组map
// [ m0 m1 m2 ] ===> [ A11 A12 b1 ]
// [ m3 m4 m5 ] ===> [ A21 A22 b2 ]
double m[6];
int w = temp->width;
int h = temp->height;
m[0] = b;
m[1] = a;
m[3] = -m[1];
m[4] = m[0];
// 将旋转中心移至图像中间
m[2] = w * 0.5f;
m[5] = h * 0.5f;
CvMat M = cvMat(2, 3, CV_32F, m);
cvGetQuadrangleSubPix(temp, img_rotate, &M);
cvReleaseImage(&temp);
return img_rotate;
}
// 仿照matlab自适应求高低两个门限
// https://blog.csdn.net/debug__boy/article/details/8179730
void _AdaptiveFindThreshold(CvMat *dx, CvMat *dy, double *low, double *high)
{
CvSize size;
IplImage *imge = 0;
int i, j;
CvHistogram *hist;
int hist_size = 255;
float range_0[] = { 0, 256 };
float* ranges[] = { range_0 };
double PercentOfPixelsNotEdges = 0.7;
size = cvGetSize(dx);
imge = cvCreateImage(size, IPL_DEPTH_32F, 1);
// 计算边缘的强度, 并存于图像中
float maxv = 0;
for (i = 0; i < size.height; i++)
{
const short* _dx = (short*)(dx->data.ptr + dx->step*i);
const short* _dy = (short*)(dy->data.ptr + dy->step*i);
float* _image = (float *)(imge->imageData + imge->widthStep*i);
for (j = 0; j < size.width; j++)
{
_image[j] = (float)(abs(_dx[j]) + abs(_dy[j]));
maxv = maxv < _image[j] ? _image[j] : maxv;
}
}
if (maxv == 0) {
*high = 0;
*low = 0;
cvReleaseImage(&imge);
return;
}
// 计算直方图
range_0[1] = maxv;
hist_size = (int)(hist_size > maxv ? maxv : hist_size);
hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1);
cvCalcHist(&imge, hist, 0, NULL);
int total = (int)(size.height * size.width * PercentOfPixelsNotEdges);
float sum = 0;
int icount = hist->mat.dim[0].size;
float *h = (float*)cvPtr1D(hist->bins, 0);
for (i = 0; i < icount; i++)
{
sum += h[i];
if (sum > total)
break;
}
// 计算高低门限
*high = (i + 1) * maxv / hist_size;
*low = *high * 0.4;
cvReleaseImage(&imge);
cvReleaseHist(&hist);
}
// 根据图片,自动获取边缘高低阈值
// https://blog.csdn.net/debug__boy/article/details/8179730
//void AdaptiveFindThreshold(const CvArr* image, double *low, double *high, int aperture_size)
void AdaptiveFindThreshold(cv::Mat& image, double *low, double *high, int aperture_size)
{
//cv::Mat src = cv::cvarrToMat(image);
const int cn = image.channels();
cv::Mat dx(image.rows, image.cols, CV_16SC(cn));
cv::Mat dy(image.rows, image.cols, CV_16SC(cn));
cv::Sobel(image, dx, CV_16S, 1, 0, aperture_size, 1, 0, cv::BORDER_REPLICATE);
cv::Sobel(image, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLICATE);
CvMat _dx = dx, _dy = dy;
_AdaptiveFindThreshold(&_dx, &_dy, low, high);
}
// 识别仪表盘读数
double dReadPointerParserImg(const char *img_file)
{
int i = 0;
if (!img_file && strlen(img_file) <= 0) {
return -1;
}
//src-->gray-->dst(src为原图gaus是经过高斯模糊平滑后的图gray是gaus经canny提取边缘的图
//用于下面霍夫变换dst是最终结果图要在gray灰度图的基础上变为彩色图才能呈现画线效果
Mat src, gray, dst, srcImage;
src = imread(img_file); //读取图片到mat
// showImg("原始图", src);
srcImage = src;
/*
// 霍夫圆变换
cvtColor(srcImage, midImage, CV_BGR2GRAY); // 转为灰度图
GaussianBlur(midImage, midImage, Size(9, 9), 2, 2); // 模糊
vector<Vec3f> circles;
HoughCircles(midImage, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0);
printf("HoughCircles return resault == %d\n", circles.size());
*/
// 霍夫圆变换
cvtColor(src, gray, CV_BGR2GRAY); //转为单通道的灰度图
GaussianBlur(gray, gray, Size(9, 9), 2, 2); // 高斯模糊平滑
//储存检测圆的容器
std::vector<Vec3f> circles;
//调用Hough变换检测圆
//参数为待检测图像gray检测结果circles检测方法这个参数唯一,累加器的分辨率2
//两个圆间的距离50canny门限的上限下限自动设为上限的一半圆心所需要的最小的投票数最大和最小半径
HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0);
printf("HoughCircles return resault == %d\n", (int)circles.size());
if (circles.size() <= 0) {
printf("HoughCircles return resault == %d\n", (int)circles.size());
return -1;
}
//找出圆盘(因为最大的不一定是的,所以加了几个限制条件)
int pos = 0;
int max = -1;
for (size_t i = 0; i < circles.size(); i++)
{
Vec3f f = circles[i];
if (f[2] > max && f[0] + f[2] < gray.rows && f[0] - f[2] >= 0 && f[1] + f[2] < gray.cols && f[1] - f[2]>0)
{
max = (int)f[2];
pos = i;
}
}
//Point center;
//if (circles.size() > 0) {
// 找到圆心
Point center((int)circles[pos][0], (int)circles[pos][1]);
// 找到的半径
int radius = (int)circles[pos][2];
// 绘制圆心
circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0);
// 绘制圆轮廓
circle(src, center, radius, Scalar(255), 2);
//}
// 效果图
//showImg("霍夫圆变换", src);
/*******************************************************************************/
// 霍夫线变换
Canny(src, gray, 100, 300, 3); // 边缘检测
cvtColor(gray, dst, CV_GRAY2BGR); // 转为RGB图
vector<Vec4i> lines;
// 检测直线最小投票为100线条不短于50间隙不小于10
HoughLinesP(gray, lines, 1, CV_PI / 180, 100, 50, 10);
printf("HoughLinesP return resault == %d\n", (int)lines.size());
list<MyLine> list_MyLine;
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
Point A(l[0], l[1]), B(l[2], l[3]);
if (DistancetoSegment(center, A, B) < 30)//根据圆心到指针的距离阈值滤掉其他线段
{
bool down = (A.y + B.y - 2 * center.y > 0);//判断长的在过圆心的水平线上部还是下部
if (A.x == B.x) //斜率为无穷的情况
{
list_MyLine.push_back(MyLine(i, 90 + (down ? 180 : 0), (int)Length(Point(A.x - B.x, A.y - B.y))));
}
else if (A.y == B.y) //水平的情况
{
list_MyLine.push_back(MyLine(i, A.x + B.x - 2 * center.x > 0 ? 0 : 180, (int)Length(Point(A.x - B.x, A.y - B.y))));
}
else
{
if (down) {
if (A.y > center.y)
list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y))));
else
list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y))));
}
else {
if (A.y < center.y)
list_MyLine.push_back(MyLine(i, abs((int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y))));
else
list_MyLine.push_back(MyLine(i, abs((int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y))));
}
}
//line(dst, A, B, Scalar(0, 0, i * 20 + 40), 2, CV_AA);
line(dst, A, B, Scalar(186, 88, 255), 1, CV_AA);
}
}
//根据角度区分所属指针
int now_k, pre_k = 720;//当前线段的角度和前一个线段的角度
int num = 0;//指针数可能为2或3
int Du[3] = { 0 };//3个指针的度数每组的平均
int Le[3] = { 0 };//Le[i]=Le_ping[i]*0.2+le_max[i]*0.8;
int Le_ping[3] = { 0 };//3个指针的长度每组的平均
int Le_max[3] = { 0 };//3个指针的长度每组区最大的
int t_num = 0;//每组的数量(求平均用)
MyLine now_Line;
list_MyLine.push_back(MyLine(99, 888, 0));//向其中插入一个右边界这样方便处理
list_MyLine.sort();
while (!list_MyLine.empty())
{
now_Line = list_MyLine.front();
now_k = now_Line.k;
if (abs(now_k - pre_k) > 10)//两个角度之差小于10°的算是同一类
{
if (num != 0) //对本组的度数和长度求平均
{
Du[num - 1] /= t_num;
Le_ping[num - 1] /= t_num;
Le[num - 1] = (int)(Le_ping[num - 1] * 0.2 + Le_max[num - 1] * 0.8);
}
if (now_k == 888) break;//右边界直接跳出
t_num = 0;//重新统计下一组
num++;//组数增加1
cout << "---------------------------\n";//输出分割线
}
t_num++;//组内多一条线
Du[num - 1] += now_Line.k;
Le_ping[num - 1] += now_Line.l;
if (now_Line.l > Le_max[num - 1]) Le_max[num - 1] = now_Line.l;
now_Line.print();
list_MyLine.pop_front();
pre_k = now_k;
}
cout << "---------------------------\n\n";
cout << "---------------------------\n";
int t;
for (int i = 0; i < num - 1; i++)
{
for (int j = i + 1; j < num; j++)
{
if (Le[i] > Le[j])
{
t = Le[i], Le[i] = Le[j], Le[j] = t;
t = Du[i], Du[i] = Du[j], Du[j] = t;
}//if end
}//for end
}//for end
char s[3][10] = { "point1:", "point2:", "point3:" };
for (int i = 0; i < num; i++)
printf("%s k: %3d° l: %3d\n", s[i], Du[i], Le[i]);
cout << "---------------------------\n";
if (num == 1)
printf("read is: %5.2f\n", (float)abs(((360 - Du[0] + 90) % 360 / 30)));
if (num == 2)
printf("read is: %5.2f %5.2f\n", (float)((360 - Du[0] + 90) % 360 / 30), (float)((360 - Du[1] + 90) % 360 / 6));
else if (num == 3)
printf("read is: %5.2f %5.2f %5.2f\n", (float)((360 - Du[0] + 90) % 360 / 30), (float)((360 - Du[1] + 90) % 360 / 6), (float)((360 - Du[2] + 90) % 360 / 6));
cout << "---------------------------\n";
showImg("src", src);
showImg("dst", dst);
return 0;
}
// 霍夫圆、线变换
double dReadValue(const char *img_file)
{
Mat srcImage = imread(img_file);
Mat midImage, dstImage;
// 显示原始图
//showImg("原始图", srcImage);
// 霍夫圆变换
cvtColor(srcImage, midImage, CV_BGR2GRAY); // 转为灰度图
GaussianBlur(midImage, dstImage, Size(9, 9), 2, 2); // 模糊
vector<Vec3f> circles;
HoughCircles(midImage, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0);
printf("HoughCircles return resault == %d\n", (int)circles.size());
// 找出圆盘
int pos = 0;
int max = -1;
for (size_t i = 0; i < circles.size(); i++)
{
Vec3f f = circles[i];
if (f[2] > max && f[0] + f[2] < midImage.rows && f[0] - f[2] >= 0 && f[1] + f[2] < midImage.cols && f[1] - f[2] > 0)
{
max = (int)f[2];
pos = i;
}
}
if (circles.size() > 0)
{
// 找到圆心
Point center((int)circles[pos][0], (int)circles[pos][1]);
// 找到的半径
int radius = (int)circles[pos][2];
// 绘制圆心
circle(srcImage, center, 3, Scalar(0, 255, 0), -1, 8, 0);
// 绘制圆轮廓
circle(srcImage, center, radius, Scalar(255), 2);
}
// 效果图
//showImg("霍夫圆变换", srcImage);
// 霍夫线变换
Canny(srcImage, midImage, 100, 300, 3); // 边缘检测
cvtColor(midImage, dstImage, CV_GRAY2BGR); // 转为灰度图
vector<Vec4i> lines;
// 检测直线最小投票为100线条不短于50间隙不小于10
HoughLinesP(midImage, lines, 1, CV_PI / 180, 100, 50, 10);
printf("HoughLinesP return resault == %d\n", (int)lines.size());
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
Point pt1(l[0], l[1]);
Point pt2(l[2], l[3]);
line(dstImage, pt1, pt2, Scalar(186, 88, 255), 1, CV_AA);
}
// showImg("边缘检测图", midImage);
//showImg("霍夫线变换", dstImage);
return 0;
}
//******************双阈值处理*************************
//第一个参数imageInput输入和输出的的Sobel梯度幅值图像
//第二个参数lowThreshold是低阈值
//第三个参数highThreshold是高阈值
// 指定一个低阈值A一个高阈值B一般取B为图像整体灰度级分布的70%且B为1.5到2倍大小的A
// 灰度值大于B的置为255灰度值小于A的置为0
//******************************************************
void DoubleThreshold(Mat &imageIput, double lowThreshold, double highThreshold)
{
for (int i = 0; i < imageIput.rows; i++)
{
for (int j = 0; j < imageIput.cols; j++)
{
//printf("[%d:%d] = %d\n", i, j, imageIput.at<uchar>(i, j));
if (imageIput.at<uchar>(i, j) > highThreshold)
{
imageIput.at<uchar>(i, j) = 255;
}
if (imageIput.at<uchar>(i, j) < lowThreshold)
{
imageIput.at<uchar>(i, j) = 0;
}
}
}
}
/*
生成高斯卷积核 kernel
*/
void Gaussian_kernel(int kernel_size, int sigma, Mat &kernel)
{
double pi = 3.1415926f;
int m = kernel_size / 2;
kernel = Mat(kernel_size, kernel_size, CV_32FC1);
float s = (float)(2 * sigma*sigma);
for (int i = 0; i < kernel_size; i++)
{
for (int j = 0; j < kernel_size; j++)
{
int x = i - m;
int y = j - m;
double w = exp(-(x*x + y * y) / s);
kernel.at<int>(i, j) = (int)(w / (pi*s));
}
}
}
/*
计算梯度值和方向
imageSource 原始灰度图
imageX X方向梯度图像
imageY Y方向梯度图像
gradXY 该点的梯度幅值
pointDirection 梯度方向角度
*/
void GradDirection(const Mat imageSource, Mat &imageX, Mat &imageY, Mat &gradXY, Mat &theta)
{
imageX = Mat::zeros(imageSource.size(), CV_32SC1);
imageY = Mat::zeros(imageSource.size(), CV_32SC1);
gradXY = Mat::zeros(imageSource.size(), CV_32SC1);
theta = Mat::zeros(imageSource.size(), CV_32SC1);
int rows = imageSource.rows;
int cols = imageSource.cols;
int stepXY = imageX.step;
int step = imageSource.step;
/*
Mat.step参数指图像的一行实际占用的内存长度
因为opencv中的图像会对每行的长度自动补齐8的倍数
编程时尽量使用指针指针读写像素是速度最快的使用at函数最慢。
*/
uchar *PX = imageX.data;
uchar *PY = imageY.data;
uchar *P = imageSource.data;
uchar *XY = gradXY.data;
for (int i = 1; i < rows - 1; i++)
{
for (int j = 1; j < cols - 1; j++)
{
int a00 = P[(i - 1)*step + j - 1];
int a01 = P[(i - 1)*step + j];
int a02 = P[(i - 1)*step + j + 1];
int a10 = P[i*step + j - 1];
int a11 = P[i*step + j];
int a12 = P[i*step + j + 1];
int a20 = P[(i + 1)*step + j - 1];
int a21 = P[(i + 1)*step + j];
int a22 = P[(i + 1)*step + j + 1];
double gradY = double(a02 + 2 * a12 + a22 - a00 - 2 * a10 - a20);
double gradX = double(a00 + 2 * a01 + a02 - a20 - 2 * a21 - a22);
//PX[i*stepXY + j*(stepXY / step)] = abs(gradX);
//PY[i*stepXY + j*(stepXY / step)] = abs(gradY);
imageX.at<int>(i, j) = (int)abs(gradX);
imageY.at<int>(i, j) = (int)abs(gradY);
if (gradX == 0)
{
gradX = 0.000000000001;
}
theta.at<int>(i, j) = (int)(atan(gradY / gradX)*57.3);
theta.at<int>(i, j) = (theta.at<int>(i, j) + 360) % 360;
gradXY.at<int>(i, j) = (int)sqrt(gradX*gradX + gradY * gradY);
//XY[i*stepXY + j*(stepXY / step)] = sqrt(gradX*gradX + gradY*gradY);
}
}
convertScaleAbs(imageX, imageX);
convertScaleAbs(imageY, imageY);
convertScaleAbs(gradXY, gradXY);
}
/*
局部非极大值抑制
沿着该点梯度方向,比较前后两个点的幅值大小,若该点大于前后两点,则保留,
若该点小于前后两点任意一点则置为0
imageInput 输入得到梯度图像
imageOutput 输出的非极大值抑制图像
theta 每个像素点的梯度方向角度
imageX X方向梯度
imageY Y方向梯度
*/
void NonLocalMaxValue(const Mat imageInput, Mat &imageOutput, const Mat &theta, const Mat &imageX, const Mat &imageY)
{
imageOutput = imageInput.clone();
int cols = imageInput.cols;
int rows = imageInput.rows;
for (int i = 1; i < rows - 1; i++)
{
for (int j = 1; j < cols - 1; j++)
{
if (0 == imageInput.at<uchar>(i, j))continue;
int g00 = imageInput.at<uchar>(i - 1, j - 1);
int g01 = imageInput.at<uchar>(i - 1, j);
int g02 = imageInput.at<uchar>(i - 1, j + 1);
int g10 = imageInput.at<uchar>(i, j - 1);
int g11 = imageInput.at<uchar>(i, j);
int g12 = imageInput.at<uchar>(i, j + 1);
int g20 = imageInput.at<uchar>(i + 1, j - 1);
int g21 = imageInput.at<uchar>(i + 1, j);
int g22 = imageInput.at<uchar>(i + 1, j + 1);
int direction = theta.at<int>(i, j); //该点梯度的角度值
int g1 = 0;
int g2 = 0;
int g3 = 0;
int g4 = 0;
double tmp1 = 0.0; //保存亚像素点插值得到的灰度数
double tmp2 = 0.0;
double weight = fabs((double)imageY.at<uchar>(i, j) / (double)imageX.at<uchar>(i, j));
if (weight == 0)weight = 0.0000001;
if (weight > 1)
{
weight = 1 / weight;
}
if ((0 <= direction && direction < 45) || 180 <= direction && direction < 225)
{
tmp1 = g10 * (1 - weight) + g20 * (weight);
tmp2 = g02 * (weight)+g12 * (1 - weight);
}
if ((45 <= direction && direction < 90) || 225 <= direction && direction < 270)
{
tmp1 = g01 * (1 - weight) + g02 * (weight);
tmp2 = g20 * (weight)+g21 * (1 - weight);
}
if ((90 <= direction && direction < 135) || 270 <= direction && direction < 315)
{
tmp1 = g00 * (weight)+g01 * (1 - weight);
tmp2 = g21 * (1 - weight) + g22 * (weight);
}
if ((135 <= direction && direction < 180) || 315 <= direction && direction < 360)
{
tmp1 = g00 * (weight)+g10 * (1 - weight);
tmp2 = g12 * (1 - weight) + g22 * (weight);
}
if (imageInput.at<uchar>(i, j) < tmp1 || imageInput.at<uchar>(i, j) < tmp2)
{
imageOutput.at<uchar>(i, j) = 0;
}
}
}
}
/*
连接处理:
灰度值介于A和B之间的考察该像素点临近的8像素是否有灰度值为255的
若没有255的表示这是一个孤立的局部极大值点予以排除置为0
若有255的表示这是一个跟其他边缘有“接壤”的可造之材置为255
之后重复执行该步骤,直到考察完之后一个像素点。
其中的邻域跟踪算法从值为255的像素点出发找到周围满足要求的点把满足要求的点设置为255
然后修改i,j的坐标值i,j值进行回退在改变后的i,j基础上继续寻找255周围满足要求的点。
当所有连接255的点修改完后再把所有上面所说的局部极大值点置为0算法可以继续优化
参数1imageInput输入和输出的梯度图像
参数2lowTh:低阈值
参数3highTh:高阈值
*/
void DoubleThresholdLink(Mat &imageInput, double lowTh, double highTh)
{
int cols = imageInput.cols;
int rows = imageInput.rows;
for (int i = 1; i < rows - 1; i++)
{
for (int j = 1; j < cols - 1; j++)
{
double pix = imageInput.at<uchar>(i, j);
if (pix != 255)continue;
bool change = false;
for (int k = -1; k <= 1; k++)
{
for (int u = -1; u <= 1; u++)
{
if (k == 0 && u == 0)continue;
double temp = imageInput.at<uchar>(i + k, j + u);
if (temp >= lowTh && temp <= highTh)
{
imageInput.at<uchar>(i + k, j + u) = 255;
change = true;
}
}
}
if (change)
{
if (i > 1)i--;
if (j > 2)j -= 2;
}
}
}
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
if (imageInput.at<uchar>(i, j) != 255)
{
imageInput.at<uchar>(i, j) = 0;
}
}
}
}
/*
Canny边缘检测主要包括
图像的灰度化;
图像的高斯滤波,来平滑图像,同时消除和降低图像噪声的影响;
计算出每一个像素点位置的梯度X方向梯度、Y方向梯度、已经该点的梯度幅值和方向角度Y方向和X方向梯度的比值得出梯度方向X梯度的平方和+Y梯度的平方和的值再进行求平方得到该点的梯度幅值。Sobel算子等
局部非极大值抑制处理梯度方向垂直于边缘方向在梯度方向上进行非极大值抑制可以细化边缘在梯度方向上比较该点前后两个点的梯度的大小如果大于两个点则保留小于任意一个点则置为0。
双阈值处理和连接处理指定高低阈值然后高阈值直接赋值为255低阈值为0中间的值进行连接处理。如果中间的值八邻域内有255则该值也变为255也就是说255往周围进行扩张收集边缘加闭合边缘。
*/
int HTCanny(char *filename)
{
Mat image = imread(filename, 0);
showImg("origin image", image);
//转换为灰度图
Mat grayImage;
//cvtColor(image, grayImage, CV_RGB2GRAY);
cvtColor(image, grayImage, CV_GRAY2BGR);
imshow("灰度图", grayImage);
//计算XY方向梯度
Mat imageX, imageY, imageXY;
Mat theta;
GradDirection(grayImage, imageX, imageY, imageXY, theta);
imshow("计算XY方向梯度", imageXY);
//对梯度幅值进行非极大值抑制
Mat localImage;
NonLocalMaxValue(imageXY, localImage, theta, imageX, imageY);
imshow("对梯度幅值进行非极大值抑制", localImage);
//双阈值算法检测和边缘连接
DoubleThreshold(localImage, 30, 100);
DoubleThresholdLink(localImage, 30, 100);
imshow("双阈值算法检测和边缘连接", localImage);
Mat temMat;
vector<Vec4i> lines2;
Canny(image, temMat, 30, 100);
HoughLinesP(temMat, lines2, 1, CV_PI / 180, 50, 30, 10);
printf("lines2 = %d\n", (int)lines2.size());
for (size_t i = 0; i < lines2.size(); i++)
{
Vec4i l = lines2[i];
Point A(l[0], l[1]), B(l[2], l[3]);
line(temMat, A, B, Scalar(255, 0, 0), 2, CV_AA);
}
imshow("线条监测", temMat);
waitKey(0);
return 0;
}
/************************************************************************/
//////////////////////////////////////////////////////////////////
//函数功能用向量来做COSα=两向量之积/两向量模的乘积求两条线段夹角
//输入: 线段3个点坐标pt1,pt2,pt0,最后一个参数为公共点
//输出: 线段夹角,单位为角度
//////////////////////////////////////////////////////////////////
double angle(CvPoint* pt1, CvPoint* pt2, CvPoint* pt0)
{
double dx1 = pt1->x - pt0->x;
double dy1 = pt1->y - pt0->y;
double dx2 = pt2->x - pt0->x;
double dy2 = pt2->y - pt0->y;
double angle_line = (dx1*dx2 + dy1 * dy2) / sqrt((dx1*dx1 + dy1 * dy1)*(dx2*dx2 + dy2 * dy2) + 1e-10);//余弦值
return acos(angle_line) * 180 / 3.141592653;
}
//////////////////////////////////////////////////////////////////
//函数功能:采用多边形检测,通过约束条件寻找矩形
//输入: img 原图像
// storage 存储
// minareamaxarea 检测矩形的最小/最大面积
// minangle,maxangle 检测矩形边夹角范围,单位为角度
//输出: 矩形序列
//////////////////////////////////////////////////////////////////
CvSeq* findSquares4(IplImage* img, CvMemStorage* storage, int minarea, int maxarea, int minangle, int maxangle, int(&temp)[30])
{
CvSeq* contours;//边缘
int N = 6; //阈值分级
CvSize sz = cvSize(img->width & -2, img->height & -2);
IplImage* timg = cvCloneImage(img);//拷贝一次img
IplImage* gray = cvCreateImage(sz, 8, 1); //img灰度图
IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3); //金字塔滤波3通道图像中间变量
IplImage* tgray = cvCreateImage(sz, 8, 1);;
CvSeq* result;
double s, t;
int sk = 0;
CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage);
cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height));
//金字塔滤波
cvPyrDown(timg, pyr, 7);
cvPyrUp(pyr, timg, 7);
//在3个通道中寻找矩形
for (int c = 0; c < 3; c++) //对3个通道分别进行处理
{
cvSetImageCOI(timg, c + 1);
cvCopy(timg, tgray, 0); //依次将BGR通道送入tgray
for (int l = 0; l < N; l++)
{
//不同阈值下二值化
cvThreshold(tgray, gray, 75, 250, CV_THRESH_BINARY);
cvShowImage("111", gray);
cvFindContours(gray, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
while (contours)
{ //多边形逼近
result = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
//如果是凸四边形并且面积在范围内
if (result->total == 4 && fabs(cvContourArea(result, CV_WHOLE_SEQ)) > minarea && fabs(cvContourArea(result, CV_WHOLE_SEQ)) < maxarea && cvCheckContourConvexity(result))
{
s = 0;
//判断每一条边
for (int i = 0; i < 5; i++)
{
if (i >= 2)
{ //角度
t = fabs(angle((CvPoint*)cvGetSeqElem(result, i), (CvPoint*)cvGetSeqElem(result, i - 2), (CvPoint*)cvGetSeqElem(result, i - 1)));
s = s > t ? s : t;
}
}
//这里的S为直角判定条件 单位为角度
if (s > minangle && s < maxangle)
{
for (int i = 0; i < 4; i++)
cvSeqPush(squares, (CvPoint*)cvGetSeqElem(result, i));
CvRect rect = cvBoundingRect(contours, 1); // 获取矩形边界框
CvPoint p1;
p1 = cvPoint(rect.x + rect.width / 2, rect.y + rect.height / 2); //矩形中心坐标
std::cout << "X:" << p1.x << "Y" << p1.y << std::endl;
}
}
contours = contours->h_next;
}
}
std::cout << "圆的数量是" << sk << std::endl;
temp[26] = sk;
sk = 0;
}
cvReleaseImage(&gray);
cvReleaseImage(&pyr);
cvReleaseImage(&tgray);
cvReleaseImage(&timg);
return squares;
}
//////////////////////////////////////////////////////////////////
//函数功能:画出所有矩形
//输入: img 原图像
// squares 矩形序列
// wndname 窗口名称
//输出: 图像中标记矩形
//////////////////////////////////////////////////////////////////
void drawSquares(IplImage* img, CvSeq* squares, const char* wndname)
{
CvSeqReader reader;
IplImage* cpy = cvCloneImage(img);
CvPoint pt[4];
int i;
cvStartReadSeq(squares, &reader, 0);
for (i = 0; i < squares->total; i += 4)
{
CvPoint* rect = pt;
int count = 4;
memcpy(pt, reader.ptr, squares->elem_size);
CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
memcpy(pt + 1, reader.ptr, squares->elem_size);
CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
memcpy(pt + 2, reader.ptr, squares->elem_size);
CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
memcpy(pt + 3, reader.ptr, squares->elem_size);
CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
//cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 );
cvPolyLine(cpy, &rect, &count, 1, 1, CV_RGB(rand() & 255, rand() & 255, rand() & 255), 1, CV_AA, 0);//彩色绘制
}
cvShowImage("22", cpy);
cvReleaseImage(&cpy);
}
void SendMessageOne(char *rtsp_url)
{
String rtsp_addr = rtsp_url;
VideoCapture capture(rtsp_addr);
//开起摄像头
//capture.open(0);
Mat edges; //定义转化的灰度图
const char* winn = "1111";
if (!capture.isOpened())
{
namedWindow("【效果图】", CV_WINDOW_NORMAL);
}
while (1)
{
int Y = 0, J = 0;
Mat frame;
capture >> frame;
IplImage img0 = frame;
//Mat E = frame(Range(1, 320), Range(1, 240));
Mat E = frame(Range(1, 160), Range(1, 240));
cvtColor(frame, edges, CV_BGR2GRAY);
//高斯滤波
GaussianBlur(edges, edges, Size(7, 7), 2, 2);
std::vector<Vec3f> circles;//存储每个圆的位置信息
//霍夫圆
HoughCircles(edges, circles, CV_HOUGH_GRADIENT, 1.5, 5, 100, 240, 0, 50);
for (size_t i = 0; i < circles.size(); i++)
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
int radius = cvRound(circles[i][2]);
//std::cout << "圆的X是" << circles[i][0] << "圆的Y是" << circles[i][1] << std:: endl;
//绘制圆轮廓
circle(frame, center, radius, Scalar(155, 50, 255), 3, 8, 0);
int R = frame.at<Vec3b>(cvRound(circles[i][1]), cvRound(circles[i][0]))[2];//R
int G = frame.at<Vec3b>(cvRound(circles[i][1]), cvRound(circles[i][0]))[1];//G
int B = frame.at<Vec3b>(cvRound(circles[i][1]), cvRound(circles[i][0]))[0];//B
int num = R + G + B;
std::cout << "圆心颜色是" << num << std::endl;
}
imshow("【效果图】", frame);
if ('q' == waitKey(30)) break;
}
}
/**************************************************/
using namespace cv;
using namespace std;
cv::Mat img;
bool select_flag = false;
cv::Rect m_select;
cv::Point origin;
int ROI_count;
void onMouseRectPicking(int event, int x, int y, int, void*)
{
if (select_flag)
{
m_select.x = MIN(origin.x, x);//不一定要等鼠标弹起才计算矩形框,而应该在鼠标按下开始到弹起这段时间实时计算所选矩形框
m_select.y = MIN(origin.y, y);
m_select.width = abs(x - origin.x);//算矩形宽度和高度
m_select.height = abs(y - origin.y);
m_select &= cv::Rect(0, 0, img.cols, img.rows);//保证所选矩形框在视频显示区域之内
}
if (event == CV_EVENT_LBUTTONDOWN)
{
select_flag = true; //鼠标按下的标志赋真值
origin = cv::Point(x, y); //保存下来单击捕捉到的点
m_select = cv::Rect(x, y, 0, 0); //这里一定要初始化,宽和高为(0,0)是因为在opencv中Rect矩形框类内的点是包含左上角那个点的但是不含右下角那个点
}
else if (event == CV_EVENT_LBUTTONUP)
{
select_flag = false;
ROI_count++;
}
}
// 鼠标选取区域,实现截图功能并保持图片
void vCutPicture(char *filename)
{
img = imread(filename);
bool stop = false;
cv::namedWindow("capframe", CV_WINDOW_AUTOSIZE);
cv::setMouseCallback("capframe", onMouseRectPicking, 0);
char pic_name[40];
ROI_count = 0;
while (!stop)
{
img = imread(filename);
cv::rectangle(img, m_select, cv::Scalar(255, 0, 0), 2, 8, 0); // 画矩形框
cv::imshow("capframe", img);
if ((m_select.x != 0) && (m_select.y != 0) && (m_select.width != 0) && (m_select.height != 0))
{
sprintf(pic_name, "ROI_%d.jpg", ROI_count);
Mat ROI = img(m_select);
imshow("ROI_WIN", ROI);
imwrite(pic_name, ROI);
}
char key = cv::waitKey(30);
if (key == 'q') stop = true;
}
waitKey(0);
return;
}
//////////////////////////////////
//检测直线方法
int iCheckLine(char *filename)
{
int i;
IplImage* src = cvLoadImage(filename, 0);
IplImage* dst;
IplImage* color_dst;
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* lines = 0;
if (!src) return -1;
dst = cvCreateImage(cvGetSize(src), 8, 1);
color_dst = cvCreateImage(cvGetSize(src), 8, 3);
cvCanny(src, dst, 50, 200, 3);
cvCvtColor(dst, color_dst, CV_GRAY2BGR);
#if 0
lines = cvHoughLines2(dst, storage, CV_HOUGH_STANDARD, 1, CV_PI / 180, 100, 0, 0);
for (i = 0; i < MIN(lines->total, 100); i++)
{
float* line = (float*)cvGetSeqElem(lines, i);
float rho = line[0];
float theta = line[1];
CvPoint 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));
cvLine(color_dst, pt1, pt2, CV_RGB(255, 0, 0), 3, CV_AA, 0);
}
#else
lines = cvHoughLines2(dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 50, 50, 10);
for (i = 0; i < lines->total; i++)
{
CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i);
cvLine(color_dst, line[0], line[1], CV_RGB(255, 0, 0), 3, CV_AA, 0);
}
#endif
cvNamedWindow("Source", 1);
cvShowImage("Source", src);
cvNamedWindow("Hough", 1);
cvShowImage("Hough", color_dst);
cvReleaseMemStorage(&storage);
cvWaitKey(0);
return 0;
}
//检测圆方法
int iCheckCircles(char *filename)
{
IplImage* img;
if (img = cvLoadImage(filename, 1))
{
IplImage* gray = cvCreateImage(cvGetSize(img), 8, 1);
CvMemStorage* storage = cvCreateMemStorage(0);
cvCvtColor(img, gray, CV_BGR2GRAY);
cvSmooth(gray, gray, CV_GAUSSIAN, 9, 9); // smooth it, otherwise a lot of false circles may be detected
CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 2, gray->height / 5, 200, 100);
int i;
for (i = 0; i < circles->total; i++)
{
float* p = (float*)cvGetSeqElem(circles, i);
cvCircle(img, cvPoint(cvRound(p[0]), cvRound(p[1])), 3, CV_RGB(0, 255, 0), -1, 8, 0);
cvCircle(img, cvPoint(cvRound(p[0]), cvRound(p[1])), cvRound(p[2]), CV_RGB(0, 255, 0), 3, 8, 0);
}
cvNamedWindow("circles", 1);
cvShowImage("circles", img);
cvReleaseImage(&gray);
cvReleaseMemStorage(&storage);
}
return 0;
}
//检测矩形代码:
/*
在程序里找寻矩形
*/
#ifdef _CH_
#pragma package <opencv>
#endif
IplImage* imgfx = 0;
CvMemStorage* storage = 0;
static const char* names[] = { "pic1.png", "pic2.png", "pic3.png", "pic4.png", "pic5.png", "pic6.png", 0 };
int thresh = 50;
// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2
//double angle(CvPoint* pt1, CvPoint* pt2, CvPoint* pt0)
//{
// double dx1 = pt1->x - pt0->x;
// double dy1 = pt1->y - pt0->y;
// double dx2 = pt2->x - pt0->x;
// double dy2 = pt2->y - pt0->y;
// return (dx1*dx2 + dy1*dy2) / sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
//}
// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
CvSeq* findSquares4(IplImage* img, CvMemStorage* storage)
{
CvSeq* contours;
int i, c, l, N = 11;
CvSize sz = cvSize(img->width & -2, img->height & -2);
IplImage* timg = cvCloneImage(img); // make a copy of input image
IplImage* gray = cvCreateImage(sz, 8, 1);
IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3);
IplImage* tgray;
CvSeq* result;
double s, t;
// create empty sequence that will contain points -
// 4 points per square (the square's vertices)
CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage);
// select the maximum ROI in the image
// with the width and height divisible by 2
cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height));
// down-scale and upscale the image to filter out the noise
cvPyrDown(timg, pyr, 7);
cvPyrUp(pyr, timg, 7);
tgray = cvCreateImage(sz, 8, 1);
// find squares in every color plane of the image
for (c = 0; c < 3; c++)
{
// extract the c-th color plane
cvSetImageCOI(timg, c + 1);
cvCopy(timg, tgray, 0);
// try several threshold levels
for (l = 0; l < N; l++)
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading  
if (l == 0)
{
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
cvCanny(tgray, gray, 0, thresh, 5);
// dilate canny output to remove potential
// holes between edge segments
cvDilate(gray, gray, 0, 1);
}
else
{
// apply threshold if l!=0:
//     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
cvThreshold(tgray, gray, (l + 1) * 255 / N, 255, CV_THRESH_BINARY);
}
// find contours and store them all as a list
cvFindContours(gray, storage, &contours, sizeof(CvContour),
CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
// test each contour
while (contours)
{
// approximate contour with accuracy proportional
// to the contour perimeter
result = cvApproxPoly(contours, sizeof(CvContour), storage,
CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
// square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (result->total == 4 &&
fabs(cvContourArea(result, CV_WHOLE_SEQ)) > 1000 &&
cvCheckContourConvexity(result))
{
s = 0;
for (i = 0; i < 5; i++)
{
// find minimum angle between joint
// edges (maximum of cosine)
if (i >= 2)
{
t = fabs(angle(
(CvPoint*)cvGetSeqElem(result, i),
(CvPoint*)cvGetSeqElem(result, i - 2),
(CvPoint*)cvGetSeqElem(result, i - 1)));
s = s > t ? s : t;
}
}
// if cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if (s < 0.3)
for (i = 0; i < 4; i++)
cvSeqPush(squares,
(CvPoint*)cvGetSeqElem(result, i));
}
// take the next contour
contours = contours->h_next;
}
}
}
// release all the temporary images
cvReleaseImage(&gray);
cvReleaseImage(&pyr);
cvReleaseImage(&tgray);
cvReleaseImage(&timg);
return squares;
}
// the function draws all the squares in the image
void drawSquares(IplImage* img, CvSeq* squares)
{
CvSeqReader reader;
IplImage* cpy = cvCloneImage(img);
int i;
CvPoint pt[4];
// initialize reader of the sequence
cvStartReadSeq(squares, &reader, 0);
// read 4 sequence elements at a time (all vertices of a square)
for (i = 0; i < squares->total; i += 4)
{
CvPoint* rect = pt;
int count = 4;
// read 4 vertices
memcpy(pt, reader.ptr, squares->elem_size);
CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
memcpy(pt + 1, reader.ptr, squares->elem_size);
CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
memcpy(pt + 2, reader.ptr, squares->elem_size);
CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
memcpy(pt + 3, reader.ptr, squares->elem_size);
CV_NEXT_SEQ_ELEM(squares->elem_size, reader);
// draw the square as a closed polyline
cvPolyLine(cpy, &rect, &count, 1, 1, CV_RGB(255, 255, 255), 3, CV_AA, 0);
}
// show the resultant image
cvShowImage("Square Detection Demo", cpy);
cvReleaseImage(&cpy);
}
void on_trackbar(int a)
{
if (imgfx)
drawSquares(imgfx, findSquares4(imgfx, storage));
}
// 检测矩形
int iCheckFangXiang(char *filename)
{
IplImage* img0 = 0;
int c;
// create memory storage that will contain all the dynamic data
storage = cvCreateMemStorage(0);
// load i-th image
img0 = cvLoadImage(filename, 1);
cvShowImage("原图", img0);
if (!img0)
{
printf("Couldn't load %s/n", filename);
return 0;
}
imgfx = cvCloneImage(img0);
// create window and a trackbar (slider) with parent "image" and set callback
// (the slider regulates upper threshold, passed to Canny edge detector)
//cvNamedWindow("Square Detection Demo1"1);
cvCreateTrackbar("canny thresh", "Square Detection Demo2", &thresh, 1000, on_trackbar);
// force the image processing
on_trackbar(0);
// wait for key.
// Also the function cvWaitKey takes care of event processing
c = cvWaitKey(0);
// release both images
cvReleaseImage(&imgfx);
cvReleaseImage(&img0);
// clear memory storage - reset free space position
cvClearMemStorage(storage);
cvDestroyWindow("Square Detection Demo3");
return 0;
}
///////////////////////////////////////////////////////////////////////////////////
double Angles(Point cen, Point first, Point second)
{
double M_PIs = 3.1415926535897;
double ma_x = first.x - cen.x;
double ma_y = first.y - cen.y;
double mb_x = second.x - cen.x;
double mb_y = second.y - cen.y;
double v1 = (ma_x * mb_x) + (ma_y * mb_y);
double ma_val = sqrt(ma_x * ma_x + ma_y * ma_y);
double mb_val = sqrt(mb_x * mb_x + mb_y * mb_y);
double cosM = v1 / (ma_val * mb_val);
double angleAMB = acos(cosM) * 180 / M_PIs;
return angleAMB;
}
/************************************************************************
*函数名: get_point_angle
*
*函数作用: 已知2个坐标点求从 0------->x 逆时针需旋转多少角度到该位置
*
* |
* |
* |
* |
*------------------------------------> x
* | 0
* |
* |
* |
* v
* y
*
*函数参数:
*CvPoint2D32f pointO - 起点
*CvPoint2D32f pointA - 终点
*
*函数返回值:
*double 向量OA从 0------->x 逆时针需旋转多少角度到该位置
**************************************************************************/
double get_point_angles(CvPoint pointO, CvPoint pointA)
{
double angle = 0;
CvPoint point;
double temp;
point = cvPoint((pointA.x - pointO.x), (pointA.y - pointO.y));
if ((0 == point.x) && (0 == point.y))
{
return 0;
}
if (0 == point.x)
{
angle = 90;
return angle;
}
if (0 == point.y)
{
angle = 0;
return angle;
}
temp = fabsf(float(point.y) / float(point.x));
temp = atan(temp);
temp = temp * 180 / CV_PI;
if ((0 < point.x) && (0 < point.y))
{
angle = 360 - temp;
return angle;
}
if ((0 > point.x) && (0 < point.y))
{
angle = 360 - (180 - temp);
return angle;
}
if ((0 < point.x) && (0 > point.y))
{
angle = temp;
return angle;
}
if ((0 > point.x) && (0 > point.y))
{
angle = 180 - temp;
return angle;
}
printf("sceneDrawing :: getAngle error!");
return -1;
}
Mat src, src_gray;
Mat dst, detected_edges;
int edgeThresh = 1;
int lowThreshold = 77;
int max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
vector<Vec3f> circles;
Point g_Center(0, 0);
int g_radius = 0;
/**
* @函数 CannyThreshold
* @简介: trackbar 交互回调 - Canny阈值输入比例1:3
*/
void CannyThreshold(int, void*)
{
int radius = 0;
int iMaxLimit = 0;
double min = 0;
string window_name = "Edge Map";
/// 使用 3x3内核降噪
//while (lowThreshold > 0) {
blur(src_gray, detected_edges, Size(3, 3));
/// 运行Canny算子
Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size);
//printf("LowThresold : %d -- %d \n", lowThreshold, lowThreshold*ratio);
/// 使用 Canny算子输出边缘作为掩码显示原图像,声明一个三通道图像像素值全为0用来将霍夫变换检测出的圆画在上面
dst = Scalar::all(0);
src.copyTo(dst, detected_edges);
HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT,
1, //dp,累加器图像的分辨率,增大则分辨率变小
100, // 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计
//算出符合自己需要的两个圆之间的最小距离。
150, //param1,canny算法的阈值上限下限为一半即100以上为边缘点50以下抛弃中间视是否相连而定
100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃
230, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围
460 //maxRadius,最大圆半径
);
//imshow(window_name, dst);
// if (circles.size() <= 0) {
// lowThreshold--;
// continue;
//}
// break;
//}
if (circles.size() <= 0) {
printf("Can not circles, return, thred=%d\n", lowThreshold);
return;
}
for (size_t i = 0; i < circles.size(); i++)//把霍夫变换检测出的圆画出来
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
radius = cvRound(circles[i][2]);
g_Center = center;
g_radius = radius;
line(dst, center, center, Scalar(0, 0, 255), 8);
//circle(dst, center, 0, Scalar(0, 255, 0), -1, 8, 0);
int a = cvRound(circles[i][0]);
int b = cvRound(circles[i][1]);
circle(dst, center, radius, Scalar(255, 0, 0), 2);
//printf("low=%.4f max=%d\n", lowThreshold, iMaxLimit);
printf("No%d - {%d} %4d %dx%d %d %d %d\n", i + 1, lowThreshold,
(int)circles.size(), a, b, radius, dst.cols, dst.rows);
//在控制台输出圆心坐标和半径
}
imshow(window_name, dst);
#if 1
// 找线段
vector<Vec4i> lines2;
int Len = 0, iCirLen = 0;
HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10);
double vv = 0.0f;
double fmin = 26.0f;
int p = 0;
//while (p <= 0) {
// min++;
for (int n = 0; n < (int)lines2.size(); n++)
{
// minangle = 60 maxangle = 320 最大刻度量 10theta = 220
Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点
Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标
double vv = DistancetoSegment(C, A, B);
Len = (int)Length(Point(A.x - B.x, A.y - B.y));
//printf("Len=%d vv = %.4f\n", Len, vv);
if (Len > 150 && vv < fmin) {
fmin = vv;
line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA);
printf("OK_Len=%d vv = %.4f\n", Len, vv);
imshow(window_name, dst);
}
//iCirLen = (int)Length(Point((int)C.x - B.x, (int)C.y - B.y));
//if (Len > g_radius) // && vv < 13.0f) {
//if (Len > 100 && vv < 10.0f) {
// line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA);
// min = vv;
// p = n;
// //
// //int x_angle = B.x - A.x;
// //int y_angle = A.y - B.y;
// //if (x_angle <= 0) continue;
// //double theta = atan(tan(y_angle / x_angle));
// //double final_theta = 0;
// //// 左右分
// ////if ((x_angle > 0 && y_angle > 0) || (x_angle > 0 && y_angle < 0))
// //// final_theta = 270 - theta;
// ////if ((x_angle < 0 && y_angle >0) || (x_angle < 0 && y_angle < 0))
// //// final_theta = 90 - theta;
// ////上下分
// //if ((x_angle > 0 && y_angle > 0) || (x_angle < 0 && y_angle > 0))
// // final_theta = 360 - theta;
// //if ((x_angle > 0 && y_angle <0) || (x_angle < 0 && y_angle < 0))
// // final_theta = 180 - theta;
// // minangle = 48, maxangle = 320 最大刻度量 10
// double du = get_point_angles(B, A);
// //double val = (final_theta - 48) * 1.6f / (270 - 24);
// //double val = dGetReadValueOfYZFTH(du, 360, Len);
// double val = (du * 10) / (270);
// //printf_s(" = %.2f° %.2f %.2f %.2f %.2f [%d]\n", Angles(C, B, A), val, final_theta, theta, vv, Len);
// //printf_s(" = %.2f° %.2f %.2f %.2f %.2f [%d]\n", get_point_angles(B,A), val, final_theta, theta, vv, Len);
// printf(" = %.2f° [%.2f] %.2f [%d]\n", get_point_angles(B, A), val, vv, Len);
//}
}
printf("OK_Len=%d vv = %.4f\n", Len, fmin);
//}
#endif
imshow(window_name, dst);
}
// 检测线段
void vHoughLines(int, void*)
{
string window_name = "Edge Map";
// 找线段
vector<Vec4i> lines2;
int Len = 0, iCirLen = 0;
HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10);
for (int n = 0; n < (int)lines2.size(); n++)
{
// minangle = 60 maxangle = 320 最大刻度量 10theta = 220
Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点
Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标
double vv = DistancetoSegment(C, A, B);
Len = (int)Length(Point(A.x - B.x, A.y - B.y));
iCirLen = (int)Length(Point((int)C.x - B.x, (int)C.y - B.y));
if (Len > g_radius && vv < 10.0f) {
line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA);
//
int x_angle = B.x - C.x;
int y_angle = C.y - B.y;
if (x_angle <= 0) continue;
double theta = atan(tan(y_angle / x_angle));
double final_theta = 0;
// 左右分
//if ((x_angle > 0 && y_angle > 0) || (x_angle > 0 && y_angle < 0))
// final_theta = 270 - theta;
//if ((x_angle < 0 && y_angle >0) || (x_angle < 0 && y_angle < 0))
// final_theta = 90 - theta;
//上下分
if ((x_angle > 0 && y_angle > 0) || (x_angle < 0 && y_angle > 0))
final_theta = 360 - theta;
if ((x_angle > 0 && y_angle < 0) || (x_angle < 0 && y_angle < 0))
final_theta = 180 - theta;
double val = (final_theta - 60) * 10 / (320 - 60);
//double val = (final_theta - 48) * 10 / (270 - 24);
printf("%.2f %.2f %.2f %.2f %.2f\n", Angles(C, A, B), val, final_theta, theta, vv);
}
}
imshow(window_name, dst);
}
// 滑动窗口检测图片圆和线段
void HTImgAnalys(const char *img_file)
{
char *window_name = NULL;
/// 装载图像
src = imread(img_file);
//AdaptiveFindThreshold(src, (double*)&lowThreshold, (double*)&max_lowThreshold, 3);
//AdaptiveFindThreshold(src, &lowThreshold, &max_lowThreshold, 3);
printf("lowThreshold = %f max_lowThreshold=%f\n", lowThreshold, max_lowThreshold);
window_name = (char*)img_file;
if (!src.data)
{
perror("error");
return;
}
printf("width x higth = %dx%d\n", src.cols, src.rows);
printf("No. Ltld Htld cirs center radius w_cols h_rows Angles value final_theta theta vv Len\n");
// 创建与src同类型和大小的矩阵(dst)
dst.create(src.size(), src.type());
// 原图像转换为灰度图像
cvtColor(src, src_gray, CV_BGR2GRAY);
// 创建显示窗口
namedWindow(window_name, CV_WINDOW_AUTOSIZE);
// 创建trackbar
createTrackbar("Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold);
// createTrackbar("Min Threshold:", window_name, (int*)&low, max, CannyThreshold);
// 显示图像
CannyThreshold(0, 0);
//vHoughLines(0, 0);
// 等待用户反应
waitKey(0);
dst.release();
}
// 上海自动化仪表股份有限公司OCr17Ni12Mo2压力表min~max=0-1.6MPa
void vInitPress_OCR17NI12MO2_16()
{
// radio, du, val
float initdu = 225.0f; //max 315-1.6, 225-0.0
float du = 225.0f;
float val = 0;
int i = 0;
for (i = 0; i < 80; i++)
{
printf("{%d,%.2f,%.2f},\n", 183, du, val);
du -= 5.40f;
if (du < 0) du = 360 - du;
val += 0.05f;
}
printf("{%d,%.2f,%.2f},\n", 180, du, val);
}
// 上海自动化仪表股份有限公司OCr17Ni12Mo2压力表min~max=0-2.5MPa
void vInitPress_OCR17NI12MO2_25()
{
}