即时在影片中绘图
这篇教学会延伸“侦测鼠标事件”和“合成半透明图片”两篇文章,实作在播放摄影机所拍摄的影片时,即时用鼠标在影片中绘图,最后将具有绘图过程的影片储存在电脑中。
因为程序中的 OpenCV 会需要使用镜头或 GPU,所以请使用本机环境 ( 参考:使用 Python 虚拟环境 ) 或使用 Anaconda Jupyter 进行实作 ( 参考:使用 Anaconda ) ,并安装 OpenCV 函数库 ( 参考:OpenCV 函数库 )。
使用鼠标在静态图像中绘图
下方的程序执行后,会使用 numpy 的 zeros 方法,建立一个 420x240 的黑色画布,接着搭配“侦测鼠标事件”文章中记录鼠标座标的技巧,就能使用 line 方法不断绘制两点间的直线 ( 如果使用画圆形,会因为鼠标移动的太快而产生不连续的现象 ),最后透过侦测键盘事件,按下 q 时结束动作,按下 r 时重新绘制。
import cv2
import numpy as np
dots = [] # 建立空串列記錄座標
w = 420
h = 240
draw = np.zeros((h,w,4), dtype='uint8') # 建立 420x240 的 RGBA 黑色畫布
def show_xy(event,x,y,flags,param):
global dots, draw # 定義全域變數
if flags == 1:
if event == 1:
dots.append([x,y]) # 如果拖曳滑鼠剛開始,記錄第一點座標
if event == 4:
dots = [] # 如果放開滑鼠,清空串列內容
if event == 0 or event == 4:
dots.append([x,y]) # 拖曳滑鼠時,不斷記錄座標
x1 = dots[len(dots)-2][0] # 取得倒數第二個點的 x 座標
y1 = dots[len(dots)-2][1] # 取得倒數第二個點的 y 座標
x2 = dots[len(dots)-1][0] # 取得倒數第一個點的 x 座標
y2 = dots[len(dots)-1][1] # 取得倒數第一個點的 y 座標
cv2.line(draw,(x1,y1),(x2,y2),(0,0,255,255),2) # 畫直線
cv2.imshow('oxxostudio', draw)
cv2.imshow('oxxostudio', draw)
cv2.setMouseCallback('oxxostudio', show_xy)
while True:
keyboard = cv2.waitKey(5) # 每 5 毫秒偵測一次鍵盤事件
if keyboard == ord('q'):
break # 如果按下 q 就跳出
if keyboard == ord('r'):
draw = np.zeros((h,w,4), dtype='uint8') # 如果按下 r 就變成原本全黑的畫布
cv2.imshow('oxxostudio', draw)
cv2.destroyAllWindows()
即时在摄影机影片中绘图
搭配“写入并储存影片”和“合成半透明图片”的范例,读取摄影机的即时图像,将图像转换为 BGRA 色彩模式,根据绘图画布的 alpha 色版数值进行合成,就能实现即时在影片中画图的效果 ( 最后可将整个过程储存为新的影片 )。
import cv2
import numpy as np
cap = cv2.VideoCapture(0) # 讀取攝影鏡頭
w = 420
h = 240
draw = np.zeros((h,w,4), dtype='uint8')
fourcc = cv2.VideoWriter_fourcc(*'MJPG') # 設定輸出影片的格式為 MJPG
out = cv2.VideoWriter('output.mov', fourcc, 20.0, (w, h)) # 產生空的影片
if not cap.isOpened():
print("Cannot open camera")
exit()
def show_xy(event,x,y,flags,param):
global dots, draw
if flags == 1:
if event == 1:
dots.append([x,y])
if event == 4:
dots = []
if event == 0 or event == 4:
dots.append([x,y])
x1 = dots[len(dots)-2][0]
y1 = dots[len(dots)-2][1]
x2 = dots[len(dots)-1][0]
y2 = dots[len(dots)-1][1]
cv2.line(draw,(x1,y1),(x2,y2),(0,0,255,255),2)
cv2.imshow('oxxostudio', draw)
cv2.setMouseCallback('oxxostudio', show_xy)
while True:
ret, img = cap.read() # 讀取影片的每一個影格
if not ret:
print("Cannot receive frame")
break
img = cv2.resize(img,(w,h)) # 縮小尺寸,加快運算速度
# 透過 for 迴圈合成影像
for i in range(w):
img[:,i,0] = img[:,i,0]*(1-draw[:,i,3]/255) + draw[:,i,0]*(draw[:,i,3]/255)
img[:,i,1] = img[:,i,1]*(1-draw[:,i,3]/255) + draw[:,i,1]*(draw[:,i,3]/255)
img[:,i,2] = img[:,i,2]*(1-draw[:,i,3]/255) + draw[:,i,2]*(draw[:,i,3]/255)
keyboard = cv2.waitKey(5)
if keyboard == ord('q'):
break
if keyboard == ord('r'):
draw = np.zeros((h,w,4), dtype='uint8')
cv2.imshow('oxxostudio', img)
out.write(img) # 儲存影片
out.release() # 釋放資源
cap.release() # 釋放資源
cv2.destroyAllWindows()
微信扫码关注
抖音扫码关注