2014-03-30 21:18:14 zhiyuanzhe007 阅读数 8563

        图像处理中的椭圆检测用处还是挺多的,找到这里来的同学大多是想用椭圆检测来解决某些实际问题吧,所以我就不做介绍,直奔主题。我研究这块也有一段时间了,也查找了挺多资料,貌似通用的椭圆算法还没有,不排除我孤陋寡闻了。前辈提出的算法适用范围比较有限,这个比较有限是相对直线检测来说的。但直接用Hough变换来找椭圆几乎是不可能的事,在5维空间里做投票,想想都觉得可怕。于是有人想到用随机Hough变换。这是一种很合理的方法,我就是这么做的,不过这种方法有个不足之处,后面会讲到。这里先介绍这方法的流程。

        二次曲线的一般方程为:,其中(x,y)为图像的坐标空间,BCDEF是二次曲线的参数。当满足时二次曲线为椭圆。方程中需要求解的参数有5个,在随机Hough变换过程中要至少采集5个点,得到5个方程以求得这5个参数。若方程有解且满足约束条件,则将解加入参数空间进行累积。思路是比较简单的,下面边贴代码边解释(P.S. 代码仅供参考)。

void FindEllipse(TImage* OrgGrayImg)
{
int ImgHeight = OrgGrayImg->nHeight;
	int ImgWidth = OrgGrayImg->nWidth;
	unsigned char * Img = OrgGrayImg->pImage; // 输入图像确保是二值图像

srand((unsigned)time(NULL));

	int totalPt = 0;// 用于统计样本点的个数

	for (i = 0; i < ImgHeight - 0; i++)
	{
		unsigned char *imgdata = Img + i * ImgWidth;
		for (j = 0; j < ImgWidth - 0; j++)
		{
			if (!imgdata[j])
				totalPt ++;
		}
	}

	if (totalPt < 5)
		return;

	POINT * seq;
	seq = new POINT [totalPt];

	int count = 0;
	for (i = 0; i < ImgHeight; i++)
	{
		unsigned char *data = Img + i * ImgWidth;
		for (j = 0; j < ImgWidth; j++)
		{
			if (!data[j])
			{
				seq[count].x = j;
				seq[count].y = i;
				count ++;
			}
		}
	}

	double Para[5];	// 存放结果(5个参数A,B,C,D,E)的数组
	int Angle_V[360]={0};// 椭圆倾斜角参数空间
	int *Center_XV = new int[ImgWidth];// 椭圆中心点x坐标参数空间
	int *Center_YV = new int[ImgHeight];// 椭圆中心点y坐标参数空间
	int *A_axis_V = new int[max(ImgWidth,ImgHeight)/2];// 椭圆长轴参数空间
	int *B_axis_V = new int[max(ImgWidth,ImgHeight)/2];// 椭圆短轴参数空间
	
	memset(Center_XV,0,sizeof(int)*ImgWidth);
	memset(Center_YV,0,sizeof(int)*ImgHeight);
	memset(A_axis_V,0,sizeof(int)*max(ImgWidth,ImgHeight)/2);
	memset(B_axis_V,0,sizeof(int)*max(ImgWidth,ImgHeight)/2);

	double Theta,X_c,Y_c,A_axis,B_axis;

int loop = 1;// 成功求出参数的迭代次数
	int looptop = loop * 1;// 总的迭代次数(也就是控制计算时间的上限,以免陷入无限循环)
while(loop > 0 && looptop > 0)
{
looptop --;
	int idx;
	for (count = totalPt; count > 0; count--)// 打乱样本点排列的顺序
	{
		POINT ptrtmp;
		idx = rand() % count;
		
		ptrtmp = seq[idx];
		seq[idx] = seq[count-1];
		seq[count-1] = ptrtmp;		
	}

	double PioMatrix[5*5];
	for (i = 0; i < 5; i++)
	{
		PioMatrix[i*5] = seq[i].x * seq[i].x;
		PioMatrix[i*5 + 1] = 2 * seq[i].x * seq[i].y;
		PioMatrix[i*5 + 2] = seq[i].y * seq[i].y;
		PioMatrix[i*5 + 3] = 2 * seq[i].x;
		PioMatrix[i*5 + 4] = 2 * seq[i].y;
	}

	if (GaussJordanInv(PioMatrix,5) == false)// Gauss-Jordan求逆
		continue;
	double sum;
	for (i = 0; i < 5; i++)
	{
		sum = 0;
		for (j = 0; j < 5; j++)
		{
			sum +=  -(PioMatrix[i*5 + j]);
		}
		Para[i] = sum;
	}

	if (pow(Para[1],2) - Para[0] * Para[2] > 0)
			continue;

		if (fabs(Para[0] - Para[2]) < 1e-20)
			Theta = 1.570796326;
		else if (Para[0] > Para[2])
			Theta = 0.5 * (atan(2.0 * Para[1] / (Para[0] - Para[2])) + PI);
		else
			Theta = 0.5 * (atan(2.0 * Para[1] / (Para[0] - Para[2])));

		X_c = (4.0 * Para[1] * Para[4] - 4.0 * Para[2] * Para[3]) / (4.0 * Para[0] * Para[2] - 4.0 * Para[1] * Para[1]);
		Y_c = (4.0 * Para[1] * Para[3] - 4.0 * Para[0] * Para[4]) / (4.0 * Para[0] * Para[2] - 4.0 * Para[1] * Para[1]);
		A_axis = 2 * (Para[0] * pow(X_c,2) + Para[2] * pow(Y_c,2) + 2 * Para[1] * X_c * Y_c - 1) 
						/ (Para[0] + Para[2] - sqrt(pow(Para[0] - Para[2],2) + pow(2.0 * Para[1],2)));
		B_axis = 2 * (Para[0] * pow(X_c,2) + Para[2] * pow(Y_c,2) + 2 * Para[1] * X_c * Y_c - 1) 
						/ (Para[0] + Para[2] + sqrt(pow(Para[0] - Para[2],2) + pow(2.0 * Para[1],2)));
		
		A_axis = sqrt(A_axis);	//长轴
		B_axis = sqrt(B_axis);	//短轴

		int AngleTmp = (int)(Theta * 180 / PI + 360 + 0.5) % 360;
		Angle_V[AngleTmp]++;
		
		if (X_c < 0 || Y_c < 0 || A_axis < 0 || B_axis < 0)
			continue;
		if (X_c >= ImgWidth || Y_c >= ImgHeight || A_axis > max(ImgWidth,ImgHeight)/2 || B_axis > max(ImgWidth,ImgHeight)/2)
			continue;

		if (X_c >= 0 && X_c < ImgWidth)
			Center_XV[(int)X_c]++;
		if (Y_c >= 0 && Y_c < ImgHeight)
			Center_YV[(int)Y_c]++;
		if (A_axis >= 0 && A_axis < max(ImgWidth,ImgHeight)/2)
			A_axis_V[(int)A_axis]++;
		if (B_axis >= 0 && B_axis < max(ImgWidth,ImgHeight)/2)
			B_axis_V[(int)B_axis]++;		
		loop--;
}
	
	int Angle,Ai,Bi,Cx,Cy;
	//	Angle
	int MaxPara = 0;
	for (i = 0; i < 360; i++)
	{
		if (MaxPara < Angle_V[i])
		{
			MaxPara = Angle_V[i];
			Angle = i;
		}
	}
	//	Cy
	MaxPara = 0;
	for (i = 0; i < ImgHeight; i++)
	{
		if (MaxPara < Center_YV[i])
		{
			MaxPara = Center_YV[i];
			Cy = i;
		}
	}
	//	Cx
	MaxPara = 0;
	for (i = 0; i < ImgWidth; i++)
	{
		if (MaxPara < Center_XV[i])
		{
			MaxPara = Center_XV[i];
			Cx = i;
		}
	}
	//	Ai
	MaxPara = 0;
	for (i = 0; i < max(ImgWidth,ImgHeight)/2; i++)
	{
		if (MaxPara < A_axis_V[i])
		{
			MaxPara = A_axis_V[i];
			Ai = i;
		}
	}
	//	Bi
	MaxPara = 0;
	for (i = 0; i < max(ImgWidth,ImgHeight)/2; i++)
	{
		if (MaxPara < B_axis_V[i])
		{
			MaxPara = B_axis_V[i];
			Bi = i;
		}
	}

	delete[] Center_XV;
	delete[] Center_YV;
	delete[] A_axis_V;
	delete[] B_axis_V;


	double sma = SinMem[Angle];
	double cma = CosMem[Angle];
	for (int n = 0; n < 360; n++)
	{
		i = (Bi) * CosMem[360 - n];
		j = (Ai) * SinMem[360 - n];
		
		int x,y;
		x = (j * cma - i * sma) + Cx;
		y = (i * cma + j * sma) + Cy;
		
		Mask[y * ImgWidth + x] = 0;
	}
	delete[] Mask;
	delete[] seq;
}

