OpenCVのスーパーピクセル

OpenCVのスーパーピクセルについて紹介がなかったので書こうかと思いました。
説明は使い方をサンプルで示す方法で記述していきます。
アルゴリズムの説明、関数インターフェイスについては、ググってお調べただけましたら
幸いです。
ただ、コンストラクタの引数については記載しないと、まるっきりとりつきが無い感じが
しましたので記載しました。


#スーパーピクセルについて
領域分割とかセグメンテーションアルゴリズムのことで、最初見たとき
その認識は無かったですが、資料を読んで分かった次第です。
OpenCVに実装されているスーパーピクセル機能は３種類あります。

<ul>
<li>LSC (Linear Spectral Clustering)<p>
</li>
<li>SEEDS(Superpixels Extracted via Energy-Driven Sampling)<p>
</li>
<li>SLIC(Simple Linear Iterative Clustering)<p>
さらに、３種類のアルゴリズムSLIC, SLICO, MSLICを引数に設定します。
</li>
</ul>
<p>
この３種類にサンプルプログラムを作成し実行させ、セグメンテーションを
実施してみます。


#構成
##サンプルプログラムの構造
サンプルプログラムの基本的な流れとして、下記のようにします。

1. 画像読込む
1. 入力画像の色空間をBGRからHSVに変換する。
1. スーパーピクセル・インスタンスを生成。cv2.ximgproc.createSuperpixel***()
1. 計算パラメータの設定。iterate(), enforceLabelConnectivity()
1. セグメンテーションの境界を取得。getLabelContourMask()
1. 境界線を読み込んだカラー画像に反映。
2. 表示


##入力画像について
![F-15_400x533.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/9b423f18-d556-52ff-072c-94af608a4dea.jpeg)
入力画像は、上記のF-15の画像を使います。
写真出典は[防衛省Webサイト 自衛隊写真館](https://www.mod.go.jp/j/kids/gallery/index.html)の画像で、それを400x533に縮小しています。


#スーパーピクセルLSC
##サンプルプログラム

```python:lsc.py
import cv2
import numpy as np

def main():
    image = cv2.imread('F-15_400x533.jpg')

    # BGR-HSV変換
    converted = cv2.cvtColor(image, cv2.COLOR_BGR2HSV_FULL)

    # パラメータ
    region_size = 20
    ruler = 0.1
    min_element_size = 10
    num_iterations = 4

    # LSCインスタンス生成
    slc = cv2.ximgproc.createSuperpixelLSC(converted, region_size,float(ruler))
    slc.iterate(num_iterations)
    slc.enforceLabelConnectivity(min_element_size)

    # スーパーピクセルセグメンテーションの境界を取得
    contour_mask = slc.getLabelContourMask(False)
    image[0 < contour_mask] = (0, 255, 255)
    cv2.imshow('LSC result', image)
    cv2.waitKey(0)

if __name__ == '__main__':
    main()
```

cv2.ximgproc.createSuperpixelLSC()関数で、パラメータを設定し、LSCインスタンスを
生成、計算パラメータを設定します。
境界線情報を取得し、それを読み込んだ画像に反映して表示します。

####スーパーピクセルLSCのインスタンス生成
retval = cv.ximgproc.createSuperpixelLSC(image[, region_size[, ruler]]])
&nbsp;&nbsp;image - セグメント化するイメージ
&nbsp;&nbsp;region_size - スーパーピクセルの平均サイズ
&nbsp;&nbsp;ruler - スーパーピクセル平滑係数
&nbsp;&nbsp;retval - 返却値:LSCインスタンス


##スーパーピクセルLSC 実行結果
![LSC_result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/e4c58bf0-65f2-4f9e-dd49-ca102bf25de0.jpeg)



#スーパーピクセルSEEDS
##サンプルプログラム

```python:seeds.py
import cv2
import numpy as np

def main():
    image = cv2.imread('F-15_400x533.jpg')

    # パラメータ
    height, width, channels= image.shape[:3]
    num_iterations = 5
    prior = 2
    double_step = True
    num_superpixels = 700
    num_levels = 4
    num_histogram_bins = 5

    # スーパーピクセルSEEDSの生成
    seeds = cv2.ximgproc.createSuperpixelSEEDS(width, height, channels, num_superpixels,
            num_levels, prior, num_histogram_bins, double_step)

    converted = cv2.cvtColor(image, cv2.COLOR_BGR2HSV_FULL)

    # 画像のスーパーピクセルSEEDSを計算
    seeds.iterate(converted, num_iterations)

    # スーパーピクセルセグメンテーションの境界を取得
    contour_mask = seeds.getLabelContourMask(False)
    result = image.copy()
    result[0 < contour_mask] = (0, 255, 255)

    # 画像表示
    cv2.imshow('SEEDS result', result)
    cv2.waitKey(0)

if __name__ == '__main__':
    main()
```

cv2.ximgproc.createSuperpixelSEEDS()でインスタンスを生成しますが、この段階では
入力画像を設定しません。iterate()メソッドに入力画像を設定します。

####スーパーピクセルSEEDSのインスタンス生成
retval = cv.ximgproc.createSuperpixelSEEDS(image_width, image_height, image_channels,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;num_superpixels, num_levels[, prior[, histogram_bins[, double_step]]] )
&nbsp;&nbsp;image_width - 画像幅
&nbsp;&nbsp;image_height - 画像高さ
&nbsp;&nbsp;image_channels - 画像チャンネル
&nbsp;&nbsp;num_superpixels - 必要なスーパーピクセルセグメンテーション数
&nbsp;&nbsp;num_levels - ブロックレベルの数
&nbsp;&nbsp;prior - 平滑オプション(0～5) 3x3平滑化を有効にします。
&nbsp;&nbsp;histogram_bins - ヒストグラムビンの数
&nbsp;&nbsp;double_step - Trueの場合、精度向上の為、各ブロックレベルをダブルで実施
&nbsp;&nbsp;retval - 返却値:スーパーピクセルSEEDSのインスタンス


