OpenCV学习笔记(Python)

image.png

一 OpenCV中的GUI特性

图片

读入图像

使用函数 cv2.imread() 读入图像。

  • cv2.IMREAD_COLOR:读入一副彩色图像。图像的透明度会被忽略,这是默认参数。
  • cv2.IMREAD_GRAYSCALE:以灰度模式读入图像
  • cv2.IMREAD_UNCHANGED:读入一幅图像,并且包括图像的 alpha 通道
1
2
3
4
5
6
# -*- coding: utf-8 -*-
import numpy as np
import cv2
# Load an color image in grayscale
img = cv2.imread('messi5.jpg',0)

警告: 就算图像的路径是错的, OpenCV 也不会提醒你的,但是当你使用命
令print img时得到的结果是None。

显示图像

使用函数 cv2.imshow() 显示图像。窗口会自动调整为图像大小。第一个参数是窗口的名字,其次才是我们的图像。

1
2
3
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.waitKey() 是一个键盘绑定函数。需要指出的是它的时间尺度是毫秒级。函数等待特定的几毫秒,看是否有键盘输入。特定的几毫秒之内,如果按下任意键,这个函数会返回按键的 ASCII 码值,程序将会继续运行。如果没有键盘输入,返回值为-1,如果我们设置这个函数的参数为0,那它将会无限期的等待键盘输入。它也可以被用来检测特定键是否被按下,例如按键 a 是否被按下。

cv2.destroyAllWindows() 可以轻易删除任何建立的窗口。如果想删除特定的窗口可以使用 cv2.destroyWindow(),在括号内输入你想删除的窗口名。

建议:一种特殊的情况是,也可以先创建一个窗口,之后再加载图像。这种情况下,可以决定窗口是否可以调整大小。使用到的函数cv2.namedWindow()。初始设定函数标签是cv2.WINDOW_AUTOSIZE。但是如果把标签改成cv2.WINDOW_NORMAL,就可以调整窗口大小了。当图像维度太大,或者要添加轨迹条时,调整窗口大小将会很有用.

1
2
3
4
5
6
7
import numpy as np
import cv2
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow('image',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

保存图像

使用函数 cv2.imwrite() 来保存一个图像。首先需要一个文件名,之后才是要保存的图像。

1
cv2.imwrite('messigray.png',img)

下面的程序将会加载一个灰度图, 显示图片,按下’s’键保存后退出,或者按下 ESC 键退出不保存。

1
2
3
4
5
6
7
8
9
10
11
import numpy as np
import cv2
img = cv2.imread('messi5.jpg',0)
cv2.imshow('image',img)
k = ccv2.waitKey(0)&0xFF
if k == 27: # wait for ESC key to exit
cv2.destroyAllWindows()
elif k == ord('s'): # wait for 's' key to save and exit
cv2.imwrite('messigray.png',img)
cv2.destroyAllWindows()

警告: 如果用的是 32 位系统,需要将 k = cv2.waitKey(0)&0xFF 这行改成
k = cv2.waitKey(0)。

使用Matplotlib

Matplotib 是 python 的一个绘图库,里头有各种各样的绘图方法。

1
2
3
4
5
6
7
8
# -*- coding: utf-8 -*-
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('messi5.jpg',0
plt.imshow(img,cmap = 'gray',interpolation = 'bicubic')
plt.xticks([]), plt.yticks([])
plt.show()

视频

用摄像头捕获视频

OpenCV提供了一个非常简单的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#-*-coding:utf-8-*-
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
while(True):
#捕获帧
ret,frame = cap.read()
#对帧进行操作
gray = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)
#展示结果
cv2.imshow('frame',gray)
if cv2.waitKey(1)&0xFF == ord('q'):
break
#当所有操作都已完成,停止捕获视频
cap.release()
cv2.destroyAllWindows()

cap.read()返回一个布尔值。可以使用函数cap.get(propID)来获得视频的一些参数信息。cap.set(propID,value)可以来修改其中的一些值。
例如:使用cap.get(3)和cap.get(4)来查看每一帧的宽和高。默认是640X480。但是可以使用ret = cap.set(3,320)
和ret = cap.set(4,240)来把宽和高改成320X240。

从文件中播放视频
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
# -*- coding: utf-8 -*-
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))
while(cap.isOpened()):
ret, frame = cap.read()
if ret==True:
frame = cv2.flip(frame,0)
# write the flipped frame
out.write(frame)
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
# Release everything if job is finished
cap.release()
out.release()
3cv2.destroyAllWindows()

注意: 你应该确保你已经装了合适版本的 ffmpeg 或者gstreamer。如果你装错了那就比较头疼了。

保存视频

对于图片来时很简单只需要使用 cv2.imwrite()。但对于视频来说就要多做点工
作。要创建一个 VideoWriter的对象。确定一个输出文件的名字。接下来指定FourCC编码。播放频率和帧的大小也都需要确定。最后一个是 isColor 标签。
FourCC码以下面的格式传给程序,以MJPG为例:cv2.cv.FOURCC(‘M’,’J’,’P’,’G’)或者cv2.cv.FOURCC(*’MJPG’)。

