#!python # -*- mode: python; Encoding: utf-8; coding: utf-8 -*- # Last updated: <2026/03/22 15:20:00 +0900> """ 画像を読み込んで子スクリプトにパイプで送って処理結果を取得するスクリプト。 Google Gemini に作成してもらった。 Windows11 x64 25H2 + Python 3.10.10 64bit """ import subprocess import struct import threading from PIL import Image def log_reader(pipe): """ 標準エラー出力をリアルタイムで読み取って表示する関数。 メインスレッドとは別に動かすことで、子プロセスが吐き出すログを メインの読み書き処理を止めずに処理できます。 """ try: with pipe: # pipe.readline はデータが来るまで待機し、b''(空)が来たら終了 for line in iter(pipe.readline, b""): # decode()でbytesを文字列に変換。strip()で末尾の改行を除去 print(f"[Child Log]: {line.decode().strip()}") except Exception as e: print(f"Log error: {e}") def run_example(image_path): # 画像を開き、RGBA形式(1ピクセル4バイト)に統一して読み込み img = Image.open(image_path).convert("RGBA") width, height = img.size img_bytes = img.tobytes() # 子プロセス(child.py)を起動 # すべての入出力を PIPE で接続し、親からコントロール可能にする process = subprocess.Popen( ["python", "child.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, # バイナリデータのやり取りで遅延を防ぐためバッファリングを無効化 ) # 1. 進捗(stderr)を読み取る専用スレッドを開始 # ※重要:ここを別スレッドにしないと、子プロセスがstderrに大量のログを出した際、 #  OSのパイプバッファがいっぱいになり、子プロセスが停止(デッドロック)してしまいます。 t = threading.Thread(target=log_reader, args=(process.stderr,)) t.daemon = True # 親スクリプトが異常終了してもこのスレッドが残らないように設定 t.start() # 2. 子プロセスへデータを送信 # child.py 側が待っている 8byte のヘッダー(幅・高さ)をパック header = struct.pack("ii", width, height) print(f"Sending image data ({width}x{height})...") # ヘッダーと画像本体を結合して stdin へ書き込み process.stdin.write(header + img_bytes) # 書き込み終了を明示的に伝える(EOFを送る)。 # これにより、child.py 側の stdin.read() が「これ以上データが来ない」と判断し、処理が進みます。 process.stdin.close() # 3. 処理済みデータ(stdout)を受信 # stderr の読み取りは別スレッドに任せているため、メインスレッドは stdout の受信に専念できます print("Waiting for processed image...") result_bytes = process.stdout.read() # 子プロセスの終了を待ち、リソースをクリーンアップ process.wait() # ログ出力スレッドが残っている場合、最大1秒待機して同期をとる t.join(timeout=1) # 受信したバイナリを Pillow で画像オブジェクトに戻す if result_bytes: res_img = Image.frombytes("RGBA", (width, height), result_bytes) # プレビュー表示(Windowsのフォトアプリ等が起動します) res_img.show() print("Success: Image received and displayed.") else: print("エラー: データを受信できませんでした。") if __name__ == "__main__": # カレントディレクトリに input.png がある前提 run_example("input.png")