#◆ はじめに
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神っすわ、まじで。
■ 使用後 [raspberrypiをキーワードに消しこみ]
惜しむらくは顔写真にモザイク処理ができないこと、ぐらい。
#◆ ツールの仕様
**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
というマスク加工済みの画像ファイルが生成される。
複数画像、複数キーワードをまとめてマスクしたい人は、引数のキーワードをマルチに設定できるように適当に加工してください。
ロジックは書きっぱなしで見なおしてもいないので、気に食わない箇所があったら、よろしく修正してやってください。