はじめに
Qiitaの久しぶりの記事投稿になります。
最近、本業の業務用として、Pythonでツール的プログラムを作る機会がありまして、そのときに、画像を表示したり、その画像に関してちょっとしたグラフ表示などを実現したので、そのあたりの処理を切り出して、画像ファイルViewer +αにしてみましたので、それを紹介してみようと思います。
GUIライブラリは
まず、Pythonで作り始めたツールで、GUIを何で書こうかなと考えて、PythonのGUIライブラリには、
- Tkinter ... Python標準のGUIライブラリ
- PyQt ... 名前から想像つくようにQtのPythonバージョン
- PySimpleGUI ... Python用のシンプルなGUIライブラリ
- wxPython ... wxWidgets(C++で記述されたクロスプラットフォームGUIツールキット)のPython版
などと色々とありますが、Python標準のTkinterではちょっと限界があるのかな、ネット上でぐぐってみると情報が多そうなのはwxPythonかな、ということで、今回、wxPythonを選んでみました。
作ったツールの内容
業務で普段は、画像のViewerとして、IrfanViewをよく使っています。また、ちょっと画像内の輝度値の分布などを見たいときは、ImageJをちょこちょこと使っています。
ゆくゆくは、そういうツールの代わりに使えるようなものにしたいな、っていうことで、今回、ひとまず、
- 画像ファイルをウィンドウに表示
- その輝度値の分布などを解析してグラフ表示
というところまで作ってみました。
プログラム内の要素をいくつか説明
画像ファイルの表示
menu_bar = wx.MenuBar()
file_menu = wx.Menu()
open_menu_item = file_menu.Append(wx.ID_OPEN, "Open...", "Open an image file")
exit_menu_item = file_menu.Append(wx.ID_EXIT, "Quit", "Exit the application")
menu_bar.Append(file_menu, "File")
などと記述するだけで、Fileメニューに、Open及びQuitというメニュー項目を作ることができて、
self.Bind(wx.EVT_MENU, self.panel.on_browse, open_menu_item)
と、上記のopen_menu_itemに、以下の関数on_browseをバインドするだけで、期待通りに動いてくれます。
def on_browse(self, event):
with wx.FileDialog(self, 'Select Image File',
wildcard='Image files (*.png;*.bmp;*.tiff;*.jpg)|*.png;*.bmp;*.tiff;*.jpg',
style=wx.FD_OPEN) as dialog:
if dialog.ShowModal() == wx.ID_OK:
self.load_image(dialog.GetPath())
輝度値の分布をグラフ化
今回、最初は、画像上でマウスでクリックした座標を拾って、その座標に対応する画素の輝度情報や、その水平位置における垂直方向の全画素の平均値1を計算して、その値を単純に標準出力に出力する、などという形で作り始めていました。
ですが、もっと、例えば、画像の拡大/縮小表示もしたい、グラフ表示などもしたい、などと考えているうちに、自分でいちから書くのはちょっと面倒だ、何かいいライブラリはないのかな(ありそうだ)、と思って探してみると。。。
matplotlibというライブラリを見つけました。
以下のように、これぐらいのコード量で、水平の画素位置を横軸に、NumPyで算出した垂直方向の平均値を縦軸に、簡単にグラフ化することができました。
x_positions = np.arange(width)
red_channel_means = np.mean(image[:, :, 0], axis=0)
green_channel_means = np.mean(image[:, :, 1], axis=0)
blue_channel_means = np.mean(image[:, :, 2], axis=0)
fig, ax = plt.subplots(figsize=(10, 5))
ax.plot(x_positions, red_channel_means, label='Red Channel', color='red')
ax.plot(x_positions, green_channel_means, label='Green Channel', color='green')
ax.plot(x_positions, blue_channel_means, label='Blue Channel', color='blue')
ax.set_xlabel('Horizontal Position')
ax.set_ylabel('Mean Value')
ax.set_title(self.image_path)
ax.legend()
# マウスをあてた部分の座標、データを表示する
mplcursors.cursor(hover=True)
plt.tight_layout()
plt.legend()
plt.show()
このコードで作成できるグラフの画面の例を貼り付けておきます。
x=830の位置にちょっと特異点が見えていますが、この部分をグラフ上で拡大表示した状態が以下の画像です。このような拡大表示が、別に特に追加でコードを書かなくても標準的に実現されています。
引数の処理
argparseを使っています。以下のような記述だけで、プログラムに引数を追加することができています。
parser = argparse.ArgumentParser(description='画像ファイルviewer')
# image_file位置引数にデフォルト値Noneを設定
parser.add_argument('image_file', default="", nargs='?', help='表示する画像ファイル')
args = parser.parse_args()
image_file_path = args.image_file
nargs='?'で、この引数が必須でないことを、そして、help=***でヘルプ表示での説明書きを、それぞれ指定しています。たったこれだけで、以下のようなヘルプ表示が用意されます。[]で囲われていることで、引数が必須ではないことが表現されていますね。
PowerShell> python3 .\MyImgViewer.py --help
usage: MyImgViewer.py [-h] [image_file]
画像ファイルviewer
positional arguments:
image_file 表示する画像ファイル
options:
-h, --help show this help message and exit
成果物
今回、はじめて、GitHubで、MyImgViewerという名前のプログラムとして公開してみました。
おわりに
私は、根っからの(?)Cプログラマで、Pythonはまだまだ初心者ですが、最近は、Pythonでちょっとしたツールプログラムを作ることがあります。そして、Cで何でもいちから作っていた、私のような古い人間から見ると、Pythonの環境は、色々とライブラリが揃っていて便利ですね。常々、そう思います。
今回も、本業の業務上、ちょっと必要になったGUIツールを、Python + wxPythonで作っていたのですが、たまにはQiitaに投稿してみよう、ということで、だけど、それをそのまま見せて紹介するなどはやりにくいため、ちょっと汎用的な画像Viewerとして切り出して、その内容を紹介してみました。
このプログラムを機能拡張できたりしたときに、また投稿しようかな、と思います。それでは。
-
ラインセンサにより取得した画像を解析するために、垂直方向に平均を取るなどしている。 ↩