一.图像识别API
1.图像识别作用
它常用于视觉任务、目标检测、图像分割等等。在 OPENCV 中通常使用 Canny
函数、findContours 函数、drawContours 函数结合在一起去做轮廓的形检测。
2.常用的API
- findContours 函数:用于寻找图片的轮廓,并把所有的数据存储在向量里面
CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,OutputArray hierarchy, int mode,int method, Point offset = Point());
第一个参数:image 输入的二值图像,这个图像通常是用在边缘检测、阈值处理等等
第二个参数:contours 输出的轮廓集合,每一个轮廓都是由点组成,通常用vector<vector<Point>>来表示
第三个参数:hierarchy 输出的轮廓层次结构,这通常表示轮廓之间的父子关系,这个是可选参数,通常用 vector<Vec4i> hierarchy来表示。比方说,第 i 个轮廓,hierarchy[i][0]、hierarchy[i][1]、hierarchy[i][2]、hierarchy[i][3], 依次为第 i 个轮廓[Next、Pervious、First_Child,Parent], 这表示的是相同等级下的下一轮廓、前一轮廓,第一个子轮廓和父轮廓的索引号。若轮廓 i 没有下一个,前一个或者父级轮廓,则层次相应的元素是负数。如下图:
第四个参数:mode 轮廓检索模式,通常有以下选项,分别是:RETR_EXTERNAL(只检测最外层轮廓)、RETR_LIST(检测所有轮廓,包括内围、外围轮廓,但都是同等级的)、RETR_CCOMP(检测所有轮廓,但是所有的轮廓只建立两个等级关系,一个外层,一个内层(不管内层有多少个,都属于内层))、RETR_TREE(检测所有轮廓并建立轮廓树,这个模式下外围轮廓包含内层轮廓,内层还可以继续嵌套)。
第 五 个 参 数 : method 轮 廓 近 似 方 法 , 通 常 有 以 下 的 几 种 方 法 , 分 别 是 CHAIN_APPROX_NONE( 存 储 所 有 顶 点 ) 、CHAIN_APPROX_SIMPLE(仅存储轮廓的拐点信息,并把所有轮廓拐点处的点保存到向量里面)、CHAIN_APPROX_TC89_L1(使用TEH_CHAIN 近似算法)。
第六个参数:offset 轮廓点偏移量,默认(0,0)
- drawContours 函数:用于绘制图像的轮廓,配合findContours 函数使用,用findContours 函数获取轮廓,用drawContours 函数画轮廓。
CV_EXPORTS_W void drawContours( InputOutputArray image, InputArrayOfArrays contours,int contourIdx, const Scalar& color,int thickness = 1, int lineType = LINE_8,InputArray hierarchy = noArray(),int maxLevel = INT_MAX, Point offset = Point() );
第一个参数:image 输出图像,即绘制轮廓后的图像
第二个参数:contours 轮廓的集合,它是由一系列的点组成
第三个参数:contourIdx、轮廓索引数组,指定要绘制哪些轮廓
第四个参数:contourColor,轮廓颜色,使用 Scalar 类型表示
第五个参数:thickness,轮廓线宽,默认 1
第六个参数:lineType ,轮廓线类型,默认为 LINE_8
第七个参数:hierarchy ,轮廓层次结构,用于绘制轮廓的父子关系。默认为 noArray()
第八个参数:maxLevel ,表示绘制轮廓的最大层级数量。若 maxLevel 为 0,则只绘制指定的轮廓;若 maxLevel 为 1,则绘制轮廓极其所有嵌套轮廓;若 maxLevel 为 2,则绘制轮廓、所有嵌套轮廓、所有嵌套到嵌套的轮廓。
第九个参数:轮廓点的偏移量,默认为(0,0)
- Canny 函数:用于边缘检测计算。效果如下:
工作原理:就是一系类数学处理方法。高斯滤波(将图像转换为灰度图像,高斯滤波作用是平滑图像,让 Canny 检测的时候准确率更高)、梯度强度和方向的计算(计算图像中每个像素的强度和方向、强度表示像素点的边缘强度、梯度表示的是边缘方向这里的梯度需要用到 sobel 因子)、非极大抑制(经过 NMS 操作后,会除去一些不是边缘的像素点)、双阈值处理(给出一个阈值,若超过这个阈值的边缘则会被保留)、边缘链接(经过双阈值处理过后,强边缘则会留下来,弱边缘则会被抑制,并会把所有的强边缘全部连接起来)。
工作原理白话文:把3通道图片(也就是彩色图片)转化成双通道图片(也就是灰色图像),然后用高斯滤波去除细小杂物让它更好识别轮廓;梯度强度和方向的计算,就是不同程度扫描轮廓,扫描很多轮廓;非极大抑制,去除边边角角不是边缘的像素点;双阈值处理,就是在之前扫描不同程度轮廓上设置两个阈值(Max,Min),大于Max值被认为是轮廓保留下来,小于Min被认为不是轮廓去除掉,在两值中间的是边缘保留,不是去掉;边缘链接,就是一个完整轮廓。
CV_EXPORTS_W void Canny( InputArray image, OutputArray edges,double threshold1, double threshold2,int apertureSize = 3, bool L2gradient = false );
第一个参数:image 输入的图像,这个图像一定要单通道灰度图
第二个参数:edges 输出的边缘图像,这个图像也必须是单通道黑白图
第三个参数:threshold1 第一个滞后性阈值,低阈值,小于低阈值则认为是弱边缘,就是需要抛弃的边缘。
第四个参数:threshold2 第二个滞后性阈值,高阈值,大于高阈值被认为强边缘,需要保留的边缘
第五个参数:apertureSize 指的是 Sobel 算子大小,这个值默认为 3,代表的是 3*3 的矩阵大小。
第六个参数:L2gradient 是计算图像梯度幅度值的情况,这个值默认为 False;若选择 True,则使用更精确的 L2 范数进行计算
二. 代码实战扫描物体轮廓
1.扫描轮廓步骤
分别是 imread 读取图片(这个图片默认是 3 通道)、利用 cvtColor 把 8VU3 的三通道图片转换成灰度图(8VU1)、调用 Canny 对灰度图像进行边缘检测、调用 findContours 去查找轮廓、循环轮廓数量然后调用 drawContours 进行画框操作。
2.代码
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
using namespace cv;
using namespace std;
int main()
{
//读取图片
Mat img = imread("zjl.jpg");
//彩色图片转化灰度图片
Mat imgGray;
cvtColor(img, imgGray, COLOR_RGB2GRAY);
//灰度图片边缘检测轮廓
Mat imgCanny;
Canny(imgGray, imgCanny, 25, 75); //Canny对图像进行边缘检测,弱阈值25,强阈值75
//查询轮廓
vector<vector<Point>> contours; //定义轮廓
vector<Vec4i> hierarchy; //定义层次结构
findContours(imgCanny, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE); //查询轮廓,RETR_EXTERNAL表示只检测外部轮廓,CHAIN_APPROX_NONE表示存储所有的轮廓点
//绘制轮廓
Mat drawing = Mat::zeros(imgCanny.size(), CV_8UC3);//创建一个空白图像,用于绘制轮廓
for (int i = 0; i < contours.size(); i++) //遍历所有轮廓
{
Scalar color = Scalar(255, 255, 0); //随机生成颜色
drawContours(drawing, contours, i, color, 1, 8, hierarchy); //对图像轮廓进行画框
}
//显示图像
imwrite("zjl1.jpg", drawing);
return 0;
}