3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ハードコピー・画像ファイル内を指定のキーワードで検索して機械的にマスクする、ぞ! by Google Cloud Vision API

Last updated at Posted at 2018-05-01

#◆ はじめに
Qiitaに記事を投稿する際にイメージを貼る機会が増えたが、公開したくないキーワードが大量に含まれている場合、イメージの貼り付け自体を尻込みしてしまっていた。
記事を読みやすく綺麗に整形したいのに、イメージの中に見せたくないキーワードが大量にちりばめられていると、手作業でマスク加工するのがめちゃくちゃ億劫だったから。

ということで、高価な加工ソフトを買うのもシャクだし、すでにごまんと世に有るとは思うが、Google検索が下手クソで見つけられなかったので、ハードコピーを任意のキーワードで機械的にマスクするツールをPython+Google Cloud Vision API+OpenCVで作成した。
誰の役に立つかわからないけど、一応Gitへコミットしておく。
https://github.com/PINTO0309/AutomaticImageMask.git

余談だが、こちらのソフト 「Tegaki」 がすごい。
手書き認識率 99.2% とか、まじかよ?
社に在籍するエンジニアもすごい人がゴロゴロいるっぽいし。
使いたい・・・金払うから、早く個人向けにAPI提供開始してくれないかな・・・
Tegaki https://www.tegaki.ai/

#◆ 環境

  • Google Cloud Vision API ← Googleアカウントの取得とAPIキーの取得が必要
  • Python3
  • OpenCV3.x

#◆ 実行イメージ
raspberrypi というキーワード周辺をザックリとマスクしたサンプル。
該当ワードの前後が余分に大きくマスクされちゃってるけど、自分は許容範囲内。 2018.05.01改善
ユーザIDやら何やらが漏れるよりはマシ。
取りこぼしが一切ないのは衝撃的。
そこらのOCRソフトより精度が高いのではなかろうか。
ベタ塗りなのでディープラーニングでも復元できませんぜ。たぶん。

えっと・・・できない・・・よね?
挑戦者求む。

Google Cloud Vision APIは、1,000APIコール/月 まで叩き放題だから、よほどカオスな使い方しない限りは実質完全無料。
画像内文字列のアノテーション結果(文字解析でヒットした単語と座標情報)は全てJSONで綺麗に返ってくるため、コレに限らず色々と活用はできそう。

いやぁ、Google神っすわ、まじで。

■ 使用前
sample.png

■ 使用後 [raspberrypiをキーワードに消しこみ]
masked.png

■ 使用前
65.png

■ 使用後 [Qtをキーワードに消しこみ]
masked.png

■ 使用前
24.png

■ 使用後 [Arduinoをキーワードに消しこみ]
masked.png

惜しむらくは顔写真にモザイク処理ができないこと、ぐらい。

#◆ ツールの仕様

**AutomaticImageMask.py**
AutomaticImageMask.py
from base64 import b64encode
import json
import requests
import argparse
import cv2
import sys

ENDPOINT_URL = 'https://vision.googleapis.com/v1/images:annotate'

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Google Cloud Vision Api')
    parser.add_argument('api_key', help='api path')
    parser.add_argument('image', help='image path')
    parser.add_argument('mask_word', help='Character to be masked')
    args = parser.parse_args()

    img_requests = []
    with open(args.image, 'rb') as f:
        ctxt = b64encode(f.read()).decode()
        img_requests.append({
                'image': {'content': ctxt},
                'features': [{
                    'type': 'TEXT_DETECTION',
                    'maxResults': 10
                }]
        })

    response = requests.post(ENDPOINT_URL,
                             data=json.dumps({"requests": img_requests}).encode(),
                             params={'key': args.api_key},
                             headers={'Content-Type': 'application/json'})

    chars = [bytes(char, 'utf-8') for char in list(args.mask_word)]
    charslen = [1 if len(char) == 1 else 2 for char in chars]
    masklength = sum(charslen)

    out = cv2.imread(args.image)
    height, width = out.shape[:2]
    json_dict = response.json()['responses']

    for x in json_dict:
        for y in x:
            if y == 'textAnnotations':
                for z in x[y]:

                    x1 = 0
                    y1 = 0
                    x2 = 0
                    y2 = 0

                    try:
                        x1 = z['boundingPoly']['vertices'][0]['x']
                    except:
                        pass
                    try:
                        y1 = z['boundingPoly']['vertices'][0]['y']
                    except:
                        pass
                    try:
                        x2 = z['boundingPoly']['vertices'][2]['x']
                    except:
                        pass
                    try:
                        y2 = z['boundingPoly']['vertices'][2]['y']
                    except:
                        pass

                    description = z['description'].replace('\n','')

                    if args.mask_word in description:

                        descchars = [bytes(char, 'utf-8') for char in list(description)]
                        desccharslen = [1 if len(char) == 1 else 2 for char in descchars]
                        desccharslength = sum(desccharslen)
                        celwidth = round((x2 + 1 - x1) / desccharslength)

                        if celwidth > 0 and desccharslength <= 50:
                            s = description.find(args.mask_word)
                            x1 = x1 + sum(desccharslen[:s]) * celwidth
                            x2 = x1 + masklength * celwidth + 1
                            rectposition = ((x1, y1), (x2+1, y2+1))
                            #print(description, '\t\t', rectposition)

                            if ((x2 - x1) * (y2 - y1)) <= ((width * height) * 0.1):
                                cv2.rectangle(out, rectposition[0], rectposition[1], (105, 105, 105), -1)

    cv2.imshow('masked', out)
    cv2.imwrite('masked.png', out)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
マスク処理の実行方法
python3 AutomaticImageMask.py [Cloud Vision APIのキー] [マスク対象の画像ファイル名] [マスクを掛けたいキーワード]

python3 AutomaticImageMask.py xxx sample.png raspberrypi

上記を実行すると、masked.png というマスク加工済みの画像ファイルが生成される。
複数画像、複数キーワードをまとめてマスクしたい人は、引数のキーワードをマルチに設定できるように適当に加工してください。
ロジックは書きっぱなしで見なおしてもいないので、気に食わない箇所があったら、よろしく修正してやってください。

3
2
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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?