*[刷书]计算机视觉:OpenCV

目录

书名 出版日期 实验环境 简要评价
《学习OpenCV》 2009.10 OpenCV作者所著,经典教材
☆《OpenCV 3计算机视觉:Python语言实现(原书第2版)》 2016.6 py2.7 通过Python开发基于OpenCV 3.0的应用,勘误Github源码第一版的Github源码疑似第一版Github源码
《基于OpenCV的计算机视觉技术实现》 2008.5 OpenCV1.0 有讲解立体视觉的部分(照相机定标、三维重建)
☆《OpenCV图像处理编程实例》 2016.5 VS2015、OpenCV3.1.0 以功能介绍,勘误,谷歌到的PDF和源码
《OpenCV编程案例详解》 2016.10 OpenCV3.0 以实例介绍,与QT配合,有配套网易云课堂的课程,源码见QQ群
《OpenCV和Visual Studio图像识别应用开发》 2017.10 VS2013、OpenCV2.4.10 分模块介绍
《OpenCV图像处理》 2016.3 OpenCV3.0 Qt
《OpenCV计算机视觉编程攻略:第2版》 2015.9 OpenCV2.4.9
《深入理解OpenCV:实用计算机视觉项目解析》 2014.9 OpenCV 高级应用,涉及Android、iOS
《OpenCV项目开发实战》 2016.9 OpenCV 高级应用,涉及Unity
☆《OpenCV3编程入门》 2015.2 VS2010、OpenCV2.4.9、OpenCV3.0 分模块介绍,作者Github作者博客勘误
《OpenCV实例精解》 2016.8 OpenCV
《数字图像处理基础及OpenCV实现》 2014.12 VS2015、OpenCV1.0 教科书

《OpenCV 3计算机视觉:Python语言实现(原书第2版)》

CH1 安装OpenCV

OpenCV官网
OpenCV源码
OpenCV文档
python示例

1
pip install opencv-python

CH2 处理文件、摄像头和图形用户界面

基本I/O脚本

读/写图像文件

imread()、imwrite()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np
import cv2

# 通过numpy创建黑色的正方形图像,并转换成BGR格式,查看图像的结构
img = np.zeros((3,3), dtype=np.uint8)
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
img.shape

# 图像格式转换
image = cv2.imread('MyPic.png')
cv2.imwrite('MyPic.jpg', image)

# 将加载的图像作为灰度图像
grayImage = cv2.imread('MyPic.png', cv2.IMREAD_GRAYSCALE)
cv2.imwrite('MyPicGray.png', grayImage)

图像与原始字节之间的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
import numpy as np
import cv2
import os

randomByteArray = bytearray(os.urandom(120000))
flatNumpyArray = np.array(randomByteArray)

grayImage = flatNumpyArray.reshape(300,400)
#另一种写法:grayImage = np.random.randint(0,256,120000).reshape(300,400)
cv2.imwrite('RandomGray.png', grayImage)

bgrImage = flatNumpyArray.reshape(100,400,3)
cv2.imwrite('RandomColor.png',bgrImage)

使用numpy.array访问图像数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import numpy as np
import cv2

# 将BGR图像在(0,0)处的像素转化为白像素
img = cv2.imread('MyPic.png')
img[0,0] = [255,255,255]

# 改变某一特定像素的值
print(img.item(150,120,0)) #坐标(150,120)的蓝色值(BGR)
img.itemset((150,120,0), 255)
print(img.item(150,120,0))

# 操作通道
img[:,:,1] = 0 #将图像所有的绿色(G)值设为0

# 设定ROI(Region Of Interest,感兴趣区域)
my_roi = img[0:100, 0:100]
img[300:400, 300:400] = my_roi # 将第一个区域的值分配给第二个区域(两个区域必须大小一样)

# 获取图像属性
print(img.shape) # 宽度、高度、通道数
print(img.size) # 像素的个数=宽度*高度*通道数
print(img.dtype) # 数据类型

视频文件的读/写

VideoCapture类、VideoWriter类

1
2
3
4
5
6
7
8
9
10
11
12
# 读取AVI文件的帧,并采用YUV颜色编码将其写入另一个帧中
import cv2