测试结果:

原图:

拟合结果(虚线为拟合的椭圆):

前面说到这种方法有缺陷,请看下面的情形:

原图:

拟合结果:

       当样本点只集中在椭圆的一边时,随机5点的hough变换总会拟合错误,实际应用中往往会发生这样的情况。这是因为公式错了吗?于是我单独提取出五点做测试,即只做一次迭代。测试结果如下图所示。图中实线为实际椭圆,我是用画图工具拖出来的“完美椭圆”,用橡皮擦擦掉一大半部分,最后再做一点旋转。打交叉的是取样的5点,虚线是用这5点代入公式求得的拟合椭圆。可见求得的椭圆穿过了5个点,表明不是求解错了,可就是跟实际的有很大差别。唯一的解释是取样点不在我们想要的椭圆上,也就是说即使是用画图工具拖出来看似完美的椭圆并不完美,这是因为样本点的坐标是整型,精度很低。所以随机5点的hough变换存在很严重的系统误差,当取样点分散在椭圆上下左右时,这种误差会比较小,当集中在某个区域时,误差就会非常大。

   

        解决办法就是多采几个点,然后用最小二乘法求解。

下图是10点随机采样的结果:

下图是将所有点一起计算的结果:

可见,采样点越多,拟合度越好。但是一次取样的点越多,这些点落入相同椭圆的概率就越小。这就需要一些手段把椭圆的边缘从噪声中提取出来。至于最小二乘法的椭圆检测算法我将另开一贴来讨论。

 

 

 


 

