最近项目,记录下来以防将来忘记
一.建立开发环境
采用技术opencv2.4.0
VS2010
1.下载opencv for windows安装包,程序采用的是opencv-2.4.0版本。双击打开解压到C:\Users\teng\working\opencv目录下,头文件目录为build\install\include,库文件目录为build\install\lib
2、建立工程名为Camera_Select_Card,将文件和库文件的路径分别加到工程的目录,就可利用其提供的函数进行开发了.
二.目前已实现的功能
1.从摄像头读取数据
Windows版本opencv提供了VideoCapture类进行摄像操作.
使用函数g_cap.open(0)打开摄像头,使用
Mat frame;
g_cap>> frame;
获取摄像头当前帧图像,并存放在Mat矩阵变量frame里.
2.对工业相机生成的变形图像进行校正
采用短焦距相机,图像发生了畸变,图像靠近边框的部分发生了明显的弯曲,需要进行校正,过程如下:
2.1对相机进行定标
定标的实际就是获取图像发生畸变的参数,根据这些参数对获取的图像进行校正.opencv自带了定标的源码以及方法.
2.1.1首先在samples\cpp目录下获取calibration.cpp源码文件。为该文件建一个工程,把其编译成可执行文件calibration.exe。
2.1.2在A4纸上打印一张10*7格黑白相间的棋盘纸,每个格子的长度是27mm*27mm。用这张纸在摄像头上晃动取取图像保存到文件,图像越多越准确。把这些图像文件名列在list_of_views.xml文件里面,格式如下:
<?xml version="1.0"?>
< opencv_storage>
<images>
left02.bmp
left03.bmp
left04.bmp
…
</images>
</opencv_storage>
2.1.3有了图像文件,就可以运行calibration.exe程序生成校正参数文件了:alibration.exe-w 9 -h 6 -p -o camera.yml -op -oe -s 2 list_view.xml
。这里需要注意的是 -w -h参数指的是棋盘格在长宽两个方向上的角点个数而不是棋盘格在两个方向上的方格个数。运行命令过后就会在目录下生成camera.yml,要的就是这个文件。
把这个复制到工程下面。
2.1.4在打开摄像的地方加入代码
Mat img;
g_cap>>img;
FileStorage fs2("config/camera.yml",FileStorage::READ);
intframeCount = (int)fs2["nframes"];
std::string date;
fs2["calibration_time"]>> date;
Mat cameraMatrix, distCoeffs;
fs2["camera_matrix"]>> cameraMatrix;
fs2["distortion_coefficients"]>> distCoeffs;
Mat view,rview, map1, map2;
initUndistortRectifyMap(cameraMatrix,distCoeffs, Mat(),getOptimalNewCameraMatrix(cameraMatrix, distCoeffs,img.size(), 1, img.size(), 0),img.size(), CV_16SC2, map1, map2);
这样就可以得到摄像头的校正矩阵map1,map2。获取图像的地方队获取的图像进行处理:
g_cap>>frame;
Mat rview;
Mat m;
remap(frame, m, map1, map2,INTER_LINEAR);
m矩阵就是经过校正的图像了。
3.把图像数据转换成BITMAPINFO在vs界面上显示
Opencv虽然提供了imshow等函数显示图像,但在具体的应用中往往需要把图像显示在自己的界面控件(如:Picture Control)中,用如下函数可以实现:
voidshowMatImgToWnd(CWnd* pWnd, const cv::Mat& img)
{
if(img.empty()) return;
static BITMAPINFO *bitMapinfo = NULL;
static bool First=TRUE;
if(First)
{
BYTE *bitBuffer = newBYTE[40+4*256];//开辟一个内存区域
if(bitBuffer == NULL)
{
return;
}
First=FALSE;
memset(bitBuffer, 0, 40+4*256);
bitMapinfo = (BITMAPINFO *)bitBuffer;
bitMapinfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bitMapinfo->bmiHeader.biPlanes = 1;
for(int i=0; i<256; i++)
{ //颜色的取值范围 (0-255)
bitMapinfo->bmiColors[i].rgbBlue =bitMapinfo->bmiColors[i].rgbGreen =bitMapinfo->bmiColors[i].rgbRed =(BYTE) i;
}
}
bitMapinfo->bmiHeader.biHeight =-img.rows;
bitMapinfo->bmiHeader.biWidth =img.cols;
bitMapinfo->bmiHeader.biBitCount=img.channels() *8;
CRect drect;
pWnd->GetClientRect(drect); //pWnd指向CWnd类的一个指针
CClientDC dc(pWnd);
HDC hDC =dc.GetSafeHdc(); //HDC是Windows的一种数据类型,是设备描述句柄;
SetStretchBltMode(hDC, COLORONCOLOR);
StretchDIBits(hDC,0,0,
drect.right, //显示窗口宽度
drect.bottom, //显示窗口高度
0,
0,
img.cols, //图像宽度
img.rows, //图像高度
img.data,
bitMapinfo,
DIB_RGB_COLORS,
SRCCOPY
);
}
4.把图像转化成灰度图
Mat gray;
cvtColor(img,gray,CV_BGR2GRAY);
将彩色图像img转成灰度图矩阵。
5.对灰度图进行边界增强处理。
边界增强处理的目的就是让物体的边界分明,便于轮廓查找,图像学算法中有Sobel算子,Laplacian算子(二阶微分)等,本程序采用Canny算子进行边缘增强。Opencv提供的函数
CV_EXPORTS_W voidCanny( InputArray image,
OutputArrayedges,double threshold1, double threshold2,
int apertureSize=3, bool L2gradient=false );
Canny算法包含许多可以调整的参数,它们将影响到算法的计算的时间与实效。
阈值:使用两个阈值比使用一个阈值更加灵活,但是它还是有阈值存在的共性问题。设置的阈值过高,可能会漏掉重要信息;阈值过低,将会把枝节信息看得很重要。
经过检验采用如下参数:
Canny(detected_edges, detected_edges, 80, 80*2.5, 3 );
6.对边界增强处理过的图像进行二值化
二值化处理的目的是让轮廓黑白分明,便于后续的处理。Opencv提供的函数如下:
Mat img_bin;
threshold(detected_edges,img_bin,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU);
7.对二值图像进行形态学处理
形态学处理的目的实现消除噪声,分割(isolate)出独立的图像元素,在图像中连接(join)相邻的元素。程序采用opencv提供的2个函数:
dilate(img_bin,m_ResImg,elementX,Point(-1,-1),1);
erode(m_ResImg,m_ResImg,elementX,Point(-1,-1),1);
先对图像进行膨胀处理,将异型卡的边界轮廓连接起来并且加粗,再进行腐蚀处理,可以把卡上的图案等去除。
8.经过以上处理以后就可以对图像进行轮廓提取了
使用函数 findContours(gray_bi, contours, hierarchy,
CV_RETR_EXTERNAL,
CV_CHAIN_APPROX_SIMPLE,
Point(0, 0) );
所有的轮廓以点集合的形式存放在变量contours,以下处理过程就是遍历这个集合进行识别了。
9.遍历集合取出各个元素进行处理
获取集合的头元素
vector<vector<Point>>::const_iterator itc=contours.begin();
10.从集合中取出轮廓点进行处理
计算出异型卡轮廓的重心点坐标,外接圆半径,最小外接矩形4个点。
RotatedRect minRect = minAreaRect(*itc);
Point2f vertices[4];
minRect.points(vertices); //获得最小外接矩形4个点
Point2f center;
float radius;
minEnclosingCircle(*itc, center, radius);
11.计算机械手坐标与图像平面坐标的映射关系
将图像坐标转换成机械手坐标,其中fRe为机械手坐标与摄像头图像坐标的比。
CDobotPoint p1;
p1.x = g_center_point.x- (g_height/2.0 - c3f.cPiont.y) * fRe;
p1.y = g_center_point.y- (g_width/2.0 - c3f.cPiont.x) * fRe;
p1.z = g_center_point.z + 50;
12.发送指令给机械手去抓取异型卡
以上就是整个应用程序的处理过程.