ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【Blender】元動画ファイルのフレームレートを変更して出力する

 今回は久しぶりにBlenderの記事になります。

 タイトルにある通り、Blenderで元動画ファイルのフレームレートを変更して出力する方法になります。


 では、始めます。


・フレームレート変更方法
 今回は「30fpsの動画ファイルを24fpsに変換する」前提で進めます。

 最初にBlenderを起動させて適当な名前を付けてファイルを保存します。

 保存できたら画面上部にあるタブの「+」をクリックします。

 出てきた中から「Video Editing」→「Video Editing」を選択します。

 以下のようにビデオ編集画面になります。

「Add」→「Movie」を選択してフレームレートを変更したいファイルを読み込みます。

 読み込ませると以下のようになります。ちなみに青色が映像部分、緑色が音声部分になっています。

「Output Properties」のアイコンをクリックし、Frame Rateのプルダウンから変更したいfpsを選択します。今回は24fpsに変換したいので「24」を選択します。

 読み込んだ動画の音声部分が指定したフレームレートに合わせて自動的に短くなります。

 次に青色の映像部分をクリックした状態で右側にある「Time」→「Duration」のところに短くなった音声のフレーム数と同じ値を入力します。今回は192なのでその値を入力します。

 音声がない場合は設定する値は以下の計算式を入力します。

元の総フレーム数 / 元フレームレート * 変換後フレームレート

 なので今回の場合は以下の計算式をDurationに入力することになります。

241/30*24

 これで映像の方も同じフレーム数分再生するようになりました。

 ただしこの状態では映像は元のフレームレート数分のままなので、速度を調節する必要があります。

「Add」→「Effect Strip」→「Speed Control」を選択します。

 Speed Controlが追加されるのでそれを選択した状態で「Channel」に映像のChannel番号を入力します。

 映像にSpeed Controlが設定され、対応するフレーム数にちょうど合うようなスピードにしてくれます。

 これで映像自体のフレームレート変更が終わったので、後は動画として書き出すだけです。

「Output Properties」のFrame Rangeの「End」に出力する最後のフレーム数を設定します。

 動画として書き出すために「Output」の各項目を設定します。今回は音声を消したかったので「No Audio」を選択しています。

 準備ができたので、「Render」→「Render Animation」で動画として書き出します。

 これで元動画ファイルを24fpsに変換したmp4が出力されます。


 以上が元動画ファイルのフレームレートを変更して出力する方法になります。

 久しぶりにBlenderを触ってみようと思い立って最新版をダウンロードしたところ、3.1とかなりバージョンが進んでいて浦島太郎状態だったので、これからはサボらずに定期的にまたBlenderを触っていきたいですね…。


・参考資料

【Windows】Windows11の右クリックのメニューをWindows10の仕様に戻す

 自分は自作PCの方をWindows11にしているのですが、ファイルを右クリックをした時のメニューがWindows10の時と変わっていて使いにくいと感じていました。

 調べたところ元の右クリックメニューに戻す方法があることがわかったので今回はその備忘録になります。
 基本的には参考資料に挙げているページ様と同じになるので、詳しくはそちらを参照してください。

 では、始めます。


・右クリックのメニューを戻す
 方法としてはレジストリを編集することになります。

 検索を開いて「regedit」と入力して出てきた「レジストリエディター」をクリックして起動させます。

 起動させたらパスを書いてあるところに以下の内容をコピーして貼り付け、Enterキーを押下します。

コンピューター\HKEY_CURRENT_USER\Software\Classes\CLSID


「CLSID」が選択された状態になるので、「右クリック」→「新規」→「キー」を選択します。

 新たなキーが作成されるので、選択された状態で名前を以下のものに変更します。

{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}


 作成したものを選択した状態で「右クリック」→「新規」→「キー」を選択します。

 新たなキーを以下の名前で作成します。

InprocServer32


 作成したキーの「既定」をダブルクリックして表示された「値のデータ」に何も入力されていないことを確認して「OK」をクリックします。

 既定のデータ部分が空になっていることを確認します。

 最後にWindowsを再起動すれば、右クリックのメニューが以前のものに戻っています。

 また、Windows11のデフォルトの状態に戻したい場合は、作成した以下のキーを削除することでデフォルトに戻せます。

