83
74

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

画像grepツールを作ってみた

Posted at

経緯

ごく稀に、プロダクト内に書かれた文言の修正をすることってありますよね。
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-ocrtesseract-ocr-jpn をインストールします
  • ライブラリは画像を扱うのでpillowと、今回の主役の pytessoract をpipで入れます
Dockerfile
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" ]
requirements.txt
pillow
pytesseract

main.py

  • main.pyはイメージ内にコピーしておくので、検索したいワードは環境変数で渡してみることにします
  • globを使って、対象ディレクトリを再帰的にファイルを取得するようにしてみます
    • このとき拡張子は、とりあえずjpg/jpeg/pngを対象にしてみます
  • あんまり小難しいことは考えず、ファイルパスを取得したらpytesseract.image_to_stringに投げてみます
  • 返ってきたテキストは、半角スペースや改行が多く含まれるので、replaceでトリミングしてみます
main.py
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

検証

こんな画像を用意してみました。

sample-image.png

期待通りに、文字の探索ができるかみてみようと思います。

$ 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するよりは信頼できそうです :sunglasses:

83
74
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
83
74

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?