きっかけ
IPPONグランプリのプロフィール画像を作りたい(わけではなかったのです).の記事がおもしろかったので、つい。
変更点
ほぼ元記事のままの処理ですが、できるだけnumpy
やopencv
の機能を使って簡潔なコードになるように努めました。また、ガウシアンフィルタによる平滑化で「ノッペリ」となるように前処理しました。
コード
rembgは導入済みが前提です。
import cv2
import numpy as np
from rembg import remove
# https://sipi.usc.edu/database/database.php?volume=misc&image=4#top
im = cv2.imread('4.1.04.tiff', cv2.IMREAD_COLOR)
# https://commons.wikimedia.org/wiki/File:Visit_of_Bill_Gates_to_the_European_Commission_-_P062021-967902.jpg
#im = cv2.imread('input.jpg', cv2.IMREAD_COLOR)
im = remove(im).copy() # 背景を削除
# 前景と背景のマスク情報 閾値は適当に127
cond_bg = (im[:,:,3] <= 127) # 背景
cond_fg = (im[:,:,3] > 127) # 前景
# 色分け用のグレイスケール画像
im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
# ガウシアンフィルタで平滑化
# カーネルサイズは画像サイズからテキトーに算出
kn = [int(max(v//256, 3)) for v in im.shape[:2]]
kn = [v+1 if v % 2 == 0 else v for v in kn] # サイズは奇数でなければならない
im = cv2.GaussianBlur(im, kn, 3)
# 結果のカラー(BGR)画像
img_res = np.zeros( list(im.shape)+[3], dtype=np.uint8)
img_res[:,:] = (0, 0, 255) # 動作(漏れ)チェック用に赤で塗りつぶし
# 前景の最明色=背景のようなのでそれにあわせる
CLR_BG = (0, 238, 238)
COLORS = [(0, 0, 0), (0, 206, 238), CLR_BG]
# とりあえず前景の最暗~最明の範囲で等分割
rng = np.linspace(im[cond_fg].min(), im[cond_fg].max(), len(COLORS)+1)
print(rng)
rng[0], rng[-1] = (0, 255) # 両端は真黒、真白に
# 色分け
for i, clr in enumerate(COLORS):
cond = ((im >= rng[i]) & (im <= rng[i+1]))
img_res[cond] = clr
# あらためて背景を塗りつぶす
img_res[cond_bg] = CLR_BG
cv2.imwrite('result.png', img_res)
結果例
その他
このラスタ画像から輪郭抽出してベクター化してみるのもおもしろそうです。