{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}


 以上がWindows11の右クリックのメニューをWindows10の仕様に戻す方法になります。

 右クリックのメニューに追加しているソフトとかもあるので、Windows11になるとそれが表示されなくなるのは困るのでもっと簡単に設定画面などから簡単に切り替えられるようになっていてほしいですね…。


・参考資料

【python/PySide6】printの内容をGUIに表示させる(PySide6でのバックグラウンド処理)

 以前PySide6を導入して簡単なGUIを作る記事とprintの内容をファイルに出力する方法の記事を書きました。


 今回はこれらの応用としてPySide6を使ってprintの内容をGUIに表示させる方法の備忘録になります。


 では、始めます。


1:PySide6でバックグラウンド処理を行う
 PySide6で何かの処理中に画面を随時更新する場合にはバックグラウンド処理を行う必要があるので、まずはその説明をします。

 基本的にはバックグラウンド処理したいクラスにSignalのスレッドを定義し、emit関数を使ってSignalの値を呼び出し側へ渡します。そして呼び出し元でそのSignalの値を取り出して処理していくという流れです。

 ちゃんと書くとそこそこ長くなるので、以下に要点だけを抽出した簡単なソースコードを記述しておきます。このソースコード単体では動かないので注意してください。

# メイン処理側
# スレッドクラスの準備
sub_thread = SubThread()
sub_thread.connect(get_signal)  # シグナルの接続
sub_thread.finished.connect(finish_thread) # スレッドが終了した際の処理の接続

def get_signal(signal_str):
    # シグナルを受け取った時の処理
    print(signal_str)

def finish_thread():
    # バックグラウンド処理が終わった時の処理
    print("finished")


# バックグラウンド処理側(Signalを送る側)
class SubThread(QThread):
    sub_signal = Signal(str) # Signalでstr型を送る

    def __init__(self, parent=None):
      QThread.__init__(self, parent)

    def send_signal(self):
      self.sub_signal.emit("test") # Signalで送信

 メイン側でバックグラウンド処理のクラスのオブジェクトを作成し、そこに「connect」関数でシグナルを受け取る処理の関数を設定します。「finished.connect」で処理が終了した後の処理も設定できます。
 そしてバックグラウンド処理のクラスではSignalをクラス変数として定義し、「emit」関数を設定することで値を渡すことができます。

 これでバックグラウンド処理がわかったので、この方法を使ってprintの内容をGUIに表示させるコードを書いていきます。


2:printの内容をGUIに表示させるサンプル
 今回はループでprintを行うpythonファイルをGUIのファイルから呼び出すという形でサンプルを作成しました。

 ループでprintを行うファイルは以下の通りです。

・print_log.py

#-*- coding:utf-8 -*-
import sys
from time import sleep

def count_up(num, count):
    print("--- start ----")

    for i in range(0, count):
        print(num + i)
        sleep(0.5)

    print("--- end ----")

if __name__ == '__main__':
    count_up(1, 10)


 GUIのファイルは以下の通りです。

gui_test.py

#-*- coding:utf-8 -*-
import sys, threading, re
from copy import deepcopy
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from PySide6.QtGui import *

import print_log