下面的代码是从摄像头中捕获视频,沿水平方向旋转每一帧并保存它。

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
# -*- coding: utf-8 -*-
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
# 定义编码格式 创建写入视频类
fourcc = cv2.cv.FOURCC(*'XVID')
out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))
while(cap.isOpened()):
ret, frame = cap.read()
if ret==True:
frame = cv2.flip(frame,0)
# 写入
out.write(frame)
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
else:
break
# Release everything if job is finished
cap.release()
out.release()
cv2.destroyAllWindows()

OpenCV中的绘图函数

需要设置的参数:
• img:你想要绘制图形的那幅图像。
• color:形状的颜色。以RGB 为例,需要传入一个元组,例如:(255,0,0)
代表蓝色。对于灰度图只需要传入灰度值。
• thickness:线条的粗细。如果给一个闭合图形设置为-1,那么这个图形
就会被填充。默认值是1.
• linetype:线条的类型,8 连接,抗锯齿等。默认情况是 8 连接。cv2.LINE_AA
为抗锯齿,这样看起来会非常平滑。

画线

要画一条线只需要告诉画这条线的起点和终点。

1
2
3
4
5
6
7
8
import numpy as np
import cv2
img=np.zeros((512,512,3), np.uint8)
# Draw a diagonal blue line with thickness of 5 px
cv2.line(img,(0,0),(511,511),(255,0,0),5)

画矩形

需要矩形的左上角顶点和右下角顶点坐标。

1
cv2.rectangle(img,(384,0),(510,128),(0,255,0),3)

画圆

指定圆形的中心坐标和半径大小。

1
cv2.circle(img,(447,63), 63, (0,0,255), -1)

画椭圆

画椭圆比较复杂,要多输入几个参数。一个参数是中心点的位置坐标。下一个参数是长轴和短轴的长度。椭圆沿逆时针方向旋转的角度。椭圆弧演顺时针方向起始的角度和结束角度,如果是0和360,就是整个椭圆。查看cv2.ellipse()可以得到更多信息。下面的例子是在图片的中心绘制半个椭圆。

1
cv2.ellipse(img,(256,256),(100,50),0,0,180,255,-1)

画多边形

画多边形,需要指出每个顶点的坐标。用这些点的坐标构建一个大小等于行数 X1X2 的数组,行数就是点的数目。这个数组的数据类型必须为int32。这里画一个黄色的具有四个顶点的多边形。

1
2
3
pts=np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)
pts=pts.reshape((-1,1,2))
# 这里 reshape 的第一个参数为-1, 表明这一维的长度是根据后面的维度的计算出来的。

注意: 如果第三个参数是 False,我们得到的多边形是不闭合的(首尾不相连)。
注意: cv2.polylines() 可以被用来画很多条线。 只需要把想要画的线放在一个列表中,将这个列表传给函数就可以了。每条线都会被独立绘制。这会比用cv2.line() 一条一条的绘制要快一些。

在图片上添加文字

要在图片上绘制文字,需要设置下列参数:
• 要绘制的文字
• 要绘制的位置
• 字体类型(通过查看 cv2.putText() 的文档找到支持的字体)
• 字体的大小
• 文字的一般属性如颜色,粗细,线条的类型等。为了更好看一点推荐使用linetype=cv2.LINE_AA。

在图像上绘制白色的 OpenCV。

1
2
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'OpenCV',(10,500), font, 4,(255,255,255),2)

警告:所有的绘图函数的返回值都是None,所以不能使用img=cv2.line(img,(0,0),(511,511),(255,0,0),5)。

结果
下面就是最终结果了,通过你前面几节学到的知识把他显示出来吧。

1
2
3
4
5
winname = 'example'
cv2.namedWindow(winname)
cv2.imshow(winname, img)
cv2.waitKey(0)
cv2.destroyWindow(winname)

image.png

完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#-*-coding:utf-8-*-
import numpy as np
import cv2
img=np.zeros((512,512,3), np.uint8)
# Draw a diagonal blue line with thickness of 5 px
cv2.line(img,(0,0),(511,511),(255,0,0),5)
cv2.rectangle(img,(384,0),(510,128),(0,255,0),3)
cv2.circle(img,(447,63), 63, (0,0,255), -1)
cv2.ellipse(img,(256,256),(100,50),0,0,180,255,-1)
pts=np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)
pts=pts.reshape((-1,1,2))
font=cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'OpenCV',(10,500), font, 4,(255,255,255),2)
winname = 'example'
cv2.namedWindow(winname)
cv2.imshow(winname, img)
cv2.waitKey(0)
cv2.destroyWindow(winname)

把鼠标当画笔

将要学习的函数是:cv2.setMouseCallback()

简单演示

