图像和视频数据的读取
图像数据通过imread(path)来读取矩阵数据,视频是通过VideoCapture返回一个视频的输入流,从而可以一帧一帧的读取图像。 正如python中opencv通过numpy存储数据,c++中的图像数据通过一个简陋的矩阵类Mat类来存储图片数据。
int show_img(string &img_path) {
Mat src = imread(path, 1);
if (src.empty()) {
printf("open pic failed");
return -1;
}
cout << src.channels() << src.size() << endl;
namedWindow("12", WINDOW_FREERATIO); //定义一个可以自由缩放大小的窗
imshow("12", src); //名字需要一样才能放在一起
waitKey();
destroyAllWindows();
return 0;
}
void show_video(string &video_path) {
VideoCapture cap;
cap.open(path);
if (!cap.isOpened()) {
cout << "fail" << endl;
}
Mat img;
while (true) {
bool a = cap.read(img);
if (!a) {
break;
}
imshow("1", img);
waitKey(1);
}
destroyAllWindows();
}
通过查看Mat类的构造函数
Mat(int rows, int cols, int type);
Mat(Size size, int type);
Mat(int rows, int cols, int type, const Scalar& s);
//例如
Mat a(Size(512,512), CV_8UC3, Scalar(0,0,255));
type:CV_8UC1, …, CV_64FC4,指明构造的位深和通道数,Size()矩阵大小,Scalar是图像的颜色(0,0,255)红色,还有许多种构造方法,用的时候查一下就可以了
裁剪图像
resize图像 裁切图像需要定义裁切的大小,用到了模板类Rect,可以在定义中找到构造函数,一般情况下Point指的的是左上角的点,Size是(width, height),分别为宽和高
void crop_img(string& path) {
Mat img = imread(path);
Mat imgcrop, resize_img;
Rect roi(Point(100, 100), Size(200, 300));
resize(img, resize_img, Size(), 0.5, 0.5, INTER_NEAREST);
imgcrop = img(roi);
imshow("1", img);
imshow("1.5", resize_img);
imshow("2", imgcrop);
waitKey();
destroyAllWindows();
}
轮廓检测
进行轮廓检测前,通常需要一些预处理操作,灰度处理或者阈值处理,进行高斯模糊(GaussianBlur),使用canny算子计算轮廓,之后利用用膨胀或者腐蚀操作使得轮廓尽可能是闭合的。
void preprocessing_(string &path) {
Mat img = imread(path);
Mat img_gray, img_Blur, img_Canny, img_Dil;
cvtColor(img, img_gray, COLOR_BGR2GRAY);
GaussianBlur(img_gray, img_Blur, Size(3, 3), 3, 0);
Canny(img_Blur, img_Canny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); //膨胀的核大小
dilate(img_Canny, img_Dil, kernel);
//imshow("1", img_Dil);
getContours(img_Dil, img);
//画出轮廓
}
需要注意轮廓数据得保存格式vector>,可能会用到的一些函数arcLength可以计算轮廓得周长,contourArea计算轮廓的面积,通过这两个函数可以过滤掉一些不必要得噪声轮廓。approxPolyDP函数用于返回一个近似得多边形 画矩形框用rectangle(tl:top left, br:bottom right) 画轮廓用drawContours
void rectangle(InputOutputArray img, Point pt1, Point pt2,
const Scalar& color)
void drawContours( InputOutputArray image, InputArrayOfArrays contours,
int contourIdx, const Scalar& color)
void getContours(Mat imgDil, Mat img) {
vector<vector<Point>> contours; //数据存储样例 {{(1,1),(1,2),(1,3)},{(),(),()},{(),(),(),()}}
vector<Vec4i> hierarchy;
findContours(imgDil, 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 << "-------------------" << endl;
cout << area << endl;
string objectType;
if (area > 1000)
{
float peri = arcLength(contours[i], true); //perimeter(周长/边缘)
approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true); //用更少的曲线来精确轮廓,所以输入和输出的数据类型应该是一致的,估计最小的角点
cout << conPoly[i].size() << endl;
boundRect[i] = boundingRect(conPoly[i]); //返回包含轮廓点的最小矩形框,
int objCor = (int)conPoly[i].size();
if (objCor == 3) { objectType = "Tri"; }
else if (objCor == 4)
{
float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
cout << aspRatio << endl;
if (aspRatio > 0.95 && aspRatio < 1.05) { objectType = "Square"; }
else { objectType = "Rect"; }
}
else if (objCor > 4) { objectType = "Circle"; }
drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);
rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5); //画矩形框用rectangle(tl:top left, br:bottom right),画轮廓用drawContours
//putText(img, objectType, { boundRect[i].x,boundRect[i].y }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 2);
putText(img, objectType, Point(boundRect[i].x, boundRect[i].y), FONT_HERSHEY_PLAIN, 1, Scalar(0, 0, 255), 1);
}
}
imshow("img", img);
waitKey();
destroyAllWindows();
}
人脸检测
用opencv实现很简单得人脸检测
void face_detection(string &path) {
Mat img = imread(path);
CascadeClassifier face_detec; //定义一个层叠的分类器(多尺度)
face_detec.load("F:/WTY/桌面/opencv/resources/haarcascade_frontalface_default.xml");
if (face_detec.empty()) {
cout << "open failed" << endl;
}
vector<Rect> faces;
int minNeighbors = 3; //目标至少被检测出几次才算真的检测出来
face_detec.detectMultiScale(img, faces, 1.1, minNeighbors);
for (int i = 0; i < faces.size(); i++) {
rectangle(img, faces.at(i), Scalar(0, 0, 255));
}
imshow("face", img);
waitKey();
destroyAllWindows();
}
透视变换
先计算一个仿射矩阵,把仿射矩阵用在图像得透视变换上。这里有一个需要注意的点,仿射变换在图像上的点需要固定,src和dst需要相互对应上,0->1->2->3->0始终按照这样一个顺序。通过findcontours和approxPolyDP得到的四个角点的顺序可能会不一致。
void warp_perspective(string &path) {
Mat img = imread(path);
float w = 200, h = 300;
Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };
Mat matrix, warp_img;
matrix = getPerspectiveTransform(src, dst);
warpPerspective(img, warp_img, matrix, Size(200, 300));
imshow("1", warp_img);
waitKey();
destroyAllWindows();
}
颜色检测
颜色检测一般在HSV颜色空间上进行,H色度(0,179),S饱和度(0,255),V明度(0,255)
void color_detection(string& path) {
Mat img = imread(path);
Mat hsv_img, mask;
Size size = img.size();
int hmin = 0, smin = 110, vmin = 153;
int hmax = 19, smax = 240, vmax = 255;
cvtColor(img, hsv_img, COLOR_BGR2HSV);
namedWindow("trackbars", WINDOW_FREERATIO);
createTrackbar("hue min", "trackbars", &hmin, 179);
while (true) {
Scalar lower(hmin, smin, vmin);
Scalar upper(hmax, smax, vmax);
inRange(hsv_img, lower, upper, mask); //输出一个蒙版,检测在lower和upper之间的颜色
imshow("trackbars", mask);
imshow("img", img);
waitKey(1);
}
destroyAllWindows();
}