Pythonの覚書

Pythonの覚書として記録します。
よく利用するもの、ハマったものなどの覚書として、順次、追加していきます。
   動作環境は
    OS:BookwormPup64
    開発環境:VSCode
         Python:Python 3.11.2

別ページにあるもの。


・tkinterでファイルを選択し、パスとファイル名を取得

import os
import tkinter as tk
from tkinter import filedialog
'''
tkinterについて
python3より標準でインストールされているはずだが、BookwormPup64ではインストールされていなかった。
    python3-tk をパッケージマネージャでインストール(pipコマンドではインストールできない)

インストールされているか確認
    python -m tkinter --> tkinterのウィンドウが表示されたらインストールずみ
'''

# Tkinterウィンドウの作成
root = tk.Tk()
root.withdraw() # 非表示

# ファイル選択ダイアログの表示
file_path = filedialog.askopenfilename() # フルパス付きのファイル名

# os.path.sep                    : osの区切り文字
# os.path.dirname(file_path)     : dir名(パス)
# os.path.basename(file_path)    : ファイル名
# os.path.split(file_path)       : dirとファイル名 戻り値はタプル

file_dir,file_name=os.path.split(file_path)

print(file_dir+os.path.sep)      # --> /root/       (パス)
print(file_name)                 # --> test.mp4    (ファイル名)

・外部コマンドの実行

スクリプトの中から、外部コマンドを実行させたり、その結果を利用したい時がある。subprocess.run が便利である。

import subprocess

# pwdの実行
subprocess.run(['pwd']) # 実行されるが、pythonスクリプト内で利用できない

# 実行した結果を利用したいとき
# 実行結果をファイルにテキストとして保存 戻り値は stdout stderr returncode(成功:0)
# subprocess.run に渡す引数は、 ['ls','-l'] のようなlistとして渡す(ls -l の例)
#  .split()関数
#   空白、改行(\n)、タブ(\t)で分離し、リストとして戻り値を返す
#    cmd = 'ls -a'
#    cmd.split(' ') --> ['ls', '-a']
# コマンド実行中に、入力するものがあるときは、input='y' のように指定することで渡せる

# lsの実行
cmd = 'ls'          # 'ls *.mp4' のような「*」は利用できない
result = subprocess.run(cmd.split(),capture_output=True, text=True) 

# stdoutの中から、拡張子がmp4のファイルを出力
fnames=result.stdout.split('\n') # (sep='\n' は、区切り文字の指定)
for n in fnames:
    if n[-3:]=='mp4':
        print(n)

# stderr returncode の出力
print(f'stderr : {result.stderr} \n終了コード : {result.returncode}')

・動画のスロー再生(2024/10/17)

動画をスロー再生するためのスクリプト。使用モジュールは、OpenCVです。
クラス化したものは、
    https://note.com/izumi78/n/n4aff2128bfdb を参照

'''  2024/10/17
動画のスロー再生
  各種の設定すべき項目
    file_path = 'IMG_4938.MOV'          :  再生する動画のファイル名
    delay = 100    # [ms] スロー設定値   :  約 1000/(fps*delay) 倍速
    size = 0.33                               :  動画のサイズ変更(縦横の比率)
    start_time = 50 # 再生開始時刻[s]
    end_time = 70   # 再生終了時刻[s]
    time = [62,70,41,48,19,23] # チャプター時刻[s]  一時停止させたい時刻

  使用方法
    実行すると、再生開始時刻から再生終了時刻の間をスロー再生し、下記のタイミングで一時停止する
        (スロー再生は、deley[ms]分待って、1フレームごとに表示。フレームの補完処理はしていない)

    再生が一時停止する箇所
        スペースキーを押す
        チャプターの時刻
        最終フレーム

    一時停止中に、
        qで終了
        q以外のキーで継続再生
            (再生開始と再生終了の間を繰り返す)

'''

import cv2
import sys

# 各設定
file_path = 'IMG_4938.MOV' # カレントディレクトリ以外の場合は、パスを付加すること
delay = 100   # [ms] 待つ時間をセット スロー再生の速さを設定   約 1000/(fps*delay) 倍速
size = 0.33    # 拡大縮小サイズ

start_time = 60 # 再生開始時刻[s]
end_time = 70   # 再生終了時刻[s]
c_time = [62,70,41,48,19,23] # チャプター時間[s]  <-- この時刻で一時停止
c_time.sort()

# windowの名前 フレームを表示する時に必要 タイトル欄に表示される
window_name = 'frame'
                    
# 動画ファイルを開く
cap = cv2.VideoCapture(file_path) # <class 'cv2.VideoCapture'>
# オープンできないときは終了
if not cap.isOpened():
    print('ファイルをオープンできません。')
    sys.exit()
    
fps = cap.get(cv2.CAP_PROP_FPS)                           # fpsを取得
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)  # フレーム数を取得 float

print(f'総フレーム数 : {frame_count:.0f}     {frame_count/fps:.3f} [s]')

