画像から簡易的にでも黒い四角■を取り除けないか?と考え実験してみました。
処理前
※画像枠はありません。
処理後
※画像枠はありません。
これなら左上の矩形範囲を検出して、塗りつぶすだけなので、簡単そうです。以下のような画像はとりあえず対象外とします。
本当は、検出した矩形範囲の画像を、別で用意した黒四角画像と比較すればもっと複雑なこともできそうです。
画像処理といえば、OpenCV が浮かびましたし、Python なら比較的簡単に扱えそうです。また、基本的な図形の検出は多くの方が試されているので、情報も多いだろうと考えました。調べたところ、多くの先人たちに助けられました。
Xserver上のPHPフレームワークから呼び出しますがPHPは特に関係ありません。
準備
Pythonの環境に悩みたくありませんので、Xserver にAnacondaをインストールして準備しました。
cv2ライブラリを利用しますので、インストールしておきます。
(base)$ conda install opencv
(base)$ python
>>> import cv2
>>> cv2.__version__
'4.5.4'
なお、もし インストール時に、conflict などと出て失敗した場合、新しい環境を作成し、そちらを使うようにします。anacondaは一貫した環境を整えてくれるのがメリットですから、新しい環境でopencvをインストールすると、依存関係がきちんと作成されます。
conda create --name myenv
activate myenv
# opencv を利用するときは、こちらの環境を利用します。
ロジック
以下の流れとします。
- 画像ファイルを開く
- 二値化
- 外接矩形検出
- 条件に当てはまる外接矩形を探す
今回は一番左上の外接矩形とする - 白で塗りつぶす
- 画像ファイルを保存する
コード
処理をする関数のみで、一部省いていますが、cv.imread でファイルを読み、cv2.imwrite で書き込みます。
ベタなロジックだとは思いますが、実験という事で。
#!/home/xserver-user/anaconda3/bin/python3
import cv2
import numpy as np
# 黒四角を除いた後、白で塗り消すが検出サイズよりどれくらい余分に塗りつぶすか。両方向に適用
WHITE_BOX_DELTA_X = 3
WHITE_BOX_DELTA_Y = 3
# 誤検出のため、小さい面積は無視する。100=10px * 10px
IGNORE_UNDER_CONTOUR_AREA = 100;
# 一番左上(Y方向優先)の矩形範囲を消す
# 引数は画像データ cv2.imread でオープンした状態の画像データ cv2 BGR画像
# 戻り値:cv2 BGR画像 , ステータス True=success
def removeSquare( img, debug=False ):
# 2値化します。
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
bin_img = cv2.inRange(hsv, (0, 0, 0), (360, 255, 50))
# 輪郭抽出します。
contours, hierarchy = cv2.findContours(
bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE
)
if len(contours) == 0:
return img , False
# 検出した矩形範囲リストを出力しています。
print("contours count:"+f"{len(contours)} found!")
for i, cnt in enumerate(contours):
x, y, w, h = cv2.boundingRect(cnt)
area = cv2.contourArea(cnt)
print(f"contour: {i}, area: {area} , x:{x},y:{y},w{w},h{h}")
# 小さい輪郭は誤検出として無視します。一定以上の面積のみにします。
contours = list(filter(lambda x: cv2.contourArea(x) > IGNORE_UNDER_CONTOUR_AREA , contours))
print("removeSquare:"+f"{len(contours)} marker found!")
# オブジェクトを検査し、一番左上(Y方向優先)にあるものを取得します。
y_min_index = 0
y_min_pos = sys.maxsize
x_min_pos = sys.maxsize
min_obj = ""
for i, cnt in enumerate(contours):
x, y, w, h = cv2.boundingRect(cnt)
# Y方向走査
if y_min_pos > y:
y_min_index = i
y_min_pos = y
x_min_pos = x
min_obj = cnt
elif y_min_pos == y:
# 同じ行にあるときは、x方向もチェックします。
if x_min_pos > x:
y_min_index = i
y_min_pos = y
x_min_pos = x
min_obj = cnt
# 矩形再取得
x, y, w, h = cv2.boundingRect(min_obj)
# 矩形右下座標取得
x2 = x + w
y2 = y + h
# 塗りつぶし範囲設定
# 塗りつぶしは黒より大きいので、マイナス側にはみ出さないようにします。
white_x1 = x - WHITE_BOX_DELTA_X if x>=WHITE_BOX_DELTA_X else 0
white_y1 = y - WHITE_BOX_DELTA_Y if y>=WHITE_BOX_DELTA_Y else 0
white_x2 = x2 + WHITE_BOX_DELTA_X
white_y2 = y2 + WHITE_BOX_DELTA_Y
# 塗りつぶし
cv2.rectangle(
img,
pt1=(white_x1,white_y1),
pt2=(white_x2,white_y2),
color=(255,255,255),
thickness=-1,
shift=0
)
return img , True
終わりに
今回は、左上の矩形を削除するという簡単なロジックでしたが、画像のどこにあっても■を取り除けるようにしたいですね。矩形範囲のピクセルがすべて黒なら対象の矩形となる、というロジックを加えればよいような気がします。