4h上手C++版Opencv
原视频在这里:4h上手C++版Opencv_哔哩哔哩_bilibili
《4h上手C++版Opencv》是很不错的opencv入门视频教程,下面是根据视频整理的源代码,为便于理解,每节课都封装在了一个函数里面。
//第11课:opencv车牌识别,参考第8课,用手机搜索车牌图片对着摄像头即可
void vPen::lesson11(){
cv::VideoCapture cap(0);
cv::Mat img;
cv::CascadeClassifier plateCascade;
plateCascade.load("E://SDK//opencv-sdk//data//haarcascades//haarcascade_russian_plate_number.xml");
if (plateCascade.empty()){
TRACE("xml加载失败!");
}
vector<cv::Rect>plates;
while (true){
cap.read(img);
plateCascade.detectMultiScale(img, plates, 1.1, 10);
for (int i = 0; i < plates.size(); i++){
cv::Mat imgCrop = img(plates[i]);
cv::imshow(to_string(i), imgCrop);
cv::imwrite("Plates/"+to_string(i)+".png",imgCrop);
cv::rectangle(img, plates[i].tl(), plates[i].br(), cv::Scalar(255, 0, 255), 3);
}
cv::imshow("img", img);
cv::waitKey(40);
}
}
//第10课:opencv扫描自动校正,参考第5课
void vPen::lesson10(){
cv::Mat imgOriginal,imgGray, imgBlur, imgCanny, imgThre,imgDil, imgErode,imgWrap;
//载入倾斜图像
imgOriginal = cv::imread("paper.png");
cv::resize(imgOriginal, imgOriginal,cv::Size(),0.8,0.8);
cv::cvtColor(imgOriginal, imgGray, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(imgGray, imgBlur, cv::Size(3, 3), 3, 0);
cv::Canny(imgBlur, imgCanny, 25, 75);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
//膨胀操作
cv::dilate(imgCanny, imgDil, kernel);
//腐蚀操作
//cv::erode(imgDil, imgErode, kernel);
//获取最大轮廓
vector<vector<cv::Point>>contours;
vector<cv::Vec4i> hierarchy;
cv::findContours(imgDil, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
//寻找轮廓外接矩形的顶端中点坐标
vector<vector<cv::Point>> conPoly(contours.size());
vector<cv::Rect> boundRect(contours.size());
vector<cv::Point> biggest;
int maxArea = 0;
cv::Point myPoint(0, 0);
for (int i = 0; i < contours.size(); i++){
int area = cv::contourArea(contours[i]);
string objectType;
if (area>1000){
float peri = cv::arcLength(contours[i], true);
cv::approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);
if (area > maxArea&&conPoly[i].size()==4){
biggest = { conPoly[i][0], conPoly[i][1], conPoly[i][2], conPoly[i][3] };
maxArea = area;
}
//cv::drawContours(imgOriginal, conPoly, i, cv::Scalar(255, 0, 255), 2);
//cv::rectangle(imgOriginal, boundRect[i].tl(), boundRect[i].br(), cv::Scalar(0, 255, 0),5);
}
}
//绘制最大外接框顶点
for (int i = 0; i < biggest.size(); i++){
//cv::circle(imgOriginal, biggest[i], 10, cv::Scalar(255, 0, 255), cv::FILLED);
//cv::putText(imgOriginal, to_string(i), biggest[i], cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar(0, 0, 255), 2);
}
//对四个顶点重新排序
vector<cv::Point> newPoints;
vector<int>sumPoints, subPoints;
for (int i = 0; i < 4; i++){
sumPoints.push_back(biggest[i].x + biggest[i].y);
subPoints.push_back(biggest[i].x - biggest[i].y);
}
//求最大元素和最小元素的位置
newPoints.push_back(biggest[min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]);//0
newPoints.push_back(biggest[max_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]);//1
newPoints.push_back(biggest[min_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]);//2
newPoints.push_back(biggest[max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]);//3
//绘制新的最大外接框顶点
for (int i = 0; i < newPoints.size(); i++){
//cv::circle(imgOriginal, newPoints[i], 10, cv::Scalar(255, 0, 255), cv::FILLED);
//cv::putText(imgOriginal, to_string(i), newPoints[i], cv::FONT_HERSHEY_PLAIN, 2, cv::Scalar(255, 0, 0), 2);
}
//开始校正
float w = 420, h = 596;
cv::Point2f src[4] = { newPoints[0], newPoints[1], newPoints[2], newPoints[3] };
cv::Point2f dst[4] = { { 0.0f, 0.0f }, { w, 0.0f }, { 0.0f, h }, { w, h } };
cv::Mat matrix = cv::getPerspectiveTransform(src, dst);
cv::Mat imgWarp;
cv::warpPerspective(imgOriginal, imgWarp, matrix, cv::Point(w, h));
//稍裁剪一些更美观
int cropVal = 8;
cv::Rect roi(cropVal, cropVal, w - (2 * cropVal), h - (2 * cropVal));
cv::Mat imgCrop = imgWarp(roi);
//显示上述操作结果
cv::imshow("imgOriginal", imgOriginal);
cv::imshow("imgCrop", imgCrop);
cv::waitKey(0);
}
//第9课:opencv凌空画笔
void vPen::lesson9(){
//定义画笔hsv颜色
//随便找个绿色的东西就可以画
vector<vector<int>> myColors{
{ 35, 43, 46, 77, 255, 255},//绿色
{ 124, 48, 117, 143, 170, 255 }//紫色
};
vector<cv::Scalar> myColorVals{
{ 0, 255, 0 },//绿色
{ 255, 0, 255 }//紫色
};
//打开摄像头
cv::Mat img, imgHSV,mask;
cv::VideoCapture cap(0);
vector<vector<int>> newPoints;
while (true) {
cap.read(img);
cv::flip(img, img, 1);
//转换颜色空间
cv::cvtColor(img, imgHSV, cv::COLOR_BGR2HSV);
for (int i = 0; i < myColors.size(); i++){
cv::Scalar lower(myColors[i][0], myColors[i][1], myColors[i][2]);
cv::Scalar upper(myColors[i][3], myColors[i][4], myColors[i][5]);
//二值化操作,在hsv图像中找到该颜色并输出该颜色对应区域遮罩
cv::inRange(imgHSV, lower, upper, mask);
//cv::imshow(to_string(i),mask);
//寻找该mask轮廓
vector<vector<cv::Point>>contours;
vector<cv::Vec4i> hierarchy;
cv::findContours(mask, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
//寻找轮廓外接矩形的顶端中点坐标
//参考第七课
vector<vector<cv::Point>> conPoly(contours.size());
vector<cv::Rect> boundRect(contours.size());
cv::Point myPoint(0, 0);
for (int i = 0; i < contours.size(); i++){
int area = cv::contourArea(contours[i]);
if (area>1000){
float peri = cv::arcLength(contours[i], true);
cv::approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);
boundRect[i] = cv::boundingRect(conPoly[i]);
//根据外接矩形找到画笔最顶端中点
myPoint.x = boundRect[i].x + boundRect[i].width / 2;
myPoint.y = boundRect[i].y;
//cv::drawContours(img, conPoly,i,cv::Scalar(255,0,255),2);
//cv::rectangle(img, boundRect[i].tl(), boundRect[i].br(), cv::Scalar(0, 255, 0),5);
}
}
//将当前顶点坐标存入画笔数组
if (myPoint.x != 0 && myPoint.y != 0){
newPoints.push_back({ myPoint.x, myPoint.y, i });
}
}
//将所有的顶点画出来,原视频教程中为画点,此处改为连线
for (int i = 0; i < newPoints.size(); i++){
//cv::circle(img, cv::Point(newPoints[i][0], newPoints[i][1]), 10, myColorVals[newPoints[i][2]], cv::FILLED);
if (newPoints.size()>i + 1){
cv::line(img, cv::Point(newPoints[i][0], newPoints[i][1]), cv::Point(newPoints[i + 1][0], newPoints[i + 1][1]), myColorVals[newPoints[i][2]], 5);
}
}
//显示上述操作结果
imshow("img", img);
//按空格键清除
if (cv::waitKey(1) == 32){
newPoints.clear();
}
}
}
//第8课:opencv人脸识别
void vPen::lesson8(){
cv::VideoCapture cap(0);
cv::Mat img;
//定义级联分类器判别某个物体是否属于某个分类
cv::CascadeClassifier faceCascade;
//导入分类器文件
faceCascade.load("E://SDK//opencv-sdk//data//haarcascades//haarcascade_frontalface_default.xml");
if (faceCascade.empty()){
TRACE("xml加载失败!");
}
vector<cv::Rect>faces;
while (true) {
cap.read(img);
cv::flip(img, img, 1);
//开始检测并获取数据
faceCascade.detectMultiScale(img,faces,1.1,10);
//绘制矩形框标记人脸
for (int i = 0; i < faces.size(); i++){
cv::rectangle(img, faces[i].tl(), faces[i].br(),cv::Scalar(255,0,255),3);
}
//显示上述操作结果
cv::imshow("img", img);
cv::waitKey(40);
}
}
//第7课:opencv图形识别,如可以从图片中识别出三角形、正方形、圆形等
void vPen::lesson7(){
cv::Mat imgGray, imgBlur, imgCanny,imgDil,imgErode;
//读取含多种图形的图片
cv::Mat img = cv::imread("shapes.png");
//转为灰度图
cv::cvtColor(img, imgGray, cv::COLOR_BGR2GRAY);
//高斯模糊
cv::GaussianBlur(imgGray, imgBlur, cv::Size(3, 3), 3, 0);
//边缘检测
cv::Canny(imgBlur,imgCanny,25,75);
//构造一个3*3的矩形结构元素
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT,cv::Size(3,3));
//对边缘图像进行膨胀操作
cv::dilate(imgCanny,imgDil,kernel);
//创建轮廓数组保存轮廓数据
vector <vector<cv::Point>> contours;
//创建轮廓继承关系数组
vector<cv::Vec4i> hierarchy;
//寻找轮廓并保存数据
cv::findContours(imgDil, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
//在原始图像上绘制所有轮廓
//cv::drawContours(img, contours,-1,cv::Scalar(255,0,255),10);
//定义逼近点集
vector <vector<cv::Point>> conPoly(contours.size());
vector<cv::Rect>boundRect(contours.size());
string objType;
//遍历轮廓数组
for (int i = 0; i < contours.size(); i++){
//获取轮廓面积以剔除杂点
int area = cv::contourArea(contours[i]);
if (area>1000){
//获取轮廓周长
float peri = cv::arcLength(contours[i], true);
//逼近操作以找出轮廓的多边形拟合曲线
cv::approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);
//绘制拟合后的轮廓
cv::drawContours(img, conPoly, i, cv::Scalar(255, 0, 255), 2);
//获取轮廓的外接矩形
boundRect[i] = cv::boundingRect(conPoly[i]);
//绘制轮廓的外接矩形
cv::rectangle(img, boundRect[i].tl(), boundRect[i].br(), cv::Scalar(0, 255, 0), 2);
//根据多边形拟合曲线顶点数判断图形类别
int objCor = (int)conPoly[i].size();
if (objCor == 3){
objType = "Tri";
}
else if (objCor == 4){
//根据长宽比判断是否为正方形
float aspRatio = (float)boundRect[i].width / boundRect[i].height;
if (aspRatio > 0.95&&aspRatio<1.05){
objType = "Square";
}
else{
objType = "Rect";
}
}
else if (objCor > 4){
objType = "Circle";
}
//在原始图像显示图形类别
cv::putText(img, objType, {boundRect[i].x, boundRect[i].y-5},cv::FONT_HERSHEY_PLAIN,1,cv::Scalar(0,69,255),1);
}
}
//显示上述操作结果
cv::imshow("img", img);
cv::imshow("imgCanny", imgCanny);
cv::imshow("imgDil", imgDil);
cv::waitKey(0);
}
//第6课:opencv用滑块调整图像HSV(Hue颜色,Saturation饱和度,Value亮度)色彩,为配合后面的凌空绘图略有改动
void vPen::lesson6(){
cv::Mat img,imgHSV,mask;
float w = 250, h = 350;
//cv::Mat img = cv::imread("shapes.png");
//cv::cvtColor(img, imgHSV, cv::COLOR_BGR2HSV);
//配合虚拟画笔将图片换成了摄像头
cv::VideoCapture cap(0);
//定义hsv取值范围
int hmin = 0, smin = 0, vmin = 0;
int hmax = 179, smax = 255, vmax = 255;
//创建一个调节窗口
cv::namedWindow("TrackWin",(640,200));
//在窗口内添加滑块,第三个参数为要改变的参数地址,第四个参数为最大值
cv::createTrackbar("HueMin","TrackWin",&hmin,179);
cv::createTrackbar("HueMax", "TrackWin", &hmax, 179);
cv::createTrackbar("SatMin", "TrackWin", &smin, 255);
cv::createTrackbar("SatMax", "TrackWin", &smax, 255);
cv::createTrackbar("ValMin", "TrackWin", &vmin, 255);
cv::createTrackbar("ValMax", "TrackWin", &vmax, 255);
while (true){
cap.read(img);
cv::flip(img,img,1);
//将图像从BGR默认色彩空间转换到HSV色彩空间
cv::cvtColor(img, imgHSV, cv::COLOR_BGR2HSV);
//定义HSV颜色区间
cv::Scalar lower(hmin, smin, vmin);
cv::Scalar upper(hmax, smax, vmax);
//将两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0)
cv::inRange(imgHSV, lower, upper, mask);
//显示上述操作结果
cv::imshow("img", img);
cv::imshow("imgHSV", imgHSV);
cv::imshow("mask", mask);
cv::waitKey(40);
}
}
//第5课:opencv校正倾斜图像
void vPen::lesson5(){
//定义校正后的宽高
float w = 250, h = 350;
//读取图像
cv::Mat img = cv::imread("card.jpg");
//定义原始图像中欲校正部分的四个顶点坐标,坐标通过windows自带的绘图软件获取
cv::Point2f src[4] = { { 1123, 407 }, { 1389, 539 }, { 872, 670 }, { 1142, 818 } };
//定义校正后图像的四个顶点坐标
cv::Point2f dst[4] = { { 0.0f, 0.0f }, { w, 0.0f }, { 0.0f, h }, { w, h } };
//根据源图像和目标图像上的四对点坐标来计算从源图像透视变换到目标图像的变换矩阵
cv::Mat matrix = cv::getPerspectiveTransform(src,dst);
//对输入图像进行透视变换
cv::Mat imgWarp;
cv::warpPerspective(img, imgWarp,matrix,cv::Point(w,h));
//在原始图像上绘制四个顶点
for (int i = 0; i < 4; i++){
cv::circle(img, src[i],10,cv::Scalar(0,0,255),cv::FILLED);
}
//显示上述操作结果
cv::imshow("img", img);
cv::imshow("imgWarp", imgWarp);
cv::waitKey(0);
}
//第4课:opencv绘图
void vPen::lesson4(){
//准备一个512*512的白色图像作为画板
cv::Mat img(512, 512, CV_8UC3, cv::Scalar(255, 255, 255));
//在画板上中心画一个半径为155的实心红色圆
cv::circle(img, cv::Point(256, 256), 155, cv::Scalar(0, 0, 255),cv::FILLED);
//在画板上中心画一个白色实心矩形
cv::rectangle(img, cv::Point(130, 226), cv::Point(382, 286), cv::Scalar(255, 255, 255), cv::FILLED);
//在画板上中心画一个白色实线
cv::line(img, cv::Point(130, 296), cv::Point(382, 296), cv::Scalar(255, 255, 255), 2);
在画板上写字
cv::putText(img, "---FMSer.CN---", cv::Point(137, 262), cv::FONT_HERSHEY_DUPLEX, 0.75, cv::Scalar(0, 69, 255),2);
//显示上述操作结果
imshow("img", img);
cv::waitKey(0);
}
//第3课:opencv图像裁剪
void vPen::lesson3(){
//打开摄像头
cv::VideoCapture cap(0);
cv::Mat img,imgResize,imgCrop;
while (true) {
cap.read(img);
//水平翻转
cv::flip(img, img, 1);
//缩放到原来的一半
cv::resize(img, imgResize,cv::Size(),0.5,0.5);
//定义感兴趣区域
cv::Rect roi(100,100,300,300);
//获取感兴趣区域图像
imgCrop = img(roi);
//显示上述操作结果
imshow("img", img);
imshow("imgResize", imgResize);
imshow("imgCrop", imgCrop);
cv::waitKey(40);
}
}
//第2课:opencv常用操作
void vPen::lesson2(){
//打开摄像头
cv::VideoCapture cap(0);
cv::Mat img,imgGray, imgBlur, imgCanny, imgDil, imgErode;
while (true) {
cap.read(img);
//水平翻转
cv::flip(img, img, 1);
//转灰度图
cv::cvtColor(img, imgGray, cv::COLOR_BGR2GRAY);
//高斯模糊
cv::GaussianBlur(img, imgBlur, cv::Size(3, 3), 3, 0);
//边缘检测
cv::Canny(imgBlur, imgCanny, 25, 75);
//构造一个5*5的矩形结构元素
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5));
//膨胀操作
cv::dilate(imgCanny, imgDil, kernel);
//腐蚀操作
cv::erode(imgDil, imgErode, kernel);
//显示上述操作结果
imshow("img", img);
imshow("imgGray", imgGray);
imshow("imgBlur", imgBlur);
imshow("imgCanny", imgCanny);
imshow("imgDil", imgDil);
imshow("imgErode", imgErode);
cv::waitKey(40);
}
}
//第1课:opencv基础操作
void vPen::lesson1(){
//定义图片路径
string path = "hand.jpg";
//读取图片
cv::Mat img = cv::imread(path);
//显示图片
imshow("img", img);
//按任意键退出
cv::waitKey(0);
}