|
|
/****************************************************************************
|
|
|
** 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,
|
|
|
//两个圆间的距离50,canny门限的上限(下限自动设为上限的一半),圆心所需要的最小的投票数,最大和最小半径
|
|
|
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;(算法可以继续优化)。
|
|
|
|
|
|
参数1,imageInput:输入和输出的梯度图像
|
|
|
参数2,lowTh:低阈值
|
|
|
参数3,highTh:高阈值
|
|
|
*/
|
|
|
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 存储
|
|
|
// minarea,maxarea 检测矩形的最小/最大面积
|
|
|
// 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 最大刻度量 10,theta = 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 最大刻度量 10,theta = 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()
|
|
|
{
|
|
|
|
|
|
}
|