##スーパーピクセルSEEDS 実行結果
![SEEDS_result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/51e3a7b8-e53c-ce05-da03-0e3a56fda01d.jpeg)


#スーパーピクセルSLIC
##サンプルプログラム

```python:slic.py
import cv2
import numpy as np

def main():
    image = cv2.imread('F-15_400x533.jpg')

    # パラメータ
    algorithms = [
        ('SLIC', cv2.ximgproc.SLIC), 
        ('SLICO', cv2.ximgproc.SLICO), 
        ('MSLIC', cv2.ximgproc.MSLIC) ]
    region_size = 20
    ruler = 30
    min_element_size = 10
    num_iterations = 4

    # BGR-HSV変換
    converted = cv2.cvtColor(image, cv2.COLOR_BGR2HSV_FULL)
    for alg in algorithms:
        slic = cv2.ximgproc.createSuperpixelSLIC(converted, alg[1], region_size,float(ruler))
        slic.iterate(num_iterations)
        slic.enforceLabelConnectivity(min_element_size)
        result = image.copy()

        # スーパーピクセルセグメンテーションの境界を取得
        contour_mask = slic.getLabelContourMask(False)
        result[0 < contour_mask] = (0, 255, 255)
        cv2.imshow('SLIC ('+ alg[0] + ') result', result)
        cv2.imwrite('images/SLIC_'+alg[0]+'_result.jpg', result)
        cv2.waitKey(0)

if __name__ == '__main__':
    main()
```

基本的身はスーパーピクセルLSCと同じです。
３種類のアルゴリズムを実行しているため、ループさせています。

####スーパーピクセルSLICのインスタンス生成
retval = cv.ximgproc.createSuperpixelSLIC(image[,algorithm[,region_size[,ruler]]])
&nbsp;&nbsp;image - セグメント化するイメージ
&nbsp;&nbsp;algorithm - アルゴリズム(SLIC, SLICO, MSLIC)
&nbsp;&nbsp;region_size - スーパーピクセルの平均サイズ
&nbsp;&nbsp;ruler - スーパーピクセル平滑係数
&nbsp;&nbsp;retval - 返却値:ーパーピクセルSLICインスタンス


##スーパーピクセルSLIC SLICアルゴリズム 実行結果
![SLIC_SLIC_result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/23937706-e964-1900-4de7-e58b51f7dcc5.jpeg)

##スーパーピクセルSLIC SLICOアルゴリズム 実行結果
![SLIC_SLICO_result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/6157145c-0c03-cdc9-08ad-88d09c217f5a.jpeg)

##スーパーピクセルSLIC MSLICアルゴリズム 実行結果
![SLIC_MSLIC_result.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/287767b6-0aec-2952-969b-725865e77c00.jpeg)


#終わりに
実行結果についてはコメントを書きませんでした。画像を見てご判断下さい。
用途、対象画像等々条件でアルゴリズムを選択することになるかともいます。
最初、規定値で行おうとも考えましたが、分割が細かくなってしまい、良く見えなかったので
見た目の分割サイズが同じようになるように、パラメータを調整してあります。
パラメータは、同じ名前であっても、アルゴリズムが異なるため動きが違ってきます。
さて、セグメンテーションについてはK-Meansなど他にも、いろいろあるかと思いますが、
そのうちの一つとして紹介しました。
ご覧いただきありがとうございました。


#実行環境
Windows10 Anaconda
Python 3.8.5
OpenCV 4.0.1


#参考
[Emotion Explorer - OpenCVのスーパーピクセル(3) - SuperpixelLSCクラスを試す。](https://emotionexplorer.blog.fc2.com/blog-entry-205.html)
[Emotion Explorer - OpenCVのスーパーピクセル(2) - SuperpixelSLICクラスを試す。](https://emotionexplorer.blog.fc2.com/blog-entry-204.html)
[Emotion Explorer - OpenCVのスーパーピクセル(1) - SuperpixelSEEDSクラスを試す。](https://emotionexplorer.blog.fc2.com/blog-entry-203.html)
[Emotion Explorer - Watershedアルゴリズムの領域分割](https://emotionexplorer.blog.fc2.com/blog-entry-92.html)
[docs.opencv.org - Superpixels Extended Image Processing](https://docs.opencv.org/4.0.1/df/d6c/group__ximgproc__superpixel.html)
[docs.opencv.org - cv::ximgproc::SuperpixelSEEDS Class Reference](https://docs.opencv.org/4.0.1/df/d81/classcv_1_1ximgproc_1_1SuperpixelSEEDS.html)
[docs.opencv.org - cv::ximgproc::SuperpixelSLIC Class Reference](https://docs.opencv.org/4.0.1/d3/da9/classcv_1_1ximgproc_1_1SuperpixelSLIC.html)
[docs.opencv.org - cv::ximgproc::SuperpixelLSC Class Reference](https://docs.opencv.org/4.0.1/d5/da0/classcv_1_1ximgproc_1_1SuperpixelLSC.html)


#写真引用
[防衛省Webサイト 自衛隊写真館](https://www.mod.go.jp/j/kids/gallery/index.html)を400x533に縮小して画像処理サンプルとして使用