ホワイトボードの写真から付箋画像を抽出したいって話を聞いて
それOpenCVでできるんじゃないかと一生懸命ググってみました。

以下記事を参考に 試しにやってみた記録です。 qiita.com www.blog.umentu.work

環境

%matplotlib inline
import cv2 # opencv
import matplotlib.pyplot as plt # matplotlibの描画系

fn_img = "test.jpeg" # 画像ファイル名
img = cv2.imread(fn_img) # 画像を読み込む

# opencvの処理系では (B,G,R) で管理されているが、
# matplotlib では (R,G,B) で画像を認識するので、
# 表示用に一旦変換する
show_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 

plt.imshow(show_img) #表示

f🆔moremagic:20180225214418p:plain

  • 付箋を抽出してみる
# 画像差分を算出
def createDiffImg(img, base_img):
    img_diff = cv2.absdiff(base_img, img)
    return cv2.threshold(img_diff, 20, 255, cv2.THRESH_BINARY)[1]

# 差分を二値化
def show_hoge(img_diff):
    return cv2.threshold(img_diff, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]

im_gray = cv2.cvtColor(show_img, cv2.COLOR_BGR2GRAY) 

# 元画像を RGBに分割
RGB =cv2.split(show_img)
Blue   = show_hoge(createDiffImg(RGB[0], im_gray))
Green = show_hoge(createDiffImg(RGB[1], im_gray))
Red    = show_hoge(createDiffImg(RGB[2], im_gray))
#plt.imshow( Green )

aaa = cv2.addWeighted(Blue,0.5,Green,0.5,0)
aaa = cv2.addWeighted(aaa,0.5,Red,0.5,0)
plt.imshow( aaa )

f🆔moremagic:20180225215639p:plain

  • 画像を切り出してみる
def getRectByPoints(points):
    # prepare simple array 
    points = list(map(lambda x: x[0], points))

    points = sorted(points, key=lambda x:x[1])
    top_points = sorted(points[:2], key=lambda x:x[0])
    bottom_points = sorted(points[2:4], key=lambda x:x[0])
    points = top_points + bottom_points

    left = min(points[0][0], points[2][0])
    right = max(points[1][0], points[3][0])
    top = min(points[0][1], points[1][1])
    bottom = max(points[2][1], points[3][1])
    return (top, bottom, left, right)

def getPartImageByRect(rect, img):
    return img[rect[0]:rect[1], rect[2]:rect[3]]

contours = cv2.findContours(aaa, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]
# filtered with area over (all area / 100 )
th_area = aaa.shape[0] * aaa.shape[1] / 100
contours_large = list(filter(lambda c:cv2.contourArea(c) > th_area, contours))

outputs = []
rects = []
approxes = []

for (i,cnt) in enumerate(contours_large):
    arclen = cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, 0.02*arclen, True)
    if len(approx) < 4:
        continue
    approxes.append(approx)
    rect = getRectByPoints(approx)
    rects.append(rect)
    outputs.append(getPartImageByRect(rect, show_img))
    cv2.imwrite(str(i)+'.jpg', getPartImageByRect(rect, show_img))

f🆔moremagic:20180225215728p:plain

結果

付箋を四角く フィルタをかけることはできたっぽいですが
二値画像にした(つもり)なのに なんか超黄色くなってるし
一部しか抽出できませんでした・・・orz

もうちょっとちゃんと勉強してみないとだめっぽいです。

※ 続くかも