class Form(QDialog):
    """ GUIクラス
    """

    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

        # Widgetsの設定(タイトル、固定横幅、固定縦幅)
        self.setWindowTitle("Title test")
        self.setFixedWidth(400)
        self.setFixedHeight(260)

        # num入力部分
        num_layout = QHBoxLayout()
        self.num_edit = QLineEdit("") # テキスト入力
        num_layout.addWidget(QLabel("num :"), 1)
        num_layout.addWidget(self.num_edit, 4)

        # count入力部分
        count_layout = QHBoxLayout()
        self.count_edit = QLineEdit("") # テキスト入力
        count_layout.addWidget(QLabel("count :"), 1)
        count_layout.addWidget(self.count_edit, 4)

        # ログ表示部分
        self.text_layout = QHBoxLayout()
        self.textbox = QListView()
        self.text_list = QStringListModel()
        self.textbox.setModel(self.text_list)
        self.text_layout.addWidget(self.textbox)

       # プログレスバー部分
        pb_layput = QHBoxLayout()
        self.pb = QProgressBar()
        self.pb.setFixedWidth(370)
        self.pb.setTextVisible(False)
        pb_layput.addWidget(self.pb)

        # ボタン部分
        run_layout = QHBoxLayout()
        self.run_button = QPushButton("start")
        self.run_button.clicked.connect(self.run_log)
        run_layout.addWidget(QLabel(""), 2)
        run_layout.addWidget(self.run_button, 1)
        run_layout.addWidget(QLabel(""), 2)

        # レイアウトを作成して各要素を配置
        layout = QVBoxLayout()
        layout.addLayout(num_layout)
        layout.addLayout(count_layout)
        layout.addLayout(self.text_layout)
        layout.addLayout(pb_layput)
        layout.addLayout(run_layout)

        # レイアウトを画面に設定
        self.setLayout(layout)

        # ログスレッドクラスの準備
        self.lp = LogThread()
        self.lp.log_thread.connect(self.show_log)  # シグナルスロットの接続
        self.lp.finished.connect(self.show_result) # スレッドが終了した際の処理の接続


    def run_log(self):
        # ログプロセスを実行する
        num = self.num_edit.text()
        count = self.count_edit.text()

        if self.is_number(num) and self.is_number(count):
            # GUIを非活性にする
            self.set_all_enabled(False)
            # プログレスバーの開始
            self.pb.setMinimum(0)
            self.pb.setMaximum(0)
            # 値を設定
            self.lp.set_count(int(num), int(count))
            # ログプロセスを実行する
            self.lp.start()
        else:
            # 入力値エラーとしてダイアログ表示
            QMessageBox.warning(self, "注意", "numとcountには正の半角整数を入力してください。")


    def is_number(self, number):
        # 正規表現で数字だった場合はTrue/そうでない場合はFalse
        if re.fullmatch(r"[0-9]+", number) is None:
            return False
        else:
            return True


    def show_log(self, log):
        # 書き込み中の進捗をGUIに表示する
        log_list = self.text_list.stringList()
        log_list.append(str(log))
        self.text_list.setStringList(log_list)
        self.textbox.scrollToBottom()


    def show_result(self):
        # 結果を表示する
        QMessageBox.information(self, "終了", "終了しました。")
        # プログレスバーの停止
        self.pb.setMinimum(0)
        self.pb.setMaximum(100)
        self.set_all_enabled(True) # GUIの表示を戻す


    def set_all_enabled(self, flg):
        # GUIの有効/無効を設定する
        self.num_edit.setEnabled(flg)
        self.count_edit.setEnabled(flg)
        self.run_button.setEnabled(flg)


class LogThread(QThread):
    """ ログファイルを読み取るクラス
    """
    log_thread = Signal(str)
    log_file_path = "./log.txt"
    read_flg = False
    num = 0
    count = 0


    def __init__(self, parent=None):
        """ コンストラクタ
        """
        QThread.__init__(self, parent)


    def __del__(self):
        # Threadオブジェクトが削除されたときにThreadを停止
        self.wait()


    def set_count(self, num, count):
        self.num = num
        self.count = count


    def run(self):
        # 標準出力の出力先をファイルにする
        sys.stdout = open(self.log_file_path, "w")

        # ログファイルを読み取るスレッドを開始
        self.read_flg = True
        read_thread = threading.Thread(target=self.read_log)
        read_thread.setDaemon(True)
        read_thread.start()

        try:
            # 別ファイルのprintする処理を開始
            print_log.count_up(self.num, self.count)
        except Exception as e:
            print(e)
        finally:
            # 標準出力を元に戻す
            sys.stdout.close()
            sys.stdout = sys.__stdout__
            self.read_flg = False


    def read_log(self):
        # 書き込み時のログファイルを読込み差分の行をSignalのemitに設定する関数
        old_lines = list()
        new_lines = list()

        while self.read_flg:
            with open(self.log_file_path, "r", encoding="utf-8") as f:
                sys.stdout.flush() # このflushの記述がないと処理途中でログ出力されない

                # listなので書き換えられないようdeepcopy
                new_lines = deepcopy(f.readlines())
                old_size = len(old_lines)
                new_size = len(new_lines)

                if  old_size < new_size:
                    # 差分の行をSignalで値を渡す
                    for i in range(old_size, new_size):
                        self.log_thread.emit(new_lines[i].replace("\n",""))
                    old_lines = deepcopy(new_lines)