videoCapture = cv2.VideoCapture('MyInputVid.avi')
fps = videoCapture.get(cv2.CAP_PROP_FPS)
size = (int(videoCapture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(videoCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
videoWriter = cv2.VideoWriter('MyInputVid.avi', cv2.VideoWriter_fourcc('I','4','2','0'), fps, size)

success, frame = videoCapture.read()
while success:
videoWriter.write(frame)
success, frame = videoCapture.read()

常见编解码器(cv2.VideoWriter_fourcc()的第一个参数)

参数 作用
‘I’,’4’,’2’,’0’ 未压缩的YUV颜色编码,是4:2:0色度子采样,扩展名.avi
‘P’,’I’,’M’,’1’ MPEG-1编码类型,扩展名.avi
‘X’,’V’,’I’,’D’ MPEG-4编码类型,扩展名.avi
‘T’,’H’,’E’,’O’ Ogg Vorbis,扩展名.ogv
‘F’,’L’,’V’,’1’ Flash视频,扩展名.flv

捕获摄像头的帧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 捕获摄像头10秒的视频信息,并将其写入一个AVI文件中
import cv2

cameraCapture = cv2.VideoCapture(0) # 传递摄像头的设备索引
fps = 30
size = (int(cameraCapture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(cameraCapture.get(cv2.CAP_PROP_FRAME_HEIGHT)))
videoWriter = cv2.VideoWriter('MyOutputVid.avi', cv2.VideoWriter_fourcc('I','4','2','0'), fps, size)

success, frame = cameraCapture.read()
numFramesRemaining = 10 * fps - 1
while success and numFramesRemaining > 0:
videoWriter.write(frame)
success, frame = cameraCapture.read()
numFramesRemaining -= 1
cameraCapture.release()

# 同步一组摄像头或一个多头摄像头
success0 = cameraCapture0.grab()
success1 = cameraCapture1.grab()
if success0 and success1:
frame0 = cameraCapture0.retrieve()
frame1 = cameraCapture1.retrieve()

在窗口显示图像

imshow()

1
2
3
4
5
6
7
8
# 显示一副图像(保证显示视频时窗口上的帧一直更新)
import cv2
import numpy as np

img = cv2.imread('my-image.png')
cv2.imshow('my image', img)
cv2.waitKey()
cv2.destroyAllWindows()

在窗口显示摄像头帧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 实时显示摄像头帧
import cv2

clicked = False

def onMouse(event, x, y, flags, param):
global clicked
if event == cv2.EVENT_LBUTTONUP:
clicked = True

cameraCapture = cv2.VideoCapture(0)
cv2.namedWindow('MyWindow')
cv2.setMouseCallback('MyWindow', onMouse)

print('Showing camera feed. Click window or press any key to stop.')
success, frame = cameraCapture.read()
while success and cv2.waitKey(1) == -1 and not clicked:
cv2.imshow('MyWindow', frame)
success, frame = cameraCapture.read()

cv2.destroyAllWindows('MyWindow')
cameraCapture.release()

Cameo项目(人脸跟踪和图像处理)——面向对象的设计

见managers.py、cameo.py

CH3 使用OpenCV 3处理图像

不同色彩空间的转换

计算机视觉中3种常用的色彩空间:

  1. 灰度色彩空间——去除彩色信息
  2. BGR——蓝、绿、红
  3. HSV——色调、饱和度、黑暗的程度

傅里叶变换

傅里叶:一切都可以用波形来描述,所有的波形都可以由一系列简单且频率不同的正弦曲线叠加得到。
图像的幅度谱:呈现了原始图像在变化方面的一种表示,把一幅图像中最明亮的像素放到图像中央,然后逐渐变暗,在边缘上的像素最暗。
边缘检测/线段检测/形状检测以高通/低通滤波器傅里叶变换为基础。

高通滤波器(HPF)

核(kernel)=滤波器矩阵=一组权重的集合=应用在源图像的一个区域,并由此生成目标图像的一个像素
用处:边缘检测

hpf.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 高通滤波器
import cv2
import numpy as np
from scipy import ndimage

kernel_3x3 = np.array([[-1, -1, -1],
[-1, 8, -1],
[-1, -1, -1]])

kernel_5x5 = np.array([[-1, -1, -1, -1, -1],
[-1, 1, 2, 1, -1],
[-1, 2, 4, 2, -1],
[-1, 1, 2, 1, -1],
[-1, -1, -1, -1, -1]])

img = cv2.imread("statue_small.jpg", 0) #将读入的图像转换为灰度格式

k3 = ndimage.convolve(img, kernel_3x3) #用给定的核与图像进行“卷积”(convolve)
k5 = ndimage.convolve(img, kernel_5x5)

blurred = cv2.GaussianBlur(img, (17,17), 0)
g_hpf = img - blurred #实现高通滤波器的另一种方法:通过对图像应用低通滤波器后,与原始图像计算差值——此方法效果最好

cv2.imshow("3x3", k3)
cv2.imshow("5x5", k5)
cv2.imshow("g_hpf", g_hpf)
cv2.waitKey()
cv2.destroyAllWindows()

低通滤波器(LPF)

用处:去噪、模糊化

见filters.py、utils.py

Canny边缘检测

Canny边缘检测的5个步骤:

  1. 用高斯滤波器对图像进行去噪
  2. 计算梯度
  3. 在边缘上使用非最大抑制(NMS)
  4. 在检测到的边缘上使用双阈值去除假阳性(false positive)
  5. 分析所有的边缘及其之间的连接

canny.py

1
2
3
4
5
6
7
8
9
# Canny边缘检测
import cv2
import numpy as np

img = cv2.imread("statue_small.jpg", 0)
cv2.imwrite("canny.jpg", cv2.Canny(img, 200, 300))
cv2.imshow("canny", cv2.imread("canny.jpg"))
cv2.waitKey()
cv2.destroyAllWindows()

轮廓检测

检测图像或视频帧中物体的轮廓、计算多边形边界、形状逼近、计算感兴趣区域
cv2.findContours()

contours.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 检测矩形轮廓(熟悉API)
import cv2
import numpy as np

img = np.zeros((200, 200), dtype=np.uint8)
img[50:150, 50:150] = 255

ret, thresh = cv2.threshold(img, 127, 255, 0)
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) #输入参数(输入图像、层次类型、轮廓逼近方法),返回值(修改后的图像、图像的轮廓、它们的层次)
color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
img = cv2.drawContours(color, contours, -1, (0,255,0), 2)
cv2.imshow("contours", color)
cv2.waitKey()
cv2.destroyAllWindows()

边界框、最小矩形区域、最小闭圆的轮廓

cv2.findContours()、cv2.boundingRect()、cv2.rectangle()
cv2.minAreaRect()、cv2.boxPoints()、 cv2.drawContours()
cv2.minEnclosingCircle()、cv2.circle()

contours_2.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import cv2
import numpy as np

img = cv2.pyrDown(cv2.imread("hammer.jpg", cv2.IMREAD_UNCHANGED))

ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY) , 127, 255, cv2.THRESH_BINARY)
image, contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

for c in contours:
# step1:计算出一个简单的边界框
# find bounding box coordinates
x,y,w,h = cv2.boundingRect(c) #返回(x,y)坐标、矩形的高度和宽度
cv2.rectangle(img, (x,y), (x+w, y+h), (0, 255, 0), 2)

# step2:计算出包围目标的最小矩形区域
# find minimum area
rect = cv2.minAreaRect(c)
# calculate coordinates of the minimum area rectangle
box = cv2.boxPoints(rect)
# normalize coordinates to integers
box = np.int0(box)
# draw contours
cv2.drawContours(img, [box], 0, (0,0, 255), 3)

# step3:检查的边界轮廓为最小闭圆
# calculate center and radius of minimum enclosing circle
(x,y),radius = cv2.minEnclosingCircle(c) #返回圆心的坐标组成的元组、圆的半径值
# cast to integers
center = (int(x),int(y))
radius = int(radius)
# draw the circle
img = cv2.circle(img,center,radius,(0,255,0),2)

cv2.drawContours(img, contours, -1, (255, 0, 0), 1)
cv2.imshow("contours", img)

cv2.waitKey()
cv2.destroyAllWindows()

凸轮廓与Douglas-Peucker算法

cv2.approxPolyDP()、 cv2.arcLength()、cv2.convexHull()

contours_hull.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import cv2
import numpy as np

img = cv2.pyrDown(cv2.imread("hammer.jpg", cv2.IMREAD_UNCHANGED))

ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY) , 127, 255, cv2.THRESH_BINARY)
black = cv2.cvtColor(np.zeros((img.shape[1], img.shape[0]), dtype=np.uint8), cv2.COLOR_GRAY2BGR)

image, contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

for cnt in contours:
epsilon = 0.01 * cv2.arcLength(cnt,True) #获取轮廓的周长信息
approx = cv2.approxPolyDP(cnt,epsilon,True) #输入参数(轮廓、ε值-源轮廓与近似多边形的最大差值、布尔标记-多边形是否闭合)
hull = cv2.convexHull(cnt) #获取处理过的轮廓信息
cv2.drawContours(black, [cnt], -1, (0, 255, 0), 2)
cv2.drawContours(black, [approx], -1, (255, 255, 0), 2)
cv2.drawContours(black, [hull], -1, (0, 0, 255), 2)

cv2.imshow("hull", black)
cv2.waitKey()
cv2.destroyAllWindows()

直线和圆检测

Hough变换
HoughLines() 标准的Hough变换
HoughLinesP() 概率Hough变换
HoughCircles() 检测圆

直线检测

hough_lines.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 直线检测
import cv2
import numpy as np

img = cv2.imread('lines.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,50,120)
minLineLength = 20
maxLineGap = 5
lines = cv2.HoughLinesP(edges,1,np.pi/180,20,minLineLength,maxLineGap) #输入参数(需要处理的图像、线段的集合表示rho、theta、阈值-低于该阈值的直线会被忽略、最小直线长度-更短的直线会被消除、最大线段间隙-大于这个值会被视为是两条分开的线段)
for x1,y1,x2,y2 in lines[0]:
cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)

