1.获取视频
获取摄像头or本地视频文件+resize大小+颜色编码格式 VideoCapture是一个摄像头类。Mat是OpenCv的矩阵类。resize函数改变图像大小。
OpenCv的图片是按BGR顺序存储的。
COLOR_BGR2HSV:更接近人直观感觉的颜色格式。颜色信息(H)、饱和度(S)、亮度(V)
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv; //引入OpenCv的命名空间
int main()
{
// 从摄像头读入视频。VideoCapture是一个摄像头类,可以读取摄像头或者本地的视频文件。
VideoCapture capture(0);
while (1)
{
//Mat是OpenCv的矩阵类,一般用来存储图像。
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
capture >> frame; //读取当前帧
resize(frame, frame, Size(360, 240)); //改变图像大小
cvtColor(frame, frame, COLOR_BGR2GRAY); //转为灰度图
imshow("aa", frame);
waitKey(30); //延时30ms
}
return 0;
}
2.获取HSV
//setMouseCallback会捕获指定窗口的鼠标消息。
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
void on_mouse(int EVENT, int x, int y, int flags, void* userdata);
int main(int argc, char** argv)
{
Mat src, hsv;
//此处更改图片地址
src = imread("E:\\XXX\\XXX\\XXX\\X.png");
namedWindow("display");
setMouseCallback("display", on_mouse, &src);
while (1)
{
imshow("display", src);
waitKey(40);
}
}
void on_mouse(int EVENT, int x, int y, int flags, void* userdata)
{
Mat rgb, hsv;
rgb = *(Mat*)userdata;
Mat temp;
cvtColor(*(Mat*)userdata, hsv, COLOR_BGR2HSV);
Point p(x, y);
switch (EVENT)
{
case EVENT_LBUTTONDOWN:
{
printf("b=%d\t", rgb.at<Vec3b>(p)[0]);
printf("g=%d\t", rgb.at<Vec3b>(p)[1]);
printf("r=%d\n", rgb.at<Vec3b>(p)[2]);
printf("H=%d\t", hsv.at<Vec3b>(p)[0]);
printf("S=%d\t", hsv.at<Vec3b>(p)[1]);
printf("V=%d\n", hsv.at<Vec3b>(p)[2]);
circle(rgb, p, 2, Scalar(255), 3);
}
break;
}
}
3.颜色识别
💛createTrackbar会在指定窗口创建滑动条。 //这个程序会创建一个滑动条来改变H的值,从而识别颜色。
(zx??)函数原型:
void inRange(InputArray src, InputArray lowerb, InputArray upperb, OutputArray dst)
dst(I) = lowerb(I)0 ≤ src(I)0 < upperb(I)0 ∧ lowerb(I)1 ≤ src(I)1 < upperb(I)1 ∧lowerb(I)2 ≤ src(I)2 < upperb(I)2
即,每个通道的像素值都必须在规定的阈值范围内!
#include <opencv2\opencv.hpp>
#include<iostream>
using namespace cv;
int alpha_slider1, alpha_slider2;
void on_trackbar(int, void*) {}
int main()
{
VideoCapture capture(0); // 从摄像头读入视频
namedWindow("[总]");
createTrackbar("Hmin", "[总]", &alpha_slider1, 180, on_trackbar);
createTrackbar("Hmax", "[总]", &alpha_slider2, 180, on_trackbar);
Mat frame; // 定义一个Mat变量,用于存储每一帧的图像
Mat HSV;
Mat mask; // 保存inRange后的值
while (1)
{
capture >> frame; // 读取当前帧
cvtColor(frame, HSV, COLOR_BGR2HSV); // 转化为HSV
inRange(HSV, Scalar(alpha_slider1, 43, 46), Scalar(alpha_slider2, 255, 255), mask);
imshow("保存", mask);
imshow("[总]", frame);
waitKey(30); // 延时30ms
}
return 0;
}
4.形态学滤波
💚eg.GaussianBlur(frame, frame, Size(9, 9), 0); //高斯滤波
getStructuringElement函数会返回指定形状和尺寸的结构元素。
💛参数1表示内核的形状;可选择: ①MORPH_RECT(矩形) ②MORPH_CORSS(交叉形) ③MORPH_ELLIPSE(椭圆形)
💛参数2表示内核的尺寸及描点的位置
💛morphologyEx(形态学滤波) 表示形态学运算的类型 MORPH_OPEN – 开运算(Opening operation)? MORPH_CLOSE – 闭运算(Closing operation)? MORPH_GRADIENT - 形态学梯度(Morphological gradient)? MORPH_TOPHAT - 顶帽(Top hat)? MORPH_BLACKHAT - 黑帽(Black hat)?
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
capture >> frame; //读取当前帧
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15)); //自定义卷积核
morphologyEx(frame, frame, MORPH_OPEN, element);
5.轮廓识别(边缘检测)
?Canny函数原型:
void cvCanny(
const CvArr * image,// 第一个参数表示输入图像,必须为单通道灰度图
CvArr * edges,// 第二个参数表示输出的边缘图像,为单通道黑白图
double threshold1,
double threshold2,// 第三个参数和第四个参数表示阈值,这二个阈值中当中的小阈值用来控制边缘连接,
// 大的阈值用来控制强边缘的初始分割即如果一个像素的梯度大与上限值,则被认为 是边缘像素,如果小于下限阈值,则被抛弃。
// 如果该点的梯度在两者之间则当这个点与高于上限值的像素点连接时我们才保留,否则删除。
int aperture_size = 3// 第五个参数表示Sobel 算子大小,默认为3即表示一个3*3的矩阵。
// Sobel 算子与高斯拉普拉斯算子都是常用的边缘算子
);
边缘检测原理:检测灰度值变化较大的点,将这些点连接起来构成的线条,称之为边缘
#include<opencv2\opencv.hpp>
#include<iostream>
using namespace cv;
int alpha_slider1, alpha_slider2;
void on_trackbar(int, void*) {}
int main()
{
namedWindow("[总]");
createTrackbar("Cmin", "[总]", &alpha_slider1, 255, on_trackbar);
createTrackbar("Cmax", "[总]", &alpha_slider2, 255, on_trackbar);
VideoCapture capture(0); // 从摄像头读入视频
while (1)
{
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
capture >> frame; //读取当前帧
imshow("原图", frame);
cvtColor(frame, frame, COLOR_BGR2GRAY); //转化为灰度图
Canny(frame, frame, alpha_slider1, alpha_slider2);
imshow("[总]", frame);
waitKey(30); //延时30ms
}
return 0;
}
具体代码: 轮廓的查找——cv::findContours() 轮廓的绘制——cv::drawContours()。
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int alpha_slider1 = 50, alpha_slider2 = 50;
void on_trackbar(int, void*) {}
int main()
{
namedWindow("[总]");
createTrackbar("Cmin", "[总]", &alpha_slider1, 255, on_trackbar);
createTrackbar("Cmax", "[总]", &alpha_slider2, 255, on_trackbar);
VideoCapture capture(0); //【1】从摄像头读入视频
while (1)
{
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
Mat dst;
capture >> frame; //读取当前帧
cvtColor(frame, frame, COLOR_RGB2GRAY); //转化为灰度图
Canny(frame, frame, alpha_slider1, alpha_slider2);
vector< vector< Point> > contours;
cv::findContours(
frame,
contours,
cv::noArray(),
cv::RETR_LIST,
/*cv::RETR_EXTERNAL:表示只提取最外面的轮廓;
cv::RETR_LIST:表示提取所有轮廓并将其放入列表;
cv::RETR_CCOMP:表示提取所有轮廓并将组织成一个两层结构,其中顶层轮廓是外部轮廓,第二层轮廓是“洞”的轮廓;
cv::RETR_TREE:表示提取所有轮廓并组织成轮廓嵌套的完整层级结构。*/
cv::CHAIN_APPROX_TC89_L1
/*cv::CHAIN_APPROX_NONE:将轮廓中的所有点的编码转换成点;
cv::CHAIN_APPROX_SIMPLE:压缩水平、垂直和对角直线段,仅保留它们的端点;
cv::CHAIN_APPROX_TC89_L1 or cv::CHAIN_APPROX_TC89_KCOS:应用Teh-Chin链近似算法中的一种风格*/
);
dst = frame.clone();
dst = cv::Scalar::all(0); //创建全黑图像
cv::drawContours(dst, contours, -1, cv::Scalar::all(255)); //绘制白色轮廓
imshow("A", frame); //原canny滤波图像
imshow("B", dst); //对frame进行轮廓查找并重新绘制的图像
waitKey(30); //延时30ms
}
return 0;
}
扩展:
轮廓查找的函数——cv::findContours()——》是从二值图像中来计算轮廓的 可以使用cv::Canny()函数处理的图像——》这样的图像含有像素边界 也可以使用cv::threshold()或者cv::adaptiveThreshold()处理后的图像——》边缘隐含在正负区域的交界处
void cv::findContours(
cv::InputOutputArray image, // 输入的8位单通道“二值”图像
cv::OutputArrayOfArrays contours, // 包含points的vectors的vector
int mode, // 轮廓检索模式
int method, // 近似方法
cv::Point offset = cv::Point() // (可选) 所有点的偏移
);
6.分析信息并定位
在上一步的程序中定义了vector< vector< Point> > contours;,其中保存了图像的轮廓信息,vector<Point>表示一系列的角点,函数findContours会将每个轮廓的点按顺序压入。使用arcLength(,true);可以得到一个轮廓周长。
vector<Point>的存储及使用介绍:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main()
{
vector<Point> point_1; //定义二维点向量
point_1.push_back(Point(0, 0));
point_1.push_back(Point(1, 0));
point_1.push_back(Point(1, 1));
point_1.push_back(Point(0, 1)); //压入值
vector< vector < Point > > point; //定义二维向量,元素为点
point.push_back(point_1); //压入二维点向量
//计算轮廓的矩
double dstLength = arcLength(point[0], true); //计算刚压入轮廓的周长
cout << "原始轮廓的长度为:" << dstLength << endl;
getchar();
return 0;
}
具体代码:
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int alpha_slider1 = 30, alpha_slider2 = 100;
void on_trackbar(int, void*) {}
int main()
{
namedWindow("[总]");
createTrackbar("Cmin", "[总]", &alpha_slider1, 500, on_trackbar);
createTrackbar("Cmax", "[总]", &alpha_slider2, 500, on_trackbar);
VideoCapture capture(0); // 从摄像头读入视频
while (1)
{
Mat frame; //定义一个Mat变量,用于存储每一帧的图像
capture >> frame; //读取当前帧
Mat dst = frame.clone();
cvtColor(frame, frame, COLOR_RGB2GRAY); //转化为灰度图
GaussianBlur(frame, frame, Size(9, 9), 0); //高斯滤波
Canny(frame, frame, alpha_slider1, alpha_slider2); //边缘检测
vector< vector< Point> > contours;
cv::findContours(
frame,
contours,
cv::noArray(),
cv::RETR_LIST,
cv::CHAIN_APPROX_SIMPLE
); //获取轮廓
for (int i = 0; i < contours.size(); i++)
{
RotatedRect rect = minAreaRect(contours[i]); //返回最小矩形
Size s = rect.size;
Point2f P[4];
rect.points(P);
if ((contourArea(contours[i], true) / s.area()) - 0.7853 < 0.1 && (contourArea(contours[i], true) / s.area()) - 0.7853 > -0.1) //(圆)轮廓面积除以最小矩形面积约为
{
circle(dst, rect.center, 3, Scalar(255, 0, 0));
circle(dst, rect.center, 5, Scalar(255, 0, 0));
}
}
imshow("A", frame);
imshow("B", dst);
waitKey(30); //延时30ms
}
return 0;
}
|