今回はopencvを使って画像から人を抜き出して
背景を切りとってみました。

解説動画はこちら



今回は簡易なセグメンテーションです。

画像の人の領域を特定して
背景と切り分けてみましょう。

まずはライブラリの読み込みです。
# 描画用のメソッド
import numpy as np
import cv2
import matplotlib.pyplot as plt
%matplotlib inline

def img_display(img):
    fig = plt.figure(figsize=(10,8))
    ax = fig.add_subplot(111)
    ax.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    ax.axis('off')

def img_display2(img,cmap=None):
    fig = plt.figure(figsize=(10,8))
    ax = fig.add_subplot(111)
    ax.imshow(img, cmap=cmap)
    ax.axis('off')
    
def img_display3(imgs,texts):
    fig = plt.figure(figsize=(16,10))
    for i , img in enumerate(imgs):
        tmp = fig.add_subplot(1, len(imgs), i+1)
        tmp.set_title(texts[i],fontsize=15)
        plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        plt.axis('off')
    plt.show() 



次に人の画像を用意しましょう。
img = cv2.imread('kuro/kuro2.jpg')
img_display(img)
download-4


今回はこちらの方を用意しました。

さてこの画像に対して
処理を行っていきましょう。

最初は画像にブラー(ぼかし)をかけてみます。
img_blur1 = cv2.medianBlur(img ,    5)
img_blur2 = cv2.medianBlur(img , 15)
img_blur3 = cv2.medianBlur(img , 25)
img_blur4 = cv2.medianBlur(img , 35)
img_blur5 = cv2.medianBlur(img , 45)

texts = ['Blur_5','Blur_15','Blur_25','Blur_35','Blur_45']
img_display3([img_blur1,img_blur2,img_blur3,img_blur4,img_blur5] , texts)

download

こんな感じでぼかされていきますね。
ぼかすと背景と物や人の領域が
やや滑らかに分かれる感じです。

次にグレースケールに加工します。
gray_img1 = cv2.cvtColor(img_blur1,cv2.COLOR_BGR2GRAY)
gray_img2 = cv2.cvtColor(img_blur2,cv2.COLOR_BGR2GRAY)
gray_img3 = cv2.cvtColor(img_blur3,cv2.COLOR_BGR2GRAY)
gray_img4 = cv2.cvtColor(img_blur4,cv2.COLOR_BGR2GRAY)
gray_img5 = cv2.cvtColor(img_blur5,cv2.COLOR_BGR2GRAY)

texts = ['gray_img1','gray_img2','gray_img3','gray_img4','gray_img5']
img_display3([gray_img1,gray_img2,gray_img3,gray_img4,gray_img5] , texts)

download-2

カラー画像を白黒にすると
白黒の濃淡は0-255の数値で表せるので
閾値を決めて領域を分けることができます。

次は閾値を決めて
人のシルエットを作ってみましょう。
mid = 204
ret, img_thresh1 = cv2.threshold(gray_img1,mid,255,cv2.THRESH_BINARY_INV)
ret, img_thresh2 = cv2.threshold(gray_img2,mid,255,cv2.THRESH_BINARY_INV)
ret, img_thresh3 = cv2.threshold(gray_img3,mid,255,cv2.THRESH_BINARY_INV)
ret, img_thresh4 = cv2.threshold(gray_img4,mid,255,cv2.THRESH_BINARY_INV)
ret, img_thresh5 = cv2.threshold(gray_img5,mid,255,cv2.THRESH_BINARY_INV)

texts = ['img_thresh1','img_thresh2','img_thresh3','img_thresh4','img_thresh5']
img_display3([img_thresh1,img_thresh2,img_thresh3,img_thresh4,img_thresh5] , texts)

download-1


うまくいくかは閾値次第ですが
今回は背景と人がくっきり分かれました。

このシルエットを使って
元の画像を切り取ってみましょう。

シルエット部分だけ抽出して背景を抜く
# ノイズ取り
kernel = np.ones((3,3),np.uint8)
mor_img = cv2.morphologyEx(img_thresh3,cv2.MORPH_OPEN,kernel,iterations = 2)

# マスク
mask_img = cv2.dilate(mor_img,kernel,iterations=2)
mask_rgb = cv2.cvtColor(mask_img, cv2.COLOR_GRAY2RGB)

# マスクの切り抜き
result_img = cv2.bitwise_and(img, mask_rgb)
img_display(result_img)
download-3

こんな感じで人と背景を分けて
人だけ抽出ができました。


まとめです。

背景を削除して物や人の画像を抽出できますが
背景が複雑だとうまくいかないです。

なにせ雑に簡単な方法なので
より精密にセグメンテーションするには
opencvでは限界がありますね。

精細に行うなら
機械学習などで学習させた
領域切り分けを使わないと
上手くいかないでしょうね。

今回はopnecvを使った
簡単な画像のセグメンテーションでした。

今回はここまでです。
それでは。