pyocr を使っていて pyinstaller でパッケージングすると WARNING が出てくる件について


# はじめに


こんにちは、はじめまして、おやすみなさい。へおんです。


この記事で得られる情報は下記の通りです

- pyocr を使っていて pyinstaller でパッケージングすると出てくるWARNINGを消す
- tesseract-OCR/pyocr を使っての exe 配布方法

# 再現

0. 別途 Tesseract-OCR をインストールします (先生！これが一番の原因だと思います！)

1. 適当な pyocr を使用した *.py を書きます

2. pyinstaller hoge.py

3. dist で吐かれた foo.exe を起動すると下記のログが出力されます、、、。
4. 
```
Running from container, but no tessdata (C:\Users\{user}\AppData\Local\Temp\_MEI{tempnumber}\data) found !
Running from container, but no tessdata (C:\Users\{user}\AppData\Local\Temp\_MEI{tempnumber}\data) found !
```

まぁこれが流れててもターゲットのPCで Tesseract-OCR を別途インストールしていれば実行はできるから、根本的に困ってる人があまりいない…

しかし、汚いのでこれを消します。

# 原因


pyocr の中をチラッとしてみると tesseract.py の Line:120 付近にあります。

これが原因です。

```Python
    if getattr(sys, 'frozen', False):  # pragma: no cover
        # Pyinstaller support
        path = os.environ["PATH"]
        if sys._MEIPASS in path:
            # already changed
            return

        tesspath = os.path.join(sys._MEIPASS, "tesseract")
        tessprefix = os.path.join(sys._MEIPASS, "data")
        logger.info("Running in packaged environment")

        if not os.path.exists(os.path.join(tessprefix, "tessdata")):
            logger.warning(
                "Running from container, but no tessdata ({}) found !".format(
                    tessprefix
                )
            )
        else:
            logger.info("TESSDATA_PREFIX set to [{}]".format(tessprefix))
            os.environ['TESSDATA_PREFIX'] = tessprefix
        if not os.path.exists(tesspath):
            logger.warning(
                "Running from container, but no tesseract ({}) found !".format(
                    tesspath
                )
            )
        else:
            logger.info("[{}] added to PATH".format(tesspath))
            os.environ['PATH'] = (
                tesspath + os.pathsep + os.environ['PATH']
            )
```
色々やってくれているみたいなんですが、使う側はこれを知らないです(泣)

どうやら sys._MEIPASS なるものに tesseract と data があれば良さそうです。

じゃあ sys._MEIPASS に tesseract と data 追加すればいけるやん！解決！熱盛！READY to FIGHT!

*.spec に下記を追記します

```
          Tree('{Tesseract-OCRのパス}',prefix='tesseract'),
          Tree('{Tesseract-OCRのパス}',prefix='data'),
```
　
### 実行！
　
　
```
Running from container, but no tessdata (C:\Users\{user}\AppData\Local\Temp\_MEI{tempnumber}\data) found !
Running from container, but no tessdata (C:\Users\{user}\AppData\Local\Temp\_MEI{tempnumber}\data) found !
```

まだ戦闘力が足りないようです。



# 方法

<details><summary>旧方法</summary>


### まずはディレクトリ構造から

- {main}.py
- {tesseract}
    - tesseract.exe
    - {etc...}
- {data}
    - *.traineddata
    - {etc..}
    - tessdata
        - *.traineddata
        - {etc..}

最終的にこうなりました。

data の中に更に tessdata がないといけない、しかも内容が重複してるので意味がわからないです。(もっといい解決方法があるのかもしれない…)

### *.spec への追記 

```python
# -*- mode: python ; coding: utf-8 -*-

block_cipher = None


a = Analysis(['{main}.py'],
             pathex=['{src_dir}'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          Tree('{data}',prefix='data'),
          Tree('{tesseract}',prefix='tesseract'),
          a.scripts,
          a.binaries,
          a.zipfiles,
          a.datas,
          [],
          name='{appname}',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          upx_exclude=[],
          runtime_tmpdir=None,
          console=True , icon='icon.ico')

```

ここはなんら変わりないです。

### {main}.py
{}.py 特に必要な記述はなし
</details>

追記(2022/4/10):
この記事を見てくださった人から情報がありまして([@hirosi1971](https://twitter.com/hirosi1971) さんありがとうございます！)
*.spec を別の記述方法にすることで tesseract を埋め込めてかつ、WARNING を消せるようです！

追記(2024/2/24):
@yk5322さんからご指摘いただきまして{
tesseract.exe のパス
}ではなく{tesseract.exe が格納されてるフォルダのパス}だとうまくいったということなので一応追記しておきます！情報提供大感謝！！

```*.spec
a = Analysis(['{main}.py'],
...
             binaries=[('{tesseract.exe が格納されるフォルダのパス}', 'tesseract')],
             datas=[('{*.traineddata のパス}', 'data/tessdata')],
...
```

これでごちゃごちゃしていたディレクトリ構造もスッキリですね！
(当プロジェクトは python から移植した後ですけど…)

# pyinstaller!
これで準備が整ったので

`$> pyinstaller --onefile --clean --icon={icon}.ico -n {appname} {main}.py`

をして{appname}.spec を上記のようにして

`$> pyinstaller {appname}.spec`

をして dist 下の {appname}.exe を配布すればおｋです！

あくまで Windows 環境の方法ですが、もちろんターゲットのPCに別途 Tesseract-OCR をインストールする必要はありません。

もしよりよい方法があれば Twitter のリプライなりココのコメントでご教授していただけると幸いです！

では
乙ノシ