OpenCVのスーパーピクセル(2). 分割領域を平均化してみる

#はじめに
OpenCVのスーパーピクセル(領域分割)画像処理について、[前回記事](https://qiita.com/sitar-harmonics/items/ba02cd14d6f362439e96)を書きましたが
分割してどうする？のどうするかについての部分を書いていませんでしたので
書きたいと思います。


#スーパーピクセルについて
スーパーピクセルは領域分割とかセグメンテーションアルゴリズムのことで
領域を分割した上で画像処理をします。
今回は、スーパーピクセルで分割した領域のラベル情報(マスク)を取得し
それを表示します。
また、そのラベル情報(マスク)で、分割領域のBGR平均値を取得し、画像に
反映することをやってみようと思います。

##1)スーパーピクセルインスタンスの生成
まず、スーパーピクセルインスタンス生成し、画像データをインスタンスに入力します。

```python
seeds = cv2.ximgproc.createSuperpixelSEEDS(width, height, channels, num_superpixels,
        num_levels, prior, num_histogram_bins, double_step)

# 画像のスーパーピクセルセグメンテーションを計算
# 入力画像は,HSVまたはL*a*b*
converted = cv2.cvtColor(input_image, cv2.COLOR_BGR2HSV)
seeds.iterate(converted, num_iterations)
```

入力画像はHSV色空間かまたはL*a*b*色空間を使います。
上記サンプルconverted画像はHSV色空間です。


##2)スーパーピクセルセグメンテーションの境界を取得
各分割された領域を分ける線を黄色で線引きします。
これは、人にわかりやすくするための処理で、必ずしも必須ではありません。

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

領域を分ける線情報へのアクセス方法を掲載しています。


##3)セグメンテーション情報取得
セグメンテーション（領域分割）の分割数と、ラベル情報を取得します。
ラベル情報は、np.int32型の２次元配列です。
ここに分割猟奇事に数字が入っています。
ちょうど、粒子解析ラベリングとかブロブ処理と言われる場合にも
同様なイメージが使われているかと思います。

```python
# セグメンテーション数の取得
nseg = seeds.getNumberOfSuperpixels()

# セグメンテーション分割情報の取得
labels = seeds.getLabels()
```

##4)セグメンテーション状況(ラベル情報)を表示
セグメント分けがどのようになっているかを見る為に
ラベル情報をランダムBGR表示しています。
これは確認用の処理です。

```python
randcolor = np.random.randint(255, size=[nseg,3])
lbsegimg = np.zeros((height, width, channels), dtype=np.uint8)
for m in range(0, nseg):
    lbsegimg[labels == m] = randcolor[m]
```

lbsegimgがラベル情報をランダムBGRタイリング画像


##5)セグメンテーション平均化処理
セグメンテーション（領域分割）のセグメント毎にBGR平均値をマスク処理で取得します。
返却値は浮動小数点型のタプルなので、これをint型配列に変換し、画像に設定します。

```python
# セグメンテーション毎のBGR平均値を取得
segavgimg = np.zeros((height, width, channels), dtype=np.uint8)
lb = np.zeros((height, width), dtype=np.uint8)
for m in range(0, nseg):
    lb.fill(0)
    lb[labels == m] = 255
    bgrm = cv2.mean(input_image, lb) # BGR平均値を取得

    # tuple float形式の平均値情報をint形式に変換
    bgr = [int(bgrm[0]), int(bgrm[1]), int(bgrm[2])]
    segavgimg[lb == 255] = bgr
```

segavgimg画像が、セグメンテーションの平均化処理イメージです。
cv2.mean()は、マスク画像をしていすることで、マスク領域の平均値を取得することができます。



#サンプルプログラム

```python
import cv2
import math
import numpy as np

def main():
    input_image = cv2.imread('Vegetables.jpg')
    if input_image is None:
        print("ファイルオープンエラー")
        return -1

    # スーパーピクセルセグメンテーションの生成
    height, width, channels= input_image.shape[:3]
    num_iterations = 4
    prior = 2
    double_step = False
    num_superpixels = 500
    num_levels = 4
    num_histogram_bins = 5
    seeds = cv2.ximgproc.createSuperpixelSEEDS(width, height, channels, num_superpixels,
            num_levels, prior, num_histogram_bins, double_step)

    # 画像のスーパーピクセルセグメンテーションを計算
    # 入力画像は,HSVまたはL*a*b*
    converted = cv2.cvtColor(input_image, cv2.COLOR_BGR2HSV)
    seeds.iterate(converted, num_iterations)

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

    # セグメンテーション数の取得
    nseg = seeds.getNumberOfSuperpixels()

    # セグメンテーション分割情報の取得
    labels = seeds.getLabels()

    # セグメンテーション状況(ラベル情報)をランダムBGRでタイリング
    randcolor = np.random.randint(255, size=[nseg,3])
    lbsegimg = np.zeros((height, width, channels), dtype=np.uint8)
    for m in range(0, nseg):
        lbsegimg[labels == m] = randcolor[m]

    # セグメンテーション毎のBGR平均値を取得
    segavgimg = np.zeros((height, width, channels), dtype=np.uint8)
    lb = np.zeros((height, width), dtype=np.uint8)
    for m in range(0, nseg):
        lb.fill(0)
        lb[labels == m] = 255
        bgrm = cv2.mean(input_image, lb) # BGR平均値を取得

        # tuple float形式の平均値情報をint形式に変換
        bgr = [int(bgrm[0]), int(bgrm[1]), int(bgrm[2])]
        segavgimg[lb == 255] = bgr

    # 画像表示
    cv2.imshow('Vegetableslbsegimg', lbsegimg)
    cv2.imshow('Vegetablesresult', result)
    cv2.imshow('Vegetables means image', segavgimg)
    cv2.waitKey(0)
    return 0

if __name__ == '__main__':
    main()

```


#結果
##入力画像
![Vegetables.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/6618d3fe-cb7b-80a8-fd8b-5c16aac5ad26.jpeg)
入力画像は、野菜の盛り合わせ写真。
「野菜生活」&emsp;[photoAC](https://www.photo-ac.com/)掲載 [koma-koma](https://www.photo-ac.com/profile/3321820)さんの写真です。記事掲載用に少し縮小しました。
この画像に対してスーパーピクセル処理を行います。

##スーパーピクセルセグメンテーションの境界画像
![Vegresult.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/c594435c-2c3f-a603-fd16-c5e9f98c2970.jpeg)

#セグメンテーションのラベル情報
![Veglbsegimg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/8178f77e-7bbb-47ff-b83c-ff7a1e60c617.jpeg)

黄色い線の黄色い線の内側をランダムカラーで塗ったイメージです。


#セグメンテーション状況(ラベル情報)画像
![Veglbavgimg.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/391380/73f81d3f-8e7e-08a6-a9a5-ff3fdad152fd.jpeg)

各セグメント毎に平均色で色分けしています。


#最後に
[前回記事](https://qiita.com/sitar-harmonics/items/ba02cd14d6f362439e96)を書いた後、すぐにこれのような記事を書こうとしていたのですが、いろいろ忙しく、
また興味が他に移っていたこともあり保留となっていました。
残件を仕上げた感じで少しさわやかないいかんじです。


#実行環境
Windows10 Anaconda (Miniforge3)
Python 3.9.2 packaged by conda-forge
OpenCV 4.5.1

#参考
[Qiita - OpenCVのスーパーピクセル](https://qiita.com/sitar-harmonics/items/ba02cd14d6f362439e96)
[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)
[Emotion Explorer - 画素の平均を計算](https://emotionexplorer.blog.fc2.com/blog-entry-206.html)
[docs.opencv.org - Superpixels Extended Image Processing](https://docs.opencv.org/4.5.1/df/d6c/group__ximgproc__superpixel.html)
[docs.opencv.org - cv::ximgproc::SuperpixelSEEDS Class Reference](https://docs.opencv.org/4.5.1/df/d81/classcv_1_1ximgproc_1_1SuperpixelSEEDS.html)
[docs.opencv.org - cv::ximgproc::SuperpixelSLIC Class Reference](https://docs.opencv.org/4.5.1/d3/da9/classcv_1_1ximgproc_1_1SuperpixelSLIC.html)
[docs.opencv.org - cv::ximgproc::SuperpixelLSC Class Reference](https://docs.opencv.org/4.5.1/d5/da0/classcv_1_1ximgproc_1_1SuperpixelLSC.html)


#写真引用
「野菜生活」&emsp;[photoAC](https://www.photo-ac.com/)掲載 [koma-koma](https://www.photo-ac.com/profile/3321820)さんの写真