赞
踩
对 《OpenCV3编程入门》第一章的学习笔记:理解什么是计算机视觉,什么是OpenCV,以及其中的联系等等。
PS:此书为2014年出版,opencv的版本和接口也与现在有些不一致了,作此笔记主要是学习opencv基本理念与操作思路原理,感谢浅墨大神,这本书会和浅墨的思想一起历久弥新。
PS:作为学习笔记,我的思路是首先将此书读薄,然后去记忆关键知识点,形成自己学习opencv的架构
思维导图如下:
目录
图像处理是指计算机对图像进行分析,从而达到所需的效果,一般包含图像压缩、增强和复原,匹配、描述和识别3个部分。
图像处理一般指数字图像处理,数字图像是指用工业相机、摄像机、扫描仪等设备经过拍摄得到的一个大的二维数组。该数组的元素称为像素,其值称为灰度值。而数字图像处理是通过计算机对图像进行去噪、增强、复原、分割、提取特征等处理的方法和技术。
计算机视觉是指用摄像机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图形处理。
图像处理和计算机视觉的区别在于:图像处理侧重于“处理”图像——如增强、还原、去噪、分割等等;而计算机视觉重点在于使用计算机来模拟人的视觉,因此模拟才是计算机视觉领域的最终目标。
OpenCV(Open Source Computer Vision Library),是基于开源发行的跨平台计算机视觉库,它实现了图像处理和计算机视觉方面的通用算法。
OpenCV于1999年由Intel建立,如今由Willow Garage支持。它是一个基于开源发行的跨平台计算机视觉库,可以运行在Linux、Windows、Mac OS、Android、iOS等操作系统上。OpenCV由一系列C函数和C++类构成,轻量且高效,同时提供C++、Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。
OpenCV的设计目标是执行速度尽量快,主要关注实时应用。它采用优化的C/C++代码编写,能够充分利用多核处理器的优势;其主要目标是建立一个简单易用的计算机视觉框架,来帮助开发用户更快更便捷地设计更复杂的计算机视觉应用。
OpenCV覆盖了计算机视觉的多个应用领域,如工厂产品检测、医学成像、信息安全、用户界面、摄像机标定、立体视觉和机器人等。
OpenCV官网主页:Home - OpenCV
OpenCV Github主页:https://github.com/Itseez/opencv
OpenCV 开发板Wiki主页:http://code.opencv.org
OpenCV项目最早由Intel公司于1999年启动,旨在促进CPU密集型应用。
在Intel的性能库团队的帮助下,OpenCV实现了一些核心代码和算法,并发给了Intel俄罗斯的库团队,由此,OpenCV的诞生,发源于Intel,实现和优化于俄罗斯。
在开始之初,OpenCV有以下三大目标:
- 为基本的视觉应用提供开放且优化的开源代码,避免“闭门造车”
- 提供通用框架传播视觉知识,代码易读且可改写
- OpenCV库采用的协议不要求商业产品继续开发代码,促进基于视觉的商业应用发展
通过OpenCV安装路径下的include目录里面的头文件的分类存放,来一窥OpenCV的组件架构。
进入到..\opencv\build\include目录,可以看到opencv和opencv2这两个文件夹,opencv为旧版头文件,opencv2为新版头文件
在..\opencv\build\include\opencv2目录下,有个名为opecv_modules.hpp的文件,存放的是OpenCV2中与新模块构造相关的说明代码,其定义了OpenCV2中所有组件的宏
此方法应与opencv3一致,只是更新换代的区别。
模块名称 | 模块内容 |
---|---|
【calib3d】 | Calibration(校准)和3D这两次的组合缩写。主要是相机校准和三维重建相关的内容,包括基本的多视角几何算法、单个摄像头标定、物体姿态估计、立体相似性算法、3D信息的重建等。 |
【contrib】 | Contributed/Experimental Stuf的缩写。新增人脸识别、立体匹配、人工视网膜模型等技术。 |
【core】 | 核心功能模块
|
【imgproc】 | Image和Process这两个单词的组合缩写。图像处理模块。
|
【features2d】 | 2D功能框架
|
【flann】 | 高维的近似近邻快速搜索算法库
|
【gpu】 | 运用GPU加速的计算机视觉模块 |
【highgui】 | 高层GUI图形用户界面,包含媒体的输入输出、视频捕捉、图像和视频的编码解码、图形交互界面的接口等内容。 |
【legacy】 | 一些已废弃的代码库 |
【ml】 | 机器学习模块,基本上是统计模型和分类算法
|
【nonfree】 | 一些具有专利的算法模块,包含特征检测和GPU相关的内容 |
【objdetect】 | 目标检测模块,包含Cascade Classification(级联分类)和Latent SVM这两部分。 |
【ocl】 | 运用OpenCL加速的计算机视觉组件模块 |
【photo】 | 包含图像修复和图像去噪两部分 |
【stitching】 | 图像拼接模块
|
【superres】 | 超分辨率技术的相关功能模块 |
【ts】 | OpenCV测试相关代码 |
【video】 | 视频分析组件,包括运动估计、背景分离、对象跟踪等视频处理相关内容 |
【Videostab】 | 视频稳定相关组件 |
可以这么说,OpenCV就是多模块组合的SDK。
这部分可能有点过时了,就跳过了,有兴趣的读者可以读读文中相关部分。
同理,根据自身设备进行下载安装。
我使用的是ROS系统,20.04 noetic,那么使用以下命令就可以进行安装了
$ sudo apt install ros-noetic-vision-opencv libopencv-dev python3-opencv
默认安装的版本是opencv4,安装好后的文件放置在/usr/include/opencv4/opencv2中,上文提及到的opecv_modules.hpp同样放置在此文件夹中,其中涉及的模块会有所不同
- #define HAVE_OPENCV_ARUCO
- #define HAVE_OPENCV_BGSEGM
- #define HAVE_OPENCV_BIOINSPIRED
- #define HAVE_OPENCV_CALIB3D
- #define HAVE_OPENCV_CCALIB
- #define HAVE_OPENCV_CORE
- #define HAVE_OPENCV_DATASETS
- #define HAVE_OPENCV_DNN
- #define HAVE_OPENCV_DNN_OBJDETECT
- #define HAVE_OPENCV_DNN_SUPERRES
- #define HAVE_OPENCV_DPM
- #define HAVE_OPENCV_FACE
- #define HAVE_OPENCV_FEATURES2D
- #define HAVE_OPENCV_FLANN
- #define HAVE_OPENCV_FREETYPE
- #define HAVE_OPENCV_FUZZY
- #define HAVE_OPENCV_HDF
- #define HAVE_OPENCV_HFS
- #define HAVE_OPENCV_HIGHGUI
- #define HAVE_OPENCV_IMG_HASH
- #define HAVE_OPENCV_IMGCODECS
- #define HAVE_OPENCV_IMGPROC
- #define HAVE_OPENCV_LINE_DESCRIPTOR
- #define HAVE_OPENCV_ML
- #define HAVE_OPENCV_OBJDETECT
- #define HAVE_OPENCV_OPTFLOW
- #define HAVE_OPENCV_PHASE_UNWRAPPING
- #define HAVE_OPENCV_PHOTO
- #define HAVE_OPENCV_PLOT
- #define HAVE_OPENCV_QUALITY
- #define HAVE_OPENCV_REG
- #define HAVE_OPENCV_RGBD
- #define HAVE_OPENCV_SALIENCY
- #define HAVE_OPENCV_SHAPE
- #define HAVE_OPENCV_STEREO
- #define HAVE_OPENCV_STITCHING
- #define HAVE_OPENCV_STRUCTURED_LIGHT
- #define HAVE_OPENCV_SUPERRES
- #define HAVE_OPENCV_SURFACE_MATCHING
- #define HAVE_OPENCV_TEXT
- #define HAVE_OPENCV_TRACKING
- #define HAVE_OPENCV_VIDEO
- #define HAVE_OPENCV_VIDEOIO
- #define HAVE_OPENCV_VIDEOSTAB
- #define HAVE_OPENCV_VIZ
- #define HAVE_OPENCV_XIMGPROC
- #define HAVE_OPENCV_XOBJDETECT
- #define HAVE_OPENCV_XPHOTO
读取图像只需要使用imread函数即可,图像显示只需要使用imshow函数即可。
- #!/usr/bin/env python3
- import rospy
- import cv2
-
- rospy.init_node("showimg")
- img = cv2.imread("/home/spark/Pictures/bluebox.png")
- cv2.imshow('box',img)
- cv2.waitKey(0)
然后在终端输入以下命令:
rosrun opencv_learn showimg.py
那么,对代码部分进行解析:
- #!/usr/bin/env python3 #告诉操作系统执行这个脚本的时候,调用/usr/bin下的python3解释器
- import rospy #导入ros python库
import cv2 #导入opencv的库- rospy.init_node("showimg") #初始化ros节点,创建名为showmsg的节点
- img = cv2.imread("/home/spark/Pictures/bluebox.png") #使用imread读取绝对路径下的图片文件
- cv2.imshow('box',img) #使用imshow函数,后面接的是显示的窗口名和显示对象
- cv2.waitKey(0) #调用waitKey函数等待按键按下,以便让窗口一直显示,直到有按键按下
由于在显示过程中,图像窗口过于大,影响到观察与操作了,所以添加窗口调节大小的部分:
- #!/usr/bin/env python3
- import rospy
- import cv2
-
- rospy.init_node("showimg")
- img = cv2.imread("/home/spark/Pictures/bluebox.png")
- cv2.namedWindow("box",0)
- cv2.resizeWindow("box",500,500)
- cv2.imshow('box',img)
- cv2.waitKey(0)
- cv2.namedWindow("box",0) # 创建一个空窗口,名为box
- cv2.resizeWindow("box",500,500) # 调整窗口的大小,指定窗口的名称,设置宽度与高度
如此,就能方便用户观察了。
图像腐蚀是指用图像中的暗色部分“腐蚀”掉图像中的高亮部分。
使用getStructuringElement函数获取指定形状和尺寸结构元素,代表进行腐蚀操作时使用的结构类型。
- #!/usr/bin/env python3
- import rospy
- import cv2
-
- rospy.init_node("showimg")
- img = cv2.imread("/home/spark/Pictures/bluebox.png")
- cv2.namedWindow("erode_box",0)
- cv2.resizeWindow("erode_box",500,500)
-
- kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(15,15))
- img = cv2.erode(img,kernel)
-
- cv2.imshow('erode_box',img)
- cv2.waitKey(0)
然后在终端输入以下命令:
rosrun opencv_learn erodeimg.py
基本的读取操作没有变化,只是在此基础上添加了腐蚀的操作
1. kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(15,15))
定义一个kernel核心,由getStructuringElement函数设定一个结构类型
cv2.getStructuringElement(shape, ksize)
2. img = cv2.erode(msg,kernel)
dst = cv.erode( src, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
其中:
使用均值滤波操作的blur函数
- #!/usr/bin/env python3
- import rospy
- import cv2
-
- rospy.init_node("blurimg")
- img = cv2.imread("/home/spark/Pictures/bluebox.png")
- cv2.namedWindow("blurbox",0)
- cv2.resizeWindow("blurbox",500,500)
-
- img = cv2.blur(img,(15,15))
-
- cv2.imshow('blurbox',img)
- cv2.waitKey(0)
dst = blur(src, ksize, dst=None, anchor=None, borderType=None)
均值滤波器,也称低通滤波器
顾名思义,均值滤波器即对滤波核内的数据求均值,然后将这个值赋值给矩阵核心位置。
均值滤波器可以使用cv2.blur() 方法实现
- src:图像
- ksize:滤波核大小,使用元组表示,如(a,b)a表示height(高度),b表示width(宽度)。
- anchor:波核锚点
- borderType:边界类型
详细使用方法可查看
使用canny进行边缘检测。首先载入图像,将其转成灰度图,再使用blur函数进行图像模糊以降噪,再使用canny函数进行边缘检测。
- #!/usr/bin/env python3
- import rospy
- import cv2
-
- rospy.init_node("cannyimg")
- img = cv2.imread("/home/spark/Pictures/bluebox.png")
- cv2.namedWindow("cannyimg",0)
- cv2.resizeWindow("cannyimg",500,500)
-
- # gray
- gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
-
- # blur
- blur_img = cv2.blur(gray_img,(10,10))
- #cv2.imshow('blur_img',blur_msg)
-
- # canny
- Canny_img = cv2.Canny(blur_img,60,110)
-
- cv2.imshow('cannyimg',Canny_img)
- cv2.waitKey(0)
Canny 边缘检测分为如下几个步骤:
步骤 1:去噪。噪声会影响边缘检测的准确性,因此首先要将噪声过滤掉。
步骤 2:计算梯度的幅度与方向。
步骤 3:非极大值抑制,即适当地让边缘“变瘦”。
步骤 4:确定边缘。使用双阈值算法确定最终的边缘信息。
edges = cv.Canny( image, threshold1, threshold2[, apertureSize[, L2gradient]])
其中:
- edges 为计算得到的边缘图像。
- image 为 8 位输入图像。
- threshold1 表示处理过程中的第一个阈值。
- threshold2 表示处理过程中的第二个阈值。
- apertureSize 表示 Sobel 算子的孔径大小。
- L2gradient 为计算图像梯度幅度(gradient magnitude)的标识。其默认值为 False。如果为 True,则使用更精确的 L2 范数进行计算(即两个方向的导数的平方和再开方),否则使用 L1 范数(直接将两个方向导数的绝对值相加)。
利用VideoCapture对视频进行读取显示,以及调用摄像头。
我暂时还没用到使用opencv处理视频的情况,所以暂且留住,待后续涉及到视频的操作的时候会补上。文中也是用简单的几个语句即可实现视频的载入和读取视频帧了。
由于是在ROS上执行,采用订阅话题的方式获取摄像机的画面,然后使用opencv进行处理,如灰度化后的canny边缘检测。
在运行程序前要记得启动摄像机。
- #!/usr/bin/env python3
- import rospy
- import cv2
- from sensor_msgs.msg import Image
- from cv_bridge import CvBridge
-
-
- def image_cb(img):
-
- prime_img = CvBridge().imgmsg_to_cv2(img, "bgr8")
- cv2.imshow("prime_img",prime_img)
-
- cv2.namedWindow("cannyimg",0)
- cv2.resizeWindow("cannyimg",500,500)
-
- # gray
- gray_img = cv2.cvtColor(prime_img,cv2.COLOR_BGR2GRAY)
-
- # blur
- blur_img = cv2.blur(gray_img,(10,10))
- #cv2.imshow('blur_img',blur_msg)
-
- # canny
- Canny_img = cv2.Canny(blur_img,60,110)
-
- cv2.imshow('cannyimg',Canny_img)
- cv2.waitKey(1)
-
- rospy.init_node('camera_canny', anonymous=False)
- rospy.Subscriber("/camera/color/image_raw", Image, image_cb, queue_size=1)
- rospy.spin()
-
对上面的代码进行一下解释吧。
首先导入头文件部分:
from sensor_msgs.msg import Image # 传入的图片消息格式
from cv_bridge import CvBridge # CVBridge 主要用作将ros的图像格式转换为opencv能够处理的格式,下文会有所提及
接下来是创建订阅者部分,负责接受摄像机的图像画面:
rospy.Subscriber("/camera/color/image_raw", Image, image_cb, queue_size=1) rospy.spin()订阅的是彩色图像画面,数据类型为Image,接受到信息的时候就会进入到image_cb回调函数进行处理了。使用订阅时记得在后面加上rospy.spin()语句,作为等待服务的循环语句,不然程序接受不到信息时就会直接跳出了。
最后是回调函数image_cb中的内容解释:
- def image_cb(img):
-
- prime_img = CvBridge().imgmsg_to_cv2(img, "bgr8")
- cv2.imshow("prime_img",prime_img)
-
- cv2.namedWindow("cannyimg",0)
- cv2.resizeWindow("cannyimg",500,500)
-
- # gray
- gray_img = cv2.cvtColor(prime_img,cv2.COLOR_BGR2GRAY)
-
- # blur
- blur_img = cv2.blur(gray_img,(10,10))
- #cv2.imshow('blur_img',blur_msg)
-
- # canny
- Canny_img = cv2.Canny(blur_img,60,110)
-
- cv2.imshow('cannyimg',Canny_img)
- cv2.waitKey(1)
操作和之前的canny边缘检测类似,只是在处理传入的图像数据img之前,需要使用cvbridge将图像格式进行一次变换,转换成opencv能够处理的图像格式,如8进制的bgr图像。
cv2.waitKey()也需要从原来的0改成1,否则只会读取视频中的一帧后等待。
至此,学习完了书中的第一章,并将其应用在ROS noetic 的opencv4中。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。