【初心者向け】基礎&実践プログラミング

初心者がつまづきやすいところ、最短で実力が身につく方法をお伝えします。

【画像認識】Pythonを使った物体抽出・切り取り

f:id:AIProgrammer:20201219112751p:plain

動かしながら学ぶ PyTorchプログラミング入門

動かしながら学ぶ PyTorchプログラミング入門

  • 作者:斎藤勇哉
  • 発売日: 2020/11/30
  • メディア: Kindle版

目次

目的

PyThonを用いて物体抽出・切り取りをする。具体的には、画像処理ライブラリであるOpenCVを用いて、画像中に存在する物体(ex. リンゴ, コイン, etc.)それぞれに外接矩形を当てはめる機能がある。その機能を用いて、画像内に存在する物体を抽出・切り取りをする。

用意するデータ

dataフォルダ内に、物体抽出したい画像(コインや瓶のふたがたくさん映っている画像)が格納されています。a.shは、物体抽出するためのスクリプトです。

dataフォルダ内の画像ファイル名およびファイル数は、任意です。

.
├── a.py
├── data
│   ├── image001.jpg
│   ├── image002.jpg
│   └── image003.jpg

使用する画像は、次の通りです。お世話になります!

image001.jpg

f:id:AIProgrammer:20201219100810j:plain

Photo by Justin Wei on Unsplash

image002.jpg

f:id:AIProgrammer:20201219100806j:plain

Photo by 🇨🇭 Claudio Schwarz | @purzlbaum on Unsplash

image003.jpg

f:id:AIProgrammer:20201219100814j:plain

Photo by Maria Oswalt on Unsplash

ソースコード

物体抽出をするための手順は、次の通りです。

  1. 画像読み込み
  2. カラー画像をグレースケール(白黒)画像に変換
  3. グレースケール画像をしきい値処理(最小値[srcmin] / 最大値[srcmax]のしきい値で画像をバイナリー化)
  4. バイナリ画像から、物体の輪郭を抽出
  5. ノイズや関係のない物体輪郭を除外するために、物体輪郭内面積のしきい値処理(最小値[areamin]/最大値[areamax])
  6. 抽出した物体の輪郭ごとに、外接矩形を当てはめる
  7. 外接矩形で画像を切り取り・保存

以上を実装するための、スクリプトは次の通り。

a.sh

import cv2
import numpy as np
import matplotlib.pyplot as plt
import os


def detect_contour(file, srcmin, srcmax, areamin, areamax):
    # 画像を読込
    org_src = cv2.imread('data/'+file+'.jpg', cv2.IMREAD_COLOR)
    src = np.copy(org_src)

    # グレースケール画像へ変換
    gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
    cv2.imwrite("split_image/{}/gray.jpg".format(file), gray)

    # グレースケール画像2値化
    retval, bw = cv2.threshold(
        gray, srcmin, srcmax, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    cv2.imwrite("split_image/{}/binary.jpg".format(file), bw)

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

    # Set variable to count the number of brain
    brain_n = 0
    # 各輪郭に対する処理
    for contour in contours:
        # 輪郭の領域を計算
        area = cv2.contourArea(contour)

        # ノイズ(小さすぎる領域)と全体の輪郭(大きすぎる領域)を除外
        if area < areamax or areamin < area:
            continue

        # 外接矩形
        if len(contour) > 0:
            x, y, w, h = cv2.boundingRect(contour)
            cv2.rectangle(src, (x, y), (x + w, y + h), (0, 255, 0), 5)
            # 外接矩形毎に画像を保存
            cv2.imwrite(
                "split_image/{}/split{:0>3}.jpg".format(file, brain_n), org_src[y:y + h, x:x + w])
            # Update brain counts
            brain_n += 1

    # 外接矩形された画像を表示
    # cv2.imshow('output', src)
    # plt.imshow(src)
    # 外接矩形された画像をsave
    cv2.imwrite("split_image/{}/all_contours.jpg".format(file), src)


if __name__ == '__main__':
    # Define
    srcmin = 100
    srcmax = 255
    areamin = 1e7
    areamax = 1e5

    for file in [os.path.splitext(x)[0] for x in sorted(os.listdir('data'))]:
        # Make result folder
        if not os.path.isdir('split_image/'+file):
            os.makedirs('split_image/'+file)

        # Run
        print("Processing {}.jpg...".format(file))
        detect_contour(file, srcmin, srcmax, areamin, areamax)

結果

処理が完了すると、split_imageフォルダが生成され、各入力画像ごとに抽出された画像が保存される。

  • gray.jpg: グレースケール画像
  • binary.jpg: バイナリー画像
  • split???.jpg: 抽出・切り取りした画像
.
├── a.py
├── data
│   ├── image001.jpg
│   ├── image002.jpg
│   └── image003.jpg
└── split_image
    ├── image001
    │   ├── all_contours.jpg
    │   ├── binary.jpg
    │   ├── gray.jpg
    │   ├── split000.jpg
    │   ├── split001.jpg
    │   ├── split002.jpg
    │   ├── split003.jpg
    │   ├── split004.jpg
    │   ├── split005.jpg
    │   └── split006.jpg
    ├── image002
    │   ├── all_contours.jpg
    │   ├── binary.jpg
    │   ├── gray.jpg
    │   ├── split000.jpg
    │   ├── split001.jpg
    │   └── split002.jpg
    └── image003
        ├── all_contours.jpg
        ├── binary.jpg
        ├── gray.jpg
        ├── split000.jpg
        ├── split001.jpg
        ├── split002.jpg
        ├── split003.jpg
        ├── split004.jpg
        ├── split005.jpg
        ├── split006.jpg
        ├── split007.jpg
        ├── split008.jpg
        ├── split009.jpg
        ├── split010.jpg
        ├── split011.jpg
        ├── split012.jpg
        └── split013.jpg

例として、image001.jpgのグレースケール画像(gray.jpg)を見てみる。

f:id:AIProgrammer:20201219101019j:plain

バイナリー画像は、この通り。

f:id:AIProgrammer:20201219101042j:plain

外接矩形を当てはめた画像は、次の通り。

f:id:AIProgrammer:20201219112932j:plain

切り取った画像は、次の通りです。

f:id:AIProgrammer:20201219101102j:plain

f:id:AIProgrammer:20201219101105j:plain

f:id:AIProgrammer:20201219101108j:plain

一方、image003.jpgに外接矩形を当てはめた画像は、次の通り。一部の瓶のふたで、外接矩形をはめれていないものがある。

f:id:AIProgrammer:20201219110702j:plain

バイナリー画像を確認すると、外接矩形のあてはめに失敗している瓶のふたで、輪郭がなくなっていることが分かる。そのため、輪郭抽出に失敗し、外接矩形のあてはめも失敗している。この場合には、バイナリー化する際のしきい値設定(最小値[srcmin] / 最大値[srcmax])を調整するとよい。

f:id:AIProgrammer:20201219101214j:plain


頑張れ!喝!!の代わりにB!ブックマークを押していただけるとただただうれしいです(^^)! ↓

動かしながら学ぶ PyTorchプログラミング入門

動かしながら学ぶ PyTorchプログラミング入門

  • 作者:斎藤勇哉
  • 発売日: 2020/11/30
  • メディア: Kindle版