2017-07-11 20:07:00 weixin_34318956 阅读数 100

 

以下是素材照片,就是寻找中间那个圆就可以了,说起来很简单,做起来不那么容易:

代码很简单,主要是预处理,下面会基体说明!

 

 处理过程的例子:

 

 

处理结果:

 

 

 


 

首先说明一下怎么寻找一个圆?(本来都忘记这篇博文没写完了,还是一个同志发邮件给我才想起来)

A.寻找圆的大方向

预处理好的话,下面哪种方法都可以得到精确的圆!

1.霍夫圆检测

    小伙伴们是不是有这种感觉,刚开始用霍夫的时候很兴奋,调调参数就能找到圆,但是随着你要求找的圆越来越复杂,是不是发现霍夫没用了?

  真的不是霍夫没用了,是你预处理没做好,看看霍夫的原理(极坐标的转换方式,我另一篇博文有说http://www.cnblogs.com/wjy-lulu/p/6677280.html

  一句话你处理的越好检测的就越好!!

2.拟合圆

  利用圆的函数去拟合,当然这里分为两种方案:

      a.点在圆的边缘,这个需要圆的函数拟合。

      b.点在圆内,这个需要圆的面积+函数拟合。

3.最小外接圆

  注意这不是和方法2相同!!这个方法是找到一个圆然后使得圆面积最小且包含所有点,算法实现应该有很多了,我个人认为用k-means寻找质心然后再找最小圆。

4.分水岭算法

  这个方法在另一篇博文实现,不在阐述:http://www.cnblogs.com/wjy-lulu/p/7056466.html

B.预处理

--------->>>>>感觉没多大意义,貌似任何算法的基础都是预处理,具体问题具体对待


 

 

上代码:

 (代码写到一半就没写了,原因不想说了,后面也不想改进了,其实针对具体问题还有很多改进的地方,精确寻找圆不是很困难的事)

 

  1 #include<iostream>
  2 #include <opencv2/opencv.hpp>
  3 #include <math.h>
  4 using namespace cv;
  5 using namespace std;
  6 
  7 int Threshold_Value = 176;
  8 const int Threshold_Max_value = 255;
  9 const int Threshold_type_value = 3;
 10 
 11 Mat input_image, threshold_image, output_image, Middle_image;
 12 
 13 void Threshold_Image_Bar(int, void *);
 14 
 15 int main(int argc, char**argv)
 16 {
 17     input_image = imread("b.jpg");
 18     if (input_image.data == NULL) {
 19         return -1; cout << "can't open image.../";
 20     }
 21     imshow("Sourse Image", input_image);
 22     blur(input_image, Middle_image, Size(3, 3), Point(-1, -1), 4);
 23     cvtColor(Middle_image, Middle_image, COLOR_RGB2GRAY);
 24     const float init_pointx =  saturate_cast<float>(Middle_image.cols / 7);
 25     const float init_pointy =  saturate_cast<float>(Middle_image.rows / 7);
 26     Rect roi_rect = Rect(Point2f(2 * init_pointx, 2 * init_pointy), Point2f(6 * init_pointx, 6 * init_pointy));
 27     Mat  roi_Image = Middle_image(roi_rect);
 28     Middle_image = roi_Image;
 29     threshold(Middle_image, threshold_image, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
 30     Mat kernel_rect   = getStructuringElement(MORPH_ELLIPSE, Size(30, 30), Point(-1, -1));
 31     Mat kernel_circle = getStructuringElement(MORPH_ELLIPSE, Size(10, 10), Point(-1, -1));
 32     morphologyEx(threshold_image, threshold_image, MORPH_CLOSE, kernel_circle);
 33     Mat RedImage = threshold_image.clone();
 34     morphologyEx(RedImage, threshold_image, MORPH_OPEN, kernel_rect);
 35     for (size_t i = 0; i < threshold_image.rows; i++)
 36     {
 37         for (size_t j = 0; j < threshold_image.cols; j++)
 38         {
 39             RedImage.at<uchar>(i, j) = saturate_cast<uchar>(RedImage.at<uchar>(i, j) - threshold_image.at<uchar>(i, j));
 40         }
 41     }
 42     vector<vector<Point>> contours;
 43     vector<Vec4i> hierarchy;
 44     Mat showImage = Mat::zeros(RedImage.size(), CV_8UC1);
 45     findContours(RedImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
 46     for (size_t i = 0; i < contours.size(); i++)
 47     {
 48         if (minAreaRect(contours[i]).size.area() > 10000 && minAreaRect(contours[i]).size.height > 80 && minAreaRect(contours[i]).size.width > 80)//这个参数大概就可以
 49         {
 50             drawContours(showImage, contours, static_cast<int>(i), Scalar(255, 255, 255), 1);
 51         }
 52     }
 53     vector<Point> points;
 54     for (int i = 0; i < showImage.rows; i++)
 55     {
 56         for (int j = 0; j < showImage.cols; j++)
 57         {
 58             if (showImage.at<uchar>(i, j) == 255)
 59             {
 60                 points.push_back(Point(j, i));
 61             }
 62         }
 63     }
 64     Point2f center;
 65     float radius;
 66     if (points.data() == 0)
 67     {
 68         printf("Don't detecte point");
 69         return -1;
 70     }
 71     minEnclosingCircle(points, center, radius);
 72     center.x += 2 * init_pointx;
 73     center.y += 2 * init_pointy;
 74     Mat result = Mat::zeros(RedImage.size(), CV_8UC3);
 75     circle(input_image, center, radius, Scalar(0, 0, 255), 2);
 76     waitKey(0);
 77     return 0;
 78 }
 79 void Threshold_Image_Bar(int, void *)
 80 {
 81     threshold(Middle_image, threshold_image, 65, 255,THRESH_BINARY_INV);//110,65
 82     imshow("Threshold Image", threshold_image);
 83     Mat kernel = getStructuringElement(MORPH_RECT, Size(50, 50), Point(-1, -1));
 84     Mat RedImage = threshold_image.clone();
 85     morphologyEx(RedImage, threshold_image, MORPH_OPEN, kernel);
 86     for (size_t i = 0; i < threshold_image.rows; i++)
 87     {
 88         for (size_t j = 0; j < threshold_image.cols; j++)
 89         {
 90             RedImage.at<uchar>(i, j) = saturate_cast<uchar>(RedImage.at<uchar>(i, j) - threshold_image.at<uchar>(i, j));
 91         }
 92     }
 93     vector<vector<Point>> contours;
 94     vector<Vec4i> hierarchy;
 95     Mat showImage = Mat::zeros(RedImage.size(), CV_8UC1);
 96     findContours(RedImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));
 97     for (size_t i = 0; i < contours.size(); i++)
 98     {
 99         if (boundingRect(contours[i]).area() > 20000)
100         {
101             drawContours(showImage, contours, static_cast<int>(i), Scalar(255, 255, 255), 1);
102         }
103     }
104     vector<Point> points;
105     for (int i = 0; i < showImage.rows; i++)
106     {
107         for (int j = 0; j < showImage.cols; j++)
108         {
109             if (showImage.at<uchar>(i, j) == 255)
110             {
111                 points.push_back(Point(j, i));
112             }
113         }
114     }
115     Point2f center;
116     float radius;
117     minEnclosingCircle(points, center, radius);
118     Mat result = Mat::zeros(RedImage.size(), CV_8UC3);
119     circle(input_image, center, radius, Scalar(0, 0, 255), 2);
120 }

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/wjy-lulu/p/7152394.html

2019-11-08 16:38:27 qq_38190041 阅读数 67

我的图像处理课程的一个实验。

一.任务

识别字符:
在这里插入图片描述

二.使用平台

Windows10专业版
VS2015企业版
C++ opencv3.2

三.图像处理的思路

1.先定位感兴趣区域(字符区域)

定位有两种方法,一种是定位圆,另一种是定位直线
在这里插入图片描述

2.然后模版匹配(matchTemplate)

模版匹配需要做好模版。将感兴趣区域找出后,可以采用像素投影分割法,将每个字符分割出来保存到本地文件夹做样本。
在这里插入图片描述

四.图像处理

整个流程:
1.读入图像
2.转为灰度
3.中值滤波
4.固定阈值二值化。(因为我定位的是圆)
5.霍夫圆检测找到圆心
6.根据圆心剪裁感兴趣区域
7.读取本地文件夹样本
8.进行模版匹配
9.标出模板匹配结果

图像:

1.转为灰度中值滤波

灰度后中值滤波

2.二值化后霍夫圆检测

二值化、霍夫圆检测

3.根据圆心坐标裁出字符区域

感兴趣区域

3.模版匹配后标记结果

结果

代码:

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#include <string>
using namespace cv;
using namespace std;

Mat src, dst, gray, binImg;
Mat eImage;

int main(int argc, char** argv)
{
	src = imread("G:\\U_lesson\\图像处理\\实验\\zf1.bmp");

	if (src.empty())
	{
		printf("can not load image....\n");
		return -1;
	}
	// 窗口命名
	namedWindow("test", CV_WINDOW_AUTOSIZE);
	imshow("test", src);

	//中值滤波
	Mat moutput;
	medianBlur(src, moutput, 11);
	imshow("moutput", moutput);

	cvtColor(moutput, gray, CV_BGR2GRAY);

	// 固定阈值二值化
	threshold(gray, binImg, 15, 255, THRESH_BINARY);
	imshow("binImg", binImg);

	// 霍夫圆检测
	vector<Vec3f> pcircles;
	HoughCircles(binImg, pcircles, CV_HOUGH_GRADIENT, 1, 50, 15, 15, 25, 80);
	src.copyTo(dst);
    
    // 画圆与圆心
	for (size_t i = 0; i < pcircles.size(); i++)
	{
		Vec3f cc = pcircles[i];
		circle(dst, Point(cc[0], cc[1]), cc[2], Scalar(0, 0, 255), 2, LINE_AA);
		circle(dst, Point(cc[0], cc[1]), 2, Scalar(98, 23, 255), 2, LINE_AA);
		cout << cc << endl;
	}
	imshow("dst", dst);

	 // 简陋判断有无圆心坐标
	if (pcircles[0][0] > 0 && pcircles[0][1] > 0)
	{
		// 剪裁出字符区域
		int start_x = pcircles[0][0] - 280;
		int start_y = pcircles[0][1] + 80;
		int rect_width = 500;
		int rect_height = 140;
		Rect  rect(start_x, start_y, rect_width, rect_height);
		Mat ROI = src(rect);
		imshow("rect", ROI);

		Mat dst_gray, dst_bin;
		cvtColor(ROI, dst_gray, CV_BGR2GRAY);  

		int height = dst_gray.rows;
		int width = dst_gray.cols;

        // 取出本地文件的样本
		vector<Mat> character_image;
		char tempstr[12] = { '2','0','1','8','1','2','0','7','2','5','1','C'};
		for (int i = 0; i <= 11; i++)
		{
			string ImgName = "character";
			ImgName = ImgName + to_string(i);
			ImgName = "G:\\U_lesson\\图像处理\\实验\\match\\" + ImgName + ".bmp";
			Mat temp = imread(ImgName);
			character_image.push_back(temp);
		}
		
		Mat tmp_dst;
		Mat find_dst;
		dst_gray.copyTo(find_dst);
		cvtColor(find_dst, find_dst, CV_GRAY2BGR);
		// 模版匹配
		for (int i = 0; i <= 11; i++)
		{
			Mat temp;
			cvtColor(character_image[i],temp, CV_BGR2GRAY);

			width = dst_gray.cols - temp.cols + 1;
			height = dst_gray.rows - temp.rows + 1;
			Mat result(width, height, CV_32FC1);

			Point minLoc;
			Point maxLoc;
			double min, max;
			Point temLoc;

			dst_gray.copyTo(tmp_dst);
			matchTemplate(dst_gray, temp, result, CV_TM_CCORR_NORMED, Mat());

			// 找到匹配的最大最小坐标
			minMaxLoc(result, &min, &max, &minLoc, &maxLoc, Mat());
			temLoc = maxLoc;
			cout << temLoc << endl;
			
			// 画出矩形框
			rectangle(find_dst, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, 8);
			
			
			string str(1, tempstr[i]);
			putText(find_dst, str, Point(temLoc.x, temLoc.y), FONT_HERSHEY_COMPLEX, 1, cv::Scalar(0, 255, 0), 1, 8, 0);
			
			cout << tempstr[i] << endl;
			//rectangle(result, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2, 8);
		}

		imshow("AAA", find_dst);

	}
	waitKey(0);
	return 0;
}
2019-09-08 18:15:18 qq_40664693 阅读数 459

说明:
1、因为需要找到图片中的四个圆,刚开始我是直接在整张图片上找,可是发现周围的干扰因素太多了,调了一组参数,发现重复的圆太多了,还有其他我们不需要的误圆检测了出来。然后就想着分别检测,为了摆脱周围相似环境的影响,我将整张图片带圆的地方,剪裁了下来,分别剪裁了只带圆的正方形进行检测,这样就比较容易检测,找到准确位置。(提示一点,可能分别检测的时候,也可能在同一个圆的边界附近检测出了很多相似圆重叠在一起,也就是检测同一个圆出来了好几组相近的坐标数据。你又无法调一组只出来一组坐标的参数,但是最后你只需要一组坐标数据,这时候你可以对矩阵进行求平均,用mean()函数即可。代码里有)
2、分别找到四个圆的准确位置后,也只是相对位置,再加上剪裁的起始坐标就得到了绝对坐标。
这是原始图,我们要进行处理的图。
这是未进行校正的视频的某一帧  图1
通过检测进行了坐标标记 如下图:
霍夫变换检测圆操作后并进行了圆心标记
main.m文件

%%     main.m文件
clc;
clear;
% BW :二值图像;
% stepR:检测的圆半径步长 
% stepAngle:角度步长,单位为弧度
% minR:最小圆半径  
% maxR:最大圆半径 p:阈值 0,1之间的数,通过调节此值可以得到图中圆的圆心和半径
% 读取视频的第一帧进行如下操作:
%1)剪裁图片的四个角 
%2)利用霍夫变换检测圆,检测圆的之后得到圆的圆心并同时输出四个圆的坐标和半径。
%3)输出的有圆心的相对坐标和圆心的绝对坐标。
I = imread('image_supple\1.jpg');
for i = 1:4 % 连续剪裁图片的四个角
   switch (i)
       case {1}
           tlc= imcrop(I,[117 50 100 100]);% 左上角
           processpic(tlc,1);
           figure,imshow(tlc); 
       case {2}
           dlc= imcrop(I,[115 520 100 100]);% 左下角
           processpic(dlc,2);
           figure,imshow(dlc);
       case {3}
           trc = imcrop(I,[810 45 100 100]); % 右上角
           processpic(trc,3);
           figure,imshow(trc);
       case {4}
           drc = imcrop(I,[745 510 100 100]); % 右下角
           processpic(drc,4);
           figure,imshow(drc);
   end