if __name__ == '__main__':
    # Qtアプリケーションの作成
    app = QApplication(sys.argv)

    # フォームを作成して表示
    form = Form()
    form.show()

    # 画面表示のためのループ
    sys.exit(app.exec())

 上記2ファイルを以下のように同階層に配置します。

 gui_test.pyの方を実行すると以下のようにGUIが表示されるのでnumとcountのところに半角数字を入力します。

 startボタンを押下して実行すると画面が非活性になりリアルタイムでログの内容が表示され、処理が終わると終了ダイアログが表示されます。


 詳しくはソースコードを読んでいただきたいのですが、少し解説すると、ログの書き出しを行っている時にログファイルを開いてその行数を取得し、以前の行数と差分があれば差分の行を1行ずつSignalで送っているという処理を行っています(read_log関数)。

 重要なのが「sys.stdout.flush() 」という一文で、これをどこかに書いていないとprintの内容がリアルタイムでGUIに反映されず、処理が終わった後にまとめてGUIに反映されるという動きになります。
 この理由は、printのみではバッファに溜まり続けるだけで出力されないため、flushでバッファの内容を出力してあげる必要があるからです。


 以上がPySide6を使ってprintの内容をGUIに表示させる方法になります。

 Signalを使うのが初めてだったりflushの必要性を知らなかったりと割と手こずりましたが、GUIのバックグラウンド処理やリアルタイムに反映する記述方法がわかったので色々と楽しいことに使えそうです。


・参考資料

【python】printの表示内容をファイルに出力する

 今回はタイトルにあるようにprintでの表示内容をファイルに出力する方法の備忘録になります。

 少し特殊ですが「printの内容をファイルにログ的なものとして残しておきたいが、そのprintの内容がライブラリになっている」など簡単に処理を追加しにくい場合に今回の内容が必要になってくるかと思います。


 では、始めます。


1:printの内容をファイルに出力する
 やり方は簡単で以下の記述でできます。

import sys

# 表示内容の出力をファイルに変更
sys.stdout = open("test.log", "w")

# ファイルを閉じて標準出力を元に戻す
sys.stdout.close()
sys.stdout = sys.__stdout__

 sys.stdoutがprintを実行した際の出力先なので、openでファイルを指定します。その後は通常通りにprint関数を使うとコンソールには表示されず、指定したファイルに内容が書き込まれます。
 printの出力をコンソールに戻す場合は一度「sys.stdout.close()」した後に「sys.stdout = sys.__stdout__」することで元に戻すことができます。


2:サンプルコード
 特に難しいところはないと思いますが、実際にサンプルコードを書いて動かしてみます。

・print_log.py

#-*- coding:utf-8 -*-
import sys


def main():
    print("---- start ----")

    log_file_path = "./log.txt"
    sys.stdout = open(log_file_path, "w") # 表示内容の出力をファイルに変更

    loop_print(10) # ファイルに出力される

    # ファイルを閉じて標準出力を元に戻す
    sys.stdout.close()
    sys.stdout = sys.__stdout__

    loop_print(20) # コンソールに表示される

    print("---- end ----")


def loop_print(num):
    for i in range(0, 10):
        print(str(num + i))


if __name__ == '__main__':
    main()


 実行すると以下のように20~29の表示しかコンソールには表示されません。

 そして作成されたlog.txtファイルを開くと以下のように10~19が記述されています。

 このようにちゃんとファイルに出力する部分とコンソールに表示させる部分を切り替えられていることがわかります。


 以上がpythonでprintの表示内容をファイルに出力する方法になります。

 この方法を使うのはかなり特殊な場合かとは思いますが、この方法を使いたい時が自分に発生したので記事にした次第です。


・参考資料

【python】プラットフォームを判別する

 pythonを書いている中でマルチプラットフォームでの実行を考えた場合、プラットフォーム(システム)毎に処理を変更する必要が出てくることがあります。

 今回はそのプラットフォームを判別する方法になります。


 では、始めます。


1:プラットフォームを取得する
 方法は簡単で以下の記述で実行しているプラットフォームの文字列を取得することができます。

