Advertisement

相机标定

阅读量:

在图像测量过程中及其在机器视觉应用中的各个方面,为了确定空间物体表面某一点在三维空间中的具体位置与其二维图像中的对应点之间的几何关系,则必须建立相机成像过程中的几何模型参数;这些参数即为相机本身的参数设定;无论是在图像测量还是机器视觉应用领域中,在大多数情况下这些参数都需要通过实验与计算的方法才能获得;这个求解过程即被定义为相机标定(或摄像机标定);由此可见,在无论是进行图像测量还是机器视觉应用时进行相机标定都是非常关键的工作环节;其标定结果与所采用算法的稳定性直接影响着最终得到准确可靠的测量数据;因此,在这一技术环节上取得高精度的结果对于后续工作的开展具有决定性的作用;提升这一环节的技术水平是科研工作中的一项重点任务

相机标定可以分为如下6步:

1、图像增强

2、边缘检测

3、椭圆提取

4、编码识别

5、初值估计

6、光速法平差

1、图像增强:

首先采用了图像增强技术,并通过提升同一幅图像中不同物体特征之间的对比度来实现目标效果。常见的图像增强算法包括直方图均衡化处理、对数域变换、指数函数调整以及沃尔什函数应用等方法。其中采用的方法是对数域变换,并通过相应的参数设置实现了更好的视觉效果。代码如下:

#define a 100.0 //控制参数,表示曲线的弯曲程度

#define b 0.0 //控制参数,表示曲线的上下偏移量

复制代码
 //图像增强,对数变换

    
 	for (i = 0; i < mheight; i++)
    
 	{
    
 		for (j = 0; j < mwidth; j++)
    
 		{
    
 			temp = a*log10((double)imageMat_Gray[i*mwidth + j] + 1.0) + b;
    
 			if (temp > 255)
    
 				temp = 255.0;
    
 			if (temp < 0)
    
 				temp = 0.0;
    
 			imageMat_Gray[i*mwidth + j] = int(temp + 0.5);
    
 		}
    
 	}

需要注意的是,在执行对数变换之前,请确保将图像转换为灰度图以获得最佳处理效果。具体效果将在下文展示

2、边缘检测:

在边缘检测领域中存在多种算子包括RobertsSobelPrewittLOGDOG和Canny等其中以LOG为例则是由高斯平滑滤波器与二阶导数构成的复合运算

有如下四个步骤:生成模板、卷积运算、零交叉点检测和边缘细化。

这里为了简便起见, 我直接采用了openCV库中的cvCanny()函数. 关于OpenCV的具体配置, 可参考我的另一篇博客:

复制代码
    cvCanny(img_gray, img_edgeExtract, 50, 150, 3);

效果如下:

3、椭圆提取:

椭圆提取主要步骤包括剔除不闭合的区域以及一些噪声点,并计算所有闭合区域及其边缘上的点坐标。判定当前图形是否为闭合区域的方法是检查当前像素与其八邻域是否存在相同值的情况。举个例子来说,在这个算法中我们把所有的边缘像素呈现在白色通道上即灰度值设定为255值的空间位置会被标记出来。我们需要做的就是对于每一个待检验的目标区域判断其边缘像素是否存在残差较大的情况如果存在则对该目标区域进行重新拟合如果残差较小则认为该目标符合标准要求并被保留下来操作流程大致如下:首先遍历整个图像找出所有符合条件的目标区域然后对每个目标区域执行椭圆拟合运算去除不符合条件的部分最终得到一组较为准确的目标椭圆参数数据集

4、编码识别:

编号识别原理:

在标定板上任意选取四个点构成一组,并保证不同分组的四点不在同一直线上。各条直线则会在标定板中心交汇。

每个小组都可以划分为大小两类点。每一组点经过编码处理后会生成不同的二进制数值。

3、标定板平面与影像平面构成二维射影变换(二维DLT变换)。

编号识别步骤(不唯一):

1、按照共线关系对点进行分组,求解每一组点在影像上的直线方程。

2、用RANSAC方法求解线束中心,并提出错误分组。

