Edited at

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

More than 1 year has passed since last update.


◆ はじめに

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 というマスク加工済みの画像ファイルが生成される。

複数画像、複数キーワードをまとめてマスクしたい人は、引数のキーワードをマルチに設定できるように適当に加工してください。

ロジックは書きっぱなしで見なおしてもいないので、気に食わない箇所があったら、よろしく修正してやってください。