生成样本:opencv-python的使用

lamdba
2个月前 阅读 25 点赞 1

首先,你需要装python,最好还要会python…………

然后安装opencv-python库。在命令行下(不是解释器下)使用命令:

pip install opencv-python

这一步比较重要。注意点包括:不要漏写install;不要把opencv写成opencl或者opengl,cv是Computer Vision的首字母,表示“计算机视觉”,主要用于处理图像,opengl是画图用的(而且可以画3d图形),opencl是调用显卡来加速某些运算;不要把中间的短横杠漏掉或者写成下划线;由于忘记名称随手写了个pyopencv(应当为opencv-python)。


然后创建一个空的源文件,往里写入

import cv2

这一步也很重要。如果只写了cv,会报错,这里解释一下:cv2表示c++库,cv表示c库,而opencv-python仅引用c++库。也有人会写成import opencv(应当为import cv2),也不行。


然后就可以正式开始了。目标是读取摄像头输入,但我没有摄像头,只能从视频文件读取(仅有一个参数的差别)。视频文件我也没有,所以用一张方形图片的4方向组装了一个视频(手动狗头)……这些都会在本文中说明。

首先你需要一张方形图片,可以自己截一张,也可以上网搜索“头像”,大多是方的。由于本教程中图片不会缩放,所以图片尺寸不要太大或太小。我用的图片是200*200,稍微有点小了……

然后复制5个,右键菜单里可以旋转,然后按顺序命名,和源文件放在同一个文件夹里,总之做成类似这样……(用pillow库也可以做到这些,但可能会繁琐)


第一个任务是,把这些图片打包成视频。cv2库提供了VideoWriter类,【由文件名、压缩格式、帧率(即fps)、尺寸 这4个参数初始化】;然后【通过write方法写入每一帧】(大小应和视频尺寸相同);最后【调用release方法保存】。其中write方法接收一个图片对象(其实是一个数组,和numpy完全一致),而【图片对象可以由imread函数由图片路径获取】。


以下是完整代码:

import cv2, os

video=cv2.VideoWriter("test.avi", cv2.VideoWriter_fourcc('D','I','V','X'), 1, (200,200))
for filename in os.listdir('.'):
    if filename.endswith('.jpg'):
        img = cv2.imread(filename)
        video.write(img)

video.release()


如果你的图片也是200*200,那么代码可以不作修改……

这里cv2.VideoWriter_fourcc('D','I','V','X'),可以搜索“divx”了解相关信息。保存的文件名最好用.avi,否则会出现警告(这里我也不是很懂)。运行程序,会发现当前目录下生成了test.avi,用暴风影音的话可以直接播放(win7自带的media player莫名不能用)


=====================================================


接下来是读取视频内容(读取每一帧),需要再创建一个源文件。先创建一个cv2.VideoCapture类对象,它和文件对象有些类似,初始化时传入视频文件的路径,就可以给出视频文件的内容;传入0,可以给出当前摄像头的内容(通过read方法,一会再说)。有趣的是,如果传入了不存在的路径,或者传入0但没有可用的摄像头,这个对象依然会产生,但是“是空的”。VideoCapture对象就像一个盒子(如果你知道接口那最好),它提供了统一的方式来访问里边的内容,但它也可以不装东西。对一个VideoCapture对象不断调用read方法,和对文件对象调用readline方法相当一致,前者每次给出一帧,后者每次给出一行。不过readline在结束时返回空字符串,VideoCapture的read则每次返回两个值,前者是True或False,表示是否读取到了东西,如果全部帧都读完,或者这是个空盒子,那么就会返回(False,None),否则返回(True,<读到的帧>),帧的格式为图像矩阵。

(不过谁不想写 for frame in video 这种漂亮的语句呢?所以一会儿我会写个7行的迭代器)

按理说可以无聊地把帧再保存成图片(cv2.imwrite(<path>,<name>)),但为了介绍一下opencv-python的即时显示功能,这里会使用它来逐帧播放之前的test.avi。opencv-python可以很神奇地仅使用一个函数来创建一个专门用于显示图片的窗口:cv2.namedWindow(<窗口名称>),然后就可以用cv2.imshow(<窗口名称>, <图像>)来显示图片。但是默认情况下,图片会一闪而过,需要用cv2.waitKey(<延迟毫秒数>)来保持显示,此时程序会停留在这一行,即‘阻塞’。当全部帧播放完,可以调用cv2.destroyAllWindows()来删除刚才创建的窗口——虽然会自动删除,之前创建窗口也可以在imshow时自动创建——但是反正你已经知道了(笑)


完整代码:

import cv2

class A:
    def __init__(self,vc): self.vc = vc
    def __iter__(self): return self
    def __next__(self):
        success, frame = self.vc.read()
        if success: return frame
        else: raise StopIteration
        
vc = cv2.VideoCapture('test.avi')
cv2.namedWindow("frame")
for frame in A(vc):
    cv2.imshow("frame", frame)
    cv2.waitKey(500)
cv2.destroyAllWindows()


可能有些人会心血来潮,干脆做个视频播放器,只要把waitKey调低一点,不就相当于播放视频(而不是更像幻灯片)了么?但是假如真的遇上了avi,如何知道其帧率(fps),又成了问题。avi文件的属性里可以看到帧率,其实VideoCapture对象也可以获取到(毕竟fps的值肯定以某种方式存在文件本身里),而且可以获取一堆其他信息,通过VideoCapture对象的get方法。get方法接收整数,但是一般的用法形如:

vc = cv2.VideoCapture('test.avi')
fps = vc.get(cv2.CAP_PROP_FPS)

(一般全写成大写的东西用作常数,这种整数作常数的用法其实很常见)

fps是一个浮点数,意味着每秒不一定播放整数帧。

假如获取fps的语句,waitkey的参数改为int(1000/fps),就可以根据原视频文件的速度播放了。


至于能get多少属性呢?以下这个程序可以把按理可用的属性都打印出来,不过干什么的我就不确定了(笑):

"""打印出VideoCapture对象可以get的属性"""
import cv2
for s in dir(cv2):
    if s.startswith("CAP_PROP"):print(s)


“多用dir和help函数。”


(2018-7-20 于地球)

| 1
登录后可评论,马上登录吧~
评论 ( 1 )
至于处理图像嘛……就是处理矩阵,cv里的图像矩阵和numpy里的数组没有任何差别,既然本文已经给出了从视频中获取帧矩阵的方法,剩下的就靠自由发挥了……
回复
2个月前
相关推荐