3、区分每一组中点的大小状态,并按照二进制编码,从而得到可能的编号。

采用RANSAC方法(以二维射影变换为基础)对已识别的点号实施重新编号,并选择具有最多内点的那个作为最终的编码方案。

5、初值估计:

二维DLT变换求解初值

基于前文提到的标志点提取与识别结果,在应用二维DLT变换方法(张永军, 2006)的基础上进行计算处理后,则能够得到每张图像的内外参数初始值。

6、光速法平差:

列出误差方程

以下是基于您的要求对原文的改写

复制代码
 // OpencvTest.cpp : 定义控制台应用程序的入口点。

    
 //
    
  
    
  
    
 #include "cv.h" 
    
 #include "highgui.h"
    
 #include "math.h"
    
 #include "vector"
    
 using namespace std;
    
  
    
 #define a 100.0	//控制参数,表示曲线的弯曲程度
    
 #define b 0.0	//控制参数,表示曲线的上下偏移量
    
  
    
 struct EllipsePoint
    
 {
    
 	int rowIndex;
    
 	int colIndex;
    
 	bool isEdge;
    
 };
    
  
    
 void LogOperator(IplImage *src, IplImage *dst);//边缘检测log算子
    
 EllipsePoint hasNextPoint(int* imageMat, int i, int j, int mwidth, EllipsePoint *ep);
    
  
    
 int main(int argc, char** argv)
    
 {
    
 	int i, j, index;
    
 	vector<vector<EllipsePoint>> ellipse;
    
 	IplImage* img = cvLoadImage("C:\ Users\ HP\ Desktop\ Buddha_001.JPG");
    
 	//IplImage* img = cvLoadImage("C:\ Users\ HP\ Desktop\ 1.jpg");
    
  
    
 	int mwidth, mheight, mtype;
    
 	mwidth = img->width;
    
 	mheight = img->height;
    
 	mtype = img->nChannels;
    
  
    
  
    
 	int *imageMat = new int[mwidth*mheight*mtype];
    
 	int *imageMat_Gray = new int[mwidth*mheight];
    
  
    
 	//去除像素点放到imageMat中
    
 	for (i = 0; i<mheight; i++)
    
 		for (j = 0; j<mwidth; j++) {
    
 			CvScalar s;
    
 			s = cvGet2D(img, i, j);// get the (i,j) pixel value
    
 			for (index = 0; index<mtype; index++) {
    
 				imageMat[3 * mwidth*i + 3 * j + index] = s.val[index];
    
 			}
    
 		}
    
  
    
 	//RGB转灰度
    
 	for (i = 0; i < mheight; i++)
    
 	{
    
 		for (j = 0; j < mwidth; j++)
    
 		{
    
 			//Gray = (R * 30 + G * 59 + B * 11 + 50) / 100
    
 			//Gray = (R*299 + G*587 + B*114 + 500) / 1000
    
 			imageMat_Gray[i*mwidth + j] = (imageMat[(i*mwidth + j) * 3] * 299 + imageMat[(i*mwidth + j) * 3 + 1] * 597 +
    
 				imageMat[(i*mwidth + j) * 3 + 2] * 114 + 500) / 1000;
    
 		}
    
 	}
    
  
    
 	double temp;
    
 	//图像增强,对数变换
    
 	for (i = 0; i < mheight; i++)
    
 	{
    
 		for (j = 0; j < mwidth; j++)
    
 		{
    
 			temp = a*log10((double)imageMat_Gray[i*mwidth + j] + 1.0) + b;
    
 			if (temp > 255)
    
 				temp = 255.0;
    
 			if (temp < 0)
    
 				temp = 0.0;
    
 			imageMat_Gray[i*mwidth + j] = int(temp + 0.5);
    
 		}
    
 	}
    
  
    
 	IplImage* img_gray = cvCreateImage(cvSize(mwidth, mheight), IPL_DEPTH_8U, 1);
    
 	//将处理后的图像值放入图像中显示
    
 	for (int i = 0; i<mheight; i++)
    
 		for (int j = 0; j<mwidth; j++) {
    
 			CvScalar s;
    
 			s.val[0] = imageMat_Gray[i*mwidth+j];
    
 			cvSet2D(img_gray, i, j, s);
    
 		}
    
  
    
 	IplImage* img_edgeExtract = cvCreateImage(cvSize(mwidth, mheight), IPL_DEPTH_8U, 1);
    
 	//LogOperator(img_gray, img_edgeExtract);
    
 	cvCanny(img_gray, img_edgeExtract, 50, 150, 3);
    
  
    
 	
    
  
    
 	int *imageMat_edgeExtract = new int[mwidth*mheight];
    
 	for (i = 0; i<mheight; i++)
    
 		for (j = 0; j<mwidth; j++) {
    
 			CvScalar s;
    
 			s = cvGet2D(img_edgeExtract, i, j);// get the (i,j) pixel value
    
 			imageMat_edgeExtract[mwidth*i + j] = s.val[0];
    
 		}
    
  
    
  
    
 	IplImage* img4 = cvCreateImage(cvSize(mwidth, mheight), IPL_DEPTH_8U, 3);
    
 	//将处理后的图像值放入图像中显示
    
 	for (int i = 0; i<mheight; i++)
    
 		for (int j = 0; j<mwidth; j++) {
    
 			CvScalar s;
    
 			s.val[0] = imageMat_edgeExtract[i*mwidth + j];
    
 			s.val[1] = imageMat_edgeExtract[i*mwidth + j];
    
 			s.val[2] = imageMat_edgeExtract[i*mwidth + j];
    
 			cvSet2D(img4, i, j, s);
    
 		}
    
 	//for (i = 0; i < 100; i++)
    
 		//printf("%d\n", imageMat_edgeExtract[i]);
    
  
    
 	EllipsePoint *ellipsePoint = new EllipsePoint[mheight*mwidth];
    
 	for (i = 0; i < mheight; i++)
    
 	{
    
 		for (j = 0; j < mwidth; j++)
    
 		{
    
 			ellipsePoint[i*mwidth+j].isEdge = 0;
    
 			ellipsePoint[i*mwidth + j].rowIndex = i;
    
 			ellipsePoint[i*mwidth + j].colIndex = j;
    
 		}
    
 	}
    
  
    
 	int num = 0;
    
 	//边缘跟踪
    
 	for (i = 1; i < mheight-1; i++)
    
 	{
    
 		for (j = 1; j < mwidth-1; j++)
    
 		{
    
 			//if (ellipsePoint[i*mwidth + j].isEdge == 1)
    
 			if (imageMat_edgeExtract[i*mwidth + j] == 0)
    
 				continue;
    
 			if (ellipsePoint[i*mwidth + j].isEdge == 1)
    
 				continue;
    
 			EllipsePoint elli = hasNextPoint(imageMat_edgeExtract, i, j, mwidth, ellipsePoint);
    
 			if ((elli.rowIndex<1 || elli.rowIndex>mheight - 2 || elli.colIndex < 1 || elli.colIndex > mwidth - 2) && elli.isEdge)
    
 			{
    
 				imageMat_edgeExtract[i*mwidth + j] = 0;
    
 				imageMat_edgeExtract[elli.rowIndex*mwidth + elli.colIndex] = 0;
    
 			}
    
 			else if (!elli.isEdge)
    
 			{
    
 				imageMat_edgeExtract[i*mwidth + j] = 0;
    
 				//num++;
    
 			}
    
 //			else if (imageMat_edgeExtract[i*mwidth + j] == 255)
    
 			else
    
 			{
    
 				//num++;
    
 				//printf("%d,%d\n", i, j);
    
 				ellipsePoint[i*mwidth + j].isEdge = 1;
    
 				//ellipsePoint[i*mwidth + j].rowIndex = i;
    
 				//ellipsePoint[i*mwidth + j].colIndex = j;
    
 				int m, n;
    
 				vector <EllipsePoint> ep;
    
 				ep.push_back(ellipsePoint[i*mwidth + j]);
    
 				ep.push_back(elli);
    
 				EllipsePoint elli2 = hasNextPoint(imageMat_edgeExtract, elli.rowIndex, elli.colIndex, mwidth, ellipsePoint);
    
 				while (elli2.isEdge)
    
 				{
    
 					ep.push_back(elli2);
    
 					m = elli2.rowIndex;
    
 					n = elli2.colIndex;
    
 					if (m<1 || m>mheight - 2 || n < 1 || n > mwidth - 2)
    
 						break;
    
 					elli2 = hasNextPoint(imageMat_edgeExtract, m, n, mwidth, ellipsePoint);
    
 				}
    
 				/*if ((ep[0].rowIndex != ep[ep.size() - 1].rowIndex) || (ep[0].colIndex != ep[ep.size() - 1].colIndex))
    
 				{
    
 					for (i = 0; i < ep.size(); i++)
    
 					{
    
 						imageMat_edgeExtract[ep[i].rowIndex*mwidth + ep[i].colIndex] = 0;
    
 						//ellipsePoint[ep[i].rowIndex*mwidth + ep[i].colIndex].isEdge = 0;
    
 					}
    
 				}*/
    
 				if (ep[ep.size() - 1].rowIndex<1 || ep[ep.size() - 1].rowIndex>mheight - 2 || ep[ep.size() - 1].colIndex < 1 || ep[ep.size() - 1].colIndex > mwidth - 2)
    
 				{
    
 					for (int ii = 0; ii < ep.size(); ii++)
    
 					{
    
 						imageMat_edgeExtract[ep[ii].rowIndex*mwidth + ep[ii].colIndex] = 0;
    
 					}
    
 				}
    
 				//else if (hasPointNum(imageMat_edgeExtract, ep[ep.size() - 1].rowIndex, ep[ep.size() - 1].colIndex, mwidth) <3)
    
 				else if(abs(ep[ep.size() - 1].rowIndex- ep[0].rowIndex)>1|| abs(ep[ep.size() - 1].colIndex - ep[0].colIndex)>1||ep.size()<100)
    
 				{
    
 					for (int ii = 0; ii < ep.size(); ii++)
    
 					{
    
 						imageMat_edgeExtract[ep[ii].rowIndex*mwidth + ep[ii].colIndex] = 0;
    
 					}
    
 				}
    
 				else
    
 					ellipse.push_back(ep);
    
 				//ep.clear();
    
 				ep.swap(vector <EllipsePoint>());
    
 			}
    
 			/*else {
    
 				ellipsePoint[elli.rowIndex*mwidth + elli.colIndex].isEdge = 0;
    
 			}*/
    
 		}
    
 	}
    
  
    
 	int numCount = ellipse.size();//椭圆个数
    
 	CvBox2D *box = new CvBox2D[numCount];
    
 	CvPoint pt;
    
 	for (i = 0; i < numCount; i++)
    
 	{
    
 		CvMemStorage* storage = cvCreateMemStorage(0);
    
 		CvSeq* ptseq = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);//创建点序列
    
 		for (int j = 0; j < ellipse[i].size(); j++)
    
 		{
    
 			pt.x = ellipse[i][j].colIndex;
    
 			pt.y = ellipse[i][j].rowIndex;
    
 			cvSeqPush(ptseq, &pt);
    
 		}
    
 		box[i] = cvFitEllipse2(ptseq);//拟合椭圆
    
 		//cvEllipseBox(img, box[i], CV_RGB(0, 255, 0), 2);//绘制在图形上
    
 		//cvLine(img, cvPoint((int)box[i].center.x - 5, (int)box[i].center.y), cvPoint((int)box[i].center.x + 5, (int)box[i].center.y), CV_RGB(255, 0, 0));
    
 		//cvLine(img, cvPoint((int)box[i].center.x, (int)box[i].center.y - 5), cvPoint((int)box[i].center.x, (int)box[i].center.y + 5), CV_RGB(255, 0, 0));
    
 	}
    
  
    
 	int distance;
    
 	int *finalellipse = new int[ellipse.size()];
    
 	for (i = 0; i < ellipse.size(); i++)
    
 		finalellipse[i] = 1;
    
 	for (i = 0; i < numCount; i++)
    
 	{
    
 		int outpointnum = 0;
    
 		for (int j = 0; j < ellipse[i].size(); j++)
    
 		{
    
 			pt.x = ellipse[i][j].colIndex;
    
 			pt.y = ellipse[i][j].rowIndex;
    
 			distance = sqrt((pt.x - box[i].center.x)*(pt.x - box[i].center.x) + (pt.y - box[i].center.y)*(pt.y - box[i].center.y));
    
 			if ((distance >(max(box[i].size.height / 2, box[i].size.width / 2)) + 1)||(distance <(min(box[i].size.height / 2, box[i].size.width / 2)) - 1))
    
 			{
    
 				outpointnum++;
    
 				if(outpointnum>10)
    
 				{
    
 					finalellipse[i] = 0;
    
 					break;
    
 				}
    
 			}
    
 		}
    
 		if (finalellipse[i])
    
 		{
    
 			cvEllipseBox(img, box[i], CV_RGB(0, 255, 0), 2);//绘制在图形上
    
 			cvLine(img, cvPoint((int)box[i].center.x - 5, (int)box[i].center.y), cvPoint((int)box[i].center.x + 5, (int)box[i].center.y), CV_RGB(255, 0, 0));
    
 			cvLine(img, cvPoint((int)box[i].center.x, (int)box[i].center.y - 5), cvPoint((int)box[i].center.x, (int)box[i].center.y + 5), CV_RGB(255, 0, 0));
    
 		}
    
 	}
    
  
    
 	//m_img.CopyOf(m_iplImg);
    
  
    
 	//printf("num is:%d\n", num);
    
 	printf("The ellipse num:%d\n",ellipse.size());
    
 	IplImage* img_edgeTrace = cvCreateImage(cvSize(mwidth, mheight), IPL_DEPTH_8U, 1);
    
 	//将处理后的图像值放入图像中显示
    
 	for (int i = 0; i<mheight; i++)
    
 		for (int j = 0; j<mwidth; j++) {
    
 			CvScalar s;
    
 			s.val[0] = imageMat_edgeExtract[i*mwidth + j];
    
 			cvSet2D(img_edgeTrace, i, j, s);
    
 		}
    
  
    
  
    
 	IplImage* img5= cvCreateImage(cvSize(mwidth, mheight), IPL_DEPTH_8U, 3);
    
 	//将处理后的图像值放入图像中显示
    
 	for (int i = 0; i<mheight; i++)
    
 		for (int j = 0; j<mwidth; j++) {
    
 			CvScalar s;
    
 			s.val[0] = imageMat_edgeExtract[i*mwidth + j];
    
 			s.val[1] = imageMat_edgeExtract[i*mwidth + j];
    
 			s.val[2] = imageMat_edgeExtract[i*mwidth + j];
    
 			cvSet2D(img5, i, j, s);
    
 		}
    
 	//保存灰度图
    
 	const char* path;
    
 	path = "C:\ Users\ HP\ Desktop\ image4.JPG";
    
 	cvSaveImage(path, img5);
    
  
    
  
    
 	cvNamedWindow("显示原图像", 0);
    
 	cvShowImage("显示原图像", img);
    
 	cvNamedWindow("显示灰度图像", 0);
    
 	cvShowImage("显示灰度图像", img_gray);
    
  
    
 	cvNamedWindow("显示边缘提取后的图像", 0);
    
 	cvShowImage("显示边缘提取后的图像", img_edgeExtract);
    
 	cvNamedWindow("显示边缘跟踪后的图像", 0);
    
 	cvShowImage("显示边缘跟踪后的图像", img_edgeTrace);
    
  
    
 	cvWaitKey(0);
    
 	cvReleaseImage(&img);
    
 	cvReleaseImage(&img_gray);
    
 	cvReleaseImage(&img_edgeExtract);
    
 	cvReleaseImage(&img_edgeTrace);
    
 	cvDestroyWindow("显示原图像");
    
 	cvDestroyWindow("显示灰度图像");
    
 	cvDestroyWindow("显示边缘提取后的图像");
    
 	cvDestroyWindow("显示边缘跟踪后的图像");
    
 	delete[] imageMat;
    
 	delete[] imageMat_Gray;
    
 	delete[] imageMat_edgeExtract;
    
 }
    
  
    
 void LogOperator(IplImage *src, IplImage *dst)
    
 {
    
 	//dst = cvCloneImage(src);  
    
 	IplImage* SmoothImg = cvCloneImage(src);
    
  
    
 	cvSmooth(src, SmoothImg, CV_GAUSSIAN, 15, 15);  //对图像做3*3的高斯平滑滤波  
    
 	cvLaplace(SmoothImg, dst, 5);
    
  
    
 	double min_val = 0; double max_val = 0;
    
 	cvMinMaxLoc(dst, &min_val, &max_val);   //取图像中的最大最小像素值  
    
 	//printf("max_val = %f\nmin_val = %f\n", max_val, min_val);
    
  
    
 	//cvNormalize(dst, dst, 0, 255, CV_MINMAX); //归一化处理  
    
  
    
 	//对梯度图加门限,二值化  
    
 	int x,y;
    
 	char* p = dst->imageData;
    
 	int w = dst->widthStep;
    
 	for(x = 0;x<dst->width;x++)
    
 	{
    
 		for(y = 0;y<dst->height;y++)
    
 		{
    
 			if(p[x+y*w]>40)
    
 				p[x+y*w] = 255;
    
 			else 
    
 				p[x+y*w] = 0;
    
 		}
    
 	}
    
 	/*for (x = 0; x < dst->height; x++)
    
 	{
    
 		for (y = 0; y < dst->width; y++)
    
 		{
    
 			if (p[x*dst->height + y]>100)
    
 				p[x*dst->height + y] = 255;
    
 			else
    
 				p[x*dst->height + y] = 0;
    
 		}
    
 	}*/
    
 	cvReleaseImage(&SmoothImg);
    
 }
    
  
    
 EllipsePoint hasNextPoint(int* imageMat, int i, int j, int mwidth, EllipsePoint *ep)
    
 {
    
 	//int arr[8];
    
 	int index[8];
    
 	EllipsePoint ell;
    
 	ell.isEdge = 0;
    
 	index[4] = (i - 1)*mwidth + j - 1;
    
 	index[0] = (i - 1)*mwidth + j;
    
 	index[5] = (i - 1)*mwidth + j + 1;
    
 	index[2] = i*mwidth + j - 1;
    
 	index[3] = i*mwidth + j + 1;
    
 	index[6] = (i + 1)*mwidth + j - 1;
    
 	index[1] = (i + 1)*mwidth + j;
    
 	index[7] = (i + 1)*mwidth + j + 1;
    
 	for (int k = 0; k < 8; k++)
    
 	{
    
 		if (!ep[index[k]].isEdge&&imageMat[index[k]]==255)
    
 		{
    
 			ell.isEdge = 1;
    
 			ep[index[k]].isEdge = 1;
    
 			//ep[i*mwidth + j].rowIndex = index[k] / mwidth;
    
 			//ep[i*mwidth + j].colIndex = index[k] - ep[i*mwidth + j].rowIndex*mwidth;
    
 			//ep[index[k]].rowIndex = index[k] / mwidth;
    
 			//ep[index[k]].colIndex = index[k] - ep[index[k]].rowIndex*mwidth;
    
 			ell.rowIndex = index[k] / mwidth;
    
 			ell.colIndex = index[k] - ell.rowIndex*mwidth;
    
 			break;
    
 		}
    
 	}
    
 	//return ep[i*mwidth + j];
    
 	//返回下一个点的信息
    
 	return ell;
    
 }

在本项目中应用了OpenCV的一些核心函数。有关OpenCV的具体参数设置,请参考我的另一篇详细文章:

全部评论 (0)

还没有任何评论哟~