経緯
ごく稀に、プロダクト内に書かれた文言の修正をすることってありますよね。
htmlやテンプレートファイルに文字列が記載されていれば、普通にgrepするなり、sedで一括置換できたりします。
問題は画像です・・・!
画像の中に置き換えなければいけない文字があることもあると思いますが、画像を目視で見ないと分からないですよね。
過去の経験的にも、あとから置き換えなければいけない文字を含む画像が見つかって、修正する・・・みたいなことを何度か経験したことがあります。
(本来は、画像内にあまり文字を書くのは良くないと思うのだけど・・・説明ページとかだと仕方ない場合もありますよね。)
画像内をgrepできたらいいのに、と思ったのでOCRを活用して画像内の文字列をテキスト化し、その中に調べたい文字列があるかをチェックするツールを作ってみたので紹介します。
OCRとは
OCR(Optical Character Recognition:光学的文字認識)は、ざっくり言うと画像データのテキストを認識して、文字データに変換する機能のことです。
方針
pythonでいくつかライブラリがあるので、今回は pytesseract
を使って日本語+英文字を画像からテキストに書き起こして、そのテキスト内に探したい文字列が含まれている場合に画像パスとテキストデータを出力するツールを作ってみようと思います。
だれでも使えるように、Dockerイメージを作成してコンテナ上で動かすようにしたいと思います。
用意するもの
- Dockerfile
- スクリプト(今回は main.py とします)
ディレクトリ構成はこんな感じにしてみようと思います。
.
|--Dockerfile
|--app
| |--main.py
|--requirements.txt
Dockerfile
- python:latestを最初は使っていたのですが、サイズが大きすぎるので
python:slim
にしました - イメージ上に
tesseract-ocr
とtesseract-ocr-jpn
をインストールします - ライブラリは画像を扱うのでpillowと、今回の主役の pytessoract をpipで入れます
FROM python:slim
RUN apt-get update
RUN apt-get -y install tesseract-ocr tesseract-ocr-jpn
RUN apt-get clean
COPY requirements.txt .
RUN pip3 install -r requirements.txt
COPY ./app /app
CMD [ "python", "/app/main.py" ]
pillow
pytesseract
main.py
- main.pyはイメージ内にコピーしておくので、検索したいワードは環境変数で渡してみることにします
- globを使って、対象ディレクトリを再帰的にファイルを取得するようにしてみます
- このとき拡張子は、とりあえずjpg/jpeg/pngを対象にしてみます
- あんまり小難しいことは考えず、ファイルパスを取得したらpytesseract.image_to_stringに投げてみます
- 返ってきたテキストは、半角スペースや改行が多く含まれるので、replaceでトリミングしてみます
from pytesseract import pytesseract
from PIL import Image
import os
import glob
def main() -> None:
word = os.getenv('word')
if not word:
print('wordを指定してください。')
return
files = get_files()
for file in files:
search_word(file, word)
def get_files() -> list:
types = ('jpg', 'jpeg', 'png')
files = []
for type in types:
files += glob.glob("/target/**/*." + type, recursive=True)
return files
def search_word(file: str, word: str) -> None:
text = get_image_text(file)
if word in text:
print(file + ': -> ' + text)
def get_image_text(filename: str) -> str:
img = Image.open(filename);
text = pytesseract.image_to_string(img, lang='eng+jpn')
return text.replace(' ', '').replace('\n', '')
if __name__ == "__main__":
main()
実行
まずイメージをビルドしてみます
docker build -t image-grep .
実行方法は以下のようなイメージ
docker run --rm -it -v {ここに検索対象のパスを指定}:/target -e word="検索ワード" image-grep:latest
検証
こんな画像を用意してみました。
期待通りに、文字の探索ができるかみてみようと思います。
$ docker run --rm -it -v ./sample_image:/target -e word="サウナ" image-grep:latest
/target/sample-image.png: -> 私の趣味はスノーボードです。ジムで筋トレレたり、仕事帰りにふらっとサウナに行くのも好きです。読書や映画鑑賞もよくします。
$ docker run --rm -it -v ./sample_image:/target -e word="プール" image-grep:latest
$ docker run --rm -it -v ./sample_image:/target -e word="スキー" image-grep:latest
$ docker run --rm -it -v ./sample_image:/target -e word="スノーボード" image-grep:latest
/target/sample-image.png: -> 私の趣味はスノーボードです。ジムで筋トレレたり、仕事帰りにふらっとサウナに行くのも好きです。読書や映画鑑賞もよくします。
筋トレしたり
が 筋トレレたり
になっていたりして、少し怪しいところもありそうですが、イイ感じです。
これで画像内に変更しなければいけない文字列があっても見つけられそうですね。
画像を目grepするよりは信頼できそうです