Summary
OpenCVでキャプチャ画像などを表示する時、cv2.imshow()はメインのループの中に記載する必要がある。子thread内に記載してもうまく表示されない。
結果として、デバイスが複数接続されていて、そのデバイスからの出力を複数のウィンドウでそれぞれ表示することができない。
いろいろな記事を検索するとthreadingでthreadを生成してもopencvのcv2.imshow()は 基本的にmainのthreadでしか動作しないとされている。 multiprocessでは動作するという記事もあるが、Macではうまくいかなかった。
結論としては、concurrent.futuresを使うと良いということがわかった。
concurrent.futuresを用いることで、複数のウィンドウでキャプチャ画像を表示することができた。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =======================================================
# concurrent.futuresを使ってcv2.imshow()で複数画面表示する
#
# concurrent_futures_cap.py
# coded by Noboru Harada (noboru@ieee.org)
#
# https://stackoverflow.com/questions/61008522/run-multiple-videos-through-multiprocessing-using-concurrent-futures
#
# Changes:
# 2021/07/15: First version
# =======================================================
import cv2
import concurrent.futures
def display_frames(name):
cap = cv2.VideoCapture(name)
while True:
ret, frames = cap.read()
if ret is False:
break
# Show frames for testing:
cv2.imshow(str(cap), frames)
cv2.waitKey(1)
cap.release()
return name
def main():
device_id = 0
names = [device_id, device_id]
with concurrent.futures.ProcessPoolExecutor(max_workers=2) as executor:
for name in executor.map(display_frames, names):
print(name)
cv2.destroyAllWindows() # For testing
# Using Python 3.6 there is an error: "TypeError: can't pickle cv2.VideoCapture objects"
if __name__ == '__main__':
main()
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# =======================================================
# concurrent.futuresを使ってcv2.imshow()で複数画面表示する
#
# concurrent_futures_ex.py
# coded by Noboru Harada (noboru@ieee.org)
#
# https://stackoverflow.com/questions/61008522/run-multiple-videos-through-multiprocessing-using-concurrent-futures
#
# Changes:
# 2021/07/15: First version
# =======================================================
import cv2
import numpy as np
import concurrent.futures
def display_frames(name):
cap = cv2.VideoCapture(name)
while True:
ret, frames = cap.read()
if ret is False:
break
# Show frames for testing:
cv2.imshow(str(cap), frames)
cv2.waitKey(100)
cap.release()
return name
def main():
names = ['test2.mp4', 'test3.mp4']
# Generate two synthetic video files to be used as input:
###############################################################################
width, height, n_frames = 640, 480, 30 # 30 frames, resolution 640x480
intput_filename1 = names[0]
intput_filename2 = names[1]
# Use MPEG4 codec (for testing)
synthetic_out = cv2.VideoWriter(intput_filename1, cv2.VideoWriter_fourcc(*'mp4v'), 25, (width, height))
for i in range(n_frames):
img = np.full((height, width, 3), 60, np.uint8)
cv2.putText(img, str(i+1), (width//2-100*len(str(i+1)), height//2+100), cv2.FONT_HERSHEY_DUPLEX, 10, (30, 255, 30), 20) # Green number
synthetic_out.write(img)
synthetic_out.release()
width, height, n_frames = 320, 240, 20 # 20 frames, resolution 320x240
synthetic_out = cv2.VideoWriter(intput_filename2, cv2.VideoWriter_fourcc(*'mp4v'), 25, (width, height))
for i in range(n_frames):
img = np.full((height, width, 3), 60, np.uint8)
cv2.putText(img, str(i+1), (width//2-50*len(str(i+1)), height//2+50), cv2.FONT_HERSHEY_DUPLEX, 5, (255, 30, 30), 10) # Blue number
synthetic_out.write(img)
synthetic_out.release()
###############################################################################
with concurrent.futures.ProcessPoolExecutor(max_workers=3) as executor:
for name in executor.map(display_frames, names):
print(name)
cv2.destroyAllWindows() # For testing
# Using Python 3.6 there is an error: "TypeError: can't pickle cv2.VideoCapture objects"
if __name__ == '__main__':
main()
うまくいった例はこちら