Main Content

L*a*b* 色空間を使った色ベースのセグメンテーション

この例では、L*a*b* 色空間を解析することで、布地内の異なる色を識別する方法を示します。

手順 1: イメージの取得

fabric.png イメージを読み取ります。これはカラフルな布地のイメージです。

fabric = imread("fabric.png");
imshow(fabric)
title("Fabric")

手順 2: 各領域の L*a*b* 色空間におけるサンプル色の計算

イメージ内には 6 つの主な色があります。背景色は赤、緑、紫、黄、およびマゼンタです。これらの色を視覚的に簡単に区別できることに注目してください。L*a*b* 色空間 (CIELAB または CIE L*a*b*) を使用すると、このような視覚的差異を定量化できます。

L*a*b* 色空間は CIE XYZ 三刺激値から導出されます。L*a*b* 空間は、輝度 'L*' または明度レイヤー、赤-緑軸方向における色の位置を示す色度レイヤー 'a*'、および青-黄軸方向における色の位置を示す色度レイヤー 'b*' から構成されます。

色ごとに小さなサンプル領域を選択し、'a*b*' 空間における各サンプル領域の平均の色を計算する方法をとります。これらの色マーカーを使用して、各ピクセルを分類します。

この例を簡単にするために、MAT ファイルに保存されている領域座標を読み込みます。

load regioncoordinates;

nColors = 6;
sample_regions = false([size(fabric,1) size(fabric,2) nColors]);

for count = 1:nColors
  sample_regions(:,:,count) = roipoly(fabric,region_coordinates(:,1,count), ...
      region_coordinates(:,2,count));
end

imshow(sample_regions(:,:,2))
title("Sample Region for Red")

関数 rgb2lab を使用して、布地の RGB イメージを L*a*b* イメージに変換します。

lab_fabric = rgb2lab(fabric);

roipoly で抽出した領域ごとに、'a*' および 'b*' の平均値を計算します。これらの値は、'a*b*' 空間の色マーカーとして使用されます。

a = lab_fabric(:,:,2);
b = lab_fabric(:,:,3);
color_markers = zeros([nColors, 2]);

for count = 1:nColors
  color_markers(count,1) = mean2(a(sample_regions(:,:,count)));
  color_markers(count,2) = mean2(b(sample_regions(:,:,count)));
end

たとえば、a*b* 空間での赤のサンプル領域の平均色は以下のようになります。

disp([color_markers(2,1), color_markers(2,2)]);
   69.8278   20.1056

手順 3: 最近傍ルールを使用した各ピクセルの分類

ここまでで、各色マーカーに a* 値および b* 値が設定されました。lab_fabric イメージ内の各ピクセルを分類するには、ピクセルと各色マーカーの間のユークリッド距離を計算します。最短距離は、ピクセルがその色マーカーに最も厳密に一致することを意味します。たとえば、ピクセルと赤の色マーカー間の距離が最小である場合、ピクセルは赤のピクセルとラベル付されます。

色ラベルを含んでいる配列を以下のように作成します。0 = 背景、1 = 赤、2 = 緑、3 = 紫、4 = マゼンタ、5 = 黄。

color_labels = 0:nColors-1;

最近傍分類で使用される行列を初期化します。

a = double(a);
b = double(b);
distance = zeros([size(a), nColors]);

分類を実行します。

for count = 1:nColors
  distance(:,:,count) = ( (a - color_markers(count,1)).^2 + ...
      (b - color_markers(count,2)).^2 ).^0.5;
end

[~,label] = min(distance,[],3);
label = color_labels(label);
clear distance;

手順 4: 最近傍分類の結果の表示

ラベル行列には、布地のイメージの各ピクセルの色ラベルが含まれています。ラベル行列を使用して、元の布地のイメージのオブジェクトを色分けします。

rgb_label = repmat(label,[1 1 3]);
segmented_images = zeros([size(fabric), nColors],"uint8");

for count = 1:nColors
  color = fabric;
  color(rgb_label ~= color_labels(count)) = 0;
  segmented_images(:,:,:,count) = color;
end 

5 つのセグメント化された色をモンタージュとして表示します。また、イメージ内で色として分類されていない背景ピクセルを表示します。

montage({segmented_images(:,:,:,2),segmented_images(:,:,:,3) ...
    segmented_images(:,:,:,4),segmented_images(:,:,:,5) ...
    segmented_images(:,:,:,6),segmented_images(:,:,:,1)});
title("Montage of Red, Green, Purple, Magenta, and Yellow Objects, and Background")

手順 5: ラベルの付いた色の a* 値および b* 値の表示

異なる色に分類されたピクセルの a* 値および b* 値のプロットを作成することで、最近傍分類によってさまざまな色の母集団が非常に良く分類されたことがわかります。表示のために、各点にその色ラベルを付けます。紫は名前付きのカラー値ではないため、16 進数カラー コードが設定された string を使用して紫の色を指定します。

purple = "#774998";
plot_labels = ["k", "r", "g", purple, "m", "y"];

figure
for count = 1:nColors
    plot_label = plot_labels(count);
    plot(a(label==count-1),b(label==count-1),".", ...
       MarkerEdgeColor=plot_label,MarkerFaceColor=plot_label);
  hold on
end
  
title("Scatterplot of Segmented Pixels in a*b* Space");
xlabel("a* Values");
ylabel("b* Values");