LoginSignup
1
1

More than 1 year has passed since last update.

OpenCVとPILでアクリルバッジの型枠画像を作成する

Last updated at Posted at 2022-09-18

概要

こんな画像から
side.png
出典:フォクすけの画像

こんな画像を作成します。
ret.png
画像は透過PNGであることが前提です。

コード

import numpy as np
import cv2
from PIL import Image, ImageFilter

# 上下左右に縁取りを追加
def add_border(img, size):
    border_h = np.zeros((size, img.shape[1], img.shape[2]), dtype=np.uint8)
    border_v = np.zeros((img.shape[0] + 2*size, size, img.shape[2]), dtype=np.uint8)

    img2 = np.vstack([border_h, img, border_h])
    img2 = np.hstack([border_v, img2, border_v])

    return img2


# 輪郭をなめらかに膨張
# img  : 中身が白、背景が黒のグレースケール画像
# size : 膨張サイズ[pix]
def inflate(img, size):

    # 円形(なめらか)に膨張
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (size,size))
    img2 = cv2.morphologyEx(img, cv2.MORPH_DILATE, kernel)
    #cv2.imwrite('morph.png', img2)

    # モード(最頻)フィルターを適用
    img2 = Image.fromarray(img2) # cv -> PIL
    img2 = img2.filter(ImageFilter.ModeFilter(size))
    img2 = np.array(img2, dtype=np.uint8) # PIL -> cv
    #cv2.imwrite('mode.png', img2)

    # 外接する輪郭を取得して内部を塗りつぶす
    contours, hierarchy = cv2.findContours(img2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    img2 = cv2.drawContours(img, contours, -1, (255,255,255), thickness=cv2.FILLED)

    return img2


size = 50 # 膨張サイズ
img = cv2.imread('side.png', cv2.IMREAD_UNCHANGED) # 透過情報もそのまま読み込む

# 上下左右に縁取りを追加
img = add_border(img, size)

# 透過画像から中身が白、背景が黒のグレースケール画像を作成
img_gray = img.copy()
th = img_gray[:,:,3] >= 127
img_gray[th] = (255,255,255,255)
img_gray[np.logical_not(th)] = (0,0,0,0)
img_gray = img_gray[:,:,0:3]
img_gray = cv2.cvtColor(img_gray, cv2.COLOR_BGR2GRAY)
#cv2.imwrite('gray.png', img_gray)

# 輪郭画像を作成
img_inf = inflate(img_gray, size)
img_inf = cv2.cvtColor(img_inf, cv2.COLOR_GRAY2BGR)

# 元画像を透過情報を維持しつつ合成
img_ret = img_inf[:,:] * (1 - img[:,:,3:] / 255) + img[:,:,:3] * (img[:,:,3:] / 255)
cv2.imwrite('ret.png', img_ret)

コード解説

まずは元の画像から、中身が白で背景が黒の2値画像を作成します。
gray.png

次にcv2.morphologyExで周囲が円形になるように画像を膨張させます。
morph.png
ただ、そのまま膨張させると凹部分が鋭角のままになってしまいます。
そこでPILのModeFilterという最頻値をとるフィルターをかけます。
mode.png
凹部分もうまくなめらかになりました。
あとはcv2.findContoursで外接する輪郭を取得して内側を白で塗りつぶします。
これはドーナツのように内側に穴のあるような画像を考慮しての後処理になります。

最後に元の画像と合成して完成です。
ret.png

その他

わりといいかんじにできました。
元ネタ:画像輪郭のなめらか処理の方法

1
1
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
1
1