import sys

platform_str = sys.platform
print(platform_str)

 このsys.platformでプラットフォーム毎に取得される文字列は以下の通りです(参考ページより引用)。

システム platformの値
AIX 'aix'
Linux 'linux'
Windows 'win32'
Windows/Cygwin 'cygwin'
macOS 'darwin'

 pythonのバージョンによっては取得できる文字列などに変更がある場合があるので注意してください。


2:プラットフォームを判別する
 多くはWindowsMacOSLinuxぐらいの判別ができればよいかと思うので、以下がそのサンプルになります。

・judge_platform.py

#-*- coding:utf-8 -*-
import sys

if sys.platform.startswith('win'):
  print("Windows")
elif sys.platform.startswith('darwin'):
  print("MacOS")
elif sys.platform.startswith('linux'):
  print("Linux")

 pythonのバージョンによっては取得できる文字列が少し変更されてきた過去があるので、startswithを使って最初の文字列で判別するのが良いようです。


 以上がpythonでプラットフォームを判別する方法になります。

 判別自体は簡単ですが、取得できる文字列を残しておきたかったので記事にした次第です。
 特にGUIなどはプラットフォームによってはかなり違ってくるので、GUIのレイアウトや内容をプラットフォーム毎に切り替えたりする場合には必要になってくるのかなと思います。


・参考資料

【Windows/Android】AndroidをWindowsのサブモニターにする

 最近ネットでの打合せの際に複数画面があると便利なのでモバイルモニターを使う機会が増えてきました。

 その他の用途で「モバイルモニターほどの大きさはなくていいけど、ちょっとしたサイズのサブモニターが欲しい」という場面も出てきたので調べてみると、WindowsAndroidであれば無料ソフトを使ってAndroidをサブモニター化することができるとわかりました。

 今回はその備忘録になります。基本的には参考資料に挙げているページ様の内容そのままなので、詳しくはそちらを参照してください。


 では、始めます。


1:Windows用spacedeskをインストールする
 まずはWindows側の準備として「spacedesk」をインストールします。

 以下の公式ページを開きます。

 ページを開くと左下あたりに「DOWNLOAD」ボタンがあるのでクリックします。
f:id:rikoubou:20220401150444p:plain

 対象のOSを選択してクリックするとインストーラがダウンロードされます。自分の場合はWindows10の64bitなのでそれを選びました。
f:id:rikoubou:20220401150709p:plain

 ダウンロードしたインストーラを起動させます。
f:id:rikoubou:20220401150902p:plain

 インストーラを起動させると以下のような画面が表示されるので「Next」をクリックします。
f:id:rikoubou:20220401151024p:plain

 ライセンスの同意のところにチェックを入れて「Next」をクリックします。
f:id:rikoubou:20220401151119p:plain

 インストール先を指定して「Next」をクリックします。特に変える必要はなくデフォルトのままでOKです。
f:id:rikoubou:20220401151227p:plain

 ファイアウォール除外の設定についての画面になりますが、ここでチェックが入っていることを確認して「Next」をクリックします。ここでチェックが入っていないとファイアウォールによって動かない場合があるようです。
f:id:rikoubou:20220401151406p:plain

 あとは次の画面で「Install」ボタンをクリックするとインストールが開始されます。

 spacedeskを起動させて以下のような画面が出てくればインストール完了です。
f:id:rikoubou:20220401151556p:plain


2:Android用spacedeskアプリをインストールする
 Windows側の準備ができたので、次はAndroid側の準備をしていきます。

 こちらは簡単で「Google Playストア」を立ち上げて「spacedesk」と入力して出てきたアプリをインストールするだけです。

 spacedeskアプリを立ち上げて以下のような画面が出てくればインストール完了です。
f:id:rikoubou:20220401152722j:plain


3:Androidをサブモニター化する
 1、2の準備が出来たらWindows機とAndroid端末の両方を同じLAN内になるようネットワークに接続します。

 その状態でWindows側のspacedeskを起動させてspacedesk Statusを「ON」にします。
f:id:rikoubou:20220401153102p:plain

 次にAndroid側のspacedeskを起動させると同じLAN内で端末を自動検索してくれるので、対象の端末を選択します。
