目录
四、绘制形状及文本 draw shapes and text
七、形状、轮廓检测 Shape/Contour Detection
一、读取图片、视频、电脑相机 Image
读图:
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace std;
void main() {
//Image
string path = "Resources/test.png";
Mat img = imread(path); //读取图像路径赋值给Mat
imshow("Image", img); //创建一个名为Image的窗口显示img
//不断刷新图像 0:一直显示这一帧;不为0 时,表示显示完上一帧后delay 某 ms后再显示下一帧
waitKey(0);
}
读视频:
void main() {
Video
string path = "Resources/test_video.mp4";
VideoCapture cap(path);
Mat img;
while (true) {
cap.read(img);
imshow("Image", img);
waitKey(1); //1 表示1ms后显示下一帧,设为20则视频速度变慢
}
}
读本地电脑摄像头:
void main() {
WebCam
VideoCapture cap(0);//获取本地摄像头
Mat img;
while (true) { //循环读图并显示
cap.read(img);
imshow("Image", img);
waitKey(1);
}
}
二、边缘检测 Basic Functions
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;
/// <summary>
/// Basic Functions
/// </summary>
void main() {
string path = "Resources/test.png";
Mat img = imread(path);
Mat imgGray,imgBlur, imgCanny, imgDilate, imgErode;
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(img, imgBlur, Size(3, 3), 3, 0); //在用Canny边缘检测前先模糊图片
Canny(img, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDilate, kernel); //膨胀
erode(imgDilate, imgErode, kernel); //腐蚀
imshow("image", img);
imshow("imageGray", imgGray);
imshow("imageBlur", imgBlur);
imshow("imageCanny", imgCanny);
imshow("imageDilate", imgDilate);
imshow("imageErode", imgErode);
waitKey(0);
}
三、缩放与剪切 resize and crop
#include <opencv2/opencv.hpp>
#include <iostream>;
using namespace cv;
using namespace std;
/// <summary>
/// resize and crop
/// </summary>
void main() {
string path = "Resources/test.png";
Mat img = imread(path);
Mat imgResize,imgCrop;
resize(img, imgResize, Size(), 0.5, 0.5); //0.5, 0.5按比例缩放
Rect roi(100,100,400,400);//指定区域剪切
imgCrop = img(roi);
imshow("img", img);
imshow("imgResize", imgResize);
imshow("imgCrop", imgCrop);
waitKey(0);
}
四、绘制形状及文本 draw shapes and text
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/// <summary>
/// draw shapes and text
/// </summary>
void main() {
//string path = "Resources/test.png";
Mat img(512, 512, CV_8SC3, Scalar(255, 255, 255)); //Blank Image
circle(img, Point(256, 256), 155, Scalar(0, 69, 255), FILLED);
//circle(img, Point(256, 256), 155, Scalar(0, 69, 255), 10);
rectangle(img, Point(130, 226), Point(137, 262), Scalar(255, 255, 255), 0);
line(img, Point(101, 256), Point(401, 256), Scalar(255, 255, 255), 8);
putText(img, "myText", Point(101, 252), FONT_HERSHEY_DUPLEX, 0.95, Scalar(255, 255, 255), 2);
imshow("img", img);
waitKey(0);
}
五、透视投影变换矫正 warp
关键在于像素点的选取、旋转矩阵的应用及warpPerspective()函数的使用
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/// <summary>
/// warp 透视投影变换矫正
/// </summary>
float w = 250, h = 300;
Mat matrix, imgWarp, matrixJ,imgWarpJ;
void main() {
string path = "Resources/cards.jpg";
Mat img = imread(path);
Point2f srcK[4] = { {528,143},{769,192}, {405,397}, {674,458} };
Point2f srcJ[4] = { {778,108},{1016,84}, {844,361}, {1116,330} };
Point2f dest[4] = { {0.0f,0.0f},{w,0.0f}, {0.0f,h}, {w,h} };
//旋转矩阵
matrix = getPerspectiveTransform(srcK, dest);
warpPerspective(img, imgWarp, matrix, Point(w, h));
matrixJ = getPerspectiveTransform(srcJ, dest);
warpPerspective(img, imgWarpJ, matrixJ, Point(w, h));
//确定四个点
for (int i = 0; i < 4; i++) {
circle(img, srcK[i], 10, Scalar(0, 69, 255), FILLED);
circle(img, srcJ[i], 10, Scalar(0, 69, 255), FILLED);
}
imshow("img", img);
imshow("imgWarp", imgWarp);
imshow("imgWarpJ", imgWarpJ);
waitKey(0);
}
六、颜色检测 color detection
关键在于hsv三类最值的选取以及inRange()函数的使用;
int hmin = 0, smin = 109, vmin = 154;
int hmax = 22, smax = 255, vmax = 255;
通过trackbar来调整阈值,观测目标颜色的选中情况,从而选定最值(注意,目标要以白色为准)。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/// <summary>
/// color dection
/// </summary>
int hmin = 0, smin = 109, vmin = 154;
int hmax = 22, smax = 255, vmax = 255;
void main() {
string path = "Resources/lambo.png";
Mat img = imread(path);
Mat imgHSV,mask;
//HSV 颜色空间 H(色调):0~180 S(饱和度):0~255 V(亮度):0~255
cvtColor(img, imgHSV, COLOR_BGR2HSV);
namedWindow("trackBar", (600, 400));
createTrackbar("hmin", "trackBar", &hmin, 180);
createTrackbar("smin", "trackBar", &smin, 255);
createTrackbar("vmin", "trackBar", &vmin, 255);
createTrackbar("hmax", "trackBar", &hmax, 180);
createTrackbar("smax", "trackBar", &smax, 255);
createTrackbar("vmax", "trackBar", &vmax, 255);//创建轨迹条,
//4个参数分别是 轨迹条名字,输出的窗口,一个指向整数的指针来表示当前的值,可到达的最大值
//调整要点:使目标颜色保持白色
while (true) {
Scalar lower(hmin, smin, vmin);//HSV最低值
Scalar upper(hmax, smax, vmax);//HSV最高值
inRange(imgHSV, lower, upper, mask);//inRange将在阈值区间内的像素设置为白色,不在的设为黑色
imshow("img", img);
imshow("imgHSV", imgHSV);
imshow("mask", mask);
waitKey(1);
}
}
七、形状、轮廓检测 Shape/Contour Detection
#include <opencv2/opencv.hpp>
#include <iostream>
// Shape/Contour Detection //
using namespace cv;
using namespace std;
Mat imgGray, imgCanny, imgGauss, imgDilate, imgErode;
void getContours(Mat imgDilate, Mat img) {
vector<vector<Point>> contours; //{{Point(20,30),Point(40,30)},{},{}}
vector<Vec4i> hierarchy; //vector里放置了四个int类型的变量
findContours(imgDilate, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//drawContours(img, contours, -1, Scalar(255, 0, 255), 2);
//存放轮廓点
vector<vector<Point>> conPoly(contours.size());
vector<Rect> boundRect(contours.size());
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
cout << "area:" <<area << endl;
if (area > 1000) {
float peri = arcLength(contours[i], true); //弧长
//把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);//把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合。
//由图像的轮廓点组成的点集 输出的多边形点集 输出的精度:两个轮廓点之间最大距离数 输出的多边形是否封闭
drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);
cout << "count points :"<<conPoly[i].size() << endl;
//画矩形边框
boundRect[i] = boundingRect(conPoly[i]);
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
//add text
string ojbType;
int objCor = conPoly[i].size();
if (objCor == 3) {
ojbType = "Tri";
}
else if (objCor == 4) {
float aspRatio = (float)boundRect[i].width / boundRect[i].height;
if (aspRatio > 0.95 && aspRatio < 1.05) {
ojbType = "Square";
}
else {
ojbType = "Rect";
}
}
else if (objCor > 4) {
ojbType = "Circle";
}
putText(img, ojbType,Point(boundRect[i].x, boundRect[i].y), FONT_HERSHEY_DUPLEX,0.75,2);
}
}
}
void main() {
string path = "Resources/shapes.png";
Mat img = imread(path);
// Preprocessing
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgGauss,Size(3,3),3,0 );//在做边缘检测前一般要进行模糊处理
Canny(imgGauss, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDilate, kernel);
erode(imgDilate, imgErode, kernel);
imshow("img", img);
getContours(imgDilate, img);
imshow("imgContour", img);
/*imshow("imgGray", imgGray);
imshow("imgGauss", imgGauss);
imshow("imgCanny", imgCanny);
imshow("imgDilate", imgDilate);*/
waitKey(0);
}
八、人脸检测 face detection
key:CascadeClassifier faceCascade; //级联分类器
detectMultiScale()函数使用
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void main() {
string path = "Resources/multiFaces.png";
Mat img = imread(path);
imshow("Image", img);
CascadeClassifier faceCascade; //级联分类器
faceCascade.load("Resources/haarcascade_frontalface_default.xml");//加载训练模型
if (faceCascade.empty()) {
cout << "XML file is empty"<<endl;
}
vector<Rect> faces;
faceCascade.detectMultiScale(img, faces, 1.1, 2); //
//input output 检测框的最小尺寸 检测的阈值,过小会出现误检现象,即把一些其他元素误判成人脸,过大可能会检测不到目标
for (int i = 0; i < faces.size(); i++) {
rectangle(img, faces[i].tl(), faces[i].br(), Scalar(255, 0, 255), 3);
}
imshow("face detect", img);
waitKey(0);
}
九、虚拟画笔 Virtual Painter
先选定颜色的阈值:ColorPicker;
基于第六部分的颜色选择器 colordetection制作该程序;
黑框中持续打印滑动条的选值,选出颜色笔后便可使用打印出的hsv最大最小值;
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/// <summary>
/// color dection
/// </summary>
void main() {
VideoCapture cap(0);
Mat img;
Mat imgHSV, mask;
//HSV 颜色空间 H(色调):0~180 S(饱和度):0~255 V(亮度):0~255
int hmin = 0, smin = 0, vmin = 0;
int hmax = 180, smax = 255, vmax = 255;
namedWindow("trackBar", (600, 400));
createTrackbar("hmin", "trackBar", &hmin, 180);
createTrackbar("smin", "trackBar", &smin, 255);
createTrackbar("vmin", "trackBar", &vmin, 255);
createTrackbar("hmax", "trackBar", &hmax, 180);
createTrackbar("smax", "trackBar", &smax, 255);
createTrackbar("vmax", "trackBar", &vmax, 255);//创建轨迹条,
//4个参数分别是 轨迹条名字,输出的窗口,一个指向整数的指针来表示当前的值,可到达的最大值
//调整要点:使目标颜色保持白色
while (true) {
cap.read(img);
cvtColor(img, imgHSV, COLOR_BGR2HSV);
Scalar lower(hmin, smin, vmin);//HSV最低值
Scalar upper(hmax, smax, vmax);//HSV最高值
inRange(imgHSV, lower, upper, mask);//inRange将在阈值区间内的像素设置为白色,不在的设为黑色
cout << hmin << "," << smin << ","<<vmin << ","<<hmax << ","<<smax << ","<<vmax << endl;
imshow("img", img);
imshow("imgHSV", imgHSV);
imshow("mask", mask);
waitKey(1);
}
}
将上一步的hsv值填入myColors二维vector中;
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
VideoCapture cap(0);
Mat img;
vector<vector<int>> newPoints;
vector<vector<int>> myColors{ {111,113,75,136,255,255}, //blue
{151,56,87,180,255,168} }; //red
vector<Scalar> myColorValues{ {0,0,255} , //blue
{255,0,0} };//red
Point getContours(Mat img) {
vector<vector<Point>> contours; //{{Point(20,30),Point(40,30)},{},{}}
vector<Vec4i> hierarchy; //vector里放置了四个int类型的变量
findContours(img, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//drawContours(img, contours, -1, Scalar(255, 0, 255), 2);
//根据点之间连接直线画出轮廓
vector<vector<Point>> conPoly(contours.size());
vector<Rect> boundRect(contours.size());
Point myPoint(0, 0);
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
cout << "area:" << area << endl;
float peri = arcLength(contours[i], true); //弧长
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);//把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合。
//由图像的轮廓点组成的点集 输出的多边形点集 输出的精度:两个轮廓点之间最大距离数 输出的多边形是否封闭
//drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);
cout << "count points :" << conPoly[i].size() << endl;
//画矩形边框
boundRect[i] = boundingRect(conPoly[i]);
//找到画笔中间位置
myPoint.x = boundRect[i].x + boundRect[i].width / 2;
myPoint.y = boundRect[i].y;
//rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);
}
return myPoint;
}
vector<vector<int>> findColor(Mat img) {
Mat imgHSV, mask;
cvtColor(img, imgHSV, COLOR_BGR2HSV);
for (int i = 0; i < myColorValues.size(); i++) {
Scalar lower(myColors[i][0], myColors[i][1], myColors[i][2]);
Scalar upper(myColors[i][3], myColors[i][4], myColors[i][5]);
inRange(img, lower, upper, mask);
Point myPoint = getContours(mask);
//imshow(to_string(i), mask);
if (myPoint.x != 0 && myPoint.y != 0) {
//存储画笔画圆点的中心、半径及颜色
newPoints.push_back({ myPoint.x,myPoint.y,i });
}
}
return newPoints;
}
void drawOnCanvas(vector<vector<int>> newPoints, vector<Scalar> myColorValues) {
for (int i = 0; i < newPoints.size(); i++) {
//画相应的颜色点
circle(img, Point(newPoints[i][0], newPoints[i][1]), 10, myColorValues[newPoints[i][2]], FILLED);
}
}
void main() {
while (true) {
cap.read(img);
newPoints = findColor(img);
drawOnCanvas(newPoints, myColorValues);
imshow("img", img);
waitKey(1);
}
}
十、 扫描文件 scan file
#include <opencv2/opencv.hpp>
#include <iostream>
// scan file //
using namespace cv;
using namespace std;
Mat imgOriginal, imgGray, imgGauss,imgCanny,imgDilate, imgThre, imgInitial,imgWarp, imgCrop;
vector<Point> biggest, initialPoints,docPoints;
float w = 420, h = 596;
Mat preProcessing(Mat img) {
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgGauss, Size(3, 3), 3, 0);
Canny(imgGauss, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDilate, kernel);
return imgDilate;
}
vector<Point> getContours(Mat img) {
vector<vector<Point>> contours;
findContours(img, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vector<vector<Point>> conPoly(contours.size());
float maxArea=0.0;
for (int i = 0; i < contours.size(); i++) {
int area = contourArea(contours[i]);
if (area > 1000) {
float peri = arcLength(contours[i], true);
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);
if (area>maxArea && conPoly[i].size() == 4) { //筛选出最大的轮廓,这里是筛选出paper纸张
maxArea = area;
biggest = { conPoly[i][0],conPoly[i][1] ,conPoly[i][2] ,conPoly[i][3] };
}
drawContours(imgOriginal,conPoly,i,Scalar(255,0,255),2);
}
}
return biggest;
}
void drawPoints(vector<Point> points,Scalar color) {
for (int i = 0; i < points.size(); i++) {
circle(imgOriginal, points[i], 10, color, FILLED);
putText(imgOriginal, to_string(i), points[i], FONT_HERSHEY_PLAIN,5, color, 5);
}
}
vector<Point> reorder(vector<Point > points) {
vector<Point> newPoints;
vector<int> sumPoints, subPoints;
for (int i = 0; i < points.size(); i++) {
sumPoints.push_back(points[i].x + points[i].y);
subPoints.push_back(points[i].x - points[i].y);
}
cout << "sumPoints,subPoints"<<endl;
for (int i = 0; i < sumPoints.size(); i++) {
cout<<sumPoints[i]<<","<< subPoints[i]<<endl;
}
// (x+y)min为左上;(x+y)max为右下;(x-y)max为右上;(x-y)min为左下
newPoints.push_back(points[min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); // 0
cout << min_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin() << endl;
newPoints.push_back(points[max_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); //1
cout << max_element(subPoints.begin(), subPoints.end()) - subPoints.begin() << endl;
newPoints.push_back(points[min_element(subPoints.begin(), subPoints.end()) - subPoints.begin()]); //2
cout << min_element(subPoints.begin(), subPoints.end()) - subPoints.begin() << endl;
newPoints.push_back(points[max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin()]); //3
cout << max_element(sumPoints.begin(), sumPoints.end()) - sumPoints.begin() << endl;
return newPoints;
}
Mat getWarp(Mat img,vector<Point> points,float w,float h) {
Point2f src[4] = {points[0],points[1], points[2], points[3]};
Point2f des[4] = { {0.0f,0.0f},{w,0.0f}, {0.0f,h}, {w,h} };
Mat matrix = getPerspectiveTransform(src, des);
warpPerspective(img, imgWarp, matrix, Point(w, h));
return imgWarp;
}
void main() {
string path = "Resources/paper.jpg";
imgOriginal = imread(path);
resize(imgOriginal, imgOriginal, Size(), 0.5, 0.5);
//preProcessing
imgThre = preProcessing(imgOriginal);
//get contours -Biggest
initialPoints = getContours(imgThre);
//drawPoints(initialPoints, Scalar(0,0,255));
docPoints = reorder(initialPoints);
//drawPoints(docPoints, Scalar(0, 255, 0));
//warp
imgWarp = getWarp(imgOriginal,docPoints,w,h);
//resize
int cropVal = 5;
Rect roi(cropVal, cropVal, w - (2 * cropVal), h - (2 * cropVal));
imgCrop = imgWarp(roi);
imshow("imgOriginal", imgOriginal);
imshow("imgThre", imgThre);
imshow("imgCrop", imgCrop);
waitKey(0);
}
其中:点排序前后如下
最终扫描图:
十一、车牌检测 plates detection
与第八部分人脸识别类似,加载训练模型后,使用detectMultiScale()函数获取检测结果;参数的使用影响检测结果。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void main() {
Mat img = imread("Resources/car.png");
resize(img, img, Size(), 0.5, 0.5);
CascadeClassifier carClassifier;
carClassifier.load("Resources/haarcascade_russian_plate_number.xml");
if (carClassifier.empty()) cout << "XML file is empty" << endl;
vector<Rect> plates;
carClassifier.detectMultiScale(img, plates, 1.1, 9);
for (int i = 0; i < plates.size(); i++) {
rectangle(img, plates[i].tl(), plates[i].br(), Scalar(255, 0, 255), 3);
Mat imgCrop = img(plates[i]);
imshow("plate", imgCrop);
}
imshow("car plate detection", img);
waitKey(0);
}
十二、 图片拼接
分为水平和竖直两个方向进行拼接。
水平方向:
将一组图片水平排列进行拼接,所有图片的宽度之和作为拼接结果图的长,所有图片中最大的长作为结果图的宽。
void opImgH(const std::vector<cv::Mat> &vecMat, cv::Mat &destImg) {
int h = 0;
int w = 0;
for (size_t i = 0; i < vecMat.size(); ++i) {
// find max height
w += vecMat[i].cols;
if (vecMat[i].rows > h) {
h = vecMat[i].rows;
}
}
destImg = cv::Mat(h, w, CV_8UC3, cv::Scalar(90, 93, 82));
w = 0;
for (size_t i = 0; i < vecMat.size(); ++i) {
vecMat[i].copyTo(destImg(cv::Rect(w, 0, vecMat[i].cols, vecMat[i].rows)));
w += vecMat[i].cols;
}
}
结果图如下:
竖直方向拼接:
void opImgV(const std::vector<cv::Mat> &vecMat, cv::Mat &destImg) {
int h = 0;
int w = 0;
for (size_t i = 0; i < vecMat.size(); ++i) {
h += vecMat[i].rows;
if (vecMat[i].cols > w) {
w = vecMat[i].cols;
}
}
destImg = cv::Mat(h, w, CV_8UC3, cv::Scalar(90, 93, 82));
h = 0;
for (size_t i = 0; i < vecMat.size(); ++i) {
vecMat[i].copyTo(destImg(cv::Rect(0, h, vecMat[i].cols, vecMat[i].rows)));
h += vecMat[i].rows;
}
}