追記
importはメインプロセス内でほかのスクリプトを実行できるので,importを弄って書いた方がいいと思います.(2021/06/29)
画像処理ソフトでBlenderのPython APIみたいなものを作りたかった(2)にてimportを用いて実装する方法を書きました.(2021/07/01)
目的
ImageJなどの画像処理ソフトは画面がごちゃごちゃになりがちで個人的に使いづらいため,シンプルな画像処理ソフトを作ろうと思いました.
ImageJや,私の作ったものと同じでPythonで書かれているImage-Pyではプラグイン形式での機能の追加が可能ですが,画像を直接扱えた方が便利だと思いタイトルの考えに至ります.
環境はWindows,python=3.9.5,pyqt5=5.15.4です.
実装はGithub上に公開しています.
仕組み
「BlenderのPython Scripting APIみたいなものを作りたかった」という割にはBlenderのソースが時間と能力的に読めなかったので出来そうな方法を採りました.
以下の画像が大まかな処理の流れです.
GUI関連の処理をメインプロセスで,スクリプトの実行をサブプロセスで行うことで実現しています.この方法だと,メインプロセスとサブプロセス間のやり取りは標準出力で行う必要があるので,サブプロセスで処理した画像はバイナリ化してメインプロセスに渡されます.コード
重要な部分だけ拾ってみていきましょう.
まずはメインプロセスからサブプロセスに渡す部分です.
def select_script_dialog(self):
"""中略"""
subprocess.run("python " + str(self.script_file_path) + " " + self.img_file_path)
"""後略"""
この関数はGUI上の「scriptを選ぶボタン」を押したら動くものです.Pythonの組み込み関数であるsubprocessを用いてスクリプトを実行することが出来ます.run関数に"python スクリプトファイルのパス 画像のパスを渡すことで「python」で,「スクリプト」を,コマンドライン引数として「画像のパス」を取って実行してくれます.
次にサブプロセスでの処理を見ます.
import sys
from PIL import Image
import numpy as np
img_path = sys.argv[1]
im_gray = np.array(Image.open(img_path).convert('L'))
im_gray = np.stack([im_gray, im_gray, im_gray], -1)
np.save('tmp.npy', im_gray)
画像をグレースケールにしてnp.saveでバイナリ化して保存しています.画像処理の部分は自由に変えることが可能だと思われます.
処理された画像は以下のようにメインプロセスに渡され,表示させられます.
def select_script_dialog(self):
"""中略"""
loaded_array = np.load('tmp.npy')
h, w = loaded_array.shape[:2]
qimg_format = QImage.Format_RGB888 if len(
loaded_array.shape) == 3 else QImage.Format_Indexed8
self.qimg = QImage(loaded_array.flatten(), w, h, qimg_format)
self.set_image_on_viewer()
画像をndarray形式で読み取った後,QImage(pyQt5で表示可能な形式)に変換します.そして,それを表示する関数set_image_on_viewer()を実行しています.
なお,pyQt5の大部分はこの記事を参考にしました.
実際の画面
実際のソフトウェアの画面は以下のようになります.
例外処理やUI周りは未完成です.
所感
確かにそれっぽいものはできたが,スクリプト内で扱えるものが標準入出力で扱えるものに限られるためコレジャナイ感が否めない.Blenderや商用のソフトウェアではそのあたりをどのように対処しているのか気になる.
参考
Pythonで画像表示、お絵描き用のアプリケーション作りの解説(Qiita記事)
OpenCVで読み込んだ画像(numpy.array)をQt5(QImage)で表示する(ブログ,Symfoware)