这次将做一个简单的运动追踪,可以说是对研究智能算法没啥帮助,仅仅看几个opencv-python提供的函数而已。也不会有第三个教程了……
以下代码可以生成一个avi视频:
import cv2
import numpy as np
class Thing_Frame: # 其对象能产生方块碰撞动画的每一帧(矩阵)
def __init__(self,size:"图像尺寸元组",r:"方块中心到顶点的距离",start:"起始位置元组",step:"每一帧移动几个像素"):
self.sizex = size[0]
self.sizey = size[1]
self.step = step
self.r = r
self.vx = 1 # 初始速度(绝对值必须是1)
self.vy = 1
self.x = start[0]
self.y = start[1]
assert not self.collidex()
assert not self.collidey()
def collidex(self):return False if self.r-1 < self.x < self.sizex-self.r else True
def collidey(self):return False if self.r-1 < self.y < self.sizey-self.r else True
def __iter__(self):
while True:
canvas = np.ones((self.sizey,self.sizex,3),dtype='uint8')*255
x,y,r = self.x, self.y, self.r
for dx in range(r):
for dy in range(r-dx):
canvas[y+dy][x+dx] = (0,0,0)
canvas[y+dy][x-dx] = (0,0,0)
canvas[y-dy][x-dx] = (0,0,0)
canvas[y-dy][x+dx] = (0,0,0)
yield canvas
for _ in range(self.step):
if self.collidex():self.vx *= -1 # 若碰撞则反向
if self.collidey():self.vy *= -1
self.x += self.vx
self.y += self.vy
if __name__ == "__main__":
SIZE = (600,400)
video=cv2.VideoWriter("thing.avi", cv2.VideoWriter_fourcc('D','I','V','X'), 30, SIZE)
for canvas,_ in zip(Thing_Frame(size=SIZE,r=70,start=(200,125),step=5),range(300)):
video.write(canvas)
video.release()
如果你有一个不错的代码编辑器,可以把类折叠掉不看,毕竟一点都不重要……(其实这一整段都不重要,不过对python中迭代器有兴趣而不精通的,可以看看)
然后获得了视频文件。这个运动检测的原理十分简单,【仅仅是比对每两帧之间不同的像素而已】,还有一些用于模糊和强调的操作,毕竟视频不像矢量动画这么清晰。整个流程表述为:
先把图像化为灰度图(不涉及颜色,方便处理)并模糊,然后把到这一步的结果逐帧比对(仅仅把相邻两帧的灰度作差,再取绝对值,未改变的像素会呈现黑色),再使高于阈值(比如设阈值为25)的像素全白、低于阈值的像素全黑,最后把得到的白色部分周围加一圈像素(我也不知道有什么用)——cv提供了一个函数来把白色部分框起来,框到的就大致是运动部分了。
代码如下:
import cv2
import numpy as np
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
def f0(img): # -灰度->
return cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
def f1(img): # -高斯模糊->
return cv2.GaussianBlur(img,(21,21),0)
def f2(img,lastimg): # -邻帧差->
return cv2.absdiff(lastimg,img)
def f3(img): # -阈值->
return cv2.threshold(img, 25, 255, cv2.THRESH_BINARY)[1]
def f4(img): # -膨胀->
return cv2.dilate(img, None, iterations=2)
a = A(cv2.VideoCapture("thing.avi"))
lastimg2=f1(f0(next(a)))
for frame in a:
img2 = f1(f0(frame))
img3 = f2(img2,lastimg2)
img5 = f4(f3(img3))
x,y,w,h=cv2.boundingRect(img5)
frame=cv2.rectangle(frame,(x,y),(x+w,y+h),(0,0,255),2)
cv2.imshow("frame", frame)
cv2.imshow("img3", img3)
if cv2.waitKey(1)&0xFF == ord("q"):break
lastimg2 = img2
a.vc.release()
cv2.destroyAllWindows()
呃……自己看吧。另外cv2.rectangle函数绘制矩形的方式是,接收绘制前的图像(比如全白图像),返回绘制后的图像。
有摄像头的朋友试试把 cv2.VideoCapture 的参数改成0?
(2018-7-24 于地球)