end

processpic.m函数

对图片进行预处理
function processpic(t,h)   
grayI = rgb2gray(t);%图片灰度处理
BW = edge(grayI,'sobel');%边缘检测
imshow(t);
hold on;
switch (h)
    case {1}% 这是检测到的第一个圆
        parm = findcircle(BW,3,0.07,16,18,0.95);
        % 对于supple因为调参之后就出现了一组数据,所以不用再求均值。
        %disp(parm);
        %paraAvg = mean(parm);%对矩阵的每一列进行求均值
        % 输出的坐标是绝对坐标坐标形式是(y,x);
        %fprintf('CenterAvg %d %d  radiusAvg %d\n',round(paraAvg(1,1)+50),round(paraAvg(1,2)+117),paraAvg(1,3));
        fprintf('CenterAvg %d %d  radiusAvg %d\n',round(parm(1,1)+50),round(parm(1,2)+117),parm(1,3));
    case {2}%这是检测到的第二个圆
        parm = findcircle(BW,3,0.07,16,18,0.95);
       % paraAvg = mean(parm);%对矩阵的每一列进行求均值
        fprintf('CenterAvg %d %d  radiusAvg %d\n',round(parm(1,1)+520),round(parm(1,2)+115),parm(1,3));
    case {3}%这是检测的第三个圆
       parm = findcircle(BW,3,0.07,16,18,0.95);
       % paraAvg = mean(parm);%对矩阵的每一列进行求均值
       fprintf('CenterAvg %d %d  radiusAvg %d\n',round(parm(1,1)+45),round(parm(1,2)+810),parm(1,3));
    case {4}%这是检测的第四个圆
       parm = findcircle(BW,3,0.07,16,18,0.95);
       % paraAvg = mean(parm);%对矩阵的每一列进行求均值
       fprintf('CenterAvg %d %d  radiusAvg %d\n',round(parm(1,1)+510),round(parm(1,2)+745),parm(1,3));
