DOBOT×AI グリーンバックから対象物の輪郭を抜き出す

# やりたいこと
以下のテキストでは、AIを用いた画像認識により対象物を分類し、ロボットアームにより仕分けを行っています。
・[DOBOT Magician AIｘ画像認識ｘロボットアーム制御](https://afrel.co.jp/product/dobot/education-product/)

上記テキストでは、画像認識によって対象物の領域を取得する際に、カメラの画像をグレースケールに変換した後、2値化して、その後、領域を取得しています。その場合でも、カメラの調整や環境（照明の明るさ、外乱光等）の調整をすることでおおよそ認識することが可能です。
テキストでは、グレースケールした際に明暗がハッキリするように、カメラに映るところに黒い下敷きを敷いています。

ただ、対象物が黒い場合、上記の方法ではうまく認識できないことがあります。
今回は、黒い下敷きを任意の色のついたものに変えて、その色を抜き出すことで対象物と背景を切り分けてみたいと思います。

# できたもの

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145092/6dad36fa-e192-4cbd-81cf-c12207495701.jpeg">

今回は、明るめの緑色の画用紙を背景に使用しました。画用紙の色が抜き出せるように範囲を調整を行いました。色が複雑に入り込んでいるものや外郭が黒いものも正しく認識できています。

<iframe width="560" height="315" src="https://www.youtube.com/embed/taTsYfZIqMk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# プログラム
領域を取得している「shoot_trainingData.py」と「classifier.py」を以下のように修正しています。

## 修正前
```python:shoot_trainingData.py
        # グレースケールに変換（4-3）
        gray = cv2.cvtColor(edframe, cv2.COLOR_BGR2GRAY)
        # 1/4サイズに縮小
        gray_s = cv2.resize(gray, (int(gray.shape[1]/2), int(gray.shape[0]/2)))
        # グレースケール画像を表示する
        cv2.imshow('Gray Frame', gray_s)

        # ２値化（4-4）
        retval, bw = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
        # 1/4サイズに縮小
        bw_s = cv2.resize(bw, (int(gray.shape[1]/2), int(gray.shape[0]/2)))
        # ２値化画像を表示する
        cv2.imshow('Binary Frame', bw_s)

        # 輪郭を抽出（4-5）
        #   contours : [領域][Point No][0][x=0, y=1]
        #   cv2.CHAIN_APPROX_NONE: 中間点も保持する
        #   cv2.CHAIN_APPROX_SIMPLE: 中間点は保持しない
        contours, hierarchy = cv2.findContours(bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
```

```python:classifier.py
        # グレースケールに変換（3-4）
        gray = cv2.cvtColor(edframe, cv2.COLOR_BGR2GRAY)
        # ２値化（3-5）
        retval, bw = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

        # 輪郭を抽出（3-6）
        contours, hierarchy = cv2.findContours(bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
```

## 修正後
```python:shoot_trainingData.py
        # グレースケールに変換（4-3）
        # gray = cv2.cvtColor(edframe, cv2.COLOR_BGR2GRAY)
        # 1/4サイズに縮小
        # gray_s = cv2.resize(gray, (int(gray.shape[1]/2), int(gray.shape[0]/2)))
        # グレースケール画像を表示する
        # cv2.imshow('Gray Frame', gray_s)

        # ２値化（4-4）
        # retval, bw = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
        # 1/4サイズに縮小
        # bw_s = cv2.resize(bw, (int(gray.shape[1]/2), int(gray.shape[0]/2)))
        # ２値化画像を表示する
        # cv2.imshow('Binary Frame', bw_s)

        # HSV色空間に変換
        LOW_COLOR  = np.array([ 30, 64, 0])
        HIGH_COLOR = np.array([ 90, 255, 255])
        hsv = cv2.cvtColor(edframe, cv2.COLOR_BGR2HSV)
        # 色を抽出する
        ex_img = cv2.inRange(hsv, LOW_COLOR, HIGH_COLOR)
        # ノイズ除去
        ex_img = cv2.medianBlur(ex_img, 5)
        kernel = np.ones((3,3), np.uint8)
        ex_img = cv2.dilate(ex_img, kernel, iterations=2)
        # 黒白反転
        ex_img = cv2.bitwise_not(ex_img)

        # 1/4サイズに縮小して表示
        ex_img_s = cv2.resize(ex_img, (int(ex_img.shape[1]/2), int(ex_img.shape[0]/2)))
        cv2.imshow("ex_img", ex_img_s)

        # 輪郭を抽出（4-5）
        #   contours : [領域][Point No][0][x=0, y=1]
        #   cv2.CHAIN_APPROX_NONE: 中間点も保持する
        #   cv2.CHAIN_APPROX_SIMPLE: 中間点は保持しない
        # contours, hierarchy = cv2.findContours(bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        contours, hierarchy = cv2.findContours(ex_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
```

```python:classifier.py
        # グレースケールに変換（3-4）
        # gray = cv2.cvtColor(edframe, cv2.COLOR_BGR2GRAY)
        # ２値化（3-5）
        # retval, bw = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

        # HSV色空間に変換
        LOW_COLOR   = np.array([30, 64, 0])
        HIGH_COLOR  = np.array([90,255,255])
        hsv = cv2.cvtColor(edframe, cv2.COLOR_BGR2HSV)
        # 色を抽出する
        ex_img = cv2.inRange(hsv, LOW_COLOR, HIGH_COLOR)
        # ノイズ除去
        ex_img = cv2.medianBlur(ex_img, 5)
        kernel = np.ones((3,3), np.uint8)
        ex_img = cv2.dilate(ex_img, kernel, iterations=2)
        # 黒白反転
        ex_img = cv2.bitwise_not(ex_img)

        # 1/4サイズに縮小して表示
        ex_img_s = cv2.resize(ex_img, (int(ex_img.shape[1]/2), int(ex_img.shape[0]/2)))
        cv2.imshow("ex_img", ex_img_s)

        # 輪郭を抽出（3-6）
        # contours, hierarchy = cv2.findContours(bw, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        contours, hierarchy = cv2.findContours(ex_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
```

## 抜き出す色の調整
上記、プログラムでは緑色を抜き出す際の調整になっています。
動かす照明環境やそもそも別の色を抜き出したい場合は、以下の部分の調整を行ってください。

```python:classifier.py
        # HSV色空間に変換
        LOW_COLOR   = np.array([30, 64, 0])
        HIGH_COLOR  = np.array([90,255,255])
```