電算倶楽部 富山県のコンピュータ社会人サークル

富山県、特に滑川市、富山市、魚津市周辺で活動している社会人サークルです。

画像処理の復習

引き続き、大学でIoTのリカレント教育に出ています。

講義では「生産現場の技術者向け」を対象としているので、コンピュータの技術面についてあまり深いところに突っ込みません。

先日の画像処理の演習でも、プログラミングすると言うよりは与えられたコードをただ実行して体験するものでした。

身につけるためにも自宅で実際に調べながら作ってみました。 講義ではRaspberry Piを使っていましたが、Raspberry Pi用のカメラを繋ぐのが面倒なので、実行環境はWindows PCで、カメラは市販のWebカメラを使用しました。 言語はPython、画像処理ライブラリはOpenCV2を使っています。

背景差分

背景との差分を取り、動きのあった場所を検出します。 createBackgroundSubtractorMOG2で生成されるオブジェクトを使うだけで、考えて作るとことは殆どありません。その他の有名なアルゴリズムも呼び出すだけで使えるようです。

from cv2 import cv2

cap = cv2.VideoCapture(0)
fgbg = cv2.createBackgroundSubtractorMOG2()

while True:
    ret, frame = cap.read()
    cv2.imshow('', fgbg.apply(frame))
    k = cv2.waitKey(1)
    if k==27:
        break
cap.release()
cv2.destroyAllWindows()

こんな感じ。ものを動かすと白くなり、差分が検知されているのがわかります。

f:id:s-densan:20191016000206g:plain

円・直線検出

円や直線のような画像に赤色でマーキングします。 こちらも検出自体はHoughCirclesHoughLinesが全部やってくれます。

from cv2 import cv2
import math
import numpy as np

cap = cv2.VideoCapture(0)
fgbg = cv2.createBackgroundSubtractorMOG2()


def mask_circle(img):
    height, width = img.shape[:2]
    mask_color = (0,0,255)
    
    # グレイスケールに変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # 円検出
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1,
                               minDist=50, param1=120, param2=40, minRadius=40, maxRadius=100)
    # 255で埋まった3次元配列(縦x横xRGB3次元)を生成
    mask = np.full((height, width, 3), 255, np.uint8)
    if circles is not None:
        for cs in circles:
            for c in cs:
                mask = cv2.bitwise_or(mask, cv2.circle(mask, (c[0], c[1]), 80, mask_color, 20))
    return mask

def mask_line(img):
    height, width = img.shape[:2]
    mask_color = (0, 0, 255)
    
    # グレイスケールに変換
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.Canny(gray, 50, 150, apertureSize=3)
    # 円検出
    lines = cv2.HoughLines(gray, 1, np.pi/180, 200)
    # 255で埋まった3次元配列(縦x横xRGB3次元)を生成
    mask = np.full((height, width, 3), 255, np.uint8)
    if lines is not None:
        for rho, theta in lines[0]:
            
            x1 = int(np.cos(theta)*rho - 1000*np.sin(theta))
            y1 = int(np.sin(theta)*rho + 1000*np.cos(theta))
            x2 = int(np.cos(theta)*rho + 1000*np.sin(theta))
            y2 = int(np.sin(theta)*rho - 1000*np.cos(theta))

            mask = cv2.bitwise_or(mask, cv2.line(mask, (x1, y1), (x2, y2), mask_color, 2))
    return mask

mask_types = [('circle', mask_circle),
              ('line', mask_line)]

selection = 0

while True:
    width = 640
    height = 480
    ret, frame = cap.read()
    img = cv2.resize(frame, (width, height))
    
    if mask_types[selection][1] is not None:
        mask = mask_types[selection][1](frame)
        masked_img = cv2.bitwise_and(img, mask)

        img_with_text = cv2.putText(masked_img, mask_types[selection][0], (0, 30), 0, 1, (255, 255, 255))
        cv2.imshow('masked', img_with_text)
    k = cv2.waitKey(1)
    if k == 27:
        break
    elif k == ord('z'):
        selection = (selection - 1) % len(mask_types)

cap.release()
cv2.destroyAllWindows()

こんな感じ。円と線が検知されています。

f:id:s-densan:20191015235940g:plain

f:id:s-densan:20191016000333g:plain

まとめ

OpenCV2はプリセットが充実していて、サクッと作るだけなら簡単にできます。いかに精度を上げるか、分析結果をどう表現するか、今後も勉強していきます。