LoginSignup
4
5

More than 3 years have passed since last update.

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

Posted at

はじめに

OpenCVのスーパーピクセル(領域分割)画像処理について、前回記事を書きましたが
分割してどうする?のどうするかについての部分を書いていませんでしたので
書きたいと思います。

スーパーピクセルについて

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

1)スーパーピクセルインスタンスの生成

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

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)スーパーピクセルセグメンテーションの境界を取得

各分割された領域を分ける線を黄色で線引きします。
これは、人にわかりやすくするための処理で、必ずしも必須ではありません。

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

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

3)セグメンテーション情報取得

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

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

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

4)セグメンテーション状況(ラベル情報)を表示

セグメント分けがどのようになっているかを見る為に
ラベル情報をランダム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]

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

5)セグメンテーション平均化処理

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

# セグメンテーション毎の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()は、マスク画像をしていすることで、マスク領域の平均値を取得することができます。

サンプルプログラム

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
入力画像は、野菜の盛り合わせ写真。
「野菜生活」 photoAC掲載 koma-komaさんの写真です。記事掲載用に少し縮小しました。
この画像に対してスーパーピクセル処理を行います。

スーパーピクセルセグメンテーションの境界画像

Vegresult.jpg

セグメンテーションのラベル情報

Veglbsegimg.jpg

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

セグメンテーション状況(ラベル情報)画像

Veglbavgimg.jpg

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

最後に

前回記事を書いた後、すぐにこれのような記事を書こうとしていたのですが、いろいろ忙しく、
また興味が他に移っていたこともあり保留となっていました。
残件を仕上げた感じで少しさわやかないいかんじです。

実行環境

Windows10 Anaconda (Miniforge3)
Python 3.9.2 packaged by conda-forge
OpenCV 4.5.1

参考

Qiita - OpenCVのスーパーピクセル
Emotion Explorer - OpenCVのスーパーピクセル(3) - SuperpixelLSCクラスを試す。
Emotion Explorer - OpenCVのスーパーピクセル(2) - SuperpixelSLICクラスを試す。
Emotion Explorer - OpenCVのスーパーピクセル(1) - SuperpixelSEEDSクラスを試す。
Emotion Explorer - Watershedアルゴリズムの領域分割
Emotion Explorer - 画素の平均を計算
docs.opencv.org - Superpixels Extended Image Processing
docs.opencv.org - cv::ximgproc::SuperpixelSEEDS Class Reference
docs.opencv.org - cv::ximgproc::SuperpixelSLIC Class Reference
docs.opencv.org - cv::ximgproc::SuperpixelLSC Class Reference

写真引用

「野菜生活」 photoAC掲載 koma-komaさんの写真

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5