end
   
% 绘制所有的圆 
for i = 1:size(parm,1)
    x0 = parm(i,1); y0 = parm(i,2);  r0 = parm(i,3);
    xi=[-r0:0 0:r0];
    yi=round((r0^2-xi.^2).^0.5);
    plot(yi+y0,xi+x0,'Color','g','LineWidth',3); % 下半圆
    plot(-yi+y0,xi+x0,'Color','g','LineWidth',3); % 上半圆
    % plot(y0,x0,'x','LineWidth',2,'Color','red');
end
end

findcircle.m函数

% 霍夫检测圆的主要代码
function [para] = findcircle(BW,stepR,stepAngle,minR,maxR,p)
%circleParaXYR = [];
[m,n] = size(BW);% BW:二值图像;
cntR = round((maxR-minR)/stepR)+1;% stepR:检测的圆半径步长, minR:最小圆半径, maxR:最大圆半径
cntAngle = round(2*pi/stepAngle);% stepAngle:角度步长,单位为弧度
hough_space = zeros(m,n,cntR);
% hough_space:参数空间,h(a,b,r)表示圆心在(a,b)半径为r的圆上的点数
[rows,cols] = find(BW);
cntPoints = size(rows,1); 
% Hough变换将图像空间(x,y)对应到参数空间(a,b,r)
% a = x-r*cos(angle), b = y-r*sin(angle)
for i=1:cntPoints
    for r=1:cntR
        for k=1:cntAngle
            a = round(rows(i)-(minR+(r-1)*stepR)*cos(k*stepAngle));
            b = round(cols(i)-(minR+(r-1)*stepR)*sin(k*stepAngle));
            if(a>0 && a<=m && b>0 && b<=n)
                hough_space(a,b,r) = hough_space(a,b,r)+1;
            end
        end
    end
