python  動画から熱帯魚が映った場面を抽出(基本)

import cv2, os

img_last = None # 前回の画像
no = 0 # 画像の枚数
save_dir = "./exfish" # 保存ディレクトリ名
os.mkdir(save_dir) # ディレクトリを作成

# 動画ファイルから入力を開始 --- (*1)
cap = cv2.VideoCapture("fish.mp4")
 while True:
  # 画像を取得
  is_ok, frame = cap.read()
   if not is_ok: break
  frame = cv2.resize(frame, (640, 360))
  # 白黒画像に変換 --- (*2)
  gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  gray = cv2.GaussianBlur(gray, (15, 15), 0)
  img_b = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1]
  # 差分を確認する
  if not img_last is None:
    frame_diff = cv2.absdiff(img_last, img_b) # --- (*3)
   cnts = cv2.findContours(frame_diff,
   cv2.RETR_EXTERNAL,
   cv2.CHAIN_APPROX_SIMPLE)[1]
   # 差分があった領域をファイルに出力 --- (*4)
    for pt in cnts:
     x, y, w, h = cv2.boundingRect(pt)
      if w < 100 or w > 500: continue # ノイズを除去
     # 抽出した領域を画像として保存
     imgex = frame[y:y+h, x:x+w]
     outfile = save_dir + "/" + str(no) + ".jpg"
     cv2.imwrite(outfile, imgex)
     no += 1
  img_last = img_b
cap.release()
print("ok")

*今回のプログラムでは魚以外のものも抽出してしまうため今後の記事でより詳細なプログラムを紹介していく。ただ、基本を抑える意味でこのプログラムについて触れていく。

 

 

osモジュールはOSに依存している様々な機能を利用するためのモジュール。

os.mkdir()は新しいディレクトリを作成する関数。

os.makedirs()という関数もあり、os.mkdir()では直上にディレクトリが存在しないとエラーになったり、現在存在しないディレクトリ直下に新規のディレクトリを作成することはできないがos.makedirs()では引数に何か指定したりすることでos.mkdir()でできないことができるようになる。

 

../で一つ上の階層を指定。二つ上の階層なら../../こう。

今回の場合は同じ階層なので./こう。

 

画像の取得

is_ok, frame = cap.read()
if not is_ok: break

read()は画像が取得できたかどうか(画像が取得できたらTrue、できなければFalse)と画像データを返すのでis_okにはTrue,False、frameには画像データが格納されている。

 

if not でis_okの値がFalseの時処理を実行するようにしている。

*画像にresizeに関しての補足

このリサイズでサイズ指定する時、元画像の高さ、幅から割合を計算して指定する。

今回の場合だと元の動画サイズが幅960✖️高さ540で 、1.7777......8: 1という割合になるのでそれを元に高さ300で考えるとそれに対応した幅を計算するには300✖️1.7777...8をすることで640という値を求めることができる。

白黒画像に変換

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (15, 15), 0)
img_b = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1]

グレースケールに変換。

cv2.GaussianBlur()でぼかし処理。これを行うことで物体の大枠の範囲がわかる。

cv2.threshold()で画像の二値化。白黒の画像データにして0と255のみのデータにしている。

 

前の画像との差分の確認

if not img_last is None:
 frame_diff = cv2.absdiff(img_last, img_b) # --- (*3)
 cnts = cv2.findContours(frame_diff,
 cv2.RETR_EXTERNAL,
 cv2.CHAIN_APPROX_SIMPLE)[1]
 # 差分があった領域をファイルに出力 --- (*4)
 for pt in cnts:
  x, y, w, h = cv2.boundingRect(pt)
   if w < 100 or w > 500: continue # ノイズを除去
 # 抽出した領域を画像として保存
  imgex = frame[y:y+h, x:x+w]
  outfile = save_dir + "/" + str(no) + ".jpg"
  cv2.imwrite(outfile, imgex)
  no += 1

if not img_last is None:では前回の画像にデータが存在しなければ実行しないようにしている。

cv2.absdiff()で前の画像と今の画像の差分を比較しcv2.findContours()で輪郭を抽出している。

その後輪郭に外接するデータをboundingRect()で取得、元画像が格納されているframeから取得したデータの座標を示す部分を切り抜き。

cv2.imwrite()でファイル書き出し。

 

img_last = img_b

この部分で現在の画像データを変数に格納し次の画像データの処理を行う。