1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

映像から数値を抽出する(Python)

Last updated at Posted at 2022-08-03

想定する場面

照度計や温度計といったデジタル表示のディスプレイを監視して記録する。
789D46C2-0FAC-445A-961D-EDF8ADEEA502.jpeg
(写真はイメージ)

プログラムの内容&流れ

既存のssocrというプログラムとpythonのコマンド操作機能を組み合わせて
映像から数値を認識してCSVに出力するプログラムを作成した。

1.動画から静止画を生成する。
2.静止画を回転させて数値が水平になる角度を探す。
3.ディスプレイ領域を把握する。
4.数値の抽出&CSVファイルへ書込み。

出力イメージ

スクリーンショット 2022-08-11 21-08-28.png

導入(Linux/Mac)

☆すべてターミナル上で実行

・ssocrのインストール

sudo apt-get update
sudo apt-get install libx11-dev
sudo apt-get install libimlib2-dev
git clone https://github.com/auerswal/ssocr.git
cd ssocr
make 
sudo mv ./ssocr /usr/local/bin/ssocr
ssocr --version

・pythonのインストール

sudo apt install python

ライブラリのインストール

pip install opencv-python

導入(Windows)

☆コマンドプロンプトとCygwinコンソールで実行

・Cygwinをインストール

インストールと同時に以下のパッケージをインストール
・git
・make
・gcc-core
・gcc-g++
・libintl-devel
・imlib2
・imlib2-devel
・cygutils-X11
※後でもインストール可能

・ssocrのインストール

git clone https://github.com/auerswal/ssocr.git
cd ssocr
make 
mv ./ssocr /usr/local/bin/ssocr
ssocr.exe --version

・Microsoft StoreのPythonをダウンロード&インストール

・ライブラリのインストール(コマンドプロンプトで)

pip install opencv-python

・ソースコードのsubprocess.run()のssocrをssocr.exeに変更する。

使い方

(Linux/Mac)
・デスクトップに抽出する動画を置く
・cdコマンドでmain.pyのあるディレクトリまで移動
・以下コマンド実行

python3 main.py

(Windows)
・デスクトップに抽出する動画を置く。
・cdコマンドでmain.pyのあるフォルダまで移動する。(Cygwinコンソールで)
・以下コマンドでpythonの実行ファイルのフルパスを調べる。

where.exe python

・python main.pyではなく以下コマンドで実行する。(Cygwinコンソールで)

pythonの実行ファイルのフルパス + main.py

ソースコード

main.py
import os
import subprocess
import cv2

