techno_memo

個人用の技術メモ。python・ROS・AI系のソフトウェア・ツールなどの情報を記載

pythonスクリプトのexeファイル化 (pyinstaller)

やりたいこと

 pythonスクリプトをexeファイル化したい

  • 別PCへの配布 (python環境がないPC/開発者以外のPCで動作させたいツールなど)

  • 簡単に起動させたい便利ツール (ドラッグアンドドロップでファイル情報を取得して起動など)

f:id:sd08419ttic:20190316002444p:plain

exe化の方法

pythonのexe化用ライブラリは複数存在するが、今回は最も手軽にできるpyinstallerを紹介する

github.com

環境

  • Windows10 (64bit)

  • Anaconda (python 3.5 64bit)

手順

  1. pip install pyinstaller で pyinstallerをインストール

    (初回のみ。anaconda 仮想環境を作っている場合はactivate コマンドで移動後に実行すること)

  2. pyinstaller XXX.py -F でexeファイルを生成

    exeファイルはpyinstallerを実行したカレントディレクトリに生成される『dist』フォルダ以下に出力される

 -Fオプションをつけることで実行時に必要なファイルが1つのexeファイルに集約される

複数ファイルが含まれるpythonファイルをexe化したい場合、mainが記載されているpythonスクリプトを指定するとその他のファイルは自動で読み込まれる。

注意点

pythonのバージョン

pyinstallerはpython 2.X~3.7まで対応しているが、利用するライブラリによってはexeの生成や実行時にエラーがおきることが多い (特にpandas/matplotlib関連)

筆者の環境ではpython3.5が比較的安定していた。(pandas/matplotlibの処理に対しても問題なく生成できた)

ファイルの容量

生成されるexe容量は読み込むライブラリに依存し、数十MB~百MBになる。小さくしたい場合にはpython仮想環境に含まれるライブラリを必要最低限にするなどの工夫が必要となる

ファイルパスの取得

pythonスクリプトファイルが存在するパスを取得する処理を記述したい場合によく記述する下記ではパスを取得できない。

   import os
   iDir = os.path.abspath(os.path.dirname(__file__))

上記の代わりにsysモジュールを使ってファイルパスを取得できる

   import os
   import sys
   dpath = os.path.dirname(sys.argv[0])

ドラッグアンドドロップによるファイルパスの受け渡し

生成したexeファイルにファイルをドラッグアンドドロップすることで、外部パスを簡単に受け渡すことができる。

exeにドラッグアンドドロップされたファイルはsys.argv[1]で読み込める。 (複数ファイル渡す場合にはarg[2]以降で取得)

参考として、exeファイルにドラッグアンドドロップすると画像の横方向反転+グレースケール化をして表示するスクリプトを実装する。

f:id:sd08419ttic:20190316145707p:plain

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import cv2
import numpy as np
import sys

#データ読み込みクラス
class Class_Img_PreProcessing():
    '''
    画像データの前処理をまとめたクラス
    '''

    def __init__(self,filepath="",cv2img = None):
        '''
        コンストラクタ (filepath=指定したパス の画像の読み込み,もしくはcv2img=numpy形式データを代入)

        '''
        if filepath !="" :  #ファイルパスが指定された場合
            self.img = cv2.imread(filepath)
        elif cv2img != None: #numpy形式データが入力された場合
            self.img = cv2img
        else:
            self,img = None

    def get_gray_img(self,color_im= "None"):
        '''
        グレースケール画像を取得する (color_im:カラー画像データ(option))
        '''
        if color_im is "None":
            if self.img is None:
                return None
            else:
                return cv2.cvtColor(self.img, cv2.COLOR_BGR2GRAY)
        else:
            return cv2.cvtColor(color_im, cv2.COLOR_BGR2GRAY)

    def flip_img(self,mode="x"):
        '''
        画像を反転させる
        x: 横方向反転、y:縦方向反転 xy:両方向反転
        '''
        if self.img is None:
            return None
        else:
            if mode == "x": #X軸方向反転
                result = cv2.flip(self.img,1)
            elif mode == "y": #Y軸方向反転
                result = cv2.flip(self.img,0)
            elif mode == "xy": #両方向反転
                result = cv2.flip(self.img,-1)
            else:
                result = self.img
        return result

if __name__ == '__main__':

    target_ext = ['.jpg','.png','.tif']

    for indx in range(1,len(sys.argv)):
        filename = sys.argv[indx]
        root, ext = os.path.splitext(filename)
        print(ext)
        for indx2 in range(len(target_ext)):
            if ext == target_ext[indx2]:
                img_inst = Class_Img_PreProcessing(filename)
                flip_img = img_inst.flip_img(mode="x")
                gray_img = img_inst.get_gray_img(color_im = flip_img)
                cv2.imshow("Windowname",gray_img)
                cv2.waitKey(0)

実装結果は下記サイトに保存済み

github.com