# チャプター時刻からフレーム番号を計算 --> 使用時は、popを使って捨てていく
frame_no = [] # チャプターをかけるフレーム番号
if len(c_time)>0:
    for t in c_time:
        frame_no.append(int(fps*t))

print(frame_no,' [frame]     ',c_time,' [s]')

# 再生制御のためのフレーム番号を設定
start_frame = int(fps*start_time) # 再生開始フレーム
end_frame = int(fps*end_time)     # 再生終了フレーム

# next_frame の設定(再生中、一時停止させるフレーム)
next_frame = frame_no.pop(0) # 取り出し捨てる
while True:
    if start_frame <= next_frame:    # チャプターをかけるフレームが、開始と終了フレームの間にある
        break
    elif len(frame_no) > 0:              # チャプター用の配列が空でない
        next_frame = frame_no.pop(0)    # 次の時刻を取り出し捨てる
    else:                                    # チャプター用の配列が空
        next_frame=end_frame
        break

# 繰り返し再生するためにbackupを取っておく    
next_frame0=next_frame
frame_no0 = frame_no.copy()

print(f'strat : {start_frame}\t end : {end_frame}\t next : {next_frame}')

# スローで再生
# 再生のための処理
cap.set(cv2.CAP_PROP_POS_FRAMES,start_frame) # 再生開始のフレームをセット
while True:
    recent_frame = cap.get(cv2.CAP_PROP_POS_FRAMES) # 現在のフレーム
    if recent_frame < frame_count: # 最終フレームを越えていない
        print(f'\r再生中 : {recent_frame:.0f} / {next_frame} [frame]\
              \t{recent_frame/fps:.3f} [s]',end='') # 有効桁:g
        
        # 1フレームを読み込む
        ret, frame0 = cap.read()
        # 読み込み成功の時、表示
        if ret:
            frame = cv2.resize(frame0, dsize=None, fx=size, fy=size)  # 等比拡大縮小
            #frame=frame0[50:150, 30:170]                               # フレームの部分抽出
            cv2.imshow(window_name, frame) # フレームの表示

    # 最終フレーム、チャプターをかけるフレーム、スペースキー入力で一時停止させるための処理
    #   スペースキー入力の入力待ち時間の調整で、スロー再生の速さ設定(delay はスロー設定値)
    #   停止中 qで終了 q以外で再生継続

    # 一時停止させる条件
    if recent_frame == end_frame or \
            recent_frame == next_frame or \
            cv2.waitKey(delay) & 0xFF == ord(' '): # スロー再生の設定を兼ねている(delay)
        
        print(f'\r一時停止(終了:q 継続:q以外) : {recent_frame:.0f} [frame]\
              \t{recent_frame/fps:.3f} [s]') # 有効桁:g
        
        # 終了の処理
        if cv2.waitKey(0) & 0xFF == ord('q'): # q で終了
            print(f'\n終了 : {recent_frame:.0f} [frame] \t{recent_frame/fps:.3f} [s]') # 有効桁:g
            break

        # 終了フレーム --> 繰り返し再生するための初期設定
        elif recent_frame == end_frame:
            cap.set(cv2.CAP_PROP_POS_FRAMES,start_frame) # 最初に戻す
            frame_no = frame_no0.copy()
            if len(frame_no) > 1:
                next_frame = frame_no.pop(0)
            else:
                next_frame = next_frame0

            print(f'strat : {start_frame}\t end : {end_frame}\t next : {next_frame}')

        # チャプターのフレーム --> 次のチャプターを設定        
        elif recent_frame == next_frame:
            if len(frame_no) > 0:
                next_frame = frame_no.pop(0) # 次のフレームをセット 0番目を取り出し、捨てる
            else:
                next_frame = frame_count-1 # 動画の最終フレーム

# ウィンドを閉じる
cv2.destroyWindow(window_name)

・エスケープシーケンスをVSCoede Jupyter で利用するときの注意点(2024/11/14)

ターミナルに出力されたものを書き直したくて、エスケープシーケンスを利用したスクリプトを作成していたときに、VSCoede Jupyterのセル出力では動作しないものがあったので、メモしておきます。(ターミナルでは動作します。)

動作しないか、変な動きをするエスケープシーケンスコード(CSI制御)
  カーソルの位置を制御したり、出力を消去するもの
    \033[1D \033[G \033[A \033[2K など
  
出力文字の着色などは問題なく動作する( \033[m )

改行をさせていなければ、\r を利用することでカーソル位置を行頭に戻すことが出きる。また、出力を消去するには、下記のように空白を出力することで消去したように見せかけることができる。

  s='\r'+' '*80+'\r' # 半角80個分を消すための文字列(上書き)

  print('123456',end='')       
  print(s,end='')  #  <<--- 現在の行出力をクリアし、行頭に戻す
       print('abc',end='')

または、IPython.displayの clear_output() を利用する。これは、セル出力を全て消去するメソッドであるので、実行後のprint()での出力は1行1列目からとなる。

from IPython.display import clear_output
import time

for i in range(10,0,-1):
    clear_output() # clsと同じ
    print(i)
    time.sleep(0.5)

この記事が気に入ったらサポートをしてみませんか?