OpenCV4函数合集开放
第二章 数据载入、显示与保存
2.1 图像存储容器
2.1.1 Mat类介绍
Mat分为矩阵头和指向存储数据的矩阵指针两部分。
代码清单2-1 创建Mat类
cv::Mat a; //创建一个名为a的矩阵头
a = cv::imread("test.jpd"); //向a中赋值图像数据,矩阵指针指向像素数据
cv::Mat b = a; //复制矩阵头,并命名为b
代码清单2-2 声明一个指定类型的Mat类
cv::Mat A = Mat_(3,3); //创建一个3*3的矩阵用于存放double类型数据
代码清单2-3 通过OpenCV数据类型创建Mat类
cv::Mat a(640,480,CV_8UC3); //创建一个640*480的3通道矩阵用于存放彩色图像
cv::Mat a(3,3,CV_8UC1); //创建一个3*3的8位无符号整数的单通道矩阵
cv::Mat a(3,3,CV_8U); //创建单通道矩阵,c1标识可以省略
2.1.2 Mat类构造与赋值
1.Mat类的构造
代码清单2-4 默认构造函数使用方式
cv::Mat::Mat();
代码清单2-5 利用矩阵尺寸和类型参数构造Mat类
cv::Mat::Mat(int rows,int cols,int type)
- rows:构造矩阵的行数
- cols:矩阵的列数
- type:矩阵中存储的数据类型
代码清单2-6 用Size()结构构造Mat
cv::Mat(Size size(),int type)
- size:二维数组变量尺寸,通过Size(cols,rows)进行赋值
- type:与代码清单2-5中的参数一致
代码清单2-7 用Size()结构构造Mat示例
cv::Mat a(Size(480,640),CV_8UC1); //构造一个行为640、列为480的单通道矩阵
cv::Mat b(Size(480,640),CV_32FC3); //构造一个行为640、列为480的3通道矩阵
代码清单2-8 利用已有矩阵构造Mat类
cv::Mat::Mat(const Mat & m);
- m:是已经构建完成的Mat类矩阵数据
提示:如果希望复制两个一模一样的Mat类而彼此之间不会受影响,那么可以使用m=a.clone()实现
代码清单2-9 构造已有Mat类的子类
cv::Mat::Mat(const Mat & m,const Range & rowRange,const Range & rowRange = Rang::all())
- m:是已经构建完成的Mat类矩阵数据
- rowRange:在已有矩阵中需要截取的行数范围,是一个Rang变量,例如从第2行到第5行可以表示位Rang(2,5)
- rowRange:在已有矩阵中需要截取的列数范围,是一个Rang变量,例如从第2列到第5列可以表示位Rang(2,5),默认所有列都会截取。
代码清单2-10 在原Mat中截取子Mat类
cv::Mat b(a, Rang(2,5), Rang(2,5)); //从a中截取部分数据构造b
cv::Mat c(a, Rang(2,5)); //默认最后一个参数构成c
2.2 图像的读取与显示
2.2.1 图像读取函数 imread
imread()
empty()
2.2.2 图像窗口函数 namedWindow
namedWindow()
2.2.3 图像显示函数 imshow
imshow()
cv::waitKey()
2.3 视频加载与摄像头调用
2.3.1 视频数据的读取
VideoCapture类构造函数
isOpened()
“>>”
empty()
get()
代码清单2-28 读取视频文件
#include
#include using namespace std;
using namespace cv;int main()
{system("color F0"); //更改输出界面颜色VideoCapture video("cup.mp4");if (video.isOpened()){cout << "视频中图像的宽度=" << video.get(CAP_PROP_FRAME_WIDTH) << endl;cout << "视频中图像的高度=" << video.get(CAP_PROP_FRAME_HEIGHT) << endl;cout << "视频帧率=" << video.get(CAP_PROP_FPS) << endl;cout << "视频的总帧数=" << video.get(CAP_PROP_FRAME_COUNT);}else{cout << "请确认视频文件名称是否正确" << endl;return -1;}while (1){Mat frame;video >> frame;if (frame.empty()){break;}imshow("video", frame);waitKey(1000 / video.get(CAP_PROP_FPS));}waitKey();return 0;
}
2.3.2 摄像头的直接调用
VideoCapture类还可以调用摄像头
2.4 数据保存
2.4.1 图像的保存
imwrite()
代码清单2-32 保存图像
#include
#include using namespace std;
using namespace cv;void AlphaMat(Mat &mat)
{CV_Assert(mat.channels() == 4);for (int i = 0; i < mat.rows; ++i){for (int j = 0; j < mat.cols; ++j){Vec4b& bgra = mat.at(i, j);bgra[0] = UCHAR_MAX; // 蓝色通道bgra[1] = saturate_cast((float(mat.cols - j)) / ((float)mat.cols) * UCHAR_MAX); // 绿色通道bgra[2] = saturate_cast((float(mat.rows - i)) / ((float)mat.rows) * UCHAR_MAX); // 红色通道bgra[3] = saturate_cast(0.5 * (bgra[1] + bgra[2])); // Alpha通道}}
}
int main(int agrc, char** agrv)
{// Create mat with alpha channelMat mat(480, 640, CV_8UC4);AlphaMat(mat);vector compression_params;compression_params.push_back(IMWRITE_PNG_COMPRESSION); //PNG格式图像压缩标志compression_params.push_back(9); //设置最高压缩质量 bool result = imwrite("alpha.png", mat, compression_params);if (!result){cout << "保存成PNG格式图像失败" << endl;return -1;}cout << "保存成功" << endl;return 0;
}
2.4.2 视频的保存
VideoWriter类构造函数
isOpened()
get()
“<<” 或者 write()
release()
#include
#include using namespace cv;
using namespace std;int main()
{Mat img;VideoCapture video(0); //使用某个摄像头//读取视频//VideoCapture video;//video.open("cup.mp4"); if (!video.isOpened()) // 判断是否调用成功{cout << "打开摄像头失败,请确实摄像头是否安装成功";return -1;}video >> img; //获取图像//检测是否成功获取图像if (img.empty()) //判断有没有读取图像成功{cout << "没有获取到图像" << endl;return -1;}bool isColor = (img.type() == CV_8UC3); //判断相机(视频)类型是否为彩色VideoWriter writer;int codec = VideoWriter::fourcc('M', 'J', 'P', 'G'); // 选择编码格式//OpenCV 4.0版本设置编码格式//int codec = CV_FOURCC('M', 'J', 'P', 'G'); double fps = 25.0; //设置视频帧率 string filename = "live.avi"; //保存的视频文件名称writer.open(filename, codec, fps, img.size(), isColor); //创建保存视频文件的视频流if (!writer.isOpened()) //判断视频流是否创建成功{cout << "打开视频文件失败,请确实是否为合法输入" << endl;return -1;}while (1){//检测是否执行完毕if (!video.read(img)) //判断能都继续从摄像头或者视频文件中读出一帧图像{cout << "摄像头断开连接或者视频读取完成" << endl;break;}writer.write(img); //把图像写入视频流//writer << img;imshow("Live", img); //显示图像char c = waitKey(50);if (c == 27) //按ESC案件退出视频保存{break;}}// 退出程序时刻自动关闭视频流//video.release();//writer.release(); return 0;
}
2.4.3 保存和读取XML和YMAL文件
FileStorage类构造函数
isOpened()
open()
“<<” 、 “>>”
#include
#include
#include using namespace std;
using namespace cv;int main(int argc, char** argv)
{system("color F0"); //修改运行程序背景和文字颜色//string fileName = "datas.xml"; //文件的名称string fileName = "datas.yaml"; //文件的名称//以写入的模式打开文件cv::FileStorage fwrite(fileName, cv::FileStorage::WRITE);//存入矩阵Mat类型的数据Mat mat = Mat::eye(3, 3, CV_8U);fwrite.write("mat", mat); //使用write()函数写入数据//存入浮点型数据,节点名称为xfloat x = 100;fwrite << "x" << x;//存入字符串型数据,节点名称为strString str = "Learn OpenCV 4";fwrite << "str" << str;//存入数组,节点名称为number_arrayfwrite << "number_array" << "[" <<4<<5<<6<< "]";//存入多node节点数据,主名称为multi_nodesfwrite << "multi_nodes" << "{" << "month" << 8 << "day" << 28 << "year"<< 2019 << "time" << "[" << 0 << 1 << 2 << 3 << "]" << "}";//关闭文件fwrite.release();//以读取的模式打开文件cv::FileStorage fread(fileName, cv::FileStorage::READ);//判断是否成功打开文件if (!fread.isOpened()){cout << "打开文件失败,请确认文件名称是否正确!" << endl;return -1;}//读取文件中的数据float xRead;fread["x"] >> xRead; //读取浮点型数据cout << "x=" << xRead << endl;//读取字符串数据string strRead;fread["str"] >> strRead;cout << "str=" << strRead << endl;//读取含多个数据的number_array节点FileNode fileNode = fread["number_array"];cout << "number_array=[";//循环遍历每个数据for (FileNodeIterator i = fileNode.begin(); i != fileNode.end(); i++){float a;*i >> a;cout << a<<" ";}cout << "]" << endl;//读取Mat类型数据Mat matRead;fread["mat="] >> matRead;cout << "mat=" << mat << endl;//读取含有多个子节点的节点数据,不使用FileNode和迭代器进行读取FileNode fileNode1 = fread["multi_nodes"];int month = (int)fileNode1["month"];int day = (int)fileNode1["day"];int year = (int)fileNode1["year"];cout << "multi_nodes:" << endl << " month=" << month << " day=" << day << " year=" << year;cout << " time=[";for (int i = 0; i < 4; i++){int a = (int)fileNode1["time"][i];cout << a << " ";}cout << "]" << endl;//关闭文件fread.release();return 0;
}
第三章 图像基本操作
3.1 图像颜色空间
3.1.1 颜色模型与转换
- RGB颜色模型
- YUV颜色模型
- HSV颜色模型
- Lab颜色模型
- GRAY颜色模型
6.不同颜色模型间的互想转换
cvtColor()函数用于将图像从一个颜色模型转换为另一个颜色模型
void cv::cvtColor(InputArray src,OutputArray dst,int code,int dstCn = 0)
- src:待转换颜色模型原始图像
- dst:转换颜色模型后的目标图像
- code:颜色空间转换的标志,如由RGB空间到HSV空间
- dstCn:目标图像中的通道数
代码清单3-2 图像颜色模型相互转换
#include
#include
#include using namespace std;
using namespace cv;int main()
{Mat img = imread("E:/BaiduNetdiskDownload/data/lena.png");if (img.empty()){cout << "请确认图像文件名称师范正确" << endl;return -1;}Mat gray, HSV, YUV, Lab, img32;img.convertTo(img32, CV_32F, 1.0 / 255); //将CV_8U类型转换成CV_32F类型//img32.convertTo(img,CV_8U,255); //将CV_32F类型转换成CV_8U类型cvtColor(img32, HSV, COLOR_BGR2HSV);cvtColor(img32, YUV, COLOR_BGR2YUV);cvtColor(img32, Lab, COLOR_BGR2Lab);cvtColor(img32, gray, COLOR_BGR2GRAY);imshow("原图", img32);imshow("HSV", HSV);imshow("YUV", YUV);imshow("Lab", Lab);imshow("gray", gray);waitKey(0);return 0;
}
convertTo()
3.1.2 多通道分离与合并
split()
merge()
代码清单3-6 实现图像分离与合并
#include
#include
#include using namespace std;
using namespace cv;int main()
{Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat HSV;cvtColor(img, HSV, COLOR_RGB2HSV);Mat imgs0, imgs1, imgs2; //用于存放数组类型的结果Mat imgv0, imgv1, imgv2; //用于存放vector类型的结果Mat result0, result1, result2; //多通道合并的结果//输入数组参数的多通道分离与合并Mat imgs[3];split(img, imgs);imgs0 = imgs[0];imgs1 = imgs[1];imgs2 = imgs[2];imshow("RGB-B通道", imgs0); //显示分离后B通道的像素值imshow("RGB-G通道", imgs1); //显示分离后G通道的像素值imshow("RGB-R通道", imgs2); //显示分离后R通道的像素值imgs[2] = img; //将数组中的图像通道数变成不统一merge(imgs, 3, result0); //合并图像//imshow("result0", result0); //imshow最多显示4个通道,因此结果在Image Watch中查看Mat zero = cv::Mat::zeros(img.rows, img.cols, CV_8UC1);imgs[0] = zero;imgs[2] = zero;merge(imgs, 3, result1); //用于还原G通道的真实情况,合并结果为绿色imshow("result1", result1); //显示合并结果//输入vector参数的多通道分离与合并vector imgv;split(HSV, imgv);imgv0 = imgv.at(0);imgv1 = imgv.at(1);imgv2 = imgv.at(2);imshow("HSV-H通道", imgv0); //显示分离后H通道的像素值imshow("HSV-S通道", imgv1); //显示分离后S通道的像素值imshow("HSV-V通道", imgv2); //显示分离后V通道的像素值imgv.push_back(HSV); //将vector中的图像通道数变成不统一merge(imgv, result2); //合并图像//imshow("result2", result2); /imshow最多显示4个通道,因此结果在Image Watch中查看waitKey(0);return 0;
}
3.2 图像像素操作处理
3.2.1 图像像素统计
1.寻找图像像素最大值与最小值
minMaxLoc()
数据类型 Point
CU::Mat::reshape()
代码清单3-9 寻找矩阵中的最值
#include
#include
#include using namespace std;
using namespace cv;int main()
{system("color F0"); //更改输出界面颜色float a[12] = { 1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };Mat img = Mat(3, 4, CV_32FC1, a); //单通道矩阵Mat imgs = Mat(2, 3, CV_32FC2, a); //多通道矩阵double minVal, maxVal; //用于存放矩阵中的最大值和最小值Point minIdx, maxIdx; 用于存放矩阵中的最大值和最小值在矩阵中的位置/*寻找单通道矩阵中的最值*/minMaxLoc(img, &minVal, &maxVal, &minIdx, &maxIdx);cout << "img中最大值是:" << maxVal << " " << "在矩阵中的位置:" << maxIdx << endl;cout << "img中最小值是:" << minVal << " " << "在矩阵中的位置:" << minIdx << endl;/*寻找多通道矩阵中的最值*/Mat imgs_re = imgs.reshape(1, 4); //将多通道矩阵变成单通道矩阵minMaxLoc(imgs_re, &minVal, &maxVal, &minIdx, &maxIdx);cout << "imgs中最大值是:" << maxVal << " " << "在矩阵中的位置:" << maxIdx << endl;cout << "imgs中最小值是:" << minVal << " " << "在矩阵中的位置:" << minIdx << endl;return 0;
}
2.计算图像的平均值和标准差
meanStdDev() 函数用于同时计算平均值和标准差
mean() 计算平均值
cv::Scalar 类型
代码清单3-12 计算矩阵平均值和标准差
#include
#include
#include using namespace std;
using namespace cv;int main()
{system("color F0"); //更改输出界面颜色float a[12] = { 1, 2, 3, 4, 5, 10, 6, 7, 8, 9, 10, 0 };Mat img = Mat(3, 4, CV_32FC1, a); //单通道矩阵Mat imgs = Mat(2, 3, CV_32FC2, a); //多通道矩阵cout << "/* 用meanStdDev同时求取图像的均值和标准差 */" << endl;Scalar myMean;myMean = mean(imgs);cout << "imgs均值=" << myMean << endl;cout << "imgs第一个通道的均值=" << myMean[0] << " "<< "imgs第二个通道的均值=" << myMean[1] << endl << endl;cout << "/* 用meanStdDev同时求取图像的均值和标准差 */" << endl;Mat myMeanMat, myStddevMat;meanStdDev(img, myMeanMat, myStddevMat);cout << "img均值=" << myMeanMat << " " << endl;cout << "img标准差=" << myStddevMat << endl << endl;meanStdDev(imgs, myMeanMat, myStddevMat);cout << "imgs均值=" << myMeanMat << " " << endl << endl;cout << "imgs标准差=" << myStddevMat << endl;return 0;
}
2.2.2 两图像间的像素操作
1.两幅图像的比较运算
max()
min()
代码清单3-14 两个矩阵或图像进行比较运算
#include
#include
#include using namespace std;
using namespace cv;int main()
{float a[12] = { 1, 2, 3.3, 4, 5, 9, 5, 7, 8.2, 9, 10, 2 };float b[12] = { 1, 2.2, 3, 1, 3, 10, 6, 7, 8, 9.3, 10, 1 };Mat imga = Mat(3, 4, CV_32FC1, a);Mat imgb = Mat(3, 4, CV_32FC1, b);Mat imgas = Mat(2, 3, CV_32FC2, a);Mat imgbs = Mat(2, 3, CV_32FC2, b);//对两个单通道矩阵进行比较运算Mat myMax, myMin;max(imga, imgb, myMax);min(imga, imgb, myMin);//对两个多通道矩阵进行比较运算Mat myMaxs, myMins;max(imgas, imgbs, myMaxs);min(imgas, imgbs, myMins);//对两张彩色图像进行比较运算Mat img0 = imread("len.png");Mat img1 = imread("noobcv.jpg");if (img0.empty() || img1.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat comMin, comMax;max(img0, img1, comMax);min(img0, img1, comMin);imshow("comMin", comMin);imshow("comMax", comMax);//与掩模进行比较运算Mat src1 = Mat::zeros(Size(512, 512), CV_8UC3);Rect rect(100, 100, 300, 300);src1(rect) = Scalar(255, 255, 255); //生成一个低通300*300的掩模Mat comsrc1, comsrc2;min(img0, src1, comsrc1);imshow("comsrc1", comsrc1);Mat src2 = Mat(512, 512, CV_8UC3, Scalar(0, 0, 255)); //生成一个显示红色通道的低通掩模min(img0, src2, comsrc2);imshow("comsrc2", comsrc2);//对两张灰度图像进行比较运算Mat img0G, img1G, comMinG, comMaxG;cvtColor(img0, img0G, COLOR_BGR2GRAY);cvtColor(img1, img1G, COLOR_BGR2GRAY);max(img0G, img1G, comMaxG);min(img0G, img1G, comMinG);imshow("comMinG", comMinG);imshow("comMaxG", comMaxG);waitKey(0);return 0;
}
2.两幅图像的逻辑运算
biwise_and()
biwise_or()
biwise_xor()
biwise_not()
代码清单3-16 两个黑白图像像素逻辑运算
#include
#include
#include using namespace std;
using namespace cv;int main()
{Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}//创建两个黑白图像Mat img0 = Mat::zeros(200, 200, CV_8UC1);Mat img1 = Mat::zeros(200, 200, CV_8UC1);Rect rect0(50, 50, 100, 100);img0(rect0) = Scalar(255);Rect rect1(100, 100, 100, 100);img1(rect1) = Scalar(255);imshow("img0", img0);imshow("img1", img1);//进行逻辑运算Mat myAnd, myOr, myXor, myNot, imgNot;bitwise_not(img0, myNot);bitwise_and(img0, img1, myAnd);bitwise_or(img0, img1, myOr);bitwise_xor(img0, img1, myXor);bitwise_not(img, imgNot);imshow("myAnd", myAnd);imshow("myOr", myOr);imshow("myXor", myXor);imshow("myNot", myNot);imshow("img", img);imshow("imgNot", imgNot);waitKey(0);return 0;
}
3.2.3 图像二值化
threshold()
adaptiveThreshold()
代码清单3-19 图像二值化
#include
#include
#include using namespace std;
using namespace cv;int main()
{Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);Mat img_B, img_B_V, gray_B, gray_B_V, gray_T, gray_T_V, gray_TRUNC;//彩色图像二值化threshold(img, img_B, 125, 255, THRESH_BINARY);threshold(img, img_B_V, 125, 255, THRESH_BINARY_INV);imshow("img_B", img_B);imshow("img_B_V", img_B_V);//灰度图BINARY二值化threshold(gray, gray_B, 125, 255, THRESH_BINARY);threshold(gray, gray_B_V, 125, 255, THRESH_BINARY_INV);imshow("gray_B", gray_B);imshow("gray_B_V", gray_B_V);//灰度图像TOZERO变换threshold(gray, gray_T, 125, 255, THRESH_TOZERO);threshold(gray, gray_T_V, 125, 255, THRESH_TOZERO_INV);imshow("gray_T", gray_T);imshow("gray_T_V", gray_T_V);//灰度图像TRUNC变换threshold(gray, gray_TRUNC, 125, 255, THRESH_TRUNC);imshow("gray_TRUNC", gray_TRUNC);//灰度图像大津法和三角形法二值化Mat img_Thr = imread("threshold.png", IMREAD_GRAYSCALE);Mat img_Thr_O, img_Thr_T;threshold(img_Thr, img_Thr_O, 100, 255, THRESH_BINARY | THRESH_OTSU);threshold(img_Thr, img_Thr_T, 125, 255, THRESH_BINARY | THRESH_TRIANGLE);imshow("img_Thr", img_Thr);imshow("img_Thr_O", img_Thr_O);imshow("img_Thr_T", img_Thr_T);//灰度图像自适应二值化Mat adaptive_mean, adaptive_gauss;adaptiveThreshold(img_Thr, adaptive_mean, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 55, 0);adaptiveThreshold(img_Thr, adaptive_gauss, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 55, 0);imshow("adaptive_mean", adaptive_mean);imshow("adaptive_gauss", adaptive_gauss);waitKey(0);return 0;
}
3.2.4 LUT
LUT()
代码清单3-21 对图像进行查找表映射
#include
#include using namespace std;
using namespace cv;int main()
{//LUT查找表第一层uchar lutFirst[256];for (int i = 0; i<256; i++){if (i <= 100)lutFirst[i] = 0;if (i > 100 && i <= 200)lutFirst[i] = 100;if (i > 200)lutFirst[i] = 255;}Mat lutOne(1, 256, CV_8UC1, lutFirst);//LUT查找表第二层uchar lutSecond[256];for (int i = 0; i<256; i++){if (i <= 100)lutSecond[i] = 0;if (i > 100 && i <= 150)lutSecond[i] = 100;if (i > 150 && i <= 200)lutSecond[i] = 150;if (i > 200)lutSecond[i] = 255;}Mat lutTwo(1, 256, CV_8UC1, lutSecond);//LUT查找表第三层uchar lutThird[256];for (int i = 0; i<256; i++){if (i <= 100)lutThird[i] = 100;if (i > 100 && i <= 200)lutThird[i] = 200;if (i > 200)lutThird[i] = 255;}Mat lutThree(1, 256, CV_8UC1, lutThird);//拥有三通道的LUT查找表矩阵vector mergeMats;mergeMats.push_back(lutOne);mergeMats.push_back(lutTwo);mergeMats.push_back(lutThree);Mat LutTree;merge(mergeMats, LutTree);//计算图像的查找表Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray, out0, out1, out2;cvtColor(img, gray, COLOR_BGR2GRAY);LUT(gray, lutOne, out0);LUT(img, lutOne, out1);LUT(img, LutTree, out2);imshow("out0", out0);imshow("out1", out1);imshow("out2", out2);waitKey(0);return 0;
}
3.3 图像变换
3.3.1 图像连接
vconcat()
hconcat()
代码清单3-26 图像拼接
#include
#include using namespace std;
using namespace cv;int main()
{//矩阵数组的横竖连接Mat matArray[] = { Mat(1, 2, CV_32FC1, cv::Scalar(1)),Mat(1, 2, CV_32FC1, cv::Scalar(2)) };Mat vout, hout;vconcat(matArray, 2, vout);cout << "图像数组竖向连接:" << endl << vout << endl;hconcat(matArray, 2, hout);cout << "图像数组横向连接:" << endl << hout << endl;//矩阵的横竖拼接Mat A = (cv::Mat_(2, 2) << 1, 7, 2, 8);Mat B = (cv::Mat_(2, 2) << 4, 10, 5, 11);Mat vC, hC;vconcat(A, B, vC);cout << "多个图像竖向连接:" << endl << vC << endl;hconcat(A, B, hC);cout << "多个图像横向连接:" << endl << hC << endl;//读取4个子图像,00表示左上角、01表示右上角、10表示左下角、11表示右下角Mat img00 = imread("lena00.png");Mat img01 = imread("lena01.png");Mat img10 = imread("lena10.png");Mat img11 = imread("lena11.png");if (img00.empty() || img01.empty() || img10.empty() || img11.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}//显示4个子图像imshow("img00", img00);imshow("img01", img01);imshow("img10", img10);imshow("img11", img11);//图像连接Mat img, img0, img1;//图像横向连接hconcat(img00, img01, img0);hconcat(img10, img11, img1);//横向连接结果再进行竖向连接vconcat(img0, img1, img);//显示连接图像的结果imshow("img0", img0);imshow("img1", img1);imshow("img", img);waitKey(0);return 0;
}
3.3.2 图像尺寸变换
resize()
代码清单3-28 图像缩放
#include
#include using namespace std;
using namespace cv;int main()
{Mat gray = imread("lena.png", IMREAD_GRAYSCALE);if (gray.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat smallImg, bigImg0, bigImg1, bigImg2;resize(gray, smallImg, Size(15, 15), 0, 0, INTER_AREA); //先将图像缩小resize(smallImg, bigImg0, Size(30, 30), 0, 0, INTER_NEAREST); //最近邻差值resize(smallImg, bigImg1, Size(30, 30), 0, 0, INTER_LINEAR); //双线性差值resize(smallImg, bigImg2, Size(30, 30), 0, 0, INTER_CUBIC); //双三次差值namedWindow("smallImg", WINDOW_NORMAL); //图像尺寸太小,一定要设置可以调节窗口大小标志imshow("smallImg", smallImg);namedWindow("bigImg0", WINDOW_NORMAL);imshow("bigImg0", bigImg0);namedWindow("bigImg1", WINDOW_NORMAL);imshow("bigImg1", bigImg1);namedWindow("bigImg2", WINDOW_NORMAL);imshow("bigImg2", bigImg2);waitKey(0);return 0;
}
3.3.3 图像翻转变换
flip()
代码清单3-29 图像翻转
#include
#include using namespace std;
using namespace cv;int main()
{Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat img_x, img_y, img_xy;flip(img, img_x, 0); //沿x轴对称flip(img, img_y, 1); //沿y轴对称flip(img, img_xy, -1); //先x轴对称,再y轴对称imshow("img", img);imshow("img_x", img_x);imshow("img_y", img_y);imshow("img_xy", img_xy);waitKey(0);return 0;
}
3.3.4 图像放射变换
getRotationMatrix2D()
warpAffine()
getAffineTransform()
代码清单3-34 图像旋转与放射变换
#include
#include
#include using namespace std;
using namespace cv;int main()
{Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat rotation0, rotation1, img_warp0, img_warp1;double angle = 30; //设置图像旋转的角度Size dst_size(img.rows, img.cols); //设置输出图像的尺寸Point2f center(img.rows / 2.0, img.cols / 2.0); //设置图像的旋转中心rotation0 = getRotationMatrix2D(center, angle, 1); //计算放射变换矩阵warpAffine(img, img_warp0, rotation0, dst_size); //进行仿射变换imshow("img_warp0", img_warp0);//根据定义的三个点进行仿射变换Point2f src_points[3];Point2f dst_points[3];src_points[0] = Point2f(0, 0); //原始图像中的三个点src_points[1] = Point2f(0, (float)(img.cols - 1));src_points[2] = Point2f((float)(img.rows - 1), (float)(img.cols - 1));dst_points[0] = Point2f((float)(img.rows)*0.11, (float)(img.cols)*0.20); //放射变换后图像中的三个点dst_points[1] = Point2f((float)(img.rows)*0.15, (float)(img.cols)*0.70);dst_points[2] = Point2f((float)(img.rows)*0.81, (float)(img.cols)*0.85);rotation1 = getAffineTransform(src_points, dst_points); //根据对应点求取仿射变换矩阵warpAffine(img, img_warp1, rotation1, dst_size); //进行仿射变换imshow("img_warp1", img_warp1);waitKey(0);return 0;
}
3.3.5 图像透视变换
getPerspectiveTransform()
warpPerspective()
代码清单3-37 二维码图像透视变换
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("noobcvqr.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Point2f src_points[4];Point2f dst_points[4];//通过Image Watch查看的二维码四个角点坐标src_points[0] = Point2f(94.0, 374.0);src_points[1] = Point2f(507.0, 380.0);src_points[2] = Point2f(1.0, 623.0);src_points[3] = Point2f(627.0, 627.0);//期望透视变换后二维码四个角点的坐标dst_points[0] = Point2f(0.0, 0.0);dst_points[1] = Point2f(627.0, 0.0);dst_points[2] = Point2f(0.0, 627.0);dst_points[3] = Point2f(627.0, 627.0);Mat rotation, img_warp;rotation = getPerspectiveTransform(src_points, dst_points); //计算透视变换矩阵warpPerspective(img, img_warp, rotation, img.size()); //透视变换投影imshow("img", img);imshow("img_warp", img_warp);waitKey(0);return 0;
}
3.3.6 极坐标变换
warpPolar()
代码清单3-39 图像极坐标变换
#include
#include using namespace std;
using namespace cv;int main()
{Mat img = imread("dial.png");if (!img.data){cout << "请检查图像文件名称是否输入正确" << endl;return -1;}Mat img1, img2;Point2f center = Point2f(img.cols / 2, img.rows/2); //极坐标在图像中的原点//正极坐标变换warpPolar(img, img1, Size(300,600), center, center.x, INTER_LINEAR + WARP_POLAR_LINEAR);//逆极坐标变换warpPolar(img1, img2, Size(img.rows,img.cols), center, center.x, INTER_LINEAR + WARP_POLAR_LINEAR + WARP_INVERSE_MAP);imshow("原表盘图", img);imshow("表盘极坐标变换结果", img1); imshow("逆变换结果", img2);waitKey(0);return 0;
}
3.4 在图像上绘制几何图形
3.4.1 绘制圆形
circle()
3.4.2 绘制直线
line()
3.4.3 绘制椭圆
ellipse()
ellipse2Poly()
3.4.4 绘制多边形
rectangle()
fillPoly()
3.4.5 文字生成
putText()
代码清单3-47 绘制基本几何图形
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = Mat::zeros(Size(512, 512), CV_8UC3); //生成一个黑色图像用于绘制几何图形//绘制圆形circle(img, Point(50, 50), 25, Scalar(255, 255, 255), -1); //绘制一个实心圆circle(img, Point(100, 50), 20, Scalar(255, 255, 255), 4); //绘制一个空心圆//绘制直线line(img, Point(100, 100), Point(200, 100), Scalar(255, 255, 255), 2, LINE_4,0); //绘制一条直线//绘制椭圆ellipse(img, Point(300, 255), Size(100, 70), 0, 0, 100, Scalar(255, 255, 255), -1); //绘制实心椭圆的一部分ellipse(img, RotatedRect(Point2f(150, 100), Size2f(30, 20), 0), Scalar(0, 0, 255), 2); //绘制一个空心椭圆vector points;ellipse2Poly(Point(200, 400), Size(100, 70),0,0,360,2,points); //用一些点来近似一个椭圆for (int i = 0; i < points.size()-1; i++) //用直线把这个椭圆画出来{if (i==points.size()-1){line(img, points[0], points[i], Scalar(255, 255, 255), 2); //椭圆中后于一个点与第一个点连线break;}line(img, points[i], points[i+1], Scalar(255, 255, 255), 2); //当前点与后一个点连线}//绘制矩形rectangle(img, Point(50, 400), Point(100, 450), Scalar(125, 125, 125), -1); rectangle(img, Rect(400,450,60,50), Scalar(0, 125, 125), 2);//绘制多边形Point pp[2][6];pp[0][0] = Point(72, 200);pp[0][1] = Point(142, 204);pp[0][2] = Point(226, 263);pp[0][3] = Point(172, 310);pp[0][4] = Point(117, 319);pp[0][5] = Point(15, 260);pp[1][0] = Point(359, 339);pp[1][1] = Point(447, 351);pp[1][2] = Point(504, 349);pp[1][3] = Point(484, 433);pp[1][4] = Point(418, 449);pp[1][5] = Point(354, 402);Point pp2[5];pp2[0] = Point(350, 83);pp2[1] = Point(463, 90);pp2[2] = Point(500, 171);pp2[3] = Point(421, 194);pp2[4] = Point(338, 141);const Point* pts[3] = { pp[0],pp[1],pp2 }; //pts变量的生成int npts[] = { 6,6,5 }; //顶点个数数组的生成fillPoly(img, pts, npts, 3, Scalar(125, 125, 125),8); //绘制3个多边形//生成文字putText(img, "Learn OpenCV 4",Point(100, 400), 2, 1, Scalar(255, 255, 255));imshow("", img);waitKey(0);return 0;
}
3.5 感兴趣区域
Rect数据结构和Rang数据结构
copyTo()
代码清单3-50 截图、深浅拷贝验证程序
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("lena.png");Mat noobcv = imread("noobcv.jpg");if (img.empty() || noobcv.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat ROI1, ROI2, ROI2_copy, mask, img2, img_copy, img_copy2;resize(noobcv, mask, Size(200, 200));img2 = img; //浅拷贝//深拷贝的两种方式img.copyTo(img_copy2);copyTo(img, img_copy, img);//两种在图中截取ROI区域的方式Rect rect(206, 206, 200, 200); //定义ROI区域ROI1 = img(rect); //截图ROI2 = img(Range(300, 500), Range(300, 500)); //第二种截图方式img(Range(300, 500), Range(300, 500)).copyTo(ROI2_copy); //深拷贝mask.copyTo(ROI1); //在图像中加入部分图像imshow("加入noobcv后图像", img);imshow("ROI对ROI2的影响", ROI2);imshow("深拷贝的ROI2_copy", ROI2_copy);circle(img, Point(300, 300), 20, Scalar(0, 0, 255), -1); //绘制一个圆形imshow("浅拷贝的img2", img2);imshow("深拷贝的img_copy", img_copy);imshow("深拷贝的img_copy2", img_copy2);imshow("画圆对ROI1的影响", ROI1);waitKey(0);return 0;
}
3.6 图像“金字塔”
3.6.1 高斯“金字塔”
pyrDown()
3.6.2 高斯“金字塔”
pyrUp()
代码清单3-53 构建 高斯“金字塔” 和 高斯“金字塔”
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}vector Gauss, Lap; //高斯金字塔和拉普拉斯金字塔int level = 3; //高斯金字塔下采样次数Gauss.push_back(img); //将原图作为高斯金字塔的第0层//构建高斯金字塔for (int i = 0; i < level; i++){Mat gauss;pyrDown(Gauss[i], gauss); //下采样Gauss.push_back(gauss);}//构建拉普拉斯金字塔for (int i = Gauss.size() - 1; i > 0; i--){Mat lap, upGauss;if (i == Gauss.size() - 1) //如果是高斯金字塔中的最上面一层图像{Mat down;pyrDown(Gauss[i], down); //上采样pyrUp(down, upGauss);lap = Gauss[i] - upGauss;Lap.push_back(lap);}pyrUp(Gauss[i], upGauss);lap = Gauss[i - 1] - upGauss;Lap.push_back(lap);}//查看两个金字塔中的图像for (int i = 0; i < Gauss.size(); i++){string name = to_string(i);imshow("G" + name, Gauss[i]);imshow("L" + name, Lap[i]);}waitKey(0);return 0;
}
3.7 窗口交互操作
3.7.1 图像窗口滑动条
createTrackbar()
代码清单3-55 在图像中创建滑动条改变图像亮度
#include
#include using namespace std;
using namespace cv;//为了能在被调函数中使用,所以设置成全局的
int value;
void callBack(int, void*); //滑动条回调函数
Mat img1, img2;int main()
{img1 = imread("lena.png");if (!img1.data){cout << "请确认是否输入正确的图像文件" << endl;return -1;}namedWindow("滑动条改变图像亮度");imshow("滑动条改变图像亮度", img1);value = 100; //滑动条创建时的初值//创建滑动条createTrackbar("亮度值百分比", "滑动条改变图像亮度", &value, 600, callBack, 0);waitKey();
}static void callBack(int, void*)
{float a = value / 100.0;img2 = img1 * a;imshow("滑动条改变图像亮度", img2);
}
3.7.2 鼠标相应
setMouseCallback()
MouseCallback类型
代码清单3-58 绘制鼠标移动轨迹
#include
#include using namespace std;
using namespace cv;Mat img, imgPoint; //全局的图像
Point prePoint; //前一时刻鼠标的坐标,用于绘制直线
void mouse(int event, int x, int y, int flags, void*);int main()
{img = imread("lena.png");if (!img.data){cout << "请确认输入图像名称是否正确! " << endl;return -1;}img.copyTo(imgPoint);imshow("图像窗口 1", img);imshow("图像窗口 2", imgPoint);setMouseCallback("图像窗口 1", mouse, 0); //鼠标影响waitKey(0);return 0;}void mouse(int event, int x, int y, int flags, void*)
{if (event == EVENT_RBUTTONDOWN) //单击右键{cout << "点击鼠标左键才可以绘制轨迹" << endl;}if (event == EVENT_LBUTTONDOWN) //单击左键,输出坐标{prePoint = Point(x, y);cout << "轨迹起始坐标" << prePoint << endl;}if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) //鼠标按住左键移动第 3 章 图像基本操作{//通过改变图像像素显示鼠标移动轨迹imgPoint.at(y, x) = Vec3b(0, 0, 255);imgPoint.at(y, x - 1) = Vec3b(0, 0, 255);imgPoint.at(y, x + 1) = Vec3b(0, 0, 255);imgPoint.at(y + 1, x) = Vec3b(0, 0, 255);imgPoint.at(y + 1, x) = Vec3b(0, 0, 255);imshow("图像窗口 2", imgPoint);//通过绘制直线显示鼠标移动轨迹Point pt(x, y);line(img, prePoint, pt, Scalar(0, 0, 255), 2, 5, 0);prePoint = pt;imshow("图像窗口 1", img);}
}
第四章 图像直方图与模板匹配
4.1 图像直方图的绘制
calcHist()
cvRound()
代码清单4-2 绘制图像直方图
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("apple.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);//设置提取直方图的相关变量Mat hist; //用于存放直方图计算结果const int channels[1] = { 0 }; //通道索引float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges }; //像素灰度值范围const int bins[1] = { 256 }; //直方图的维度,其实就是像素灰度值的最大值calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges); //计算图像直方图//准备绘制直方图int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width*(i - 1), hist_h - 1),Point(width*i - 1, hist_h - cvRound(hist.at(i - 1) / 15)),Scalar(255, 255, 255), -1);}namedWindow("histImage", WINDOW_AUTOSIZE);imshow("histImage", histImage);imshow("gray", gray);waitKey(0);return 0;
}
4.2 直方图操作
4.2.1 直方图归一化
normalize()
代码清单4-4 直方图归一化操作
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色vector positiveData = { 2.0, 8.0, 10.0 };vector normalized_L1, normalized_L2, normalized_Inf, normalized_L2SQR;//测试不同归一化方法normalize(positiveData, normalized_L1, 1.0, 0.0, NORM_L1); //绝对值求和归一化cout << "normalized_L1=[" << normalized_L1[0] << ", "<< normalized_L1[1] << ", " << normalized_L1[2] << "]" << endl;normalize(positiveData, normalized_L2, 1.0, 0.0, NORM_L2); //模长归一化cout << "normalized_L2=[" << normalized_L2[0] << ", "<< normalized_L2[1] << ", " << normalized_L2[2] << "]" << endl;normalize(positiveData, normalized_Inf, 1.0, 0.0, NORM_INF); //最大值归一化cout << "normalized_Inf=[" << normalized_Inf[0] << ", "<< normalized_Inf[1] << ", " << normalized_Inf[2] << "]" << endl;normalize(positiveData, normalized_L2SQR, 1.0, 0.0, NORM_MINMAX); //偏移归一化cout << "normalized_MINMAX=[" << normalized_L2SQR[0] << ", "<< normalized_L2SQR[1] << ", " << normalized_L2SQR[2] << "]" << endl;//将图像直方图归一化Mat img = imread("apple.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray, hist;cvtColor(img, gray, COLOR_BGR2GRAY);const int channels[1] = { 0 };float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges };const int bins[1] = { 256 };calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage_L1 = Mat::zeros(hist_h, hist_w, CV_8UC3);Mat histImage_Inf = Mat::zeros(hist_h, hist_w, CV_8UC3);Mat hist_L1, hist_Inf;normalize(hist, hist_L1, 1, 0, NORM_L1, -1, Mat());for (int i = 1; i <= hist_L1.rows; i++){rectangle(histImage_L1, Point(width*(i - 1), hist_h - 1),Point(width*i - 1, hist_h - cvRound(30 * hist_h*hist_L1.at(i - 1)) - 1),Scalar(255, 255, 255), -1);}normalize(hist, hist_Inf, 1, 0, NORM_INF, -1, Mat());for (int i = 1; i <= hist_Inf.rows; i++){rectangle(histImage_Inf, Point(width*(i - 1), hist_h - 1),Point(width*i - 1, hist_h - cvRound(hist_h*hist_Inf.at(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow("histImage_L1", histImage_L1);imshow("histImage_Inf", histImage_Inf);waitKey(0);return 0;
}
4.2.2 直方图比较
compareHist()
代码清单4-6 比较两个直方图的相似性
#include
#include using namespace cv;
using namespace std;void drawHist(Mat &hist, int type, string name) //归一化并绘制直方图函数
{int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);normalize(hist, hist, 1, 0, type, -1, Mat());for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width*(i - 1), hist_h - 1),Point(width*i - 1, hist_h - cvRound(hist_h*hist.at(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow(name, histImage);
}
//主函数
int main()
{system("color F0"); //更改输出界面颜色Mat img = imread("apple.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray, hist, gray2, hist2, gray3, hist3;cvtColor(img, gray, COLOR_BGR2GRAY);resize(gray, gray2, Size(), 0.5, 0.5);gray3 = imread("lena.png", IMREAD_GRAYSCALE);const int channels[1] = { 0 };float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges };const int bins[1] = { 256 };calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);calcHist(&gray2, 1, channels, Mat(), hist2, 1, bins, ranges);calcHist(&gray3, 1, channels, Mat(), hist3, 1, bins, ranges);drawHist(hist, NORM_INF, "hist");drawHist(hist2, NORM_INF, "hist2");drawHist(hist3, NORM_INF, "hist3");//原图直方图与原图直方图的相关系数double hist_hist = compareHist(hist, hist, HISTCMP_CORREL);cout << "apple_apple=" << hist_hist << endl;//原图直方图与缩小原图直方图的相关系数double hist_hist2 = compareHist(hist, hist2, HISTCMP_CORREL);cout << "apple_apple256=" << hist_hist2 << endl;//两张不同图像直方图相关系数double hist_hist3 = compareHist(hist, hist3, HISTCMP_CORREL);cout << "apple_lena=" << hist_hist3 << endl;waitKey(0);return 0;
}
4.3 直方图应用
4.3.1 直方图均衡化
equalizeHist()
代码清单4-8 直方图均衡化实现
#include
#include using namespace cv;
using namespace std;void drawHist(Mat &hist, int type, string name) //归一化并绘制直方图函数
{int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);normalize(hist, hist, 1, 0, type, -1, Mat());for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width*(i - 1), hist_h - 1),Point(width*i - 1, hist_h - cvRound(hist_h*hist.at(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow(name, histImage);
}
//主函数
int main()
{Mat img = imread("gearwheel.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray, hist, hist2;cvtColor(img, gray, COLOR_BGR2GRAY);Mat equalImg;equalizeHist(gray, equalImg); //将图像直方图均衡化const int channels[1] = { 0 };float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges };const int bins[1] = { 256 };calcHist(&gray, 1, channels, Mat(), hist, 1, bins, ranges);calcHist(&equalImg, 1, channels, Mat(), hist2, 1, bins, ranges);drawHist(hist, NORM_INF, "hist");drawHist(hist2, NORM_INF, "hist2");imshow("原图", gray);imshow("均衡化后的图像", equalImg);waitKey(0);return 0;
}
4.3.2 直方图匹配
代码清单4-9 图像直方图匹配
#include
#include using namespace cv;
using namespace std;void drawHist(Mat &hist, int type, string name) //归一化并绘制直方图函数
{int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);normalize(hist, hist, 1, 0, type, -1, Mat());for (int i = 1; i <= hist.rows; i++){rectangle(histImage, Point(width*(i - 1), hist_h - 1),Point(width*i - 1, hist_h - cvRound(20 * hist_h*hist.at(i - 1)) - 1),Scalar(255, 255, 255), -1);}imshow(name, histImage);
}
//主函数
int main()
{Mat img1 = imread("histMatch.png");Mat img2 = imread("equalLena.png");if (img1.empty() || img2.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat hist1, hist2;//计算两张图像直方图const int channels[1] = { 0 };float inRanges[2] = { 0,255 };const float* ranges[1] = { inRanges };const int bins[1] = { 256 };calcHist(&img1, 1, channels, Mat(), hist1, 1, bins, ranges);calcHist(&img2, 1, channels, Mat(), hist2, 1, bins, ranges);//归一化两张图像的直方图drawHist(hist1, NORM_L1, "hist1");drawHist(hist2, NORM_L1, "hist2");//计算两张图像直方图的累积概率float hist1_cdf[256] = { hist1.at(0) };float hist2_cdf[256] = { hist2.at(0) };for (int i = 1; i < 256; i++){hist1_cdf[i] = hist1_cdf[i - 1] + hist1.at(i);hist2_cdf[i] = hist2_cdf[i - 1] + hist2.at(i);}//构建累积概率误差矩阵float diff_cdf[256][256];for (int i = 0; i < 256; i++){for (int j = 0; j < 256; j++){diff_cdf[i][j] = fabs(hist1_cdf[i] - hist2_cdf[j]);}}//生成LUT映射表Mat lut(1, 256, CV_8U);for (int i = 0; i < 256; i++){// 查找源灰度级为i的映射灰度// 和i的累积概率差值最小的规定化灰度float min = diff_cdf[i][0];int index = 0;//寻找累积概率误差矩阵中每一行中的最小值for (int j = 1; j < 256; j++){if (min > diff_cdf[i][j]){min = diff_cdf[i][j];index = j;}}lut.at(i) = (uchar)index;}Mat result, hist3;LUT(img1, lut, result);imshow("待匹配图像", img1);imshow("匹配的模板图像", img2);imshow("直方图匹配结果", result);calcHist(&result, 1, channels, Mat(), hist3, 1, bins, ranges);drawHist(hist3, NORM_L1, "hist3"); //绘制匹配后的图像直方图waitKey(0);return 0;
}
4.3.3 直方图反向投影
calcBackProject()
代码清单4-11 图像直方图反向摄影
#include
#include using namespace cv;
using namespace std;void drawHist(Mat &hist, int type, string name) //归一化并绘制直方图函数
{int hist_w = 512;int hist_h = 400;int width = 2;Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);normalize(hist, hist, 255, 0, type, -1, Mat());namedWindow(name, WINDOW_NORMAL);imshow(name, hist);
}
//主函数
int main()
{Mat img = imread("apple.jpg");Mat sub_img = imread("sub_apple.jpg");Mat img_HSV, sub_HSV, hist, hist2;if (img.empty() || sub_img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("img", img);imshow("sub_img", sub_img);//转成HSV空间,提取S、V两个通道cvtColor(img, img_HSV, COLOR_BGR2HSV);cvtColor(sub_img, sub_HSV, COLOR_BGR2HSV);int h_bins = 32; int s_bins = 32;int histSize[] = { h_bins, s_bins };//H通道值的范围由0到179float h_ranges[] = { 0, 180 };//S通道值的范围由0到255float s_ranges[] = { 0, 256 };const float* ranges[] = { h_ranges, s_ranges }; //每个通道的范围int channels[] = { 0, 1 }; //统计的通道索引//绘制H-S二维直方图calcHist(&sub_HSV, 1, channels, Mat(), hist, 2, histSize, ranges, true, false);drawHist(hist, NORM_INF, "hist"); //直方图归一化并绘制直方图Mat backproj;calcBackProject(&img_HSV, 1, channels, hist, backproj, ranges, 1.0); //直方图反向投影imshow("反向投影后结果", backproj);waitKey(0);return 0;
}
4.4 图像的模板匹配
matchTemplate()
代码清单4-13 图像的模板匹配
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("lena.png");Mat temp = imread("lena_face.png");if (img.empty() || temp.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat result;matchTemplate(img, temp, result, TM_CCOEFF_NORMED);//模板匹配double maxVal, minVal;Point minLoc, maxLoc;//寻找匹配结果中的最大值和最小值以及坐标位置minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);//绘制最佳匹配区域rectangle(img, cv::Rect(maxLoc.x, maxLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255), 2);imshow("原图", img);imshow("模板图像", temp);imshow("result", result);waitKey(0);return 0;
}
第五章 图像滤波
5.1 图像卷积
filter2D()
代码清单5-2 图像卷积
#include
#include using namespace cv;
using namespace std;int main()
{//待卷积矩阵uchar points[25] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 };Mat img(5, 5, CV_8UC1, points);//卷积模板Mat kernel = (Mat_(3, 3) << 1, 2, 1,2, 0, 2,1, 2, 1);Mat kernel_norm = kernel / 12; //卷积模板归一化//未归一化卷积结果和归一化卷积结果Mat result, result_norm;filter2D(img, result, CV_32F, kernel, Point(-1, -1), 2, BORDER_CONSTANT);filter2D(img, result_norm, CV_32F, kernel_norm, Point(-1, -1), 2, BORDER_CONSTANT);cout << "result:" << endl << result << endl;cout << "result_norm:" << endl << result_norm << endl;//图像卷积Mat lena = imread("lena.png");if (lena.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat lena_fillter;filter2D(lena, lena_fillter, -1, kernel_norm, Point(-1, -1), 2, BORDER_CONSTANT);imshow("lena_fillter", lena_fillter);imshow("lena", lena);waitKey(0);return 0;
}
5.2 噪声的种类与生成
5.2.1 椒盐噪声
rand()
rand_double() 、 rand_int()
cvflann类
stlib.h头文件
代码清单5-4 图像中添加椒盐噪声
#include
#include using namespace cv;
using namespace std;//盐噪声函数
void saltAndPepper(cv::Mat image, int n)
{for (int k = 0; k(j, i) = 255; //白色噪声}else if (image.type() == CV_8UC3) //处理彩色图像{image.at(j, i)[0] = 255; //cv::Vec3b为opencv定义的一个3个值的向量类型 image.at(j, i)[1] = 255; //[]指定通道,B:0,G:1,R:2 image.at(j, i)[2] = 255;}}else //添加黑色噪声{if (image.type() == CV_8UC1){image.at(j, i) = 0;}else if (image.type() == CV_8UC3){image.at(j, i)[0] = 0; //cv::Vec3b为opencv定义的一个3个值的向量类型 image.at(j, i)[1] = 0; //[]指定通道,B:0,G:1,R:2 image.at(j, i)[2] = 0;}}}
}int main()
{Mat lena = imread("lena.png");Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH);if (lena.empty() || equalLena.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("lena原图", lena);imshow("equalLena原图", equalLena);saltAndPepper(lena, 10000); //彩色图像添加椒盐噪声saltAndPepper(equalLena, 10000); //灰度图像添加椒盐噪声imshow("lena添加噪声", lena);imshow("equalLena添加噪声", equalLena);waitKey(0);return 0;
}
5.2.2 高斯噪声
fill()
RNG::fill()
代码清单5-7 图像中添加高斯噪声
#include
#include using namespace cv;
using namespace std;int main()
{Mat lena = imread("lena.png");Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH);if (lena.empty() || equalLena.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}//生成与原图像同尺寸、数据类型和通道数的矩阵Mat lena_noise = Mat::zeros(lena.rows, lena.cols, lena.type());Mat equalLena_noise = Mat::zeros(lena.rows, lena.cols, equalLena.type());imshow("lena原图", lena);imshow("equalLena原图", equalLena);RNG rng; //创建一个RNG类rng.fill(lena_noise, RNG::NORMAL, 10, 20); //生成三通道的高斯分布随机数rng.fill(equalLena_noise, RNG::NORMAL, 15, 30); //生成三通道的高斯分布随机数imshow("三通道高斯噪声", lena_noise);imshow("单通道高斯噪声", equalLena_noise);lena = lena + lena_noise; //在彩色图像中添加高斯噪声equalLena = equalLena + equalLena_noise; //在灰度图像中添加高斯噪声//显示添加高斯噪声后的图像imshow("lena添加噪声", lena);imshow("equalLena添加噪声", equalLena);waitKey(0);return 0;
}
5.3 线性滤波
5.3.1 均值滤波
blur()
代码清单5-9 图像均值滤波
#include
#include using namespace cv;
using namespace std;int main()
{Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH);Mat equalLena_gauss = imread("equalLena_gauss.png", IMREAD_ANYDEPTH);Mat equalLena_salt = imread("equalLena_salt.png", IMREAD_ANYDEPTH);if (equalLena.empty() || equalLena_gauss.empty() || equalLena_salt.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat result_3, result_9; //存放不含噪声滤波结果,后面数字代表滤波器尺寸Mat result_3gauss, result_9gauss; //存放含有高斯噪声滤波结果,后面数字代表滤波器尺寸Mat result_3salt, result_9salt; //存放含有椒盐噪声滤波结果,后面数字代表滤波器尺寸//调用均值滤波函数blur()进行滤波blur(equalLena, result_3, Size(3, 3));blur(equalLena, result_9, Size(9, 9));blur(equalLena_gauss, result_3gauss, Size(3, 3));blur(equalLena_gauss, result_9gauss, Size(9, 9));blur(equalLena_salt, result_3salt, Size(3, 3));blur(equalLena_salt, result_9salt, Size(9, 9));//显示不含噪声图像imshow("equalLena ", equalLena);imshow("result_3", result_3);imshow("result_9", result_9);//显示含有高斯噪声图像imshow("equalLena_gauss", equalLena_gauss);imshow("result_3gauss", result_3gauss);imshow("result_9gauss", result_9gauss);//显示含有椒盐噪声图像imshow("equalLena_salt", equalLena_salt);imshow("result_3salt", result_3salt);imshow("result_9salt", result_9salt);waitKey(0);return 0;
}
5.3.2 方框滤波
boxFilter()
sqrBoxFilter()
代码清单5-12 图像方框滤波
#include
#include using namespace cv;
using namespace std;int main()
{Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH); //用于方框滤波的图像if (equalLena.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}//验证方框滤波算法的数据矩阵float points[25] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 };Mat data(5, 5, CV_32FC1, points);//将CV_8U类型转换成CV_32F类型Mat equalLena_32F;equalLena.convertTo(equalLena_32F, CV_32F, 1.0 / 255);Mat resultNorm, result, dataSqrNorm, dataSqr, equalLena_32FSqr;//方框滤波boxFilter()和sqrBoxFilter()boxFilter(equalLena, resultNorm, -1, Size(3, 3), Point(-1, -1), true); //进行归一化boxFilter(equalLena, result, -1, Size(3, 3), Point(-1, -1), false); //不进行归一化sqrBoxFilter(data, dataSqrNorm, -1, Size(3, 3), Point(-1, -1),true, BORDER_CONSTANT); //进行归一化sqrBoxFilter(data, dataSqr, -1, Size(3, 3), Point(-1, -1),false, BORDER_CONSTANT); //不进行归一化sqrBoxFilter(equalLena_32F, equalLena_32FSqr, -1, Size(3, 3), Point(-1, -1),true, BORDER_CONSTANT);//显示处理结果imshow("resultNorm", resultNorm);imshow("result", result);imshow("equalLena_32FSqr", equalLena_32FSqr);waitKey(0);return 0;
}
5.3.3 高斯滤波
GaussianBlur()
getGaussianKernel()
代码清单5-15 图像高斯滤波
#include
#include using namespace cv;
using namespace std;int main()
{Mat equalLena = imread("equalLena.png", IMREAD_ANYDEPTH);Mat equalLena_gauss = imread("equalLena_gauss.png", IMREAD_ANYDEPTH);Mat equalLena_salt = imread("equalLena_salt.png", IMREAD_ANYDEPTH);if (equalLena.empty() || equalLena_gauss.empty() || equalLena_salt.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat result_5, result_9; //存放不含噪声滤波结果,后面数字代表滤波器尺寸Mat result_5gauss, result_9gauss; //存放含有高斯噪声滤波结果,后面数字代表滤波器尺寸Mat result_5salt, result_9salt; 存放含有椒盐噪声滤波结果,后面数字代表滤波器尺寸//调用均值滤波函数blur()进行滤波GaussianBlur(equalLena, result_5, Size(5, 5), 10, 20);GaussianBlur(equalLena, result_9, Size(9, 9), 10, 20);GaussianBlur(equalLena_gauss, result_5gauss, Size(5, 5), 10, 20);GaussianBlur(equalLena_gauss, result_9gauss, Size(9, 9), 10, 20);GaussianBlur(equalLena_salt, result_5salt, Size(5, 5), 10, 20);GaussianBlur(equalLena_salt, result_9salt, Size(9, 9), 10, 20);//显示不含噪声图像imshow("equalLena ", equalLena);imshow("result_5", result_5);imshow("result_9", result_9);//显示含有高斯噪声图像imshow("equalLena_gauss", equalLena_gauss);imshow("result_5gauss", result_5gauss);imshow("result_9gauss", result_9gauss);//显示含有椒盐噪声图像imshow("equalLena_salt", equalLena_salt);imshow("result_5salt", result_5salt);imshow("result_9salt", result_9salt);waitKey(0);return 0;
}
5.3.4 可分离滤波
sepFilter2D()
filter2D()
代码清单5-17 可分离图像滤波
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色float points[25] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 };Mat data(5, 5, CV_32FC1, points);//X方向、Y方向和联合滤波器的构建Mat a = (Mat_(3, 1) << -1, 3, -1);Mat b = a.reshape(1, 1);Mat ab = a*b;//验证高斯滤波的可分离性Mat gaussX = getGaussianKernel(3, 1);Mat gaussData, gaussDataXY;GaussianBlur(data, gaussData, Size(3, 3), 1, 1, BORDER_CONSTANT);sepFilter2D(data, gaussDataXY, -1, gaussX, gaussX, Point(-1, -1), 0, BORDER_CONSTANT);//输入两种高斯滤波的计算结果cout << "gaussData=" << endl<< gaussData << endl;cout << "gaussDataXY=" << endl<< gaussDataXY << endl;//线性滤波的可分离性Mat dataYX, dataY, dataXY, dataXY_sep;filter2D(data, dataY, -1, a, Point(-1, -1), 0, BORDER_CONSTANT);filter2D(dataY, dataYX, -1, b, Point(-1, -1), 0, BORDER_CONSTANT);filter2D(data, dataXY, -1, ab, Point(-1, -1), 0, BORDER_CONSTANT);sepFilter2D(data, dataXY_sep, -1, b, b, Point(-1, -1), 0, BORDER_CONSTANT);//输出分离滤波和联合滤波的计算结果cout << "dataY=" << endl<< dataY << endl;cout << "dataYX=" << endl<< dataYX << endl;cout << "dataXY=" << endl<< dataXY << endl;cout << "dataXY_sep=" << endl<< dataXY_sep << endl;//对图像的分离操作Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat imgYX, imgY, imgXY;filter2D(img, imgY, -1, a, Point(-1, -1), 0, BORDER_CONSTANT);filter2D(imgY, imgYX, -1, b, Point(-1, -1), 0, BORDER_CONSTANT);filter2D(img, imgXY, -1, ab, Point(-1, -1), 0, BORDER_CONSTANT);imshow("img", img);imshow("imgY", imgY);imshow("imgYX", imgYX);imshow("imgXY", imgXY);waitKey(0);return 0;
}
5.4 非线性滤波
5.4.1 中值滤波
medianBlur()
代码清单5-19 中值滤波
#include
#include using namespace cv;
using namespace std;int main()
{Mat gray = imread("equalLena_salt.png", IMREAD_ANYCOLOR);Mat img = imread("lena_salt.png", IMREAD_ANYCOLOR);if (gray.empty() || img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat imgResult3, grayResult3, imgResult9, grayResult9;//分别对含有椒盐噪声的彩色和灰度图像进行滤波,滤波模板为3×3medianBlur(img, imgResult3, 3);medianBlur(gray, grayResult3, 3);//加大滤波模板,图像滤波结果会变模糊medianBlur(img, imgResult9, 9);medianBlur(gray, grayResult9, 9);//显示滤波处理结果imshow("img", img);imshow("gray", gray);imshow("imgResult3", imgResult3);imshow("grayResult3", grayResult3);imshow("imgResult9", imgResult9);imshow("grayResult9", grayResult9);waitKey(0);return 0;
}
5.4.2 双边滤波
bilateralFilter()
代码清单5-21 人脸图像双边滤波
#include
#include using namespace cv;
using namespace std;int main()
{//读取两张含有人脸的图像Mat img1 = imread("img1.png", IMREAD_ANYCOLOR);Mat img2 = imread("img2.png", IMREAD_ANYCOLOR);if (img1.empty() || img2.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat result1, result2, result3, result4;//验证不同滤波器直径的滤波效果bilateralFilter(img1, result1, 9, 50, 25 / 2);bilateralFilter(img1, result2, 25, 50, 25 / 2);//验证不同标准差值的滤波效果bilateralFilter(img2, result3, 9, 9, 9);bilateralFilter(img2, result4, 9, 200, 200);//显示原图imshow("img1", img1);imshow("img2", img2);//不同直径滤波结果imshow("result1", result1);imshow("result2", result2);//不同标准差值滤波结果imshow("result3 ", result3);imshow("result4", result4);waitKey(0);return 0;
}
5.5 图像的边缘检测
5.5.1 边缘检测原理
convertScaleAbs()
代码清单5-23 图像边缘检测
#include
#include using namespace cv;
using namespace std;int main()
{//创建边缘检测滤波器Mat kernel1 = (Mat_(1, 2) << 1, -1); //X方向边缘检测滤波器Mat kernel2 = (Mat_(1, 3) << 1, 0, -1); //X方向边缘检测滤波器Mat kernel3 = (Mat_(3, 1) << 1, 0, -1); //X方向边缘检测滤波器Mat kernelXY = (Mat_(2, 2) << 1, 0, 0, -1); //由左上到右下方向边缘检测滤波器Mat kernelYX = (Mat_(2, 2) << 0, -1, 1, 0); //由右上到左下方向边缘检测滤波器//读取图像,黑白图像边缘检测结果较为明显Mat img = imread("equalLena.png", IMREAD_ANYCOLOR);if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat result1, result2, result3, result4, result5, result6;//检测图像边缘//以[1 -1]检测水平方向边缘filter2D(img, result1, CV_16S, kernel1);convertScaleAbs(result1, result1);//以[1 0 -1]检测水平方向边缘filter2D(img, result2, CV_16S, kernel2);convertScaleAbs(result2, result2);//以[1 0 -1]'检测由垂直方向边缘filter2D(img, result3, CV_16S, kernel3);convertScaleAbs(result3, result3);//整幅图像的边缘result6 = result2 + result3;//检测由左上到右下方向边缘filter2D(img, result4, CV_16S, kernelXY);convertScaleAbs(result4, result4);//检测由右上到左下方向边缘filter2D(img, result5, CV_16S, kernelYX);convertScaleAbs(result5, result5);//显示边缘检测结果imshow("result1", result1);imshow("result2", result2);imshow("result3", result3);imshow("result4", result4);imshow("result5", result5);imshow("result6", result6);waitKey(0);return 0;
}
5.5.2 Sobel算子
Sobel()
代码清单5-25 图像Sobel边缘提取
#include
#include using namespace cv;
using namespace std;int main()
{//读取图像,黑白图像边缘检测结果较为明显Mat img = imread("equalLena.png", IMREAD_ANYCOLOR);if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat resultX, resultY, resultXY;//X方向一阶边缘Sobel(img, resultX, CV_16S, 2, 0, 1);convertScaleAbs(resultX, resultX);//Y方向一阶边缘Sobel(img, resultY, CV_16S, 0, 1, 3);convertScaleAbs(resultY, resultY);//整幅图像的一阶边缘resultXY = resultX + resultY;//显示图像imshow("resultX", resultX);imshow("resultY", resultY);imshow("resultXY", resultXY);waitKey(0);return 0;
}
5.5.3 Scharr算子
Scharr()
代码清单5-27 图像Scharr边缘提取
#include
#include using namespace cv;
using namespace std;int main()
{//读取图像,黑白图像边缘检测结果较为明显Mat img = imread("equalLena.png", IMREAD_ANYDEPTH);if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat resultX, resultY, resultXY;//X方向一阶边缘Scharr(img, resultX, CV_16S, 1, 0);convertScaleAbs(resultX, resultX);//Y方向一阶边缘Scharr(img, resultY, CV_16S, 0, 1);convertScaleAbs(resultY, resultY);//整幅图像的一阶边缘resultXY = resultX + resultY;//显示图像imshow("resultX", resultX);imshow("resultY", resultY);imshow("resultXY", resultXY);waitKey(0);return 0;
}
5.5.4 生成边缘检测滤波器
getDerivKernels()
代码清单5-29 计算Sobel算子和Scharr算子
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色Mat sobel_x1, sobel_y1, sobel_x2, sobel_y2, sobel_x3, sobel_y3; //存放分离的Sobel算子Mat scharr_x, scharr_y; //存放分离的Scharr算子Mat sobelX1, sobelX2, sobelX3, scharrX; //存放最终算子//一阶X方向Sobel算子getDerivKernels(sobel_x1, sobel_y1, 1, 0, 3);sobel_x1 = sobel_x1.reshape(CV_8U, 1);sobelX1 = sobel_y1*sobel_x1; //计算滤波器//二阶X方向Sobel算子getDerivKernels(sobel_x2, sobel_y2, 2, 0, 5);sobel_x2 = sobel_x2.reshape(CV_8U, 1);sobelX2 = sobel_y2*sobel_x2; //计算滤波器//三阶X方向Sobel算子getDerivKernels(sobel_x3, sobel_y3, 3, 0, 7);sobel_x3 = sobel_x3.reshape(CV_8U, 1);sobelX3 = sobel_y3*sobel_x3; //计算滤波器//X方向Scharr算子getDerivKernels(scharr_x, scharr_y, 1, 0, FILTER_SCHARR);scharr_x = scharr_x.reshape(CV_8U, 1);scharrX = scharr_y*scharr_x; //计算滤波器//输出结果cout << "X方向一阶Sobel算子:" << endl << sobelX1 << endl;cout << "X方向二阶Sobel算子:" << endl << sobelX2 << endl;cout << "X方向三阶Sobel算子:" << endl << sobelX3 << endl;cout << "X方向Scharr算子:" << endl << scharrX << endl;waitKey(0);return 0;
}
5.5.5 Laplacian算子
Laplacian()
代码清单5-31 利用Laplacian算子检测图像边缘
#include
#include using namespace cv;
using namespace std;int main()
{//读取图像,黑白图像边缘检测结果较为明显Mat img = imread("equalLena.png", IMREAD_ANYDEPTH);if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat result, result_g, result_G;//未滤波提取边缘Laplacian(img, result, CV_16S, 3, 1, 0);convertScaleAbs(result, result);//滤波后提取Laplacian边缘GaussianBlur(img, result_g, Size(3, 3), 5, 0); //高斯滤波Laplacian(result_g, result_G, CV_16S, 3, 1, 0);convertScaleAbs(result_G, result_G);//显示图像imshow("result", result);imshow("result_G", result_G);waitKey(0);return 0;
}
5.5.6 Canny算法
Canny()
代码清单5-3 利用Canny算法提取图像边缘
#include
#include using namespace cv;
using namespace std;int main()
{//读取图像,黑白图像边缘检测结果较为明显Mat img = imread("equalLena.png", IMREAD_ANYDEPTH);if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat resultHigh, resultLow, resultG;//大阈值检测图像边缘Canny(img, resultHigh, 100, 200, 3);//小阈值检测图像边缘Canny(img, resultLow, 20, 40, 3);//高斯模糊后检测图像边缘GaussianBlur(img, resultG, Size(3, 3), 5);Canny(resultG, resultG, 100, 200, 3);//显示图像imshow("resultHigh", resultHigh);imshow("resultLow", resultLow);imshow("resultG", resultG);waitKey(0);return 0;
}
第六章 图像形态学操作
6.1 像素距离与连通域
6.1.1 图像像素距离变换
distanceTransform()
代码清单6-3 图像距离变换
#include
#include using namespace cv;
using namespace std;int main()
{//构建建议矩阵,用于求取像素之间的距离Mat a = (Mat_(5, 5) << 1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 0, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1);Mat dist_L1, dist_L2, dist_C, dist_L12;//计算街区距离distanceTransform(a, dist_L1, 1, 3, CV_8U);cout << "街区距离:" << endl << dist_L1 << endl;//计算欧式距离distanceTransform(a, dist_L2, 2, 5, CV_8U);cout << "欧式距离:" << endl << dist_L2 << endl;//计算棋盘距离distanceTransform(a, dist_C, 3, 5, CV_8U);cout << "棋盘距离:" << endl << dist_C << endl;//对图像进行距离变换Mat rice = imread("rice.png", IMREAD_GRAYSCALE);if (rice.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat riceBW, riceBW_INV;//将图像转成二值图像,同时把黑白区域颜色呼唤threshold(rice, riceBW, 50, 255, THRESH_BINARY);threshold(rice, riceBW_INV, 50, 255, THRESH_BINARY_INV);//距离变换Mat dist, dist_INV;distanceTransform(riceBW, dist, 1, 3, CV_32F); //为了显示清晰,将数据类型变成CV_32FdistanceTransform(riceBW_INV, dist_INV, 1, 3, CV_8U);//显示变换结果imshow("riceBW", riceBW);imshow("dist", dist);imshow("riceBW_INV", riceBW_INV);imshow("dist_INV", dist_INV);waitKey(0);return 0;
}
6.1.2 图像连通域分析
connectedComponents()
代码清单6-8 图像连通域计算
#include
#include
#include using namespace cv;
using namespace std;int main()
{//对图像进行距离变换Mat img = imread("rice.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat rice, riceBW;//将图像转成二值图像,用于统计连通域cvtColor(img, rice, COLOR_BGR2GRAY);threshold(rice, riceBW, 50, 255, THRESH_BINARY);//生成随机颜色,用于区分不同连通域RNG rng(10086);Mat out;int number = connectedComponents(riceBW, out, 8, CV_16U); //统计图像中连通域的个数vector colors;for (int i = 0; i < number; i++){//使用均匀分布的随机数确定颜色Vec3b vec3 = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));colors.push_back(vec3);}//以不同颜色标记出不同的连通域Mat result = Mat::zeros(rice.size(), img.type());int w = result.cols;int h = result.rows;for (int row = 0; row < h; row++){for (int col = 0; col < w; col++){int label = out.at(row, col);if (label == 0) //背景的黑色不改变{continue;}result.at(row, col) = colors[label];}}//显示结果imshow("原图", img);imshow("标记后的图像", result);waitKey(0);return 0;
}
connectedComponentsWithStats()
代码清单6-9 连通域信息统计
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色//对图像进行距离变换Mat img = imread("rice.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("原图", img);Mat rice, riceBW;//将图像转成二值图像,用于统计连通域cvtColor(img, rice, COLOR_BGR2GRAY);threshold(rice, riceBW, 50, 255, THRESH_BINARY);//生成随机颜色,用于区分不同连通域RNG rng(10086);Mat out, stats, centroids;//统计图像中连通域的个数int number = connectedComponentsWithStats(riceBW, out, stats, centroids, 8, CV_16U);vector colors;for (int i = 0; i < number; i++){//使用均匀分布的随机数确定颜色Vec3b vec3 = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));colors.push_back(vec3);}//以不同颜色标记出不同的连通域Mat result = Mat::zeros(rice.size(), img.type());int w = result.cols;int h = result.rows;for (int i = 1; i < number; i++){// 中心位置int center_x = centroids.at(i, 0);int center_y = centroids.at(i, 1);//矩形边框int x = stats.at(i, CC_STAT_LEFT);int y = stats.at(i, CC_STAT_TOP);int w = stats.at(i, CC_STAT_WIDTH);int h = stats.at(i, CC_STAT_HEIGHT);int area = stats.at(i, CC_STAT_AREA);// 中心位置绘制circle(img, Point(center_x, center_y), 2, Scalar(0, 255, 0), 2, 8, 0);// 外接矩形Rect rect(x, y, w, h);rectangle(img, rect, colors[i], 1, 8, 0);putText(img, format("%d", i), Point(center_x, center_y),FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 1);cout << "number: " << i << ",area: " << area << endl;}//显示结果imshow("标记后的图像", img);waitKey(0);return 0;
}
6.2 腐蚀与膨胀
6.2.1 图像腐蚀
erode()
getStructuringElement()
代码清单6-12 图像腐蚀
#include
#include
#include using namespace cv;
using namespace std;
//绘制包含区域函数
void drawState(Mat &img, int number, Mat centroids, Mat stats, String str) {RNG rng(10086);vector colors;for (int i = 0; i < number; i++){//使用均匀分布的随机数确定颜色Vec3b vec3 = Vec3b(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));colors.push_back(vec3);}for (int i = 1; i < number; i++){// 中心位置int center_x = centroids.at(i, 0);int center_y = centroids.at(i, 1);//矩形边框int x = stats.at(i, CC_STAT_LEFT);int y = stats.at(i, CC_STAT_TOP);int w = stats.at(i, CC_STAT_WIDTH);int h = stats.at(i, CC_STAT_HEIGHT);// 中心位置绘制circle(img, Point(center_x, center_y), 2, Scalar(0, 255, 0), 2, 8, 0);// 外接矩形Rect rect(x, y, w, h);rectangle(img, rect, colors[i], 1, 8, 0);putText(img, format("%d", i), Point(center_x, center_y),FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255), 1);}imshow(str, img);
}int main()
{//生成用于腐蚀的原图像Mat src = (Mat_(6, 6) << 0, 0, 0, 0, 255, 0,0, 255, 255, 255, 255, 255,0, 255, 255, 255, 255, 0,0, 255, 255, 255, 255, 0,0, 255, 255, 255, 255, 0,0, 0, 0, 0, 0, 0);Mat struct1, struct2;struct1 = getStructuringElement(0, Size(3, 3)); //矩形结构元素struct2 = getStructuringElement(1, Size(3, 3)); //十字结构元素Mat erodeSrc; //存放腐蚀后的图像erode(src, erodeSrc, struct2);namedWindow("src", WINDOW_GUI_NORMAL);namedWindow("erodeSrc", WINDOW_GUI_NORMAL);imshow("src", src);imshow("erodeSrc", erodeSrc);Mat LearnCV_black = imread("LearnCV_black.png", IMREAD_ANYCOLOR);Mat LearnCV_write = imread("LearnCV_write.png", IMREAD_ANYCOLOR);Mat erode_black1, erode_black2, erode_write1, erode_write2;//黑背景图像腐蚀erode(LearnCV_black, erode_black1, struct1);erode(LearnCV_black, erode_black2, struct2);imshow("LearnCV_black", LearnCV_black);imshow("erode_black1", erode_black1);imshow("erode_black2", erode_black2);//白背景腐蚀erode(LearnCV_write, erode_write1, struct1);erode(LearnCV_write, erode_write2, struct2);imshow("LearnCV_write", LearnCV_write);imshow("erode_write1", erode_write1);imshow("erode_write2", erode_write2);//验证腐蚀对小连通域的去除Mat img = imread("rice.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat img2;copyTo(img, img2, img); //克隆一个单独的图像,用于后期图像绘制Mat rice, riceBW;//将图像转成二值图像,用于统计连通域cvtColor(img, rice, COLOR_BGR2GRAY);threshold(rice, riceBW, 50, 255, THRESH_BINARY);Mat out, stats, centroids;//统计图像中连通域的个数int number = connectedComponentsWithStats(riceBW, out, stats, centroids, 8, CV_16U);drawState(img, number, centroids, stats, "未腐蚀时统计连通域"); //绘制图像erode(riceBW, riceBW, struct1); //对图像进行腐蚀number = connectedComponentsWithStats(riceBW, out, stats, centroids, 8, CV_16U);drawState(img2, number, centroids, stats, "腐蚀后统计连通域"); //绘制图像waitKey(0);return 0;
}
6.2.2 图像膨胀
dilate()
getStructuringElement()
代码清单6-14 图像膨胀
#include
#include
#include using namespace cv;
using namespace std;int main()
{//生成用于腐蚀的原图像Mat src = (Mat_(6, 6) << 0, 0, 0, 0, 255, 0,0, 255, 255, 255, 255, 255,0, 255, 255, 255, 255, 0,0, 255, 255, 255, 255, 0,0, 255, 255, 255, 255, 0,0, 0, 0, 0, 0, 0);Mat struct1, struct2;struct1 = getStructuringElement(0, Size(3, 3)); //矩形结构元素struct2 = getStructuringElement(1, Size(3, 3)); //十字结构元素Mat erodeSrc; //存放膨胀后的图像dilate(src, erodeSrc, struct2);namedWindow("src", WINDOW_GUI_NORMAL);namedWindow("dilateSrc", WINDOW_GUI_NORMAL);imshow("src", src);imshow("dilateSrc", erodeSrc);Mat LearnCV_black = imread("LearnCV_black.png", IMREAD_ANYCOLOR);Mat LearnCV_write = imread("LearnCV_write.png", IMREAD_ANYCOLOR);if (LearnCV_black.empty() || LearnCV_write.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat dilate_black1, dilate_black2, dilate_write1, dilate_write2;//黑背景图像膨胀dilate(LearnCV_black, dilate_black1, struct1);dilate(LearnCV_black, dilate_black2, struct2);imshow("LearnCV_black", LearnCV_black);imshow("dilate_black1", dilate_black1);imshow("dilate_black2", dilate_black2);//白背景图像膨胀dilate(LearnCV_write, dilate_write1, struct1);dilate(LearnCV_write, dilate_write2, struct2);imshow("LearnCV_write", LearnCV_write);imshow("dilate_write1", dilate_write1);imshow("dilate_write2", dilate_write2);//比较膨胀和腐蚀的结果Mat erode_black1, resultXor, resultAnd;erode(LearnCV_black, erode_black1, struct1);bitwise_xor(erode_black1, dilate_write1, resultXor);bitwise_and(erode_black1, dilate_write1, resultAnd);imshow("resultXor", resultXor);imshow("resultAnd", resultAnd);waitKey(0);return 0;
}
6.3 形态学应用
6.3.1 开运算
morphologyEx()
6.3.2 闭运算
morphologyEx()
6.3.3 形态学梯度
morphologyEx()
6.3.4 顶帽运算
morphologyEx()
6.3.5 黑帽运算
morphologyEx()
6.3.6 击中击不中变换
morphologyEx()
代码清单6-16 形态学操作应用
#include
#include
#include using namespace cv;
using namespace std;int main()
{//用于验证形态学应用的二值化矩阵Mat src = (Mat_(9, 12) << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 0,0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,0, 255, 255, 255, 0, 255, 255, 255, 0, 0, 0, 0,0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 0,0, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);namedWindow("src", WINDOW_NORMAL); //可以自由调节显示图像的尺寸imshow("src", src);//3×3矩形结构元素Mat kernel = getStructuringElement(0, Size(3, 3));//对二值化矩阵进行形态学操作Mat open, close, gradient, tophat, blackhat, hitmiss;//对二值化矩阵进行开运算morphologyEx(src, open, MORPH_OPEN, kernel);namedWindow("open", WINDOW_NORMAL); //可以自由调节显示图像的尺寸imshow("open", open);//对二值化矩阵进行闭运算morphologyEx(src, close, MORPH_CLOSE, kernel);namedWindow("close", WINDOW_NORMAL); //可以自由调节显示图像的尺寸imshow("close", close);//对二值化矩阵进行梯度运算morphologyEx(src, gradient, MORPH_GRADIENT, kernel);namedWindow("gradient", WINDOW_NORMAL); //可以自由调节显示图像的尺寸imshow("gradient", gradient);//对二值化矩阵进行顶帽运算morphologyEx(src, tophat, MORPH_TOPHAT, kernel);namedWindow("tophat", WINDOW_NORMAL); //可以自由调节显示图像的尺寸imshow("tophat", tophat);//对二值化矩阵进行黑帽运算morphologyEx(src, blackhat, MORPH_BLACKHAT, kernel);namedWindow("blackhat", WINDOW_NORMAL); //可以自由调节显示图像的尺寸imshow("blackhat", blackhat);//对二值化矩阵进行击中击不中变换morphologyEx(src, hitmiss, MORPH_HITMISS, kernel);namedWindow("hitmiss", WINDOW_NORMAL); //可以自由调节显示图像的尺寸imshow("hitmiss", hitmiss);//用图像验证形态学操作效果Mat keys = imread("keys.jpg",IMREAD_GRAYSCALE);imshow("原图像", keys);threshold(keys, keys, 80, 255, THRESH_BINARY);imshow("二值化后的keys", keys);//5×5矩形结构元素Mat kernel_keys = getStructuringElement(0, Size(5, 5));Mat open_keys, close_keys, gradient_keys, tophat_keys, blackhat_keys, hitmiss_keys;//对图像进行开运算morphologyEx(keys, open_keys, MORPH_OPEN, kernel_keys);imshow("open_keys", open_keys);//对图像进行闭运算morphologyEx(keys, close_keys, MORPH_CLOSE, kernel_keys);imshow("close_keys", close_keys);//对图像进行梯度运算morphologyEx(keys, gradient_keys, MORPH_GRADIENT, kernel_keys);imshow("gradient_keys", gradient_keys);//对图像进行顶帽运算morphologyEx(keys, tophat_keys, MORPH_TOPHAT, kernel_keys);imshow("tophat_keys", tophat_keys);//对图像进行黑帽运算morphologyEx(keys, blackhat_keys, MORPH_BLACKHAT, kernel_keys);imshow("blackhat_keys", blackhat_keys);//对图像进行击中击不中变换morphologyEx(keys, hitmiss_keys, MORPH_HITMISS, kernel_keys);imshow("hitmiss_keys", hitmiss_keys);waitKey(0);return 0;
}
6.3.7 图像细化
thinning()
ximgproc构造函数
代码清单6-18 图像细化
#include
#include //细化函数thining所在的头文件
#include using namespace cv;
using namespace std;int main()
{//中文字进行细化Mat img = imread("LearnCV_black.png", IMREAD_ANYCOLOR);if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}//英文字+实心圆和圆环细化Mat words = Mat::zeros(100, 200, CV_8UC1); //创建一个黑色的背景图片putText(words, "Learn", Point(30, 30), 2, 1, Scalar(255), 2); //添加英文putText(words, "OpenCV 4", Point(30, 60), 2, 1, Scalar(255), 2);circle(words, Point(80, 75), 10, Scalar(255), -1); //添加实心圆circle(words, Point(130, 75), 10, Scalar(255), 3); //添加圆环//进行细化Mat thin1, thin2;ximgproc::thinning(img, thin1, 0); //注意类名ximgproc::thinning(words, thin2, 0);//显示处理结果imshow("thin1", thin1);imshow("img", img);namedWindow("thin2", WINDOW_NORMAL);imshow("thin2", thin2);namedWindow("words", WINDOW_NORMAL);imshow("words", words);waitKey(0);return 0;
}
第七章 目标检测
7.1 形状检测
7.1.1 直线检测
HoughLines()
Canny()
line()
代码清单7-2 检测直线并绘制直线
#include
#include using namespace cv;
using namespace std;void drawLine(Mat &img, //要标记直线的图像vector lines, //检测的直线数据double rows, //原图像的行数(高)double cols, //原图像的列数(宽)Scalar scalar, //绘制直线的颜色int n //绘制直线的线宽
)
{Point pt1, pt2;for (size_t i = 0; i < lines.size(); i++){float rho = lines[i][0]; //直线距离坐标原点的距离float theta = lines[i][1]; //直线过坐标原点垂线与x轴夹角double a = cos(theta); //夹角的余弦值double b = sin(theta); //夹角的正弦值double x0 = a*rho, y0 = b*rho; //直线与过坐标原点的垂线的交点double length = max(rows, cols); //图像高宽的最大值//计算直线上的一点pt1.x = cvRound(x0 + length * (-b));pt1.y = cvRound(y0 + length * (a));//计算直线上另一点pt2.x = cvRound(x0 - length * (-b));pt2.y = cvRound(y0 - length * (a));//两点绘制一条直线line(img, pt1, pt2, scalar, n);}
}int main()
{Mat img = imread("HoughLines.jpg", IMREAD_GRAYSCALE);if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat edge;//检测边缘图像,并二值化Canny(img, edge, 80, 180, 3, false);threshold(edge, edge, 170, 255, THRESH_BINARY);//用不同的累加器进行检测直线vector lines1, lines2;HoughLines(edge, lines1, 1, CV_PI / 180, 50, 0, 0);HoughLines(edge, lines2, 1, CV_PI / 180, 150, 0, 0);//在原图像中绘制直线Mat img1, img2;img.copyTo(img1);img.copyTo(img2);drawLine(img1, lines1, edge.rows, edge.cols, Scalar(255), 2);drawLine(img2, lines2, edge.rows, edge.cols, Scalar(255), 2);//显示图像imshow("edge", edge);imshow("img", img);imshow("img1", img1);imshow("img2", img2);waitKey(0);return 0;
}
HoughLinesP()
代码清单7-4 检测图像中的现代
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("HoughLines.jpg", IMREAD_GRAYSCALE);if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat edge;//检测边缘图像,并二值化Canny(img, edge, 80, 180, 3, false);threshold(edge, edge, 170, 255, THRESH_BINARY);//利用渐进概率式霍夫变换提取直线vector linesP1, linesP2;HoughLinesP(edge, linesP1, 1, CV_PI / 180, 150, 30, 10); //两个点连接最大距离10HoughLinesP(edge, linesP2, 1, CV_PI / 180, 150, 30, 30); //两个点连接最大距离30//绘制两个点连接最大距离10直线检测结果Mat img1;img.copyTo(img1);for (size_t i = 0; i < linesP1.size(); i++){line(img1, Point(linesP1[i][0], linesP1[i][1]),Point(linesP1[i][2], linesP1[i][3]), Scalar(255), 3);}//绘制两个点连接最大距离30直线检测结果Mat img2;img.copyTo(img2);for (size_t i = 0; i < linesP2.size(); i++){line(img2, Point(linesP2[i][0], linesP2[i][1]),Point(linesP2[i][2], linesP2[i][3]), Scalar(255), 3);}//显示图像imshow("img1", img1);imshow("img2", img2);waitKey(0);return 0;
}
HoughLinesPointSet()
代码清单7-6 在二维点集中检测直线
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色Mat lines; //存放检测直线结果的矩阵vector line3d; //换一种结果存放形式vector point; //待检测是否存在直线的所有点const static float Points[20][2] = {{ 0.0f, 369.0f },{ 10.0f, 364.0f },{ 20.0f, 358.0f },{ 30.0f, 352.0f },{ 40.0f, 346.0f },{ 50.0f, 341.0f },{ 60.0f, 335.0f },{ 70.0f, 329.0f },{ 80.0f, 323.0f },{ 90.0f, 318.0f },{ 100.0f, 312.0f },{ 110.0f, 306.0f },{ 120.0f, 300.0f },{ 130.0f, 295.0f },{ 140.0f, 289.0f },{ 150.0f, 284.0f },{ 160.0f, 277.0f },{ 170.0f, 271.0f },{ 180.0f, 266.0f },{ 190.0f, 260.0f }};//将所有点存放在vector中,用于输入函数中for (int i = 0; i < 20; i++){point.push_back(Point2f(Points[i][0], Points[i][1]));}//参数设置double rhoMin = 0.0f; //最小长度double rhoMax = 360.0f; //最大长度double rhoStep = 1; //离散化单位距离长度double thetaMin = 0.0f; //最小角度double thetaMax = CV_PI / 2.0f; //最大角度double thetaStep = CV_PI / 180.0f; 离散化单位角度弧度HoughLinesPointSet(point, lines, 20, 1, rhoMin, rhoMax, rhoStep,thetaMin, thetaMax, thetaStep);lines.copyTo(line3d);//输出结果for (int i = 0; i < line3d.size(); i++){cout << "votes:" << (int)line3d.at(i).val[0] << ", "<< "rho:" << line3d.at(i).val[1] << ", "<< "theta:" << line3d.at(i).val[2] << endl;}return 0;
}
7.1.2 直线拟合
fitLine()
代码清单7-8 直线拟合
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色Vec4f lines; //存放你和后的直线vector point; //待检测是否存在直线的所有点const static float Points[20][2] = {{ 0.0f, 0.0f },{ 10.0f, 11.0f },{ 21.0f, 20.0f },{ 30.0f, 30.0f },{ 40.0f, 42.0f },{ 50.0f, 50.0f },{ 60.0f, 60.0f },{ 70.0f, 70.0f },{ 80.0f, 80.0f },{ 90.0f, 92.0f },{ 100.0f, 100.0f },{ 110.0f, 110.0f },{ 120.0f, 120.0f },{ 136.0f, 130.0f },{ 138.0f, 140.0f },{ 150.0f, 150.0f },{ 160.0f, 163.0f },{ 175.0f, 170.0f },{ 181.0f, 180.0f },{ 200.0f, 190.0f }};//将所有点存放在vector中,用于输入函数中for (int i = 0; i < 20; i++){point.push_back(Point2f(Points[i][0], Points[i][1]));}//参数设置double param = 0; //距离模型中的数值参数Cdouble reps = 0.01; //坐标原点与直线之间的距离精度double aeps = 0.01; //角度精度fitLine(point, lines, DIST_L1, 0, 0.01, 0.01);double k = lines[1] / lines[0]; //直线斜率cout << "直线斜率:" << k << endl;cout << "直线上一点坐标x:" << lines[2] << ", y::" << lines[3] << endl;cout << "直线解析式:y=" << k << "(x-" << lines[2] << ")+" << lines[3] << endl;return 0;
}
7.1.3 圆形检测
HoughCircles()
代码清单7-10 圆形检测
#include
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("keys.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("原图", img);Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);GaussianBlur(gray, gray, Size(9, 9), 2, 2); //平滑滤波//检测圆形vector circles;double dp = 2; //double minDist = 10; //两个圆心之间的最小距离double param1 = 100; //Canny边缘检测的较大阈值double param2 = 100; //累加器阈值int min_radius = 20; //圆形半径的最小值int max_radius = 100; //圆形半径的最大值HoughCircles(gray, circles, HOUGH_GRADIENT, dp, minDist, param1, param2,min_radius, max_radius);//图像中标记出圆形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]);//绘制圆心circle(img, center, 3, Scalar(0, 255, 0), -1, 8, 0);//绘制圆circle(img, center, radius, Scalar(0, 0, 255), 3, 8, 0);}//显示结果imshow("圆检测结果", img);waitKey(0);return 0;
}
7.2 轮廓检测
findContours()
drawContours()
7.2.1 轮廓发现与绘制
代码清单7-14 轮廓发现与绘制
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色Mat img = imread("keys.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("原图", img);Mat gray, binary;cvtColor(img, gray, COLOR_BGR2GRAY); //转化成灰度图GaussianBlur(gray, gray, Size(13, 13), 4, 4); //平滑滤波threshold(gray, binary, 170, 255, THRESH_BINARY | THRESH_OTSU); //自适应二值化// 轮廓发现与绘制vector> contours; //轮廓vector hierarchy; //存放轮廓结构变量findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());//绘制轮廓for (int t = 0; t < contours.size(); t++){drawContours(img, contours, t, Scalar(0, 0, 255), 2, 8);}//输出轮廓结构描述子for (int i = 0; i < hierarchy.size(); i++){cout << hierarchy[i] << endl;}//显示结果imshow("轮廓检测结果", img);waitKey(0);return 0;
}
7.2.2 轮廓面积
contourArea()
代码清单7-16 计算轮廓面积
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色//用四个点表示三角形轮廓vector contour;contour.push_back(Point2f(0, 0));contour.push_back(Point2f(10, 0));contour.push_back(Point2f(10, 10));contour.push_back(Point2f(5, 5));double area = contourArea(contour);cout << "area =" << area << endl;Mat img = imread("coins.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("原图", img);Mat gray, binary;cvtColor(img, gray, COLOR_BGR2GRAY); //转化成灰度图GaussianBlur(gray, gray, Size(9, 9), 2, 2); //平滑滤波threshold(gray, binary, 170, 255, THRESH_BINARY | THRESH_OTSU); //自适应二值化// 轮廓检测vector> contours; //轮廓vector hierarchy; //存放轮廓结构变量findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());//输出轮廓面积for (int t = 0; t < contours.size(); t++){double area1 = contourArea(contours[t]);cout << "第" << t << "轮廓面积=" << area1 << endl;}return 0;
}
7.2.3 轮廓长度(周长)
arcLength()
代码清单7-18 计算轮廓长度
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色//用四个点表示三角形轮廓vector contour;contour.push_back(Point2f(0, 0));contour.push_back(Point2f(10, 0));contour.push_back(Point2f(10, 10));contour.push_back(Point2f(5, 5));double length0 = arcLength(contour, true);double length1 = arcLength(contour, false);cout << "length0 =" << length0 << endl;cout << "length1 =" << length1 << endl;Mat img = imread("coins.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("原图", img);Mat gray, binary;cvtColor(img, gray, COLOR_BGR2GRAY); //转化成灰度图GaussianBlur(gray, gray, Size(9, 9), 2, 2); //平滑滤波threshold(gray, binary, 170, 255, THRESH_BINARY | THRESH_OTSU); //自适应二值化// 轮廓检测vector> contours; //轮廓vector hierarchy; //存放轮廓结构变量findContours(binary, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());//输出轮廓长度for (int t = 0; t < contours.size(); t++){double length2 = arcLength(contours[t], true);cout << "第" << t << "个轮廓长度=" << length2 << endl;}return 0;
}
7.2.4 轮廓外接多边形
boundingRect()
minAreaRect()
代码清单7-21 计算轮廓外接矩形
#include
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("stuff.jpg");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat img1, img2;img.copyTo(img1); //深拷贝用来绘制最大外接矩形img.copyTo(img2); //深拷贝用来绘制最小外接矩形imshow("img", img);// 去噪声与二值化Mat canny;Canny(img, canny, 80, 160, 3, false);imshow("", canny);//膨胀运算,将细小缝隙填补上Mat kernel = getStructuringElement(0, Size(3, 3));dilate(canny, canny, kernel);// 轮廓发现与绘制vector> contours;vector hierarchy;findContours(canny, contours, hierarchy, 0, 2, Point());//寻找轮廓的外接矩形for (int n = 0; n < contours.size(); n++){// 最大外接矩形Rect rect = boundingRect(contours[n]);rectangle(img1, rect, Scalar(0, 0, 255), 2, 8, 0);// 最小外接矩形RotatedRect rrect = minAreaRect(contours[n]);Point2f points[4];rrect.points(points); //读取最小外接矩形的四个顶点Point2f cpt = rrect.center; //最小外接矩形的中心// 绘制旋转矩形与中心位置for (int i = 0; i < 4; i++){if (i == 3){line(img2, points[i], points[0], Scalar(0, 255, 0), 2, 8, 0);break;}line(img2, points[i], points[i + 1], Scalar(0, 255, 0), 2, 8, 0);}//绘制矩形的中心circle(img, cpt, 2, Scalar(255, 0, 0), 2, 8, 0);}//输出绘制外接矩形的结果imshow("max", img1);imshow("min", img2);waitKey(0);return 0;
}
approxPolyDP()
代码清单7-23 对多个轮廓进行多边形逼近
#include
#include
#include using namespace cv;
using namespace std;//绘制轮廓函数
void drawapp(Mat result, Mat img2)
{for (int i = 0; i < result.rows; i++){//最后一个坐标点与第一个坐标点连接if (i == result.rows - 1){Vec2i point1 = result.at(i);Vec2i point2 = result.at(0);line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);break;}Vec2i point1 = result.at(i);Vec2i point2 = result.at(i + 1);line(img2, point1, point2, Scalar(0, 0, 255), 2, 8, 0);}
}int main(int argc, const char *argv[])
{Mat img = imread("approx.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}// 边缘检测Mat canny;Canny(img, canny, 80, 160, 3, false);//膨胀运算Mat kernel = getStructuringElement(0, Size(3, 3));dilate(canny, canny, kernel);// 轮廓发现与绘制vector> contours;vector hierarchy;findContours(canny, contours, hierarchy, 0, 2, Point());//绘制多边形for (int t = 0; t < contours.size(); t++){//用最小外接矩形求取轮廓中心RotatedRect rrect = minAreaRect(contours[t]);Point2f center = rrect.center;circle(img, center, 2, Scalar(0, 255, 0), 2, 8, 0);Mat result;approxPolyDP(contours[t], result, 4, true); //多边形拟合drawapp(result, img);cout << "corners : " << result.rows << endl;//判断形状和绘制轮廓if (result.rows == 3){putText(img, "triangle", center, 0, 1, Scalar(0, 255, 0), 1, 8);}if (result.rows == 4){putText(img, "rectangle", center, 0, 1, Scalar(0, 255, 0), 1, 8);}if (result.rows == 8){putText(img, "poly-8", center, 0, 1, Scalar(0, 255, 0), 1, 8);}if (result.rows > 12){putText(img, "circle", center, 0, 1, Scalar(0, 255, 0), 1, 8);}}imshow("result", img);waitKey(0);return 0;
}
7.2.5 点到轮廓距离
pointPolygonTest()
代码清单7-25 点到轮廓距离
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色Mat img = imread("approx.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}// 边缘检测Mat canny;Canny(img, canny, 80, 160, 3, false);//膨胀运算Mat kernel = getStructuringElement(0, Size(3, 3));dilate(canny, canny, kernel);// 轮廓发现vector> contours;vector hierarchy;findContours(canny, contours, hierarchy, 0, 2, Point());//创建图像中的一个像素点并绘制圆形Point point = Point(250, 200);circle(img, point, 2, Scalar(0, 0, 255), 2, 8, 0);//多边形for (int t = 0; t < contours.size(); t++){//用最小外接矩形求取轮廓中心RotatedRect rrect = minAreaRect(contours[t]);Point2f center = rrect.center;circle(img, center, 2, Scalar(0, 255, 0), 2, 8, 0); //绘制圆心点//轮廓外部点距离轮廓的距离double dis = pointPolygonTest(contours[t], point, true);//轮廓内部点距离轮廓的距离double dis2 = pointPolygonTest(contours[t], center, true);//输出点结果cout << "外部点距离轮廓距离:" << dis << endl;cout << "内部点距离轮廓距离:" << dis2 << endl;}return 0;
}
7.2.6 凸包检测
convexHull()
代码清单7-27 凸包检测
#include
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("hand.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}// 二值化Mat gray, binary;cvtColor(img, gray, COLOR_BGR2GRAY);threshold(gray, binary, 105, 255, THRESH_BINARY);//开运算消除细小区域Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));morphologyEx(binary, binary, MORPH_OPEN, k);imshow("binary", binary);// 轮廓发现vector> contours;vector hierarchy;findContours(binary, contours, hierarchy, 0, 2, Point());for (int n = 0; n < contours.size(); n++){//计算凸包vector hull;convexHull(contours[n], hull);//绘制凸包for (int i = 0; i < hull.size(); i++){//绘制凸包顶点circle(img, hull[i], 4, Scalar(255, 0, 0), 2, 8, 0);//连接凸包if (i == hull.size() - 1){line(img, hull[i], hull[0], Scalar(0, 0, 255), 2, 8, 0);break;}line(img, hull[i], hull[i + 1], Scalar(0, 0, 255), 2, 8, 0);}}imshow("hull", img);waitKey(0);return 0;
}
7.3 矩的计算
7.3.1 几何矩与中心矩
moments()
代码清单7-29 计算图像矩
#include
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("approx.png");// 二值化Mat gray, binary;cvtColor(img, gray, COLOR_BGR2GRAY);threshold(gray, binary, 105, 255, THRESH_BINARY);//开运算消除细小区域Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));morphologyEx(binary, binary, MORPH_OPEN, k);// 轮廓发现vector> contours;vector hierarchy;findContours(binary, contours, hierarchy, 0, 2, Point());for (int n = 0; n < contours.size(); n++) {Moments M;M = moments(contours[n], true);cout << "spatial moments:" << endl<< "m00:" << M.m00 << " m01:" << M.m01 << " m10:" << M.m10 << endl<< "m11:" << M.m11 << " m02:" << M.m02 << " m20:" << M.m20 << endl<< "m12:" << M.m12 << " m21:" << M.m21 << " m03:" << M.m03 << " m30:"<< M.m30 << endl;cout << "central moments:" << endl<< "mu20:" << M.mu20 << " mu02:" << M.mu02 << " mu11:" << M.mu11 << endl<< "mu30:" << M.mu30 << " mu21:" << M.mu21 << " mu12:" << M.mu12 << " mu03:" << M.mu03 << endl;cout << "central normalized moments:" << endl<< "nu20:" << M.nu20 << " nu02:" << M.nu02 << " nu11:" << M.nu11 << endl<< "nu30:" << M.nu30 << " nu21:" << M.nu21 << " nu12:" << M.nu12 << " nu03:" << M.nu03 << endl;}return 0;
}
7.3.2 Hu矩
HuMoments()
代码清单7-31 计算图像的Hu矩
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //更改输出界面颜色Mat img = imread("approx.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}// 二值化Mat gray, binary;cvtColor(img, gray, COLOR_BGR2GRAY);threshold(gray, binary, 105, 255, THRESH_BINARY);//开运算消除细小区域Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));morphologyEx(binary, binary, MORPH_OPEN, k);// 轮廓发现vector> contours;vector hierarchy;findContours(binary, contours, hierarchy, 0, 2, Point());for (int n = 0; n < contours.size(); n++){Moments M;M = moments(contours[n], true);Mat hu;HuMoments(M, hu); //计算Hu矩cout << hu << endl;}return 0;
}
7.3.3基于Hu矩的轮廓匹配
matchShapes()
代码清单7-33 基于Hu矩的轮廓匹配
#include
#include
#include using namespace cv;
using namespace std;void findcontours(Mat &image, vector> &contours)
{Mat gray, binary;vector hierarchy;//图像灰度化cvtColor(image, gray, COLOR_BGR2GRAY);//图像二值化threshold(gray, binary, 0, 255, THRESH_BINARY | THRESH_OTSU);//寻找轮廓findContours(binary, contours, hierarchy, 0, 2);
}int main()
{Mat img = imread("ABC.png");Mat img_B = imread("B.png");if (img.empty() || img_B.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}resize(img_B, img_B, Size(), 0.5, 0.5);imwrite("B.png", img_B);imshow("B", img_B);// 轮廓提取vector> contours1;vector> contours2;findcontours(img, contours1);findcontours(img_B, contours2);// hu矩计算Moments mm2 = moments(contours2[0]);Mat hu2;HuMoments(mm2, hu2);// 轮廓匹配for (int n = 0; n < contours1.size(); n++){Moments mm = moments(contours1[n]);Mat hum;HuMoments(mm, hum);//Hu矩匹配double dist;dist = matchShapes(hum, hu2, CONTOURS_MATCH_I1, 0);if (dist < 1){drawContours(img, contours1, n, Scalar(0, 0, 255), 3, 8);}}imshow("match result", img);waitKey(0);return 0;
}
7.4 点集拟合
minEnclosingTriangle()
minEnclosingCircle()
代码清单7-36 点集外包轮廓
#include
#include
#include using namespace cv;
using namespace std;int main()
{Mat img(500, 500, CV_8UC3, Scalar::all(0));RNG& rng = theRNG(); //生成随机点while (true){int i, count = rng.uniform(1, 101);vector points;//生成随机点for (i = 0; i < count; i++){Point pt;pt.x = rng.uniform(img.cols / 4, img.cols * 3 / 4);pt.y = rng.uniform(img.rows / 4, img.rows * 3 / 4);points.push_back(pt);}//寻找包围点集的三角形 vector triangle;double area = minEnclosingTriangle(points, triangle);//寻找包围点集的圆形Point2f center;float radius = 0;minEnclosingCircle(points, center, radius);//创建两个图片用于输出结果img = Scalar::all(0);Mat img2;img.copyTo(img2);//在图像中绘制坐标点for (i = 0; i < count; i++){circle(img, points[i], 3, Scalar(255, 255, 255), FILLED, LINE_AA);circle(img2, points[i], 3, Scalar(255, 255, 255), FILLED, LINE_AA);}//绘制三角形for (i = 0; i < 3; i++){if (i==2){line(img, triangle[i], triangle[0], Scalar(255, 255, 255), 1, 16);break;}line(img, triangle[i], triangle[i + 1], Scalar(255, 255, 255), 1, 16);}//绘制圆形circle(img2, center, cvRound(radius), Scalar(255, 255, 255), 1, LINE_AA);//输出结果imshow("triangle", img);imshow("circle", img2);//按q键或者ESC键退出程序char key = (char)waitKey();if (key == 27 || key == 'q' || key == 'Q'){break;}}return 0;
}
7.5 QR二维码检测
QRCodeDetector类
detect()
decode()
detectAndDecode()
代码清单7-40 二维码识别
#include
#include
#include using namespace cv;
using namespace std;int main(int argc, char** argv)
{Mat img = imread("qrcode2.png");Mat gray, qrcode_bin;cvtColor(img, gray, COLOR_BGR2GRAY);QRCodeDetector qrcodedetector;vector points;string information;bool isQRcode;isQRcode = qrcodedetector.detect(gray, points); //识别二维码if (isQRcode){//解码二维码information = qrcodedetector.decode(gray, points, qrcode_bin);cout << points << endl; //输出二维码四个顶点的坐标}else{cout << "无法识别二维码,请确认图像时候含有二维码" << endl;return -1;}//绘制二维码的边框for (int i = 0; i < points.size(); i++){if (i== points.size()-1){line(img, points[i], points[0], Scalar(0, 0, 255), 2, 8);break;}line(img, points[i], points[i + 1], Scalar(0, 0, 255), 2, 8);}//将解码内容输出到图片上putText(img, information.c_str(),Point(20, 30), 0, 1.0, Scalar(0, 0, 255), 2, 8);//利用函数直接定位二维码并解码string information2;vector points2;information2 = qrcodedetector.detectAndDecode(gray,points2);cout << points2 << endl;putText(img, information2.c_str(), Point(20, 55), 0, 1.0, Scalar(0, 0, 0), 2, 8);//输出结果imshow("result", img);namedWindow("qrcode_bin", WINDOW_NORMAL);imshow("qrcode_bin", qrcode_bin);waitKey(0);return 0;
}
第八章 图像分析与修复
8.1 傅里叶变换
8.1.1 离散傅里叶变换
df()
idft()
getOptimalDFTSize()
magnitude()
代码清单8-6 离散傅里叶变换
#include
#include using namespace std;
using namespace cv;int main()
{//对矩阵进行处理,展示正逆变换的关系Mat a = (Mat_(5, 5) << 1, 2, 3, 4, 5,2, 3, 4, 5, 6,3, 4, 5, 6, 7,4, 5, 6, 7, 8,5, 6, 7, 8, 9);Mat b, c, d;dft(a, b, DFT_COMPLEX_OUTPUT); //正变换dft(b, c, DFT_INVERSE | DFT_SCALE | DFT_REAL_OUTPUT); //逆变换只输出实数idft(b, d, DFT_SCALE); //逆变换//对图像进行处理Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);resize(gray, gray, Size(502, 502));imshow("原图像", gray);//计算合适的离散傅里叶变换尺寸int rows = getOptimalDFTSize(gray.rows);int cols = getOptimalDFTSize(gray.cols);//扩展图像Mat appropriate;int T = (rows - gray.rows) / 2; //上方扩展行数int B = rows - gray.rows - T; //下方扩展行数int L = (cols - gray.cols) / 2; //左侧扩展行数int R = cols - gray.cols - L; //右侧扩展行数copyMakeBorder(gray, appropriate, T, B, L, R, BORDER_CONSTANT);imshow("扩展后的图像", appropriate);//构建离散傅里叶变换输入量Mat flo[2], complex;flo[0] = Mat_(appropriate); //实数部分flo[1] = Mat::zeros(appropriate.size(), CV_32F); //虚数部分merge(flo, 2, complex); //合成一个多通道矩阵//进行离散傅里叶变换Mat result;dft(complex, result);//将复数转化为幅值Mat resultC[2];split(result, resultC); //分成实数和虚数Mat amplitude;magnitude(resultC[0], resultC[1], amplitude);//进行对数放缩公式为: M1 = log(1+M),保证所有数都大于0amplitude = amplitude + 1;log(amplitude, amplitude);//求自然对数//与原图像尺寸对应的区域 amplitude = amplitude(Rect(T, L, gray.cols, gray.rows));normalize(amplitude, amplitude, 0, 1, NORM_MINMAX); //归一化imshow("傅里叶变换结果幅值图像", amplitude); //显示结果//重新排列傅里叶图像中的象限,使得原点位于图像中心int centerX = amplitude.cols / 2;int centerY = amplitude.rows / 2;//分解成四个小区域Mat Qlt(amplitude, Rect(0, 0, centerX, centerY));//ROI区域的左上Mat Qrt(amplitude, Rect(centerX, 0, centerX, centerY));//ROI区域的右上Mat Qlb(amplitude, Rect(0, centerY, centerX, centerY));//ROI区域的左下Mat Qrb(amplitude, Rect(centerX, centerY, centerX, centerY));//ROI区域的右下//交换象限,左上和右下进行交换Mat med;Qlt.copyTo(med);Qrb.copyTo(Qlt);med.copyTo(Qrb);//交换象限,左下和右上进行交换Qrt.copyTo(med);Qlb.copyTo(Qrt);med.copyTo(Qlb);imshow("中心化后的幅值图像", amplitude);waitKey(0);return 0;
}
8.1.2 傅里叶变换进行卷积
dft()
mulSpectrums()
代码清单8-8 通过傅里叶变换进行卷积
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("lena.png");if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);Mat grayfloat = Mat_(gray); //更改图像数据类型为floatMat kernel = (Mat_(5, 5) << 1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1,1, 1, 1, 1, 1);//构建输出图像Mat result;int rwidth = abs(grayfloat.rows - kernel.rows) + 1;int rheight = abs(grayfloat.cols - kernel.cols) + 1;result.create(rwidth, rheight, grayfloat.type());// 计算最优离散傅里叶变换尺寸int width = getOptimalDFTSize(grayfloat.cols + kernel.cols - 1);int height = getOptimalDFTSize(grayfloat.rows + kernel.rows - 1);//改变输入图像尺寸Mat tempA;int A_T = 0;int A_B = width - grayfloat.rows;int A_L = 0;int A_R = height - grayfloat.cols;copyMakeBorder(grayfloat, tempA, 0, A_B, 0, A_R, BORDER_CONSTANT);//改变滤波器尺寸Mat tempB;int B_T = 0;int B_B = width - kernel.rows;int B_L = 0;int B_R = height - kernel.cols;copyMakeBorder(kernel, tempB, 0, B_B, 0, B_R, BORDER_CONSTANT);//分别进行离散傅里叶变换dft(tempA, tempA, 0, grayfloat.rows);dft(tempB, tempB, 0, kernel.rows);//多个傅里叶变换的结果相乘mulSpectrums(tempA, tempB, tempA, DFT_COMPLEX_OUTPUT);//相乘结果进行逆变换//dft(tempA, tempA, DFT_INVERSE | DFT_SCALE, result.rows);idft(tempA, tempA, DFT_SCALE, result.rows);//对逆变换结果进行归一化normalize(tempA, tempA, 0, 1, NORM_MINMAX);//截取部分结果作为滤波结果tempA(Rect(0, 0, result.cols, result.rows)).copyTo(result);//显示结果imshow("原图像", gray);imshow("滤波结果", result);waitKey(0);
}
8.1.3 离散余弦变换
dct()
idct()
代码清单8-11 图像离散余弦变换
#include
#include using namespace cv;
using namespace std;int main()
{Mat kernel = (Mat_(5, 5) << 1, 2, 3, 4, 5,2, 3, 4, 5, 6,3, 4, 5, 6, 7,4, 5, 6, 7, 8,5, 6, 7, 8, 9);Mat a, b;dct(kernel, a);idct(a, b);//对图像进行处理Mat img = imread("lena.png");if (!img.data){cout << "读入图像出错,请确认图像名称是否正确" << endl;return -1;}imshow("原图像", img);//计算最佳变换尺寸int width = 2 * getOptimalDFTSize((img.cols + 1) / 2);int height = 2 * getOptimalDFTSize((img.rows + 1) / 2);//扩展图像尺寸int T = 0;int B = height - T - img.rows;int L = 0;int R = width - L - img.rows;Mat appropriate;copyMakeBorder(img, appropriate, T, B, L, R, BORDER_CONSTANT, Scalar(0));//提三个通道需要分别进行DCT变换vector channels;split(appropriate, channels);//提取NGR颜色各个通道的值Mat one = channels.at(0); Mat two = channels.at(1);Mat three = channels.at(2);//进行DCT变换Mat oneDCT, twoDCT, threeDCT;dct(Mat_(one), oneDCT);dct(Mat_(two), twoDCT);dct(Mat_(three), threeDCT);//重新组成三个通道vector channelsDCT;channelsDCT.push_back(Mat_(oneDCT));channelsDCT.push_back(Mat_(twoDCT));channelsDCT.push_back(Mat_(threeDCT));//输出图像Mat result;merge(channelsDCT, result);imshow("DCT图像", result);waitKey();return 0;
}
8.2 积分图像
integral()
代码清单8-15 计算积分图像
#include
#include using namespace cv;
using namespace std;int main()
{//创建一个16×16全为1的矩阵,因为256=16×16Mat img = Mat::ones(16, 16, CV_32FC1);//在图像中加入随机噪声RNG rng(10086);for (int y = 0; y < img.rows; y++){for (int x = 0; x < img.cols; x++){float d = rng.uniform(-0.5, 0.5);img.at(y, x) = img.at(y, x) + d;}}//计算标准求和积分Mat sum;integral(img, sum);//为了便于显示,转成CV_8U格式Mat sum8U = Mat_(sum);//计算平方求和积分Mat sqsum;integral(img, sum, sqsum);//为了便于显示,转成CV_8U格式Mat sqsum8U = Mat_(sqsum);//计算倾斜求和积分Mat tilted;integral(img, sum, sqsum, tilted);//为了便于显示,转成CV_8U格式Mat tilted8U = Mat_(tilted);//输出结果namedWindow("sum8U", WINDOW_NORMAL);namedWindow("sqsum8U", WINDOW_NORMAL);namedWindow("tilted8U", WINDOW_NORMAL);imshow("sum8U", sum8U);imshow("sqsum8U", sqsum8U);imshow("tilted8U", tilted8U);waitKey();return 0;
}
8.3 图像分割
8.3.1 漫水填充法
floodFill()
代码清单8-18 漫水填充法分割图像
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //将DOS界面调成白底黑字Mat img = imread("lena.png");if (!(img.data)){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}RNG rng(10086);//随机数,用于随机生成像素//设置操作标志flagsint connectivity = 4; //连通邻域方式int maskVal = 255; //掩码图像的数值int flags = connectivity|(maskVal<<8)| FLOODFILL_FIXED_RANGE; //漫水填充操作方式标志//设置与选中像素点的差值Scalar loDiff = Scalar(20, 20, 20);Scalar upDiff = Scalar(20, 20, 20);//声明掩模矩阵变量Mat mask = Mat::zeros(img.rows + 2, img.cols + 2, CV_8UC1);while (true){//随机产生图像中某一像素点int py = rng.uniform(0,img.rows-1);int px = rng.uniform(0, img.cols - 1);Point point = Point(px, py);//彩色图像中填充的像素值Scalar newVal = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));//漫水填充函数int area = floodFill(img, mask, point, newVal, &Rect(),loDiff,upDiff,flags);//输出像素点和填充的像素数目cout << "像素点x:" << point.x << " y:" << point.y<< " 填充像数数目:" << area << endl;//输出填充的图像结果imshow("填充的彩色图像", img);imshow("掩模图像", mask);//判断是否结束程序int c = waitKey(0);if ((c&255)==27){break;}}return 0;
}
8.3.2 分水岭法
watershed()
代码清单8-20 分水岭法分割图像
#include
#include using namespace std;
using namespace cv;int main()
{Mat img, imgGray, imgMask;Mat maskWaterShed; // watershed()函数的参数img = imread("HoughLines.jpg"); //原图像if (img.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}cvtColor(img, imgGray, COLOR_BGR2GRAY);//GaussianBlur(imgGray, imgGray, Size(5, 5), 10, 20); //模糊用于减少边缘数目//提取边缘并进行闭运算Canny(imgGray, imgMask, 150, 300);//Mat k = getStructuringElement(0, Size(3, 3));//morphologyEx(imgMask, imgMask, MORPH_CLOSE, k);imshow("边缘图像", imgMask);imshow("原图像", img);//计算连通域数目vector> contours;vector hierarchy;findContours(imgMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);//在maskWaterShed上绘制轮廓,用于输入分水岭算法maskWaterShed = Mat::zeros(imgMask.size(), CV_32S);for (int index = 0; index < contours.size(); index++){drawContours(maskWaterShed, contours, index, Scalar::all(index + 1),-1, 8, hierarchy, INT_MAX);}//分水岭算法 需要对原图像进行处理watershed(img, maskWaterShed);vector colors; // 随机生成几种颜色for (int i = 0; i < contours.size(); i++){int b = theRNG().uniform(0, 255);int g = theRNG().uniform(0, 255);int r = theRNG().uniform(0, 255);colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));}Mat resultImg = Mat(img.size(), CV_8UC3); //显示图像for (int i = 0; i < imgMask.rows; i++){for (int j = 0; j < imgMask.cols; j++){// 绘制每个区域的颜色int index = maskWaterShed.at(i, j);if (index == -1) // 区域间的值被置为-1(边界){resultImg.at(i, j) = Vec3b(255, 255, 255);}else if (index <= 0 || index > contours.size()) // 没有标记清楚的区域被置为0 {resultImg.at(i, j) = Vec3b(0, 0, 0);}else // 其他每个区域的值保持不变:1,2,…,contours.size(){resultImg.at(i, j) = colors[index - 1]; // 把些区域绘制成不同颜色}}}resultImg = resultImg * 0.6 + img * 0.4;imshow("分水岭结果", resultImg);//绘制每个区域的图像for (int n = 1; n <= contours.size(); n++){Mat resImage1 = Mat(img.size(), CV_8UC3); // 声明一个最后要显示的图像for (int i = 0; i < imgMask.rows; i++){for (int j = 0; j < imgMask.cols; j++){int index = maskWaterShed.at(i, j);if (index == n)resImage1.at(i, j) = img.at(i, j);elseresImage1.at(i, j) = Vec3b(0, 0, 0);}}//显示图像imshow(to_string(n), resImage1);}waitKey(0);return 0;
}
8.3.3 Grabcut 法
grabCut()
代码清单8-22 利用Grabcut 法进行图像分割
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("lena.png");if (!img.data) //防止错误读取图像{cout<<"读取图像错误,请确认图像文件是否正确" << endl; return 0;}//绘制矩形Mat imgRect;img.copyTo(imgRect); //备份图像,方式绘制矩形框对结果产生影响Rect rect(80, 30, 340, 390);rectangle(imgRect, rect, Scalar(255, 255, 255),2);imshow("选择的矩形区域", imgRect);//进行分割Mat bgdmod = Mat::zeros(1, 65, CV_64FC1);Mat fgdmod = Mat::zeros(1, 65, CV_64FC1);Mat mask = Mat::zeros(img.size(), CV_8UC1);grabCut(img, mask, rect, bgdmod, fgdmod, 5, GC_INIT_WITH_RECT);//将分割出的前景绘制回来Mat result;for (int row = 0; row < mask.rows; row++) {for (int col = 0; col < mask.cols; col++) {int n = mask.at(row, col);//将明显是前景和可能是前景的区域都保留if (n == 1 || n == 3) {mask.at(row, col) = 255;}//将明显是背景和可能是背景的区域都删除else {mask.at(row, col) = 0;}}}bitwise_and(img, img, result, mask);imshow("分割结果", result);waitKey(0);return 0;
}
8.3.4 Mean-Shift 法
pyrMeanShiftFiltering()
TermCriteria构造函数
代码清单8-25 利用Mean-Shift 法分割图像
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("coins.png");if (!img.data){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}//分割处理Mat result1, result2;TermCriteria T10 = TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 10, 0.1);pyrMeanShiftFiltering(img, result1, 20, 40, 2, T10); //第一次分割pyrMeanShiftFiltering(result1, result2, 20, 40, 2, T10); //第一次分割的结果再次分割//显示分割结果imshow("img", img);imshow("result1", result1);imshow("result2", result2);//对图像提取Canny边缘Mat imgCanny, result1Canny, result2Canny;Canny(img, imgCanny, 150, 300);Canny(result1, result1Canny, 150, 300);Canny(result2, result2Canny, 150, 300);//显示边缘检测结果imshow("imgCanny", imgCanny);imshow("result1Canny", result1Canny);imshow("result2Canny", result2Canny);waitKey(0);return 0;
}
8.4 图像修复
inpaint()
代码清单8-27 图像修复
#include
#include using namespace cv;
using namespace std;int main()
{Mat img1 = imread("inpaint1.png");Mat img2 = imread("inpaint2.png");if (img1.empty() || img2.empty()){cout << "请确认图像文件名称是否正确" << endl;return -1;}imshow("img1", img1);imshow("img2", img2);//转换为灰度图Mat img1Gray, img2Gray;cvtColor(img1, img1Gray, COLOR_RGB2GRAY, 0);cvtColor(img2, img2Gray, COLOR_RGB2GRAY, 0);//通过阈值处理生成Mask掩模Mat img1Mask, img2Mask;threshold(img1Gray, img1Mask, 245, 255, THRESH_BINARY);threshold(img2Gray, img2Mask, 245, 255, THRESH_BINARY);//对Mask膨胀处理,增加Mask面积Mat Kernel = getStructuringElement(MORPH_RECT, Size(3, 3));dilate(img1Mask, img1Mask, Kernel);dilate(img2Mask, img2Mask, Kernel);//图像修复Mat img1Inpaint, img2Inpaint;inpaint(img1, img1Mask, img1Inpaint, 5, INPAINT_NS);inpaint(img2, img2Mask, img2Inpaint, 5, INPAINT_NS);//显示处理结果imshow("img1Mask", img1Mask);imshow("img1修复后", img1Inpaint);imshow("img2Mask", img2Mask);imshow("img2修复后", img2Inpaint);waitKey();return 0;
}
第九章 特征点检测与匹配
9.1 角点检测
9.1.1 显示关键点
drawKeypoints()
Keypoint数据类型
代码清单9-3 绘制关键点
#include
#include using namespace cv;
using namespace std;int main()
{Mat img = imread("lena.png", IMREAD_COLOR);//判断加载图像是否存在if (!img.data){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}Mat imgGray;cvtColor(img, imgGray, COLOR_BGR2GRAY);//生成关键点vector keypoints;RNG rng(10086);for (int i = 0; i < 100; i++){float pty = rng.uniform(0, img.rows - 1);float ptx = rng.uniform(0, img.cols - 1);KeyPoint keypoint; //对KeyPoint类进行赋值keypoint.pt.x = ptx;keypoint.pt.y = pty;keypoints.push_back(keypoint); //保存进关键点向量中}//绘制关键点drawKeypoints(img, keypoints, img, Scalar(0, 0, 0));drawKeypoints(imgGray, keypoints, imgGray, Scalar(255, 255, 255));//显示图像绘制结果imshow("img", img);imshow("imgGray", imgGray);waitKey(0);return 0;
}
9.1.2 Harris 角点检测
cornerHarris()
代码清单9-5 检测Harris 角点
#include
#includeusing namespace cv;
using namespace std;int main()
{Mat img = imread("lena.png", IMREAD_COLOR);if (!img.data){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}//转成灰度图像Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);//计算Harris系数Mat harris;int blockSize = 2; //邻域半径int apertureSize = 3; //cornerHarris(gray, harris, blockSize, apertureSize, 0.04);//归一化便于进行数值比较和结果显示Mat harrisn;normalize(harris, harrisn, 0, 255, NORM_MINMAX);//将图像的数据类型变成CV_8UconvertScaleAbs(harrisn, harrisn);//寻找Harris角点vector keyPoints;for (int row = 0; row < harrisn.rows; row++){for (int col = 0; col < harrisn.cols; col++){int R = harrisn.at(row, col);if (R > 125){//向角点存入KeyPoint中KeyPoint keyPoint;keyPoint.pt.y = row;keyPoint.pt.x = col;keyPoints.push_back(keyPoint);}}}//绘制角点与显示结果drawKeypoints(img, keyPoints, img);imshow("系数矩阵", harrisn);imshow("Harris角点", img);waitKey(0);return 0;
}
9.1.3 Shi-Tomas 角点检测
goodFeaturesToTrack()
代码清单9-7 检测Shi-Tomas 角点
#include
#includeusing namespace cv;
using namespace std;int main()
{Mat img = imread("lena.png");if (!img.data){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}//深拷贝用于第二种方法绘制角点Mat img2;img.copyTo(img2);Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);// Detector parameters//提取角点int maxCorners = 100; //检测角点数目double quality_level = 0.01; //质量等级,或者说阈值与最佳角点的比例关系double minDistance = 0.04; //两个角点之间的最小欧式距离vector corners;goodFeaturesToTrack(gray, corners, maxCorners, quality_level, minDistance, Mat(), 3, false);//绘制角点vector keyPoints; //存放角点的KeyPoint类,用于后期绘制角点时用RNG rng(10086);for (int i = 0; i < corners.size(); i++) {//第一种方式绘制角点,用circle()函数绘制角点int b = rng.uniform(0, 256);int g = rng.uniform(0, 256);int r = rng.uniform(0, 256);circle(img, corners[i], 5, Scalar(b, g, r), 2, 8, 0);//将角点存放在KeyPoint类中KeyPoint keyPoint;keyPoint.pt = corners[i];keyPoints.push_back(keyPoint);}//第二种方式绘制角点,用drawKeypoints()函数drawKeypoints(img2, keyPoints, img2);//输出绘制角点的结果imshow("用circle()函数绘制角点结果", img);imshow("通过绘制关键点函数绘制角点结果", img2);waitKey(0);return 0;
}
9.1.4 亚像素级别角点检测
cornerSubPix()
代码清单9-9 计算亚像素级别角点坐标
#include
#include
#include using namespace cv;
using namespace std;int main()
{system("color F0"); //改变DOS界面颜色Mat img = imread("lena.png",IMREAD_COLOR);if (!img.data){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}//彩色图像转成灰度图像Mat gray;cvtColor(img, gray, COLOR_BGR2GRAY);//提取角点int maxCorners = 100; //检测角点数目double quality_level = 0.01; //质量等级,或者说阈值与最佳角点的比例关系double minDistance = 0.04; //两个角点之间的最小欧式距离vector corners;goodFeaturesToTrack(gray, corners, maxCorners, quality_level, minDistance, Mat(), 3, false);//计算亚像素级别角点坐标vector cornersSub = corners; //角点备份,方式别函数修改Size winSize = Size(5, 5);Size zeroZone = Size(-1, -1);TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 40, 0.001);cornerSubPix(gray, cornersSub , winSize, zeroZone, criteria);//输出初始坐标和精细坐标for (size_t i = 0; i < corners.size(); i++){string str = to_string(i);str = "第" + str + "个角点点初始坐标:";cout << str << corners[i] << " 精细后坐标:" << cornersSub[i] << endl;}return 0;
}
9.2 特征点检测
9.2.1关键点
KeyPoint()
detect()
9.2.2 描述子
compute()
detectAndCompute()
9.2.3 SIFT 特征点检测
SIFT ::create()
9.2.4 SURF 特征点检测
SURF::create()
代码清单9-16 计算SURF 特征点
#include
#include //SURF特征点头文件
#include
#include using namespace std;
using namespace cv;
using namespace xfeatures2d; //SURF特征点命名空间int main()
{Mat img = imread("lena.png");if (!img.data){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}//创建SURF特征点类变量Ptr surf = SURF::create(500, //关键点阈值4, //4组金字塔3, //每组金字塔有3层true, //使用128维描述子false); //计算关键点方向//计算SURF关键点vector Keypoints;surf->detect(img, Keypoints); //确定关键点//计算SURF描述子Mat descriptions;surf->compute(img, Keypoints, descriptions); //计算描述子//绘制特征点Mat imgAngel;img.copyTo(imgAngel);//绘制不含角度和大小的结果drawKeypoints(img, Keypoints, img,Scalar(255,255,255));//绘制含有角度和大小的结果drawKeypoints(img, Keypoints, imgAngel, Scalar(255, 255, 255),DrawMatchesFlags::DRAW_RICH_KEYPOINTS);//显示结果imshow("不含角度和大小的结果", img);imshow("含有角度和大小的结果", imgAngel);waitKey(0); return 0;
}
9.2.5 ORB特征点检测
ORB::create()
ORB::HARRIS_SCORE
代码清单9-18 计算ORB特征点
#include
#include
#include using namespace std;
using namespace cv;int main()
{Mat img = imread("lena.png");if (!img.data){cout << "请确认图像文件名称是否正确" << endl;return -1;}//创建 ORB 特征点类变量Ptr orb = ORB::create(500, //特征点数目1.2f, //金字塔层级之间的缩放比例8, //金字塔图像层数系数31, //边缘阈值0, //原图在金字塔中的层数2, //生成描述子时需要用的像素点数目ORB::HARRIS_SCORE, //使用 Harris 方法评价特征点31, //生成描述子时关键点周围邻域的尺寸20 //计算 FAST 角点时像素值差值的阈值);//计算 ORB 关键点vector Keypoints;orb->detect(img, Keypoints); //确定关键点//计算 ORB 描述子Mat descriptions;orb->compute(img, Keypoints, descriptions); //计算描述子//绘制特征点Mat imgAngel;img.copyTo(imgAngel);//绘制不含角度和大小的结果drawKeypoints(img, Keypoints, img, Scalar(255, 255, 255));//绘制含有角度和大小的结果drawKeypoints(img, Keypoints, imgAngel, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);//显示结果imshow("不含角度和大小的结果", img);imshow("含有角度和大小的结果", imgAngel);waitKey(0);return 0;
}
9.3 特征点匹配
9.3.1 DescriptorMatcher类介绍
DescriptorMatcher::match()
DMatch类
DescriptorMatcher::knnMatch()
DescriptorMatcher::radiusMatch()
9.3.2 暴力匹配
BFMatcher构造函数
9.3.3 显示特征点匹配结果
drawMatches()
代码清单9-25 ORB特征点暴力匹配
#include
#include
#include using namespace std;
using namespace cv; void orb_features(Mat &gray, vector &keypionts, Mat &descriptions)
{Ptr orb = ORB::create(1000, 1.2f);orb->detect(gray, keypionts);orb->compute(gray, keypionts, descriptions);
}int main()
{Mat img1, img2; img1 = imread("box.png"); img2 = imread("box_in_scene.png");if (!(img1.data && img2.dataend)){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}//提取ORB特征点vector Keypoints1, Keypoints2;Mat descriptions1, descriptions2;//计算特征点orb_features(img1, Keypoints1, descriptions1);orb_features(img2, Keypoints2, descriptions2);//特征点匹配vector matches; //定义存放匹配结果的变量BFMatcher matcher(NORM_HAMMING); //定义特征点匹配的类,使用汉明距离matcher.match(descriptions1, descriptions2, matches); //进行特征点匹配cout << "matches=" << matches.size() << endl; //匹配成功特征点数目//通过汉明距离删选匹配结果double min_dist = 10000, max_dist = 0;for (int i = 0; i < matches.size(); i++){double dist = matches[i].distance;if (dist < min_dist) min_dist = dist;if (dist > max_dist) max_dist = dist;}//输出所有匹配结果中最大韩明距离和最小汉明距离cout << "min_dist=" << min_dist << endl;cout << "max_dist=" << max_dist << endl;//将汉明距离较大的匹配点对删除vector good_matches;for (int i = 0; i < matches.size(); i++){if (matches[i].distance <= max(2 * min_dist, 20.0)){good_matches.push_back(matches[i]);}}cout << "good_min=" << good_matches.size() << endl; //剩余特征点数目//绘制匹配结果Mat outimg, outimg1;drawMatches(img1, Keypoints1, img2, Keypoints2, matches, outimg);drawMatches(img1, Keypoints1, img2, Keypoints2, good_matches, outimg1);imshow("未筛选结果", outimg);imshow("最小汉明距离筛选", outimg1);waitKey(0); return 0;
}
9.3.4 FLANN匹配
FlannBasedMatcher构造函数
代码清单9-27 用FLANN方法匹配特征点
#include
#include
#include using namespace std;
using namespace cv;void orb_features(Mat &gray, vector &keypionts, Mat &descriptions)
{Ptr orb = ORB::create(1000, 1.2f);orb->detect(gray, keypionts);orb->compute(gray, keypionts, descriptions);
}int main()
{Mat img1, img2;img1 = imread("box.png");img2 = imread("box_in_scene.png");if (!(img1.data && img2.dataend)){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}//提取ORB特征点vector Keypoints1, Keypoints2;Mat descriptions1, descriptions2;//计算SURF特征点orb_features(img1, Keypoints1, descriptions1);orb_features(img2, Keypoints2, descriptions2);//判断描述子数据类型,如果数据类型不符需要进行类型转换,主要针对ORB特征点if ((descriptions1.type() != CV_32F) && (descriptions2.type() != CV_32F)){descriptions1.convertTo(descriptions1, CV_32F);descriptions2.convertTo(descriptions2, CV_32F);}//特征点匹配vector matches; //定义存放匹配结果的变量FlannBasedMatcher matcher; //使用默认值即可matcher.match(descriptions1, descriptions2, matches);cout << "matches=" << matches.size() << endl; //匹配成功特征点数目//寻找距离最大值和最小值,如果是ORB特征点min_dist取值需要大一些double max_dist = 0; double min_dist = 100;for (int i = 0; i < descriptions1.rows; i++){double dist = matches[i].distance;if (dist < min_dist) min_dist = dist;if (dist > max_dist) max_dist = dist;}cout << " Max dist :" << max_dist << endl;cout << " Min dist :" << min_dist << endl;//将最大值距离的0.4倍作为最优匹配结果进行筛选std::vector< DMatch > good_matches;for (int i = 0; i < descriptions1.rows; i++){if (matches[i].distance < 0.40 * max_dist){good_matches.push_back(matches[i]);}}cout << "good_matches=" << good_matches.size() << endl; //匹配成功特征点数目//绘制匹配结果Mat outimg, outimg1;drawMatches(img1, Keypoints1, img2, Keypoints2, matches, outimg);drawMatches(img1, Keypoints1, img2, Keypoints2, good_matches, outimg1);imshow("未筛选结果", outimg);imshow("筛选结果", outimg1);waitKey(0);return 0;
}
9.3.5 RANSAC 优化特征点匹配
findHomography()
代码清单9-29 RANSAC 算法优化特征点匹配结果
#include
#include
#include
using namespace std;
using namespace cv; void match_min(vector matches, vector & good_matches)
{double min_dist = 10000, max_dist = 0;for (int i = 0; i < matches.size(); i++){double dist = matches[i].distance;if (dist < min_dist) min_dist = dist;if (dist > max_dist) max_dist = dist;}cout << "min_dist=" << min_dist << endl;cout << "max_dist=" << max_dist << endl;for (int i = 0; i < matches.size(); i++)if (matches[i].distance <= max(2 * min_dist, 20.0))good_matches.push_back(matches[i]);
}//RANSAC算法实现
void ransac(vector matches, vector queryKeyPoint, vector trainKeyPoint, vector &matches_ransac)
{//定义保存匹配点对坐标vector srcPoints(matches.size()), dstPoints(matches.size());//保存从关键点中提取到的匹配点对的坐标for (int i = 0; i inliersMask(srcPoints.size());//Mat homography;//homography = findHomography(srcPoints, dstPoints, RANSAC, 5, inliersMask);findHomography(srcPoints, dstPoints, RANSAC, 5, inliersMask);//手动的保留RANSAC过滤后的匹配点对for (int i = 0; i &keypionts, Mat &descriptions)
{Ptr orb = ORB::create(1000, 1.2f);orb->detect(gray, keypionts);orb->compute(gray, keypionts, descriptions);
}int main()
{Mat img1 = imread("box.png"); //读取图像,根据图片所在位置填写路径即可Mat img2 = imread("box_in_scene.png");if (!(img1.data && img2.data)){cout << "读取图像错误,请确认图像文件是否正确" << endl;return -1;}//提取ORB特征点vector Keypoints1, Keypoints2;Mat descriptions1, descriptions2;//基于区域分割的ORB特征点提取orb_features(img1, Keypoints1, descriptions1);orb_features(img2, Keypoints2, descriptions2);//特征点匹配vector matches, good_min,good_ransac;BFMatcher matcher(NORM_HAMMING);matcher.match(descriptions1, descriptions2, matches);cout << "matches=" << matches.size() << endl;//最小汉明距离match_min(matches, good_min);cout << "good_min=" << good_min.size() << endl;//用ransac算法筛选匹配结果ransac(good_min, Keypoints1, Keypoints2, good_ransac);cout << "good_matches.size=" << good_ransac.size() << endl;//绘制匹配结果Mat outimg, outimg1, outimg2;drawMatches(img1, Keypoints1, img2, Keypoints2, matches, outimg);drawMatches(img1, Keypoints1, img2, Keypoints2, good_min, outimg1);drawMatches(img1, Keypoints1, img2, Keypoints2, good_ransac, outimg2);imshow("未筛选结果", outimg);imshow("最小汉明距离筛选", outimg1);imshow("ransac筛选", outimg2);waitKey(0); //等待键盘输入return 0; //程序结束
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
