Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

動画のダイジェスト画像

More than 1 year has passed since last update.

はじめに

この動画ファイルなんだっけ?と思うことが良くあり、
一発で確認できないものかと動画ファイルをダイジェスト的に画像を
並べて表示するものを作ってみました。

プログラム

videocap.py
import numpy as np
import cv2
import sys

# 画像の右下にテキストを描画
def drawTextRightBottom(image, text) :
    hmargin = 5
    color = (0,0,0)
    thickness   = 1
    fontFace    = cv2.FONT_HERSHEY_SIMPLEX
    fontScale   = 0.5
    image_size = image.shape[:2]

    # テキストの描画サイズ取得
    sz  = cv2.getTextSize(text, fontFace, fontScale, thickness)

    # テキスト描画位置
    x = image_size[1] - sz[0][0] - hmargin
    y = image_size[0] - sz[1]

    # 矩形描画位置
    rx = x 
    ry = image_size[0] - sz[0][1] - sz[1]

    cv2.rectangle(image, (rx, ry), (image_size[1]-1, image_size[0]-1), (255, 255, 255), -1, cv2.LINE_AA)
    cv2.putText(image, text, (x, y), fontFace, fontScale, color, thickness, cv2.LINE_AA)


cap = cv2.VideoCapture('D0002100117_00000_V_000.mp4')
if not cap.isOpened():
    sys.exit()

#動画プロパティ取得
v_w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))    # 縦の大きさ
v_h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))   # 横の大きさ
fps = cap.get(cv2.CAP_PROP_FPS)                 # フレームレート
fcnt =  cap.get(cv2.CAP_PROP_FRAME_COUNT)       # フレーム数

div_horizontal = 5  # タイリング横個数
div_vertical = 5    # タイリング縦個数

# 1タイル画像の縮小率
sc = min(1 / div_horizontal, 1 / div_vertical)
print("w=%d h=%d fps=%f fcnt=%d total=%f" % (v_w, v_h, fps, fcnt, fcnt/fps ))

loop_cnt = div_horizontal * div_vertical
resimage = np.zeros((v_h, v_w, 3),np.uint8)
resize_width = int(v_w * sc)
resize_height = int(v_h * sc)

for i in range(0, loop_cnt):
    # フレーム位置算出
    if i == 0:
        # 初回
        fpos = 0
        fpos2 = int((i+1 * fcnt) / loop_cnt)
        # 先頭だけ5秒を読み込み
        fpos1 = int(5.1 * fps)
        if fpos2 < fpos1 :
            fpos = int(fpos2 / 2)
        else:
            fpos = fpos1
    else:
        fpos = int(i * fcnt / loop_cnt)

    # フレームシーク 読み出し位置指定
    cap.set(cv2.CAP_PROP_POS_FRAMES, fpos)
    ret, frame = cap.read()

    # 画像縮小
    resize_img = cv2.resize(frame, None, fx=sc, fy=sc, interpolation=cv2.INTER_CUBIC)

    # 画像の描画位置計算
    iy = i // div_horizontal
    ix = i % div_horizontal
    y = iy * resize_height
    x = ix * resize_width

    # 画像の時間を右下に描画
    totalsec = fpos // fps
    mi = totalsec // 60
    se = totalsec % 60
    text = '%3d:%02d' % (mi, se)
    drawTextRightBottom(resize_img, text)

    # 画像転送
    resimage[y:y+resize_height, x:x + resize_width, :] = resize_img

    # 画像ファイル出力
    cv2.imwrite('resimage.jpg', resimage)

# 画像ファイル表示
cv2.imshow('Scene Tiling',resimage)
cv2.waitKey(0)

cap.release()
cv2.destroyAllWindows()

プログラム説明

特に難しいところは無いと思います。
OpenCVでビデオデータのシーク(シークと呼んでよいやら)
といいますか指定フレームを読みだすランダムアクセスについては、
ちょっと調べました。
フレーム番号をセットして読み込めば該当フレーム画像が読み込めます。
下記は、fposはフレーム番号

cap.set(cv2.CAP_PROP_POS_FRAMES, fpos)
ret, frame = cap.read()

各画像の右隅に経過時間を表示するようにしています。
timeモジュールを使えば、時間を計算しなくて良かったのでしょうが、
面倒だったので計算でやっつけました。

totalsec = fpos // fps
mi = totalsec // 60
se = totalsec % 60

動作環境

Anaconda
Python 3.7.4
OpenCV 3.4.1

結果画像

NHK CREATIVE LIBRARYの動画でテストを行いました。

結果画像 その1 天の橋立

天野橋立.jpg

結果画像 その2 日没

日没.jpg

まとめ

サンプルとしてはこんなところかと思います。
実用的に使用とすればGUIがあれば便利で、私はtkinterで画面作って使っています。

参照

NHK CREATIVE LIBRARY

sitar-harmonics
画像処理などのソフト作成や、たまに自動化装置の 制御設計,回路設計をおこなってます。
https://emotionexplorer.blog.fc2.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away