opencv学习系列:实例练习(含多个实例)
2017-12-25 15:44
417 查看
//-----------------------------------(续10月份)------------------------------------- //int main(int argc,char* argv[])和int main(int argc, char **argv)的使用方法 int main(int argc, char* argv[]) { int i; for (i = 0; i<argc; i++) cout<<argv[i]<<endl; cin>>i; return 0; } 在DOS执行时敲入 F:\MYDOCU~1\TEMPCODE\D1\DEBUG\D1.EXE aaaa bbb ccc ddd 输出如下: F:\MYDOCU~1\TEMPCODE\D1\DEBUG\D1.EXE aaaa bbb ccc ddd //-----------------------------------OpenCV学习------------------------------------- // 程序名称:OSTU算法选自适应阈值 // 2016年10月 Created by孙立波(Visual Studio 2013+OpenCV2.4.9) #include <opencv2\opencv.hpp> #include <iostream> #include <string> #include <cassert> using namespace cv; using namespace std; ////隐藏控制台窗口 //#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") int otsu(IplImage *image) { assert(NULL != image);//若执行该函数,则终止程序运行 int width = image->width; int height = image->height; int x = 0, y = 0; int pixelCount[256]; float pixelPro[256]; int i, j, pixelSum = width * height, threshold = 0; uchar* data = (uchar*)image->imageData; //初始化 for (i = 0; i < 256; i++) { pixelCount[i] = 0; pixelPro[i] = 0; } //统计灰度级中每个像素在整幅图像中的个数 for (i = y; i < height; i++) { for (j = x; j<width;j++) pixelCount[data[i * image->widthStep + j]]++; } //计算每个像素在整幅图像中的比例 for (i = 0; i < 256; i++) { pixelPro[i] = (float)(pixelCount[i]) / (float)(pixelSum); } //经典ostu算法,得到前景和背景的分割 //遍历灰度级[0,255],计算出方差最大的灰度值,为最佳阈值 float w0, w1, u0tmp, u1tmp, u0, u1, u, deltaTmp, deltaMax = 0; for (i = 0; i < 256; i++)//i为阈值 { w0 = w1 = u0tmp = u1tmp = u0 = u1 = u = deltaTmp = 0; for (j = 0; j < 256; j++)//遍历阈值数组当i=0,1,2,3...255 { if (j <= i) //背景部分 { //以i为阈值分类,第一类总的概率 w0 += pixelPro[j]; u0tmp += j * pixelPro[j]; } else //前景部分 { //以i为阈值分类,第二类总的概率 w1 += pixelPro[j]; u1tmp += j * pixelPro[j]; } } u0 = u0tmp / w0; //第一类的平均灰度 u1 = u1tmp / w1; //第二类的平均灰度 u = u0tmp + u1tmp; //整幅图像的平均灰度 //计算类间方差 deltaTmp = w0 * (u0 - u)*(u0 - u) + w1 * (u1 - u)*(u1 - u); //找出最大类间方差以及对应的阈值 if (deltaTmp > deltaMax) { deltaMax = deltaTmp; threshold = i; } } //返回最佳阈值; return threshold; } int main(int argc, char* argv[]) { IplImage* srcImage = cvLoadImage("F:\\workplace\\opencv_training\\test2.png", 0); assert(NULL != srcImage); cvNamedWindow("src"); cvShowImage("src", srcImage); IplImage* biImage = cvCreateImage(cvGetSize(srcImage), 8, 1); //计算最佳阈值 int threshold = otsu(srcImage); //对图像二值化 cvThreshold(srcImage, biImage, threshold, 255, CV_THRESH_BINARY); cvNamedWindow("binary"); cvShowImage("binary", biImage); cvWaitKey(0); cvReleaseImage(&srcImage); cvReleaseImage(&biImage); cvDestroyWindow("src"); cvDestroyWindow("binary"); return 0; } //************************************************************************************************ //-----------------------------------OpenCV2---标定焊接图像------------------------------------- // 程序名称:OpenCV标定焊接图像,该程序根据DOS提示,一步步实现标定的自动化,求取内参 // 所用IDE版本: Visual Studio 2013 // 开发所用OpenCV版本: 2.4.9 // 2016年10月 Created by 孙立波 //包含程序所依赖的头文件:为方便起见把经常用的头文件都写在这里(前三必须包含),也可以用#include "opencv.hpp"包含下面所有头文件 #include <opencv2\opencv.hpp> #include <iostream> #include <string> #include <vector> #include <iomanip> #include <fstream> using namespace cv; using namespace std; //隐藏控制台窗口 //#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void main() { /************************************************************************ 读取21张图像,存入内存F:\biaoding文件中 *************************************************************************/ cout << "开始提取21张标定板图像………………" << endl; int imageCount = 21; int key; int count1=0; for (int i=0; i != imageCount; i++) { cout << "Frame#" << i + 1 << "..." << endl; std::stringstream StrStm; //为了将i转化为字符型,用StrStm做中介 string imageFileName; StrStm << i + 1; StrStm >> imageFileName; imageFileName += ".jpg"; key = waitKey(); cout << "按Enter开始抽取图像,进入后可按q或者ESC键重新抽取图像,若按Enter键表明这帧图像被存入文件夹中" << endl; if (key == 13) { int flag = 1; while (flag) { Mat image0;//抽取到的临时图像数据并显示出来 imshow("显示抓取图像", image0);//显示是否符合要求 int key2 = waitKey(); if (key2 == 13) { cout << "提取标定板图像成功!………………" << endl; std::stringstream str; str << "F:\\biaoding\\img" << std::setw(2) << std::setfill('0') << i + 1 << ".jpg"; std::cout<<"提取的图像保存路径及文件名" << str.str() << endl; imwrite(str.str(), image0); flag = 0; count1 += 1;//已经得到的标定图像计数总数 } else if (key2 == 113 || key2 == 27) cout << "这次提取的标定板图像不成功!重新提取!!!!………………" << endl; } } } if (count1 == 21) { cout << "***********************………………" << endl; cout << "***********************………………" << endl; cout << "下面开始标定图像...................." << endl; count1 = 0; } /************************************************************************ 读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化 *************************************************************************/ cout << "开始提取角点………………" << endl; double time0 = static_cast<double>(getTickCount()); ofstream fout("F:\\biaoding\\caliberation_result.txt"); /** 保存定标结果的文件 **/ Size image_size; /**** 图像的尺寸 ****/ Size board_size = Size(9, 6); /**** 定标板上每行、列的角点数 ****/ vector<Point2f> corners; /**** 缓存每幅图像上检测到的角点 ****/ vector<vector<Point2f>> corners_Seq; /**** 保存检测到的所有角点 ****/ vector<Mat> image_Seq; int count = 0; int image_count = 21; for (int i = 0; i != image_count; i++) { cout << "Frame #" << i + 1 << "..." << endl; std::stringstream str; str << "F:\\biaoding\\img" << std::setw(2) << std::setfill('0') << i + 1 << ".jpg"; std::cout << str.str() << std::endl; Mat image = cv::imread(str.str()); image_size = image.size(); //image_size = Size(image.cols , image.rows); /* 提取角点 */ int channel = 3; Mat imageGray; if (channel == image.channels() ) cvtColor(image, imageGray, CV_RGB2GRAY); else imageGray=image; bool patternfound = findChessboardCorners(image, board_size, corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK); if (!patternfound) { cout << "can not find chessboard corners!\n"; continue; exit(1); } else { /* 亚像素精确化 :迭代过程的终止条件可以是最大迭代次数CV_TERMCRIT_ITER类型,或者是设定的精度CV_TERMCRIT_EPS类型(或者是两 者的组合)。终止条件的设置在极大程度上影响最终得到的亚像素值的精度。在此,指定为0.10,则求得的亚像素级精度为像素的十分 之一 */ cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1)); //Size(11, 11)为搜索窗口的一半尺寸,Size(-1, -1)死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。/ //它是用来避免自相关矩阵出现的某些可能的奇异性。当值为(-1,-1)表示没有死区。 //TermCriteria为求角点的迭代过程的终止条件。即角点位置的确定,要么迭代数大于某个设定值,或者是精确懂达到某个设定值。 //criteria可以是最大迭代数目,或者是设定的精确度,也可以是它们的组合。 /* 绘制检测到的角点并保存 */ Mat imageTemp = image.clone(); for (int j = 0; j < corners.size(); j++) { circle(imageTemp, corners[j], 10, Scalar(0, 0, 255), 2, 8, 0); } string imageFileName1; std::stringstream StrStm; StrStm << i + 1; StrStm >> imageFileName1; imageFileName1 += "_corner.jpg"; //保存提取角点的图像 imwrite("F:\\biaoding\\imageFileName1", imageTemp); cout << "Frame corner#" << i + 1 << "...end" << endl; count = count + corners.size();//记录这一副保存到coners容器中的角点总数 //将该角点压入角点序列堆栈 corners_Seq.push_back(corners);//将所有图像的coners全部压入一个序列的堆栈,即元素为vector容器的堆栈 } //将处理过的图像压入源图像堆栈 image_Seq.push_back(image); } cout << "角点提取完成!下一步摄像机定标\n"; /************************************************************************ 摄像机定标 *************************************************************************/ cout << "开始定标………………" << endl; Size square_size = Size(80, 80); /**** 实际测量得到的定标板上每个棋盘格的大小,单位为像素 ****/ vector<vector<Point3f>> object_Points; /**** 保存定标板上角点的三维坐标 ****/ Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0)); /***** 保存提取的所有角点1*序列图像的角点总数 *****/ vector<int> point_counts; /***** 每幅图像中角点的数量 ****/ Mat intrinsic_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /***** 摄像机内参数矩阵 ****/ Mat distortion_coeffs = Mat(1, 4, CV_32FC1, Scalar::all(0)); /* 摄像机的4个畸变系数:k1,k2,p1,p2 */ vector<cv::Mat> rotation_vectors; /* 图像序列图像的旋转向量,每一行代表一个旋转向量 */ vector<cv::Mat> translation_vectors; /* 每幅图像的平移向量,每一行代表一个平移向量 */ /* 初始化定标板上角点的三维坐标,此用的是像素坐标 */ for (int t = 0; t<image_count; t++) { vector<Point3f> tempPointSet;//存储每幅图像的像素坐标 for (int i = 0; i<board_size.height; i++) { for (int j = 0; j<board_size.width; j++) { /* 假设定标板放在世界坐标系中z=0的平面上 */ Point3f tempPoint; //在这里,board_size中棋盘格子为单位长度,当square_size存储的像素边长为边长为80,代表width、height为80 tempPoint.x = i*square_size.width; tempPoint.y = j*square_size.height; tempPoint.z = 0; tempPointSet.push_back(tempPoint); } } object_Points.push_back(tempPointSet);//存储图像序列中每幅图像的像素坐标 } /* 初始化每幅图像中的角点数量,这里我们假设每幅图像中都可以看到完整的定标板 */ for (int i = 0; i< image_count; i++) { point_counts.push_back(board_size.width*board_size.height); } /* 开始定标 */ calibrateCamera(object_Points, corners_Seq, image_size, intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, 0); cout << "定标完成!\n"; /************************************************************************ 对定标结果进行评价 *************************************************************************/ cout << "开始评价定标结果………………" << endl; double total_err = 0.0; /* 所有图像的平均误差的总和 */ double err = 0.0; /* 每幅图像的平均误差 */ vector<Point2f> image_points2; /**** 保存重新计算得到的投影点 ****/ cout << "每幅图像的定标误差:" << endl; fout << "每幅图像的定标误差:" << endl << endl; for (int i = 0; i<image_count; i++) { vector<Point3f> tempPointSet = object_Points[i]; /**** 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 ****/ projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs, image_points2); /* 计算新的投影点和旧的投影点之间的误差*/ vector<Point2f> tempImagePoint = corners_Seq[i]; Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2); Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2); for (size_t i = 0; i != tempImagePoint.size(); i++) { image_points2Mat.at<Vec2f>(0, i) = Vec2f(image_points2[i].x, image_points2[i].y); tempImagePointMat.at<Vec2f>(0, i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y); } err = norm(image_points2Mat, tempImagePointMat, NORM_L2); total_err += err /= point_counts[i]; cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl; fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl; } cout << "总体平均误差:" << total_err / image_count << "像素" << endl; fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl; cout << "评价完成!" << endl; /************************************************************************ 保存定标结果 *************************************************************************/ cout << "开始保存定标结果………………" << endl; Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */ fout << "相机内参数矩阵:" << endl; cout << "相机内参数矩阵:" << endl; fout << intrinsic_matrix << endl; cout << intrinsic_matrix << endl; fout << "畸变系数:\n"; cout << "畸变系数:\n"; fout << distortion_coeffs << endl; cout << distortion_coeffs << endl; for (int i = 0; i<image_count; i++) { fout << "第" << i + 1 << "幅图像的旋转向量:" << endl; fout << rotation_vectors[i] << endl; /* 将旋转向量转换为相对应的旋转矩阵 */ Rodrigues(rotation_vectors[i], rotation_matrix); fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl; fout << rotation_matrix << endl; fout << "第" << i + 1 << "幅图像的平移向量:" << endl; fout << translation_vectors[i] << endl; } cout << "完成保存" << endl; fout << endl; /************************************************************************ 显示定标结果 *************************************************************************/ Mat mapx = Mat(image_size, CV_32FC1); Mat mapy = Mat(image_size, CV_32FC1); Mat R = Mat::eye(3, 3, CV_32F); cout << "保存矫正图像" << endl; for (int i = 0; i != image_count; i++) { cout << "Frame #" << i + 1 << "..." << endl; //newCameraMatrix——输入的校正后的3X3摄像机矩阵(也可用cvStereoRectify()得出的3X4的左或右投影矩阵,其实系统会自动提取该 //矩阵前三列的有用部分作为输入参数)注:!!无校正变换的相机仍用求得内参矩阵 Mat newCameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); //得到映射关系:R——输入的第一和第二相机坐标系之间的旋转矩阵,一般般无校正变换的相机,默认为单位矩阵 // opencv中,remap与undistortion都是消除畸变的函数,undistortion在设置了一些参数后调用了remap函数,二者的算法实质是一样 //的。由目标图像的坐标,找到对应的原始图像坐标,然后将其值复制到目标图像。大致思路是如此,由于图像大小和变换,需要插值或 //近似的方法,如最邻近法、线性插值等 initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, R, intrinsic_matrix, image_size, CV_32FC1, mapx, mapy); Mat t = image_Seq[i].clone(); cv::remap(image_Seq[i], t, mapx, mapy, INTER_LINEAR); string imageFileName; std::stringstream StrStm; StrStm << i + 1; StrStm >> imageFileName; imageFileName += "_校正图像.jpg"; imwrite("F:\\biaoding\\imageFileName", t);//*********************************把每幅校正后的图像保存到内存F:\biaoding文件中 } cout << "保存结束" << endl; time0 = ((double)getTickCount() - time0) / getTickFrequency(); cout << "定标及用畸变参数和内参矩阵矫正所有图像总用时:" << time0 << "秒" << endl; waitKey(0); } *********************************************************************************************** //-----------------------------------OpenCV3标定焊接图像------------------------------------- // 程序名称:OpenCV标定焊接图像,该程序根据DOS提示,一步步实现标定的自动化,求取内参,标定请在F盘创建“biaoding”用于储存采集的图像和结果 // 需要设置的参数: 需要提取的的图像数imageCount = 21,每个标定板图像角点数board_size = Size(9, 6),每个格子像素边长Size square_size = Size(80, 80); // 过程描述:提取图像并保存,提取各图像序列角点,角点亚像素精确化,摄像机标定畸变参数和内参,定标结果评价(保存结果在F:\\biaoding\\caliberation_result.txt),最后矫正所有图像到F:\\biaoding // 所用IDE版本: Visual Studio 2013 // 开发所用OpenCV版本: 2.4.9 // 2016年10月 Created by 孙立波 //包含程序所依赖的头文件:为方便起见把经常用的头文件都写在这里(前三必须包含),也可以用#include "opencv.hpp"包含下面所有头文件 #include <opencv2\opencv.hpp> #include <iostream> #include <string> #include <vector> #include <iomanip> #include <fstream> using namespace cv; using namespace std; //隐藏控制台窗口 //#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void main() { /************************************************************************ 读取21张图像,存入内存F:\biaoding文件中 *************************************************************************/ cout << "开始提取21张标定板图像………………" << endl; int imageCount = 21; int key; int count1=0; for (int i=0; i != imageCount; i++) { cout << "Frame#" << i + 1 << "..." << endl; std::stringstream StrStm; //为了将i转化为字符型,用StrStm做中介 string imageFileName; StrStm << i + 1; StrStm >> imageFileName; imageFileName += ".jpg"; key = waitKey(); cout << "按Enter开始抽取图像,进入后可按q或者ESC键重新抽取图像,若按Enter键表明这帧图像被存入文件夹中" << endl; if (key == 13) { int flag = 1; while (flag) { Mat image0;//抽取到的临时图像数据并显示出来 imshow("显示抓取图像", image0);//显示是否符合要求 int key2 = waitKey(); if (key2 == 13) { cout << "提取标定板图像成功!………………" << endl; std::stringstream str; str << "F:\\biaoding\\img" << std::setw(2) << std::setfill('0') << i + 1 << ".jpg"; std::cout<<"提取的图像保存路径及文件名" << str.str() << endl; imwrite(str.str(), image0); flag = 0; count1 += 1;//已经得到的标定图像计数总数 } else if (key2 == 113 || key2 == 27) cout << "这次提取的标定板图像不成功!重新提取!!!!………………" << endl; } } } if (count1 == imageCount) { cout << "***********************………………" << endl; cout << "***********************………………" << endl; cout << "下面开始标定图像...................." << endl; count1 = 0; } /************************************************************************ 读取每一幅图像,从中提取出角点,然后对角点进行亚像素精确化 *************************************************************************/ cout << "开始提取角点………………" << endl; double time0 = static_cast<double>(getTickCount());//记录定标参数求取和将所有图像矫正用的总时间 ofstream fout("F:\\biaoding\\caliberation_result.txt"); /** 保存定标结果的文件 **/ Size image_size; /**** 图像的尺寸 ****/ Size board_size = Size(9, 6); /**** 定标板上每行、列的角点数 ****/ vector<Point2f> corners; /**** 缓存每幅图像上检测到的角点 ****/ vector<vector<Point2f>> corners_Seq; /**** 保存检测到的所有角点 ****/ vector<Mat> image_Seq; int count = 0; int image_count = imageCount; for (int i = 0; i != image_count; i++) { cout << "Frame #" << i + 1 << "..." << endl; std::stringstream str; str << "F:\\biaoding\\img" << std::setw(2) << std::setfill('0') << i + 1 << ".jpg"; std::cout << str.str() << std::endl; Mat image = cv::imread(str.str()); image_size = image.size(); //image_size = Size(image.cols , image.rows); /* 提取角点 */ int channel = 3; Mat imageGray; if (channel == image.channels() ) cvtColor(image, imageGray, CV_RGB2GRAY); else imageGray=image; bool patternfound = findChessboardCorners(image, board_size, corners, CALIB_CB_ADAPTIVE_THRESH + CALIB_CB_NORMALIZE_IMAGE + CALIB_CB_FAST_CHECK); if (!patternfound) { cout << "can not find chessboard corners!\n"; continue; exit(1); } else { /* 亚像素精确化 :迭代过程的终止条件可以是最大迭代次数CV_TERMCRIT_ITER类型,或者是设定的精度CV_TERMCRIT_EPS类型(或者是两 者的组合)。终止条件的设置在极大程度上影响最终得到的亚像素值的精度。在此,指定为0.10,则求得的亚像素级精度为像素的十分 之一 */ cornerSubPix(imageGray, corners, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1)); //Size(11, 11)为搜索窗口的一半尺寸,Size(-1, -1)死区的一半尺寸,死区为不对搜索区的中央位置做求和运算的区域。/ //它是用来避免自相关矩阵出现的某些可能的奇异性。当值为(-1,-1)表示没有死区。 //TermCriteria为求角点的迭代过程的终止条件。即角点位置的确定,要么迭代数大于某个设定值,或者是精确懂达到某个设定值。 //criteria可以是最大迭代数目,或者是设定的精确度,也可以是它们的组合。 /* 绘制检测到的角点并保存 */ Mat imageTemp = image.clone(); for (int j = 0; j < corners.size(); j++) { circle(imageTemp, corners[j], 10, Scalar(0, 0, 255), 2, 8, 0); } string imageFileName1; std::stringstream StrStm; StrStm << i + 1; StrStm >> imageFileName1; imageFileName1 += "_corner.jpg"; //保存提取角点的图像 imwrite("F:\\biaoding\\imageFileName1", imageTemp); cout << "Frame corner#" << i + 1 << "...end" << endl; count = count + corners.size();//记录这一副保存到coners容器中的角点总数 //将该角点压入角点序列堆栈 corners_Seq.push_back(corners);//将所有图像的coners全部压入一个序列的堆栈,即元素为vector容器的堆栈 } //将处理过的图像压入源图像堆栈 image_Seq.push_back(image); } cout << "角点提取完成!下一步摄像机定标\n"; /************************************************************************ 摄像机定标 *************************************************************************/ cout << "开始定标………………" << endl; Size square_size = Size(80, 80); /**** 实际测量得到的定标板上每个棋盘格的大小,单位为像素 ****/ vector<vector<Point3f>> object_Points; /**** 保存定标板上角点的三维坐标 ****/ Mat image_points = Mat(1, count, CV_32FC2, Scalar::all(0)); /***** 保存提取的所有角点1*序列图像的角点总数 *****/ vector<int> point_counts; /***** 每幅图像中角点的数量 ****/ Mat intrinsic_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /***** 摄像机内参数矩阵 ****/ Mat distortion_coeffs = Mat(1, 4, CV_32FC1, Scalar::all(0)); /* 摄像机的4个畸变系数:k1,k2,p1,p2 */ vector<cv::Mat> rotation_vectors; /* 图像序列图像的旋转向量,每一行代表一个旋转向量 */ vector<cv::Mat> translation_vectors; /* 每幅图像的平移向量,每一行代表一个平移向量 */ /* 初始化定标板上角点的三维坐标,此用的是像素坐标 */ for (int t = 0; t<image_count; t++) { vector<Point3f> tempPointSet;//存储每幅图像的像素坐标 for (int i = 0; i<board_size.height; i++) { for (int j = 0; j<board_size.width; j++) { /* 假设定标板放在世界坐标系中z=0的平面上 */ Point3f tempPoint; //在这里,board_size中棋盘格子为单位长度,当square_size存储的像素边长为边长为80,代表width、height为80 tempPoint.x = i*square_size.width; tempPoint.y = j*square_size.height; tempPoint.z = 0; tempPointSet.push_back(tempPoint); } } object_Points.push_back(tempPointSet);//存储图像序列中每幅图像的像素坐标 } /* 初始化每幅图像中的角点数量,这里我们假设每幅图像中都可以看到完整的定标板 */ for (int i = 0; i< image_count; i++) { point_counts.push_back(board_size.width*board_size.height); } /* 开始定标 */ calibrateCamera(object_Points, corners_Seq, image_size, intrinsic_matrix, distortion_coeffs, rotation_vectors, translation_vectors, 0); cout << "定标完成!\n"; /************************************************************************ 对定标结果进行评价 *************************************************************************/ cout << "开始评价定标结果………………" << endl; double total_err = 0.0; /* 所有图像的平均误差的总和 */ double err = 0.0; /* 每幅图像的平均误差 */ vector<Point2f> image_points2; /**** 保存重新计算得到的投影点 ****/ cout << "每幅图像的定标误差:" << endl; fout << "每幅图像的定标误差:" << endl << endl; for (int i = 0; i<image_count; i++) { vector<Point3f> tempPointSet = object_Points[i]; /**** 通过得到的摄像机内外参数,对空间的三维点进行重新投影计算,得到新的投影点 ****/ projectPoints(tempPointSet, rotation_vectors[i], translation_vectors[i], intrinsic_matrix, distortion_coeffs, image_points2); /* 计算新的投影点和旧的投影点之间的误差*/ vector<Point2f> tempImagePoint = corners_Seq[i]; Mat tempImagePointMat = Mat(1, tempImagePoint.size(), CV_32FC2); Mat image_points2Mat = Mat(1, image_points2.size(), CV_32FC2); for (size_t i = 0; i != tempImagePoint.size(); i++) { image_points2Mat.at<Vec2f>(0, i) = Vec2f(image_points2[i].x, image_points2[i].y); tempImagePointMat.at<Vec2f>(0, i) = Vec2f(tempImagePoint[i].x, tempImagePoint[i].y); } err = norm(image_points2Mat, tempImagePointMat, NORM_L2); total_err += err /= point_counts[i]; cout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl; fout << "第" << i + 1 << "幅图像的平均误差:" << err << "像素" << endl; } cout << "总体平均误差:" << total_err / image_count << "像素" << endl; fout << "总体平均误差:" << total_err / image_count << "像素" << endl << endl; cout << "评价完成!" << endl; /************************************************************************ 保存定标结果 *************************************************************************/ cout << "开始保存定标结果………………" << endl; Mat rotation_matrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); /* 保存每幅图像的旋转矩阵 */ fout << "相机内参数矩阵:" << endl; cout << "相机内参数矩阵:" << endl; fout << intrinsic_matrix << endl; cout << intrinsic_matrix << endl; fout << "畸变系数:\n"; cout << "畸变系数:\n"; fout << distortion_coeffs << endl; cout << distortion_coeffs << endl; for (int i = 0; i<image_count; i++) { fout << "第" << i + 1 << "幅图像的旋转向量:" << endl; fout << rotation_vectors[i] << endl; /* 将旋转向量转换为相对应的旋转矩阵 */ Rodrigues(rotation_vectors[i], rotation_matrix); fout << "第" << i + 1 << "幅图像的旋转矩阵:" << endl; fout << rotation_matrix << endl; fout << "第" << i + 1 << "幅图像的平移向量:" << endl; fout << translation_vectors[i] << endl; } cout << "完成保存" << endl; fout << endl; /************************************************************************ 显示定标结果 *************************************************************************/ Mat mapx = Mat(image_size, CV_32FC1); Mat mapy = Mat(image_size, CV_32FC1); Mat R = Mat::eye(3, 3, CV_32F); cout << "保存矫正图像" << endl; for (int i = 0; i != image_count; i++) { cout << "Frame #" << i + 1 << "..." << endl; //newCameraMatrix——输入的校正后的3X3摄像机矩阵(也可用cvStereoRectify()得出的3X4的左或右投影矩阵,其实系统会自动提取该 //矩阵前三列的有用部分作为输入参数)注:!!无校正变换的相机仍用求得内参矩阵 Mat newCameraMatrix = Mat(3, 3, CV_32FC1, Scalar::all(0)); //得到映射关系:R——输入的第一和第二相机坐标系之间的旋转矩阵,一般般无校正变换的相机,默认为单位矩阵 // opencv中,remap与undistortion都是消除畸变的函数,undistortion在设置了一些参数后调用了remap函数,二者的算法实质是一样 //的。由目标图像的坐标,找到对应的原始图像坐标,然后将其值复制到目标图像。大致思路是如此,由于图像大小和变换,需要插值或 //近似的方法,如最邻近法、线性插值等 initUndistortRectifyMap(intrinsic_matrix, distortion_coeffs, R, intrinsic_matrix, image_size, CV_32FC1, mapx, mapy); Mat t = image_Seq[i].clone(); cv::remap(image_Seq[i], t, mapx, mapy, INTER_LINEAR); string imageFileName; std::stringstream StrStm; StrStm << i + 1; StrStm >> imageFileName; imageFileName += "_校正图像.jpg"; imwrite("F:\\biaoding\\imageFileName", t);//*********************************把每幅校正后的图像保存到内存F:\biaoding文件中 } cout << "保存结束" << endl; time0 = ((double)getTickCount() - time0) / getTickFrequency(); cout << "定标及用畸变参数和内参矩阵矫正所有图像总用时:" << time0 << "秒" << endl; waitKey(0); } //-----------------------------------(车牌识别之主要代码)------------------------------------- //**********主要测试程序 #include "include/plate_recognize.h" #include "include/util.h" #include "include/features.h" using namespace easypr; int svmMain(); int acurayTestMain();//用general数据集测试模型系统模型效果 namespace easypr { int svmTrain(bool dividePrepared = true, bool trainPrepared = true, svmCallback getFeatures = getHistogramFeatures); } extern const string GENERAL_TEST_PATH = "image/general_test"; extern const string NATIVE_TEST_PATH = "image/native_test"; //////////////////////////////////////////////////////////// const string option[] = { "1. 测试;" , "2. 批量测试;" , "3. SVM训练;" , "4. ANN训练;" , "5. GDTS生成;" , "6. 退出;" , }; const int optionCount = 6; int main() { bool isExit = false; while (isExit != true) { stringstream selectOption(stringstream::in | stringstream::out); selectOption << "EasyPR Option:"<< endl; for(int i = 0; i < optionCount; i++) { selectOption << option[i] << endl; } cout << "////////////////////////////////////"<< endl; cout << selectOption.str(); cout << "////////////////////////////////////"<< endl; cout << "请选择一项操作:"; int select = -1; bool isRepeat = true; while (isRepeat) { cin >> select; isRepeat = false; switch (select) { case 1: testMain(); break; case 2: acurayTestMain(); break; case 3: svmMain(); break; case 4: // TODO //annMain();还未实现 break; case 5: generate_gdts(); break; case 6: isExit = true; break; default: cout << "输入错误,请重新输入:"; isRepeat = true; break; } } } return 0; } //**************************************************************************************** // 通用正确率测试文件 // AcurayTest对应到main控制命令中的选项2 #include "../include/plate_recognize.h" #include "../include/util.h" #include "../include/features.h" using namespace easypr; int acurayTest(const string& test_path) { ////获取该路径下的所有文件 vector<string> files; getFiles(test_path, files); CPlateLocate lo; CPlateJudge ju; CPlateRecognize pr; pr.LoadANN("model/ann.xml"); pr.LoadSVM("model/svm.xml"); pr.setLifemode(true); int size = files.size(); //int size = 200; if (0 == size) { cout << "No File Found in general_test/native_test!" << endl; return 0; } cout << "Begin to test the easypr accuracy!" << endl; // 总的测试图片数量 int count_all = 0; // 错误的图片数量 int count_err = 0; // 未识别的图片数量:即无车牌图片数量 int count_norecogin = 0; // 总的字符差距 float diff_all = 0; // 平均字符差距 float diff_avg = 0; // 完全匹配的识别次数 float match_count = 0; // 完全匹配的识别次数所占识别图片中的比例 float match_rate = 0; for (int i = 0; i < size; i++) { string filepath = files[i].c_str(); cout << "------------------" << endl; // 获取真实的车牌 string plateLicense = ""; getFileName(filepath, plateLicense); cout << "原牌:" << plateLicense << endl; // EasyPR开始判断车牌 Mat src = imread(filepath); vector<string> plateVec; int result = pr.plateRecognize(src, plateVec); if (result == 0) { int num = plateVec.size(); if (num == 0) { cout << ""<< "无车牌" <<endl; if (plateLicense != "无车牌") count_norecogin++; } else if ( num > 1) { // 多车牌使用diff最小的那个记录 int mindiff = 10000; for (int j = 0; j < num; j++) { cout << plateVec[j] << " (" << j+1 << ")"<<endl; string colorplate = plateVec[j]; // 计算"蓝牌:苏E7KU22"中冒号后面的车牌大小" vector<string> spilt_plate; SplitString(colorplate, spilt_plate, ":"); int size = spilt_plate.size(); if (size == 2) { int diff = levenshtein_distance(plateLicense, spilt_plate[size-1]); if (diff < mindiff) mindiff = diff; } } //cout << "差距:" << mindiff << "个字符" << endl; if(mindiff == 0) { // 完全匹配 match_count++; } diff_all = diff_all + mindiff; } else { // 单车牌只计算一次diff for (int j = 0; j < num; j++) { cout << plateVec[j] <<endl; string colorplate = plateVec[j]; // 计算"蓝牌:苏E7KU22"中冒号后面的车牌大小" vector<string> spilt_plate; SplitString(colorplate, spilt_plate, ":"); int size = spilt_plate.size(); if (size == 2) { int diff = levenshtein_distance(plateLicense, spilt_plate[size-1]); //cout << "差距:" << diff << "个字符" << endl; if(diff == 0) { // 完全匹配 match_count++; } diff_all = diff_all + diff; } } } } else { cout << "错误码:" << result << endl; count_err++; } count_all++; } cout << "------------------" << endl; cout << "系统批量测试 end!" << endl; cout << "------------------" << endl; cout << endl; cout << "统计数据如下:" << endl; cout << "总图片数:" << count_all << "张, "; //cout << "未识别图片:" << count_norecogin << "张, "; float count_recogin = count_all - (count_err + count_norecogin); float count_rate = count_recogin / count_all * 100; //cout << "识别率:" << count_rate << "% " << endl; diff_avg = diff_all / count_recogin; match_rate = match_count/ count_recogin * 100; //cout << "平均字距:" << diff_avg << "个, "; cout << "完全匹配数:" << match_count << "张, "; cout << "完全匹配率:" << match_rate << "% " << endl; cout << endl; cout << "------------------" << endl; return 0; } //*********************************************************************************************** 单张图片测试程序 #include "../include/plate_locate.h" #include "../include/plate_judge.h" #include "../include/chars_segment.h" #include "../include/chars_identify.h" #include "../include/plate_detect.h" #include "../include/chars_recognise.h" #include "../include/plate_recognize.h" using namespace easypr; int test_plate_locate(); int test_plate_judge(); int test_chars_segment(); int test_chars_identify(); int test_plate_detect(); int test_chars_recognise(); int test_plate_recognize(); int testMain(); //把你要测试的图片地址写在下面 const string test_img = ""; const string testOption[] = { "1. test plate_locate(车牌定位);" /* 车牌定位 */, "2. test plate_judge(车牌判断);" /* 车牌判断 */, "3. test plate_detect(车牌检测);" /* 车牌检测(包含车牌定位与车牌判断) */, "4. test chars_segment(字符分隔);" /* 字符分隔 */, "5. test chars_identify(字符鉴别);" /* 字符鉴别 */, "6. test chars_recognise(字符识别);" /* 字符识别(包含字符分隔与字符鉴别) */, "7. test plate_recognize(车牌识别);" /* 车牌识别 */, "8. test all(测试全部);" /* 以上全部 */, "9. 返回;" /* 退出 */, }; const int testOptionCount = 9; int testMain() { bool isExit = false; while (isExit != true) { stringstream selectOption(stringstream::in | stringstream::out); selectOption << "EasyPR Test:"<< endl; for(int i = 0; i < testOptionCount; i++) { selectOption << testOption[i] << endl; } cout << "////////////////////////////////////"<< endl; cout << selectOption.str(); cout << "////////////////////////////////////"<< endl; cout << "请选择一项操作:"; int select = -1; bool isRepeat = true; while (isRepeat) { cin >> select; isRepeat = false; switch (select) { case 1: assert (test_plate_locate() == 0); break; case 2: assert (test_plate_judge() == 0); break; case 3: assert (test_plate_detect() == 0); break; case 4: assert (test_chars_segment() == 0); break; case 5: assert (test_chars_identify() == 0); break; case 6: assert (test_chars_recognise() == 0); break; case 7: assert (test_plate_recognize() == 0); break; case 8: assert (test_plate_locate() == 0); assert (test_plate_judge() == 0); assert (test_plate_detect() == 0); assert (test_chars_segment() == 0); assert (test_chars_identify() == 0); assert (test_chars_recognise() == 0); assert (test_plate_recognize() == 0); break; case 9: isExit = true; break; default: cout << "输入错误,请重新输入:"; isRepeat = true; break; } } } return 0; } int test_plate_locate() { cout << "test_plate_locate" << endl; Mat src = imread("image/plate_locate.jpg"); vector<Mat> resultVec; CPlateLocate plate; plate.setDebug(1); plate.setLifemode(true); int result = plate.plateLocate(src, resultVec); if (result == 0) { int num = resultVec.size(); for (int j = 0; j < num; j++) { Mat resultMat = resultVec[j]; imshow("plate_locate", resultMat); waitKey(0); } } return result; } int test_plate_judge() { cout << "test_plate_judge" << endl; Mat src = imread("image/plate_judge.jpg"); //可能是车牌的图块集合 vector<Mat> matVec; //经过SVM判断后得到的图块集合 vector<Mat> resultVec; CPlateLocate lo; lo.setDebug(1); lo.setLifemode(true); int resultLo = lo.plateLocate(src, matVec); if (0 != resultLo) return -1; cout << "plate_locate_img" << endl; if (resultLo == 0) { int num = matVec.size(); for (int j = 0; j < num; j++) { Mat resultMat = matVec[j]; imshow("plate_judge", resultMat); waitKey(0); } } CPlateJudge ju; int resultJu = ju.plateJudge(matVec, resultVec); if (0 != resultJu) return -1; cout << "plate_judge_img" << endl; if (resultJu == 0) { int num = resultVec.size(); for (int j = 0; j < num; j++) { Mat resultMat = resultVec[j]; imshow("plate_judge", resultMat); waitKey(0); } } return resultJu; } int test_chars_segment() { cout << "test_chars_segment" << endl; Mat src = imread("image/chars_segment.jpg"); vector<Mat> resultVec; CCharsSegment plate; int result = plate.charsSegment(src, resultVec); if (result == 0) { int num = resultVec.size(); for (int j = 0; j < num; j++) { Mat resultMat = resultVec[j]; imshow("chars_segment", resultMat); waitKey(0); } } return result; } int test_chars_identify() { cout << "test_chars_identify" << endl; Mat src = imread("image/chars_identify.jpg"); vector<Mat> resultVec; CCharsSegment cs; CCharsIdentify ci; string plateIdentify = ""; int result = cs.charsSegment(src, resultVec); if (result == 0) { int num = resultVec.size(); for (int j = 0; j < num; j++) { Mat resultMat = resultVec[j]; bool isChinses = false; //默认首个字符块是中文字符 if (j == 0) isChinses = true; string charcater = ci.charsIdentify(resultMat, isChinses); plateIdentify = plateIdentify + charcater; } } const string plateLicense = "苏E771H6"; cout << "plateLicense: " << plateLicense << endl; cout << "plateIdentify: " << plateIdentify << endl; if (plateLicense != plateIdentify) { cout << "Identify Not Correct!" << endl; return -1; } cout << "Identify Correct!" << endl; cout << "Enter 1 for coninue:"; int a = 0; cin >> a; return result; } int test_plate_detect() { cout << "test_plate_detect" << endl; //Mat src = imread("image/plate_detect.jpg"); Mat src = imread("image/baidu_image/test1.jpg"); vector<Mat> resultVec; CPlateDetect pd; pd.setPDLifemode(true); int result = pd.plateDetect(src, resultVec); if (result == 0) { int num = resultVec.size(); for (int j = 0; j < num; j++) { Mat resultMat = resultVec[j]; imshow("plate_detect", resultMat); waitKey(0); } } return result; } int test_chars_recognise() { cout << "test_chars_recognise" << endl; Mat src = imread("image/chars_recognise.jpg"); CCharsRecognise cr; string charsRecognise = ""; int result = cr.charsRecognise(src, charsRecognise); if (result == 0) { cout << "charsRecognise: " << charsRecognise << endl; } cout << "Enter 1 for coninue:"; int a = 0; cin >> a; return result; } int test_plate_recognize() { cout << "test_plate_recognize" << endl; //Mat src = imread("image/plate_locate.jpg"); Mat src = imread("image/test.jpg"); CPlateRecognize pr; pr.LoadANN("model/ann.xml"); pr.LoadSVM("model/svm.xml"); pr.setLifemode(true); pr.setDebug(true); vector<string> plateVec; int result = pr.plateRecognize(src, plateVec); if (result == 0) { int num = plateVec.size(); for (int j = 0; j < num; j++) { cout << "plateRecognize: " << plateVec[j] << endl; } } if (result != 0) cout << "result:" << result << endl; cout << "Enter 1 for coninue:"; int a = 0; cin >> a; return result; }
相关文章推荐
- opencv学习系列:实例练习,含多个工程实例
- opencv学习系列:实例练习,含多个工程实例
- opencv学习系列:实例练习,含多个工程实例
- opencv学习系列:实例练习(含多个实例)
- opencv学习系列:实例练习(含多个实例)
- OpenCV学习系列:标定相关程序练习
- (Java2D 学习笔记系列) (一)一个简单的图像填充实例及其分析理解
- selenium系列----->脚本元素定位实例学习
- 实例学习ansible系列(2)从Helloworld深度解析Ansible执行原理
- 实例学习ansible系列(4)常用模块之command/shell/raw
- 一步一个脚印学习WCF系列之WCF概要—WCF服务的创建与调用HelloWorld实例,通过编码方式(四)
- 【OpenCV学习】OpenMP并行化实例
- 【OpenCV学习笔记】【编程实例】六 (霍夫圆检测续)
- 1个练习引发的系列学习之pytest生成测试报告 (三)
- SSE图像算法优化系列六:OpenCv关于灰度积分图的SSE代码学习和改进。
- CUDA系列学习(三)GPU设计与结构QA & coding练习
- 基础学习笔记之opencv(18):kmeans函数使用实例
- 学习OpenCV的学习笔记系列(三)显示图片及视频
- 学习OpenCV:滤镜系列(9)——扩散(毛玻璃)
- Java并发20:Lock系列-Condition接口基本方法学习实例