end
 
% 寻找满足阈值的圆的参数
max_para = max(max(max(hough_space)));
index = find(hough_space>=max_para*p); % p:以p*hough_space的最大值为阈值,p取0,1之间的数
length = size(index,1);
hough_circle=zeros(m,n);
for i=1:cntPoints
    for k=1:length
        par3 = floor(index(k)/(m*n))+1;
        par2 = floor((index(k)-(par3-1)*(m*n))/m)+1;
        par1 = index(k)-(par3-1)*(m*n)-(par2-1)*m;
        if((rows(i)-par1)^2+(cols(i)-par2)^2<(minR+(par3-1)*stepR)^2+5 && (rows(i)-par1)^2+(cols(i)-par2)^2>(minR+(par3-1)*stepR)^2-5)
            hough_circle(rows(i),cols(i)) = 1;% hough_circl:二值图像,检测到的圆
        end
    end
end
 
for k=1:length
    par3 = floor(index(k)/(m*n))+1;     
    par2 = floor((index(k)-(par3-1)*(m*n))/m)+1;    % 圆心y坐标
    par1 = index(k)-(par3-1)*(m*n)-(par2-1)*m;      % 圆心x坐标
    par3 = minR+(par3-1)*stepR;                    % 圆的半径
    fprintf(1,'Center %d %d radius %d\n',par1,par2,par3);
    para(k,:) = [par1,par2,par3];% para:检测到的圆的圆心、半径
    plot(par2,par1,'x','LineWidth',5,'Color','red');%用红的的X对圆心位置进行标记
        
end


注:本人处理的就是上面显示图片,第一张是原始图片,第二张是处理之后的图片。因为我要处理的是视频,所以里面做了循环和选择。具体实现还是根据自己的需求进行修改。

2016-04-07 18:03:21 u013521188 阅读数 4044


之前一段时间一直使用halcon做图像处理的工作,在这里写下自己对处理图像的一般过程和自己所遇到的问题,同大家共同分享。


5步骤:尽可能直接由灰度通道图找出对应区域,不要使用ROI,除非必要的情况。

 

11步骤:结构元素可以是圆形(circle),也可以是矩形(rectangle),同样面积域的结构 元素,圆形的比矩形的易于将图像边缘平滑,填平阶梯形或锯齿形。

 

13步骤:可以去掉周边微小的干扰像素,利于OCR识别。

 

 

     除此之外,halcon可以导出c++CC#等程序,提供相应的接口,便于将生成的算法与自己的程序相结合。之后我会不断完善相关内容。


图像处理心得

阅读数 27

没有更多推荐了,返回首页