创建一个简单的程序,会在图片上双击过的位置绘制一个圆圈。
首先来创建一个鼠标事件回调函数,但鼠标事件发生就会被执行。鼠标事件可以是鼠标上的任何动作,比如左键按下,左键松开,左键双击等。可以通过鼠标事件获得与鼠标对应的图片上的坐标。根据这些信息可以做任何想做的事。可以通过执行下列代码查看所有被支持的鼠标事件。
鼠标事件回调函数只用做一件事:在双击过的地方绘制一个圆圈。下面是代码,不懂的地方可以看看注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import cv2
import numpy as np
#mouse callback function
def draw_circle(event,x,y,flags,param):
if event==cv2.EVENT_LBUTTONDBLCLK:
cv2.circle(img,(x,y),100,(0,0,255),-1)
# 创建图像与窗口并将窗口与回调函数绑定
img=np.zeros((512,512,3),np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('image',img)
if cv2.waitKey(20)&0xFF==27:
break
cv2.destroyAllWindows()

结果:
image.png

高级一点的示例

现在来创建一个更好的程序。这次的程序要完成的任务是根据选择的模式在拖动鼠标时绘制矩形或者是圆圈(就像画图程序中一样)。所以回调函数包含两部分,一部分画矩形,一部分画圆圈。这是一个典型的例子可以更好理解与构建人机交互式程序,比如物体跟踪,图像分割等。

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
39
# -*- coding: utf-8 -*-
import cv2
import numpy as np
# 当鼠标按下时变为 True
drawing=False
# 如果 mode 为 true 绘制矩形。按下'm' 变成绘制曲线。
mode=True
ix,iy=-1,-1
# 创建回调函数
def draw_circle(event,x,y,flags,param):
global ix,iy,drawing,mode
# 当按下左键是返回起始位置坐标
if event==cv2.EVENT_LBUTTONDOWN:
drawing=True
ix,iy=x,y
# 当鼠标左键按下并移动是绘制图形。 event 可以查看移动, flag 查看是否按下
elif event==cv2.EVENT_MOUSEMOVE and flags==cv2.EVENT_FLAG_LBUTTON:
if drawing==True:
if mode==True:
cv2.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
else:
# 绘制圆圈,小圆点连在一起就成了线, 3 代表了笔画的粗细
cv2.circle(img,(x,y),3,(0,0,255),-1)
# 下面注释掉的代码是起始点为圆心,起点到终点为半径的
# r=int(np.sqrt((x-ix)**2+(y-iy)**2))
# cv2.circle(img,(x,y),r,(0,0,255),-1)
# 当鼠标松开停止绘画。
elif event==cv2.EVENT_LBUTTONUP:
drawing==False
img=np.zeros((512,512,3),np.uint8)
cv2.namedWindow('image')
cv2.setMouseCallback('image',draw_circle)
while(1):
cv2.imshow('image',img)
k=cv2.waitKey(1)&0xFF
if k==ord('m'):
mode=not mode
elif k==27:
break

结果:
image.png

用滑动条做调色板

• 把滑动条绑定到 OpenCV 的窗口
• 学习这些函数:cv2.getTrackbarPos(),cv2.creatTrackbar()等。

代码示例

创建一个简单的程序:通过调节滑动条来设定画板颜色。要创建一个窗口来显示显色,还有三个滑动条来设置B,G,R的颜色。
当滑动滚动条是窗口的颜色也会发生相应改变。默认情况下窗口的起始颜色为黑。

cv2.getTrackbarPos()函数的一个参数是滑动条的名字,第二个参数是滑动条被放置窗口的名字,第三个参数是滑动条的默认位置。第四个参数是滑动条的最大值,第五个函数是回调函数,每次滑动条的滑动都会调用回调函数。回调函数通常都会含有一个默认参数,就是滑动条的位置。在本例中这个函数不用做任何事情,只需要 pass 就可以了。

滑动条的另外一个重要应用就是用作转换按钮。默认情况下 OpenCV 本身不带有按钮函数。所以使用滑动条来代替。在程序中,要创建一个转换按钮,只有当装换按钮指向 ON 时,滑动条的滑动才有用,否则窗户口都是黑的。

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
# -*- coding: utf-8 -*-
import cv2
import numpy as np
def nothing(x):
pass
# 创建一副黑色图像
img=np.zeros((300,512,3),np.uint8)
cv2.namedWindow('image')
cv2.createTrackbar('R','image',0,255,nothing)
cv2.createTrackbar('G','image',0,255,nothing)
cv2.createTrackbar('B','image',0,255,nothing)
switch='0:OFF\n1:ON'
cv2.createTrackbar(switch,'image',0,1,nothing)
while(1):
cv2.imshow('image',img)
k=cv2.waitKey(1)&0xFF
if k==27:
break
r=cv2.getTrackbarPos('R','image')
g=cv2.getTrackbarPos('G','image')
b=cv2.getTrackbarPos('B','image')
s=cv2.getTrackbarPos(switch,'image')
if s==0:
img[:]=0
else:
img[:]=[b,g,r]
cv2.destroyAllWindows()

附录:数字图像处理基础知识

数字图像处理基础知识

谢谢你请我吃糖果!