class NumberExtraction(object):
    def __init__(self,base_path_name,movie_file_name):
        self.base_path_name   = base_path_name
        self.movie_file_name  = movie_file_name
        self.movie_path_name  = base_path_name + "/" + movie_file_name
        self.rotate_path_name = base_path_name + "/angle"
        self.divide_path_name = base_path_name + "/frame"
        self.csv_path_name    = base_path_name + "/extracted_number.csv"
        self.cap   = cv2.VideoCapture(self.movie_path_name)
        self.digit = len(str(int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))))

    def fps(self, movie_path_name):
        cap = cv2.VideoCapture(a.movie_path_name)  # 動画を読み込む
        video_fps = cap.get(cv2.CAP_PROP_FPS)  # フレームレートを取得する
        return video_fps

    def save_all_frames(self,dir_path, basename, ext='jpg'):
        subprocess.run(["mkdir", self.divide_path_name])
        if not self.cap.isOpened():
            return
        os.makedirs(dir_path, exist_ok=True)
        base_path = os.path.join(dir_path, basename)
        n = 1
        while True:
            print(f"フレーム分解中:{n}")
            ret, frame = self.cap.read()
            if ret:
                cv2.imwrite('{}_{}.{}'.format(base_path, str(n).zfill(self.digit), ext), frame)
                n += 1
            else:
                return

    def rotate_picture(self,start_number,change_angle):
        subprocess.run(["mkdir", self.rotate_path_name])
        path = self.divide_path_name + "/div_" + str(start_number).zfill(self.digit) + ".jpg"
        img = cv2.imread(path)
        height, width = img.shape[:2]
        img = cv2.resize(img,(round(width/4), round(height/4)))
        height, width = img.shape[:2]
        center = (width/2, height/2)
        i = 1
        while True:
            print(f"{i}度回転画像生成")
            rotate_matrix = cv2.getRotationMatrix2D(center=center, angle=i, scale=1)
            rotated_image = cv2.warpAffine(src=img, M=rotate_matrix, dsize=(width, height))
            cv2.imwrite(self.rotate_path_name +"/"+ str(i) + ".jpg",rotated_image)
            cv2.waitKey(0)
            cv2.destroyAllWindows()
            i += change_angle
            if i > 360:
                break

    def coordinate(self,start_number):
        print("ディスプレイ範囲の設定")
        print("1:左上と右下をクリックする")
        print("2:適当なボタンを押すと画面ビューが終了する")
        print("3:コンソールで半角スペースを入れて4つの数値を入力(例:279 180 733 447)")
        print("4:Enter")
        def onMouse(event, x, y, flags, params):
            if event == cv2.EVENT_LBUTTONDOWN:
                print(x,y)
        img = cv2.imread(self.divide_path_name + "/div_" + str(start_number).zfill(self.digit) + ".jpg")
        cv2.imshow('area_setting', img)
        cv2.setMouseCallback('area_setting', onMouse)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        x1,x2,y1,y2= input().split()
        trimming_area = [int(x1),int(x2),int(y1),int(y2)]
        return trimming_area

    def width_hight(self, trimming_area):
            w = trimming_area[2] - trimming_area[0]
            h = trimming_area[3] - trimming_area[1]
            wh = [w,h]
            return wh

    def extract_number(self, rotate, trimming_area, wh, start_frame, finish_frame, fps):
        # 回転角度
        r = str(360-rotate)
        # 座標指定
        x = str(trimming_area[0])
        y = str(trimming_area[1])
        w = str(wh[0])
        h = str(wh[1])
        i = start_frame
        count_null = 0
        count_extract = 0
        count_frames = 0
        while True:
            j = self.divide_path_name + "/div_" + str(start_frame).zfill(self.digit) + ".jpg"
            a = subprocess.run(["ssocr","-D","crop",x,y,w,h,"rotate",r,"-T","-d","-1",j],stdout=subprocess.PIPE)
            start_frame += fps
            #CSVファイルへの書き込み
            path = self.csv_path_name
            if start_frame / fps < 60:
                print(f"0:{str(int(start_frame / fps)).zfill(2)}")
                with open(path, "a") as file:
                    file.write(f"0:{str(int(start_frame / fps)).zfill(2)}")
                    file.write(",")
            else:
                print(f"{int(start_frame / fps) // 60}:{str(int(start_frame / fps) % 60).zfill(2)}")
                with open(path, "a") as file:
                    file.write(f"{int(start_frame / fps) // 60}:{str(int(start_frame / fps) % 60).zfill(2)}")
                    file.write(",")
            try:
                print(int(a.stdout))
                count_extract += 1
                count_frames += 1
                with open(path,"a") as file:
                    file.write(str(int((a.stdout))))
                    file.write("\n")
            except:
                print("NULL")
                count_null += 1
                count_frames += 1
                with open(path, "a") as file:
                    file.write("NULL")
                    file.write("\n")
            if start_frame > finish_frame:
                print(f"測定フレーム数:{count_frames}\n抽出成功フレーム数:{count_extract}\n抽出失敗フレーム数:{count_null}\n")
                print(f"抽出成功率:{int((count_extract/count_frames)*100)}%")
                break



desktop_path = input("デスクトップまでのパスを入力してください。>>")
movie_name = input("測定する動画のファイル名を入力してください。>>")
a = NumberExtraction(desktop_path,movie_name)
fps = int(a.fps(a.movie_path_name))
a.save_all_frames(a.divide_path_name,"div")
view_number = int(input("[frame]フォルダを確認してビューに使用するフレーム番号を入力してください。>>"))
a.rotate_picture(view_number,1)
trimming_area = a.coordinate(view_number)
wh = a.width_hight(trimming_area)
start_number = int(input("[frame]フォルダを確認して測定する最初のフレーム番号を入力してください。>>"))
finish_number = int(input("[frame]フォルダを確認して測定する最後のフレーム番号を入力してください。>>"))
angle = int(input("[angle]フォルダを確認して数値が水平となるファイル番号を入力してください。>>"))
a.extract_number(angle,trimming_area,wh,start_number,finish_number,fps)



※随時、変更・修正・追加しています。
指摘・不明点・要望などあれば気軽にメッセージください。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?