先上源码运行图, 分析和详细见后面
![](https://img.haomeiwen.com/i20172887/925ade7e99493af7.png)
myTest.png
![](https://img.haomeiwen.com/i20172887/ccd02132b9fc50dd.png)
read 网络摄像头.png
![](https://img.haomeiwen.com/i20172887/38173f17e86947f3.png)
Gray image.png
![](https://img.haomeiwen.com/i20172887/f28b808ae584a1d4.png)
Canny.png
![](https://img.haomeiwen.com/i20172887/b9c756a0cfabb169.png)
imageResize(0.5 0.5).png
![](https://img.haomeiwen.com/i20172887/0322f6171cff6071.png)
imageResize(640, 480).png
![](https://img.haomeiwen.com/i20172887/93e5117a2b48ba1d.png)
[ 1706 * 1279 ] RectCrop(200, 100, 300, 300);.png
![](https://img.haomeiwen.com/i20172887/af2e1d5a82a3732f.png)
[ 1706 * 1279 ] RectCrop(500, 200, 1200, 1000).png
![](https://img.haomeiwen.com/i20172887/f995e33c9c287d4c.png)
画 Shapes 和 Text .png
![](https://img.haomeiwen.com/i20172887/ff38015dc25ce643.png)
图片中 4个点 => 确定1个 矩形 area.png
![](https://img.haomeiwen.com/i20172887/8b9d413aa956b709.png)
被标注的 srcImag.png
![](https://img.haomeiwen.com/i20172887/c9fa1a75b2690691.png)
wrapImag.png
![](https://img.haomeiwen.com/i20172887/477210720162764b.png)
想据 colorDetectRange 滤出 BGR 图 / 图中 某些部分的 framework.png
![](https://img.haomeiwen.com/i20172887/e0f7cd0ed3b91f4d.png)
想 据 colorDetectRange 检测物体 .png
![](https://img.haomeiwen.com/i20172887/24e5183c633fc2d5.png)
image.png
![](https://img.haomeiwen.com/i20172887/b50ef40abeb47dd1.png)
Image Dilate.png
![](https://img.haomeiwen.com/i20172887/90514865659c64bd.png)
Image + Contours + boundRect + contoursTypeText.png
![](https://img.haomeiwen.com/i20172887/524e4d53163821c9.png)
FaceDetection: ImageWithDetectPositionRect.png
![](https://img.haomeiwen.com/i20172887/41558de05c80a5bb.png)
Project1-虚拟绘画: 从摄像头 read 的 每张 Image -> ColorDetection: 滤出 ColorDetectRange (green/purple) 的 imageMask -> Shapes Detection:每个 Shape -> contours(轮廓) -> 边界矩形 -> 特定位置 绘制 指定 Color(green/purple).png
![](https://img.haomeiwen.com/i20172887/6a5ac627fdb96678.png)
Project3: 牌照 检测器.png
![](https://img.haomeiwen.com/i20172887/2a23ab26805943dd.png)
保存的 Crop(剪裁) 牌照1.png
![](https://img.haomeiwen.com/i20172887/e1836d28248dadd5.png)
保存的 Crop(剪裁) 牌照2.png
![](https://img.haomeiwen.com/i20172887/3fd618b8b49e3434.png)
保存的 Crop(剪裁) 牌照3.png
// source code
https://github.com/murtazahassan/Learn-OpenCV-cpp-in-4-Hours.git
0 Windows下 环境配置
step1 opencv 官网下载 opencv
.exe
step2 增加 bin folder 到 环境变量 path
D:\OpenCv\ExtractDir\opencv\build\x64\vc15\bin
vs2019 -> 用 opencv 解压包 opencv\build\x64\vc15
step3 建 vs 工程 C++ console
平台目标 设为 x64 // debug 右侧
空项目
step4 在 工程-属性-配置 下 add 目录
VC++ 目录
1 增加 Build 目录
...\opencv\build\include
2 增加 库目录
...\opencv\build\x64\vc15\lib
链接器 -> input -> 附加依赖项
3 增加 linker input: opencv_world450d.lib // ...\opencv\build\x64\vc15\lib 目录下
|
d for debug
without d for release
项目
属性
VC++ 目录
链接器
step5 运行 test code
先 add lena image 到 源文件
本例中 the image 用 "Resourses/test.png"
源码 中的 Resources 文件夹 copy 1份放 test.cpp 目录下
// test.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
///////////////// Images //////////////////////
void main() {
string path = "Resources/myTest.png";
Mat img = imread(path);
imshow("Image", img);
waitKey(0);
}
运行后, 图片可以用鼠标拖动
chapter1 Read Images / Videos / Webcams (网络摄像头)
1 Images
1] 构造 "Image" 对象
imread(imagePathString)
用 Mat 类型 ( Handle ) 管理
2] imshow "Image" 对象
3] waitKey(delay = 0); // delay = 0 => image 一直不 close
2 Video
1] 构造 VideoCapture 类型 对象
Ctor: para 为 vedioPathString
2] 循环 从其中
2-1] read "Image" // one by one
VideoCaptureObj.read(Mat型对象)
|
|
Mat(Handle) 型对象 作 arg
接收 "Image" 管理权
2-2] imshow "Image"
2-3] waitKey(delay = 20); // dealy 越大, 视频显示越慢
3 Webcam
构造 VideoCapture 类型 对象
Ctor: para 为 cameraId
|
|/
1个摄像头, 只用 0
n 摄像头, 0 ~ n-1
chapter2 基本函数
imread
cvtColor
GaussianBlur
Canny
...
chapter3 Resize(伸/缩) 和 Crop(剪裁) Images
resize(imgSrc, imgDst, Size(640, 480) );
|
|/
精确
resize(imgSrc, imgDst, Size(),0.5,0.5);
| | |
|_ _ _ _|_ _| 比例
Rect crop(200, 100, 300, 300);
Rect crop(x, y, width, height);
| | |
| | |
剪裁起点 宽 高
|
|/
左上角 x = 0, y = 0
[ 1706 * 1279 ]
Rect crop(500, 200, 1200, 1000);
imgCrop = img(crop);
chapter4 画 Shapes 和 Text
[0] Position + Scalar(BGR)
[2] shapes
circle
rectangle
line
[2] Text
putText
chapter5 Wrap(包装) Perspective(视角)
思想
[1] 图片中 4个点 => 确定1个 矩形 area
Point2f src[4] = {Point1(左上), Point2(右上), Point3(左下), Point4(右下)}
[2] get `源图 到 dst 图` 的 `矩形 area` 间 `转换 矩阵`
getPerspectiveTransform
Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
Point2f dst[4] = { {0.0f,0.0f},{weight,0.0f},{0.0f,height},{weight, height} };
transformMatrix = getPerspectiveTransform(src, dst);
[3] warpPerspective
warpPerspective(srcImag, warpImag, transformMatrix, Point(weight, height));
chapter6 Color Detection (颜色识别)
前提
目标 检测物体 的 `color 范围` 在 源图中 是 `独特的` (与其他物体 不同 )
解决的问题
[1] 源图 + 合理 colorDetectRange => `过滤出 BGR 图 / 图中 某些部分的 framework`
|
|/
想据 colorDetectRange 滤出 BGR 图 / 图中 某些部分的 framework
[2] 源图 + `目标 检测物体` => 如何设 colorDetectRange
|
|/
想 据 colorDetectRange 检测物体
1 通常, 可以准确定义 你想要的 color, 如 红 / 黄 等
但 实际中, 由于 lighting、shadow 等因素,
Color 不是一种精确的 Color
=> 用 `range(范围)` 去 界定
2 逆向思维
知道 图 => 想检测出其中 某个物体(如 汽车框架 )
如何设 detect 门限: lower(下限) & upper(上限) ?
思路
上/下 限 * 每个维度用1个 Trackbar
排列组合 + `手动拖动 Trackbar`
=> 可 `快速` 尝试出 合理 门限 range
3 实现
[1] 源图 -> HSV 图: 带像素(BGR) 的
cvtColor(img, imgHSV, COLOR_BGR2HSV)
[2] createTrackbar("Hue Min", "Trackbars", &hmin, 179); // 等
[3] while
inRange(imgHSV, lower, upper, mask);
| | | |
|/ |/ |/ |
HSVImage detect 的 Color 范围 colorDetectOutputImage
imshow("Image Mask", mask);
chapter7 Shapes Detection (Shape 识别)
1 目标
检测 different Shapes 或 contours (轮廓) of Images
2 思想
[1] 预处理
以得到 更好的 edge 特性
[2] 据 edge (边缘) 知 Shape
|
| 即
|/
contours (轮廓)
每种 Shape 有其 独特 contours
若 发现 edges (边缘) of Images, 则可发现 `哪种 Shape 是 那种 edges/contours`
3 4种 图
img
|
|/
1] cvtColor
|
|/
imgGray
|
|/
2] GaussianBlur
|
|/
imgBlur
|
|/
3] Canny
|
|/
imgCanny
|
|
|/
4] dilate + getStructuringElement
|
|/
imgDilate
三角形
imgCanny : 轮廓线 small gap
imgDilate: 轮廓线 是 solid(实) 线
=> 对 检测来说, imgDilate 的 边缘特性 更好
4 实现
[1] 预处理
得 imgDilate
[2] geontours(Mat imgDilate, Mat img)
1] 发现 contoursVec
findContours(imgDilate, contoursVec, ...)
循环
2] DP 算法 计算出 每个 `contours(轮廓) 的 近似多边形`
approxPolyDP(contoursVec[i], approxPolygonVec[i], ...)
|
|/
size() 即 `边数`
3] boundRectVec[i] = boundingRect(approxPolygonVec[i]);
据 近似多边形 求 其相应
点集 最外面的 `边界矩形` boundRectVec[i]
| |
|/ |/
up-right .width / .height
|
|/
可用于其他 计算: 如 width / .height 在 [0.95, 1.05] => 正方形
4] 当前 轮廓 `近似多边形 / 边界矩形 / 轮廓类型`
1> `匹配` 源图 中 `哪个 shape
|
|/
搜索
并 加到 源图上
drawContours(img, approxPolygonVec)
|
| 引用传递
|/
approxPolygonVec 中 有效元素
approxPolygon 的 轮廓 加到 img 中(n 个 shapes) `相应` shape 上
rectangle(img, boundRectVec[i].tl(), boundRectVec[i].br(), ... );
|
|/
加 边界矩形
putText(img, objectType, { boundRectVec[i].x, boundRectVec[i].y - 5 },...);
|
|/
加 contoursTypeText
8 Face Detection (人脸识别)
1 目标
detect faces: 在 Images 中
2 思想
人脸: 人脸特征族
|\
| 如
|
[1] 族 分类 (类型) 对象
|
|/
CascadeClassifier 对象
[2] .load("族检测器")
|
| 如: 第三方库
|/
额骨人脸检测器: frontal face detector
|
| 视为
|
族特征数据库
|\ |
| | xml 文件
| |/
| ...haarcascade_frontalface_default.xml
| 依赖于
|
族检测算法
|\
|
[3] .detectMultiScale(img, targetDetectPositionAreaVec, 1.1, 10);
| |
| |vector<Rect>
|/ |/
检测源图 目标 检测 PositionArea ( 源图中 )
[4] rectangle(img, detectedFacesAreaVec[i].tl(), detectedFacesAreaVec[i].br(), Scalar(255, 0, 255), 3);
目标 检测 PositionArea 加到 源图中
|
| 如
|/
矩形
应用
Project1 virtual paint (虚拟绘画)
功能
[1] 开 摄像头
VideoCapture vc(0);
while (true)
[2] 从 摄像头中 每 read 1 张 Image
vc.read(img);
[3] ColorDetection
滤出 设定(固定) ColorDetectRange 的 imageMask
[4] Shapes Detection: 针对 滤出的 imageMask
每个 Shape
contours(轮廓)
边界矩形
Point(x + width/2, y) 返回
放 pointVec
|
| 出 shape 循环
|/
作 center 在 源图上 绘画 Circle
Project3 License Plate Detector (牌照 检测器)
思路
[0] 人脸 识别
|
|
|/
人脸族 特征
人脸族数据库 / 额骨人脸检测器 haarcascade_frontalface_default.xml
|
|
|/
车牌族 特征
车牌族数据库 / 俄罗斯 车牌系统 检测器 haarcascade_russian_plate_number.xml
[1] 从 网络摄像头 read 每张 Image
|
| 含
|/
[2] `若干`
目标检测 PositionArea (Rect: 矩形)
|
| 用于
|/
1] Crop(剪裁) 该 Image
|
|
|/
CropPartImage 写/imwrite 到 指定目录指定 .png 文件
imwrite("Resources/Plates/" + to_string(i) + ".png", imgCrop);
2] 加到 源 Image
= = = = = = 详细
chapter1 Read Images / Videos / Webcams (网络摄像头)
1 Images
1] 构造 "Image" 对象
imread(imagePathString)
用 Mat 类型 ( Handle ) 管理
2] imshow "Image" 对象
3] waitKey(delay = 0); // delay = 0 => image 一直不 close
2 Video
1] 构造 VideoCapture 类型 对象
Ctor: para 为 vedioPathString
2] 循环 从其中
2-1] read "Image" // one by one
VideoCaptureObj.read(Mat型对象)
|
|
Mat(Handle) 型对象 作 arg
接收 "Image" 管理权
2-2] imshow "Image"
2-3] waitKey(delay = 20); // dealy 越大, 视频显示越慢
3 Webcam
构造 VideoCapture 类型 对象
Ctor: para 为 cameraId
|
|/
1个摄像头, 只用 0
n 摄像头, 0 ~ n-1
// chapter1.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv; // cv.imread() / imshow / ...
using namespace std;
///////////////// 1 Images //////////////////////
/*
void main() {
string path = "Resources/test.png";
Mat img = imread(path);
imshow("Image", img);
waitKey(0); // delay = 0
}
*/
/////////////// 2 Video //////////////////////
/*
void main() {
string path = "Resources/test_video.mp4";
VideoCapture vc(path);
Mat img;
while (true) {
vc.read(img);
imshow("Image", img);
waitKey(20);
}
}
*/
///////////////// 3 Webcam //////////////////////
void main() {
VideoCapture vc(0);
Mat img;
while (true) {
vc.read(img);
imshow("Image", img);
waitKey(1);
}
}
chapter2 基本函数
imread
cvtColor
GaussianBlur
Canny
...
// Chapter2.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/////////////// Basic Functions //////////////////////
void main() {
string path = "Resources/myTest.png";
Mat img = imread(path);
Mat imgGray, imgBlur, imgCanny, imgDil, imgErode;
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(7, 7), 5, 0);
Canny(imgBlur, imgCanny, 25,75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDil, kernel);
erode(imgDil, imgErode, kernel);
imshow("Image", img);
imshow("Image Gray", imgGray);
imshow("Image Blur", imgBlur);
imshow("Image Canny", imgCanny);
imshow("Image Dilation", imgDil);
imshow("Image Erode", imgErode);
waitKey(0);
}
chapter3 Resize(伸/缩) 和 Crop(剪裁) Images
resize(imgSrc, imgDst, Size(640, 480) );
|
|/
精确
resize(imgSrc, imgDst, Size(),0.5,0.5);
| | |
|_ _ _ _|_ _| 比例
Rect crop(200, 100, 300, 300);
Rect crop(x, y, width, height);
| | |
| | |
剪裁起点 宽 高
|
|/
左上角 x = 0, y = 0
[ 1706 * 1279 ]
Rect crop(500, 200, 1200, 1000);
imgCrop = img(crop);
// chapter3.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/////////////// Resize and Crop //////////////////////
void main() {
string path = "Resources/myTest.png";
Mat img = imread(path);
Mat imgResize, imgCrop;
// [ 1706 * 1279 ]
cout << img.size() << endl;
resize(img, imgResize, Size(640, 480));
// resize(img, imgResize, Size(),0.5,0.5);
// Rect crop(200, 100, 300, 300);
Rect crop(500, 200, 1200, 1000);
imgCrop = img(crop);
imshow("Image", img);
imshow("Image Resize", imgResize);
imshow("Image Crop", imgCrop);
waitKey(0);
}
chapter4 画 Shapes 和 Text
[0] Position + Scalar(BGR)
[2] shapes
circle
rectangle
line
[2] Text
putText
// chapter4.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
////////////// Draw Shapes and Text //////////////////////
void main() {
// Blank(空) Image
Mat img(512, 512, CV_8UC3, Scalar(255, 255, 255)); //
circle(img, Point(256, 256), 155, Scalar(0, 69, 255),FILLED);
rectangle(img, Point(130, 226), Point(382, 286), Scalar(255, 255, 255), FILLED);
line(img, Point(130, 296), Point(382, 296), Scalar(255, 255, 255), 2);
putText(img, "Hello Opencv!", Point(180, 262), FONT_HERSHEY_DUPLEX, 0.75, Scalar(0, 69, 255),2);
imshow("Image", img);
waitKey(0);
}
chapter5 Wrap(包装) Perspective(视角)
思想
[1] 图片中 4个点 => 确定1个 矩形 area
Point2f src[4] = {Point1(左上), Point2(右上), Point3(左下), Point4(右下)}
[2] get `源图 到 dst 图` 的 `矩形 area` 间 `转换 矩阵`
getPerspectiveTransform
Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
Point2f dst[4] = { {0.0f,0.0f},{weight,0.0f},{0.0f,height},{weight, height} };
transformMatrix = getPerspectiveTransform(src, dst);
[3] warpPerspective
warpPerspective(srcImag, warpImag, transformMatrix, Point(weight, height));
// chapter5.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/////////////// Warp Images //////////////////////
void main() {
string path = "Resources/cards.jpg";
Mat img = imread(path);
Mat matrix, imgWarp;
float weight = 250, height = 350;
Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
Point2f dst[4] = { {0.0f,0.0f},{weight,0.0f},{0.0f,height},{weight, height} };
matrix = getPerspectiveTransform(src, dst);
warpPerspective(img, imgWarp, matrix, Point(weight, height));
// 标注 源图中 4个顶点: 以各顶点为圆心画 r = 10 的 圆
for (int i = 0; i < 4; i++)
circle(img, src[i], 10, Scalar(0, 0, 255), FILLED);
imshow("Image", img);
imshow("Image Warp", imgWarp);
waitKey(0);
}
chapter6 Color Detection (颜色识别)
前提
目标 检测物体 的 `color 范围` 在 源图中 是 `独特的` (与其他物体 不同 )
解决的问题
[1] 源图 + 合理 colorDetectRange => `过滤出 BGR 图 / 图中 某些部分的 framework`
|
|/
想据 colorDetectRange 滤出 BGR 图 / 图中 某些部分的 framework
[2] 源图 + `目标 检测物体` => 如何设 colorDetectRange
|
|/
想 据 colorDetectRange 检测物体
1 通常, 可以准确定义 你想要的 color, 如 红 / 黄 等
但 实际中, 由于 lighting、shadow 等因素,
Color 不是一种精确的 Color
=> 用 `range(范围)` 去 界定
2 逆向思维
知道 图 => 想检测出其中 某个物体(如 汽车框架 )
如何设 detect 门限: lower(下限) & upper(上限) ?
思路
上/下 限 * 每个维度用1个 Trackbar
排列组合 + `手动拖动 Trackbar`
=> 可 `快速` 尝试出 合理 门限 range
3 实现
[1] 源图 -> HSV 图: 带像素(BGR) 的
cvtColor(img, imgHSV, COLOR_BGR2HSV)
[2] createTrackbar("Hue Min", "Trackbars", &hmin, 179); // 等
[3] while
inRange(imgHSV, lower, upper, mask);
| | | |
|/ |/ |/ |
HSVImage detect 的 Color 范围 colorDetectOutputImage
imshow("Image Mask", mask);
// chapter6.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/////////////// Color Detection //////////////////////
void main() {
string path = "Resources/lambo.png"; // [1] 想据 colorDetectRange 滤出 BGR 图 / 图中 某些部分的 framework
// string path = "Resources/shapes.png"; // [2] 想 据 colorDetectRange 检测物体
Mat img = imread(path);
Mat imgHSV, mask;
int hmin = 0, smin = 110, vmin = 153;
int hmax = 19, smax = 240, vmax = 255;
cvtColor(img, imgHSV, COLOR_BGR2HSV);
namedWindow("Trackbars", (640, 200));
createTrackbar("Hue Min", "Trackbars", &hmin, 179);
createTrackbar("Hue Max", "Trackbars", &hmax, 179);
createTrackbar("Sat Min", "Trackbars", &smin, 255);
createTrackbar("Sat Max", "Trackbars", &smax, 255);
createTrackbar("Val Min", "Trackbars", &vmin, 255);
createTrackbar("Val Max", "Trackbars", &vmax, 255);
while (true) {
Scalar lower(hmin, smin, vmin);
Scalar upper(hmax, smax, vmax);
inRange(imgHSV, lower, upper, mask);
imshow("Image", img);
imshow("Image HSV", imgHSV);
imshow("Image Mask", mask);
waitKey(1);
}
}
chapter7 Shapes Detection (Shape 识别)
1 目标
检测 different Shapes 或 contours (轮廓) of Images
2 思想
[1] 预处理
以得到 更好的 edge 特性
[2] 据 edge (边缘) 知 Shape
|
| 即
|/
contours (轮廓)
每种 Shape 有其 独特 contours
若 发现 edges (边缘) of Images, 则可发现 `哪种 Shape 是 那种 edges/contours`
3 4种 图
img
|
|/
1] cvtColor
|
|/
imgGray
|
|/
2] GaussianBlur
|
|/
imgBlur
|
|/
3] Canny
|
|/
imgCanny
|
|
|/
4] dilate + getStructuringElement
|
|/
imgDilate
三角形
imgCanny : 轮廓线 small gap
imgDilate: 轮廓线 是 solid(实) 线
=> 对 检测来说, imgDilate 的 边缘特性 更好
4 实现
[1] 预处理
得 imgDilate
[2] geontours(Mat imgDilate, Mat img)
1] 发现 contoursVec
findContours(imgDilate, contoursVec, ...)
循环
2] DP 算法 计算出 每个 `contours(轮廓) 的 近似多边形`
approxPolyDP(contoursVec[i], approxPolygonVec[i], ...)
|
|/
size() 即 `边数`
3] boundRectVec[i] = boundingRect(approxPolygonVec[i]);
据 近似多边形 求 其相应
点集 最外面的 `边界矩形` boundRectVec[i]
| |
|/ |/
up-right .width / .height
|
|/
可用于其他 计算: 如 width / .height 在 [0.95, 1.05] => 正方形
4] 当前 轮廓 `近似多边形 / 边界矩形 / 轮廓类型`
1> `匹配` 源图 中 `哪个 shape
|
|/
搜索
并 加到 源图上
drawContours(img, approxPolygonVec)
|
| 引用传递
|/
approxPolygonVec 中 有效元素
approxPolygon 的 轮廓 加到 img 中(n 个 shapes) `相应` shape 上
rectangle(img, boundRectVec[i].tl(), boundRectVec[i].br(), ... );
|
|/
加 边界矩形
putText(img, objectType, { boundRectVec[i].x, boundRectVec[i].y - 5 },...);
|
|/
加 contoursTypeText
// chapter7.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/////////////// Color Detection //////////////////////
void getContours(Mat imgDilate, Mat img) {
vector< vector<Point> > contoursVec; // 图中有 n 个 轮廓
vector<Vec4i> hierarchy; // Vec4i: 4 个 integral values
findContours(imgDilate, contoursVec, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// drawContours(img, contoursVec, -1, Scalar(255, 0, 255), 2);
vector<vector<Point> > approxPolygonVec(contoursVec.size());
vector<Rect> boundRectVec(contoursVec.size());
for (int i = 0; i < contoursVec.size(); i++)
{
int area = contourArea(contoursVec[i]);
cout << area << endl;
if (area > 1000)
{
float peri = arcLength(contoursVec[i], true);
approxPolyDP(contoursVec[i], approxPolygonVec[i], 0.02 * peri, true);
cout << approxPolygonVec[i].size() << endl;
boundRectVec[i] = boundingRect(approxPolygonVec[i]);
int edgesNum = (int)approxPolygonVec[i].size();
string contoursType;
if (edgesNum == 3)
{ contoursType = "Tri"; }
else if (edgesNum == 4)
{
float aspRatio = (float)boundRectVec[i].width / (float)boundRectVec[i].height;
cout << aspRatio << endl;
if (aspRatio> 0.95 && aspRatio< 1.05)
{ contoursType = "Square"; }
else { contoursType = "Rect";}
}
else if (edgesNum > 4)
{ contoursType = "Circle"; }
drawContours(img, approxPolygonVec, i, Scalar(255, 0, 255), 2);
rectangle(img, boundRectVec[i].tl(), boundRectVec[i].br(), Scalar(0, 255, 0), 5);
putText(img, contoursType, { boundRectVec[i].x, boundRectVec[i].y - 5 }, FONT_HERSHEY_PLAIN,1, Scalar(0, 69, 255), 2);
}
}
}
void main() {
string path = "Resources/shapes.png";
Mat img = imread(path);
Mat imgGray, imgBlur, imgCanny, imgDilate;
// [1] Preprocessing
cvtColor(img, imgGray, COLOR_BGR2GRAY);
GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
Canny(imgBlur, imgCanny, 25, 75);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
dilate(imgCanny, imgDilate, kernel);
imshow("Image", img);
// [2] 据 imgDilate getContours
getContours(imgDilate, img);
imshow("Image + Contours + boundRect + contoursTypeText", img);
imshow("Image Gray", imgGray);
imshow("Image Blur", imgBlur);
imshow("Image Canny", imgCanny);
imshow("Image Dilate", imgDilate);
waitKey(0);
}
8 Face Detection (人脸识别)
1 目标
detect faces: 在 Images 中
2 思想
人脸: 人脸特征族
|\
| 如
|
[1] 族 分类 (类型) 对象
|
|/
CascadeClassifier 对象
[2] .load("族检测器")
|
| 如: 第三方库
|/
额骨人脸检测器: frontal face detector
|
| 视为
|
族特征数据库
|\ |
| | xml 文件
| |/
| ...haarcascade_frontalface_default.xml
| 依赖于
|
族检测算法
|\
|
[3] .detectMultiScale(img, targetDetectPositionAreaVec, 1.1, 10);
| |
| |vector<Rect>
|/ |/
检测源图 目标 检测 PositionArea ( 源图中 )
[4] rectangle(img, detectedFacesAreaVec[i].tl(), detectedFacesAreaVec[i].br(), Scalar(255, 0, 255), 3);
目标 检测 PositionArea 加到 源图中
|
| 如
|/
矩形
// chapter8.cpp
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp> //
#include <iostream>
using namespace cv;
using namespace std;
/////////////// Images //////////////////////
void main() {
string path = "Resources/myTest.jpg";
Mat img = imread(path);
// [1]
CascadeClassifier faceCascade;
// [2]
faceCascade.load("Resources/haarcascade_frontalface_default.xml");
if (faceCascade.empty()) { cout << "XML file not loaded" << endl;}
vector<Rect> targetDetectPositionAreaVec;
// [3]
faceCascade.detectMultiScale(img, targetDetectPositionAreaVec, 1.1, 10);
imshow("srcImage", img);
for (int i = 0; i < targetDetectPositionAreaVec.size(); i++)
{
// [4]
rectangle(img, targetDetectPositionAreaVec[i].tl(), targetDetectPositionAreaVec[i].br(), Scalar(255, 0, 255), 3);
}
imshow("ImageWithDetectPositionRect", img);
waitKey(0);
}
应用
Project1 virtual paint (虚拟绘画)
功能
[1] 开 摄像头
VideoCapture vc(0);
while (true)
[2] 从 摄像头中 每 read 1 张 Image
vc.read(img);
[3] ColorDetection
滤出 设定(固定) ColorDetectRange 的 imageMask
[4] Shapes Detection: 针对 滤出的 imageMask
每个 Shape
contours(轮廓)
边界矩形
Point(x + width/2, y) 返回
放 pointVec
|
| 出 shape 循环
|/
作 center 在 源图上 绘画 Circle
Note: 本程序 逻辑不严谨
// project1:
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
///////////////// Project 1 - Virtual Painter //////////////////////
Mat img;
vector<vector<int> > newPoints; // to store all points
///////////////////// COLOR VALUES ////////////////////////////////
// hmin, smin, vmin hmax, smax, vmax
// 指定了 ColorDetectRange
vector<vector<int> > myColors{ {124,48,117,143,170,255}, // Purple
{68,72,156,102,126,255} };// Green
vector<Scalar> myColorValues{ {255,0,255}, // Purple
{0,255,0} }; // Green
////////////////////////////////////////////////////////////////////
Point getContours(Mat image) {
vector<vector<Point>> contoursVec;
vector<Vec4i> hierarchy;
findContours(image, contoursVec, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//drawContours(img, contoursVec, -1, Scalar(255, 0, 255), 2);
vector<vector<Point>> contoursPolygonVec(contoursVec.size());
vector<Rect> boundRectVec(contoursVec.size());
Point myPoint(0, 0); // vector<Point> 才对
for (int i = 0; i < contoursVec.size(); i++)
{
int area = contourArea(contoursVec[i]);
cout << area << endl;
string objectType;
if (area > 1000)
{
float peri = arcLength(contoursVec[i], true);
approxPolyDP(contoursVec[i], contoursPolygonVec[i], 0.02 * peri, true);
cout << contoursPolygonVec[i].size() << endl;
boundRectVec[i] = boundingRect(contoursPolygonVec[i]);
myPoint.x = boundRectVec[i].x + boundRectVec[i].width / 2;
myPoint.y = boundRectVec[i].y;
//drawContours(img, contoursPolygonVec, i, Scalar(255, 0, 255), 2);
//rectangle(img, boundRectVec[i].tl(), boundRectVec[i].br(), Scalar(0, 255, 0), 5);
}
}
return myPoint;
}
void findColor(Mat img)
{
Mat imgHSV;
cvtColor(img, imgHSV, COLOR_BGR2HSV);
for (int i = 0; i < myColors.size(); i++)
{
Scalar lower(myColors[i][0], myColors[i][1], myColors[i][2]);
Scalar upper(myColors[i][3], myColors[i][4], myColors[i][5]);
Mat mask;
inRange(imgHSV, lower, upper, mask);
imshow(to_string(i), mask);
Point myPoint = getContours(mask);
if (myPoint.x != 0 ) {
newPoints.push_back({ myPoint.x,myPoint.y,i });
}
}
}
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() {
VideoCapture vc(0);
while (true) {
vc.read(img);
findColor(img);
drawOnCanvas(newPoints, myColorValues);
imshow("Image", img);
waitKey(1);
}
}
Project2 Document Scanner
待更新
Project3 License Plate Detector (牌照 检测器)
思路
[0] 人脸 识别
|
|
|/
人脸族 特征
人脸族数据库 / 额骨人脸检测器 haarcascade_frontalface_default.xml
|
|
|/
车牌族 特征
车牌族数据库 / 俄罗斯 车牌系统 检测器 haarcascade_russian_plate_number.xml
[1] 从 网络摄像头 read 每张 Image
|
| 含
|/
[2] `若干`
目标检测 PositionArea (Rect: 矩形)
|
| 用于
|/
1] Crop(剪裁) 该 Image
|
|
|/
CropPartImage 写/imwrite 到 指定目录指定 .png 文件
imwrite("Resources/Plates/" + to_string(i) + ".png", imgCrop);
2] 加到 源 Image
// Project3
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/objdetect.hpp>
#include <iostream>
using namespace cv;
using namespace std;
/////////////// Project 3 - License Plate Detector //////////////////////
void main() {
Mat img;
VideoCapture vc(0);
CascadeClassifier plateCascade;
plateCascade.load("Resources/haarcascade_russian_plate_number.xml");
if (plateCascade.empty()) { cout << "XML file not loaded" << endl; }
vector<Rect> targetDetectPositionAreaVec;
while (true) {
// [1]
vc.read(img);
// [2]
plateCascade.detectMultiScale(img, targetDetectPositionAreaVec, 1.1, 10);
for (int i = 0; i < targetDetectPositionAreaVec.size(); i++)
{
// [3] image Crop
Mat imgCrop = img(targetDetectPositionAreaVec[i]);
//imshow(to_string(i), imgCrop);
// [4]
imwrite("Resources/Plates/" + to_string(i) + ".png", imgCrop);
// [5]
rectangle(img, targetDetectPositionAreaVec[i].tl(), targetDetectPositionAreaVec[i].br(), Scalar(255, 0, 255), 3);
}
imshow("Image", img);
waitKey(1);
}
}
网友评论