f:id:rikoubou:20220401153402j:plain

 これでAndroid端末をサブモニター化することができます。あとの操作はWindows側で他のディスプレイを接続した時と同じように操作して表示することができます。

 ただし画面解像度についてはAndroid端末依存になるため、スマホなどの場合はかなり横長や縦長のサイズになるので注意してください。

 サブモニター化をやめたい場合はWindows側のspacedesk Statusを「OFF」にします。
f:id:rikoubou:20220401153709p:plain

 また未検証ですが、スマホ側でアプリを強制終了させるとフリーズしてしまうようなのでWindows側でStatusを「OFF」にして切断するようにした方が良いらしいです。
 

 以上がAndroidWindowsのサブモニターにする方法になります。

 スマホではなくタブレットAndroidであればそれなりの画面サイズになるので、それを相手に見せつつ画面操作をして情報を共有するみたいな使い方もできて割と便利だと思います。


・参考資料

【python/PySide6】PySide6の導入と簡単なGUIを作ってみる

 以前のtkinerを使ってpythonGUIを作る記事を書きました。

 基本的にデフォルトで入っていてちょっとしたGUIならすぐに作れて便利ではあるのですが、自由度というか書いていく内に若干の使い難さを感じるようになっていました。

 この他にも何かいいライブラリはないかと思って探していたところ、PySide6というQtを使ったものが良さそうだと感じたので今回はそのインストール方法と簡単なGUIを作ってみた備忘録になります。
 また今回はWindowsでの方法になるので注意してください。


 では、始めます。


1:PySide6のインストール方法
 インストール方法は簡単で以下のpipコマンドでインストールします。

$ pip install pyside6

 ちゃんとPySide6が使えるかの確認は以下のプログラムをhello.pyとして保存し実行することで確認できます。

・hello.py

#-*- coding:utf-8 -*-
import sys
from PySide6.QtWidgets import QApplication, QLabel

app = QApplication(sys.argv)
label = QLabel("Hello World!")
label.show()
app.exec()

 画面サイズが設定されてないせいなのか自分の場合はかなり小さい画面になっていましたが、以下のようにGUIが表示されればインストール完了です。
f:id:rikoubou:20220218105246p:plain


2:PySide6で簡単なGUI
 では実際に簡単なGUIを作ってみます。今回作ったのは参考資料にあるサイト様の内容をいくつか組み合わせています。

gui_sample.py

#-*- coding:utf-8 -*-
import sys
from PySide6.QtWidgets import *

class Form(QDialog):

    def __init__(self, parent=None):
        super(Form, self).__init__(parent)

        # Widgetsの設定(タイトル、固定横幅、固定縦幅)
        self.setWindowTitle("Title test")
        self.setFixedWidth(300)
        self.setFixedHeight(100)

        self.edit = QLineEdit("Write here") # テキスト入力
        self.button = QPushButton("Button") # ボタン

        # レイアウトを作成して各要素を配置
        layout = QVBoxLayout()
        layout.addWidget(self.edit)
        layout.addWidget(self.button)

        # レイアウトを画面に設定
        self.setLayout(layout)

        # ボタンを押したときの関数を設定
        self.button.clicked.connect(self.showMessageBox)


    # テキスト入力の内容をメッセージボックスで表示する関数
    def showMessageBox(self):
        QMessageBox.information(self, "Message", self.edit.text())


if __name__ == '__main__':
    # Qtアプリケーションの作成
    app = QApplication(sys.argv)

    # フォームを作成して表示
    form = Form()
    form.show()

    # 画面表示のためのループ
    sys.exit(app.exec())

 上記のプログラムを実行すると、以下のようなGUIが表示されます。
f:id:rikoubou:20220218112436p:plain

 ボタンを押すとテキストに入力された内容がメッセージボックスで表示されます。
f:id:rikoubou:20220218112544p:plain


 以上がPySide6の導入と簡単なGUIを作ってみた内容になります。

 Qt自体あまり触ったことなかったのもあり、まだ何もわかっていない状態ですが印象としてはtkinterよりも自由度は高そうな気がしています。

 tkinterはどうやらMacなどでは非推奨になりつつあるらしいので、多くの環境に合わせたい場合はこのPySide6を使うのもいいかもしれません。


・参考資料