第2章 处理文件、摄像头和图形用户界面
2.1 基本IO脚本
大多数的CV应用程序需要将将图像作为输入,同时也会将图像作为输出。
还可能使用摄像头作为输入源,也可能是其他输入源,如视频等。
2.1.1 读/写图像文件
OpenCv的imread()函数和 imwrite()函数支持各种静态图像文件格式。
都支持bmp格式,通常也支持png,jpeg和tiff格式
读取文件,保存为另一种格式:
import cv2
image = cv2.imread('mypic.png')
cv2.imwrite('mypic.jpg',image)
#tip:图像文件直接写文件名(相对路径)时需要将图片和程序在同一文件夹,
使用绝对路径(例如D://mypic.png),则图像可以不和程序在同一文件夹。
彩色图像变黑白:#将加载的图像作为灰度图像(丢失颜色信息),然后保存。
import cv2
grayImage = cv2.imread('lion.jpg',cv2.IMREAD_GRAYSCALE)
cv2.imwrite('liongray.jpg',grayImage)
2.1.2 图像与原始字节之间的转换
一个字节(8比特,)能表示0到255(2^8)的整数。一个像素通常由每个通道的一个字节表示。
一个OpenCV图像是.array类型的二维或三维数组。8位的灰度图像是一个含有字节值的二维数组;一个24位的BGR图像是一个三维数组。
可使用表达式访问这些值,如image[0,0],image[0,0,0].
第一个代表y坐标,第二个是x坐标,第三个值表示颜色。
*坐标以左上角为原点[0,0]
对于一个左上角有白色像素的8位灰度图像而言,image[0,0]的值为255。
对于一个左上角有蓝色像素的24位BGR图像而言,image[0,0]的值是[255,0,0]。
若一幅图像的每个通道为8位,可以将其转换为标准的一维Python bytearray格式:
byteArray = bytearray[image]
反之,也可以将相应的bytearray转换成numpy.array形式的图像:
grayImage = numpy.array(grayByteArray).reshape(height,width)
bgrImage = numpy.array(bgrByteArray).reshape(height,width,3)
2.1.3 使用numpy.array访问图像数据
先看一个最基础的例子:将BGR图像在(0,0)处的像素转换成白像素。
import cv2
import numpy as np
img = cv2.imread('lion.png')
img[0,0]=[255,255,255]
如果想改变一个特定像素的颜色值,numpy.array提供的item()方法会非常方便。该函数由三个参数x,y,以及(x,y)位置的数组索引,该函数能返回索引位置的值。另一个方法时使用itemset()函数设置指定像素在指定通道的值,
itemset()有两个参数:一个三元组(x,y,索引)和要设定的值。
例如,将坐标(150,120)的当前值变成255:
import cv2
import numpy as np
img = cv2.imread('lion.png')
print (img.item(150,120,0))
img.itemset((150,120,0),255)
print (img.item(150,120,0))
下面介绍一个常见的操作:操作通道:将指定通道(B、G或R)的所有值设置为0。
import cv2
import numpy as np
img = cv2.imread('lion.png')
img[:,:,1] = 0
cv2.imwrite('lionWithoutG.png',img)
查看图片会发现图片没有绿色(可能看起来是一张红色的照片)
通过NumPy数组访问原始像素,可以做一些有趣的事情,例如设定感兴趣区域(Region Of Interest,ROI)。一旦设定,就可以做许多操作,如,将该区域和变量绑定,然后设定第二个区域,并将第一个区域的值分配给第二个区域。
(即拷贝图像到另一个位置)。
*两个区域大小形状相同
import cv2
import numpy as np
img = cv2.imread('lion.png')
my_roi = img[0:100, 0:100] #点[0,0]和[100,100]形成的矩形区域
img[300:400, 300:400] = my_roi
cv2.imwrite('lionCopySome.png',img)
使用numpy.array获得一些图像的细节:
import cv2
import numpy as np
img = cv2.imread('lion.png')
print(img.shape)
print(img.size)
print(img.dtype)
shpe:返回包含宽度、高度和通道数的数组,
size:图像像素的大小
dtype:Datatype 图像的数据类型,通常是一个无符号整数类型。
2.1.4 视频文件的读/写
OpenCV提供了VideoCapture和VideoWriter类来支持各种格式的视频文件。通常情况下都支持AVI格式。 在到达文件末尾之前,VideoCapture可以通过read()函数来获取新的帧,每一帧都是一幅BGR格式的图像。
可以将一幅图像传递给VideoWriter的write()函数。该函数会将图像加到VideoWriter类指向的文件中。
下面是一个读取AVI文件中的帧,并采用YUV颜色编码将其写入另一个帧中:
import cv2
videoCapture = cv2.VideoCapture('MyInpuyVid.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('myOutVid.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'))生成的myOutVid.avi非常大,我原视频只有8分钟95MB,生成的就有16GB的视频文件。而其他的会好很多,大概400MB。
需要为VideoWriter类的构造函数指定视频文件名,视频解码器。
常用的视频解码器选项:
帧速率和帧大小也必须指定,因为需要从一个视频复制视频帧,这些属性可以通过VideoCapture类的get()函数得到。
2.1.5 捕获摄像头的帧
VideoCapture类可以获得摄像头的帧。但对摄像头而言,不是同文件名来构造VideoCapture类,而是传入摄像头的设备索引(device index)。
例子:捕获10秒摄像头视频信息,写入AVI文件:
import cv2
cameraCapture = cv2.VideoCapture(0) #打开笔记本自带摄像头
fps = 30 #假设帧速为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('X','V','I','D'),
fps,size)
success,frame = cameraCapture.read()
numFrameRemaing = 10*fps-1 #录制10秒
print(numFrameRemaing)
while success and numFrameRemaing>0:
videoWriter.write(frame)
success,frame = cameraCapture.read()
numFrameRemaing-=1
cameraCapture.release()
当需要同步一组或一个多头摄像头时,使用grab()和retrive()方法替代read():
success0 = cameraCaputre0.grab()
success1 = cameraCaputre1.grab()
if success0 and success1:
frame0 = cameraCapture0.retrieve()
frame1 = cameraCapture1.retrieve()
2.1.6 在窗口显示图像
使用imshow()。imshow有两个参数,显示图像的帧名称(窗口名)以及图像本身。
以下几行代码可以显示一幅图像:
import cv2
import numpy as np
img = cv2.imread('lion.png')
cv2.imshow('my image',img) #调用imshow,显示一个叫my image的窗口 #,图像为img
cv2.waitKey() #等待按键
cv2.destroyAllWindows()
2.1.7 在窗口显示摄像头帧
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 yo stop")
success,frame = cameraCapture.read()
while success and cv2.waitKey(1) == -1 and not clicked:
cv2.imshow('MyWindow',frame)
success,frame =cameraCapture.read()
cv2.destroyWindow('MyWindow')
cameraCapture.release()
*waitKey()的参数为等待键盘触发的时间,返回值为-1(没有按下)或按下的ASCII码。