1
1

はじめに

Qiitaの久しぶりの記事投稿になります。

最近、本業の業務用として、Pythonでツール的プログラムを作る機会がありまして、そのときに、画像を表示したり、その画像に関してちょっとしたグラフ表示などを実現したので、そのあたりの処理を切り出して、画像ファイルViewer +αにしてみましたので、それを紹介してみようと思います。

GUIライブラリは

まず、Pythonで作り始めたツールで、GUIを何で書こうかなと考えて、PythonGUIライブラリには、

  • 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()

このコードで作成できるグラフの画面の例を貼り付けておきます。

white_image_plot.png

x=830の位置にちょっと特異点が見えていますが、この部分をグラフ上で拡大表示した状態が以下の画像です。このような拡大表示が、別に特に追加でコードを書かなくても標準的に実現されています。

white_image_plot_2.png

引数の処理

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として切り出して、その内容を紹介してみました。

このプログラムを機能拡張できたりしたときに、また投稿しようかな、と思います。それでは。

  1. ラインセンサにより取得した画像を解析するために、垂直方向に平均を取るなどしている。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1