cv2.imshow("edges", edges)
cv2.imshow("lines", img)
cv2.waitKey()
cv2.destroyAllWindows()

圆检测

hough_circles.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 圆检测
import cv2
import numpy as np

planets = cv2.imread('planet_glow.jpg')
gray_img = cv2.cvtColor(planets, cv2.COLOR_BGR2GRAY)
img = cv2.medianBlur(gray_img, 5)
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)

circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,120,param1=100,param2=30,minRadius=0,maxRadius=0) #参数包括:圆心间的最小距离、圆的最小及最大半径

circles = np.uint16(np.around(circles))

for i in circles[0,:]:
# draw the outer circle
cv2.circle(planets,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv2.circle(planets,(i[0],i[1]),2,(0,0,255),3)

cv2.imwrite("planets_circles.jpg", planets)
cv2.imshow("HoughCirlces", planets)
cv2.waitKey()
cv2.destroyAllWindows()

检测其他形状

cv2.approxPolyDP()、cv2.findContours()

CH4 深度估计与分割——没细看

使用深度摄像头(如微软的Kinect)的数据来识别前景区域和背景区域,需要自己构建OpenCV
两种方法:使用深度摄像头来进行深度估计、使用立体图像来进行深度估计

本章涉及的主题:深度估计和分割,介绍了深度摄像头、极几何、立体图像、视差图的计算、两种图像分割的流行方法(GrabCut、分水岭)

CH5 人脸检测和识别

特征描述符、关键点检测

CH6 图像检索以及基于图像描述符的搜索

CH7 目标检测与识别

CH8 目标跟踪

CH9 基于OpenCV的神经网络简介

《OpenCV3编程入门》

  1. OpenCV各模块功能 P8

  2. OpenCV2.0与OpenCV3.0的区别
    OpenCV2.0:各个模块以整体的形式构建然后组合在一起
    OpenCV3.0:内核+插件

  3. 官方例程引导与赏析
    示例代码路径:
    D:_imv\opencv\sources\samples\cpp\tutorial_code
    D:_imv\opencv\sources\samples\cpp

  4. 编译OpenCV源代码 P45

  5. argc与argv
    int argc表示命令行字串的个数
    char *argv[]表示命令行参数的字符串

配置VS2012+OpenCV2.4.9

参考博文

VS2012官方免费中文旗舰版下载(含激活密钥)
安装visual studio 2012,并配置opencv

VC与VS对应关系

VC VS
VC8 VS2005
VC9 VS2008
VC10 VS2010
VC11 VS2012
VC12 VS2013
VC14 VS2015
VC15 VS2017

文档

OpenCV2.4.9