concurrent.futuresを使ってopencvのcv2.imshow()画面を複数表示する(2021.7.15)

Summary

OpenCVでキャプチャ画像などを表示する時、cv2.imshow()はメインのループの中に記載する必要がある。子thread内に記載してもうまく表示されない。
結果として、デバイスが複数接続されていて、そのデバイスからの出力を複数のウィンドウでそれぞれ表示することができない。

いろいろな記事を検索するとthreadingでthreadを生成してもopencvのcv2.imshow()は 基本的にmainのthreadでしか動作しないとされている。 multiprocessでは動作するという記事もあるが、Macではうまくいかなかった。

結論としては、concurrent.futuresを使うと良いということがわかった。

concurrent.futuresを用いたcv2.imshow()の並列実行

うまくいった例

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()

うまくいかなかった例

参考

concurrent.futures

うまくいった例はこちら

その他の屍たち

一般的なマルチタスク処理の解説

Back to Index