Main Content

자동 화이트 밸런스 알고리즘 비교하기

이 예제에서는 세 가지 조도 알고리즘을 사용하여 장면의 조도를 추정하고 화이트 밸런스를 수행하는 방법을 보여줍니다.

사람의 눈은 각기 다른 조도 조건에서도 무엇이 흰색인지를 매우 정확히 판단합니다. 그러나 디지털 카메라는 어떤 종류의 조정을 해 주지 않을 경우 실제와 다른 강한 색조의 영상을 촬영하기 일쑤입니다. AWB(자동 화이트 밸런스) 알고리즘은 최소한의 사용자 입력으로 주변광에 맞게 수정하여 육안으로 볼 때와 가까운 결과 영상을 만듭니다.

자동 화이트 밸런스는 2단계를 거칩니다.

  • 1단계: 장면 광원을 추정합니다.

  • 2단계: 영상의 색 밸런스를 보정합니다.

장면 광원의 추정에 사용할 수 있는 알고리즘은 여러 가지가 있습니다.

  • White Patch Retinex [1]

  • Gray World [2]

  • Cheng의 주성분 분석(PCA, Principal Component Analysis) 방법 [3]

각 알고리즘의 성능은 장면, 조명, 영상 조건에 따라 달라집니다. 이 예제에서는 특정 영상에 세 가지 알고리즘을 사용해 보고 ColorChecker® 차트를 사용해 계산된 ground truth 장면 광원과 비교하여 각 알고리즘의 품질을 평가합니다.

원시 카메라 데이터를 읽어 들여서 전처리하기

일반적으로 AWB 알고리즘은 영상이 압축되어 메모리 카드에 저장되기 전에 최소한의 전처리를 거친 원시 영상 데이터에 적용됩니다.

16비트 원시 영상을 작업 공간으로 읽어 들입니다. foosballraw.tiff는 검은색 수준을 보정하고 명암을 픽셀당 16비트로 스케일링한 원시 센서 데이터가 들어 있는 영상 파일입니다. 이 영상은 카메라에 의한 화이트 밸런스뿐만 아니라 모자이크 제거, 잡음 제거, 색수차 보정, 색조 조정, 감마 보정 등의 기타 전처리 작업을 거치지 않았습니다.

A = imread("foosballraw.tiff");

누락된 색 정보를 복원하기 위해 보간하기

디지털 카메라는 색각을 시뮬레이션하기 위해 영상 센서 위에 컬러 필터 배열을 겹쳐 놓고 사용하므로 각 픽셀에서 빨간색, 녹색 또는 파란색을 잘 인식하게 됩니다. 모든 픽셀에서 누락된 색 정보를 복원하려면 demosaic 함수를 사용하여 보간하십시오. 사진을 촬영한 카메라(Canon EOS 30D)는 RGGB라는 베이어 패턴을 사용합니다.

A = demosaic(A,"rggb");

검출 및 표시를 위해 영상 감마 보정하기

영상 A에는 선형 RGB 값이 있습니다. 선형 RGB 값은 장면 광원을 추정하고 영상의 색 밸런스를 보정하는 데 적합합니다. 그러나 선형 RGB 영상을 표시하려 하면, 디스플레이 장치의 비선형 특성 때문에 매우 흐리게 나타납니다. 따라서 디스플레이를 위해서는 lin2rgb 함수를 사용하여 영상을 sRGB 컬러스페이스로 감마 보정합니다.

A_sRGB = lin2rgb(A);

모자이크 제거된 영상의 감마 보정 전후를 표시합니다.

montage({A,A_sRGB})
title("Original Image Before and After Gamma Correction")

ColorChecker 차트를 사용하여 ground truth 광원 측정하기

장면에 포함된 ColorChecker 차트를 사용하여 ground truth 광원을 계산합니다. 이 차트는 스펙트럼 반사율이 확인된 총 24개의 무채색 패치와 유채색 패치로 구성됩니다.

colorChecker 함수를 사용하여, 감마 보정된 영상에서 차트를 검출합니다. 선형 RGB 영상은 너무 어두워서 colorChecker가 자동으로 차트를 검출할 수 없습니다.

chart_sRGB = colorChecker(A_sRGB);

차트가 올바르게 검출되었음을 확인합니다.

displayChart(chart_sRGB)

차트의 네 개 코너에서 정합점의 좌표를 가져옵니다.

registrationPoints = chart_sRGB.RegistrationPoints;

선형 RGB 데이터에서 새 colorChecker 객체를 만듭니다. 정합점의 좌표를 사용하여 차트의 위치를 지정합니다.

chart = colorChecker(A,RegistrationPoints=registrationPoints);

measureIlluminant 함수를 사용하여 선형 RGB 데이터의 ground truth 광원을 측정합니다.

illuminant_groundtruth = measureIlluminant(chart)
illuminant_groundtruth = 1×3
103 ×

    4.5407    9.3226    6.1812

ColorChecker 차트의 마스크 만들기

AWB 알고리즘을 테스트할 때는 알고리즘에서 차트를 부당하게 이용하지 않도록 차트를 제외해야 합니다.

drawpolygon 함수를 사용하여 차트 위에 다각형 ROI를 만듭니다. 다각형의 꼭짓점을 정합점으로 지정합니다.

chartROI = drawpolygon(Position=registrationPoints);

createMask 함수를 사용하여 다각형 ROI를 이진 마스크로 변환합니다.

mask_chart = createMask(chartROI);

마스크를 반전합니다. 차트 안의 픽셀은 마스크에서 제외되고 장면의 나머지 부분에 있는 픽셀은 마스크에 포함됩니다.

mask_scene = ~mask_chart;

마스크의 정확도를 확인하기 위해 영상 위에 마스크를 표시합니다. 마스크에 포함된 픽셀은 연한 파란색을 띱니다.

imshow(labeloverlay(A_sRGB,mask_scene));

각도 오차

광원은 3차원 RGB 컬러스페이스에 있는 벡터라고 생각할 수 있습니다. 추정된 광원의 크기는 방향만큼 중요하지는 않습니다. 영상의 화이트 밸런스에는 광원의 방향이 사용되기 때문입니다.

추정된 광원의 품질을 평가하기 위해 추정된 광원과 ground truth 광원 사이의 각도 오차를 계산합니다. 각도 오차는 두 벡터에 의해 형성되는 각도(단위: 도)입니다. 각도 오차가 작을수록 추정의 품질이 좋습니다.

각도 오차의 개념을 더 정확히 이해하기 위해 어떤 임의 광원과 ColorChecker 차트를 사용하여 측정한 ground truth 결과를 시각화한 아래의 내용을 살펴보십시오. plotColorAngle 헬퍼 함수는 3차원 RGB 컬러스페이스에 광원의 단위 벡터를 플로팅하며, 이 예제의 마지막 부분에 정의되어 있습니다.

sample_illuminant = [0.066 0.1262 0.0691];

p = plot3([0 1],[0 1],[0,1],LineStyle=":",Color="k");
ax = p.Parent;
hold on
plotColorAngle(illuminant_groundtruth,ax)
plotColorAngle(sample_illuminant,ax)
title("Illuminants in RGB space")  
view(28,36)
legend("Achromatic Line","Ground Truth Illuminant","Sample Illuminant")
grid on
axis equal

White Patch Retinex

광원 추정을 위한 White Patch Retinex 알고리즘에서는 장면에 밝은 무채색 패치가 있다고 가정합니다. 이 패치는 각 색 대역에서 가능한 최대 조도를 나타내는데, 이것이 장면 광원의 색입니다. illumwhite 함수를 사용하여 White Patch Retinex 알고리즘으로 조도를 추정합니다.

모든 장면 픽셀 포함하기

장면의 모든 픽셀을 사용하여 광원을 추정합니다. Mask 이름-값 쌍 인수를 사용하여 장면에서 ColorChecker 차트를 제외합니다.

percentileToExclude = 0;
illuminant_wp1 = illumwhite(A,percentileToExclude,Mask=mask_scene);

White Patch Retinex로 추정된 광원에 대한 각도 오차를 계산합니다.

err_wp1 = colorangle(illuminant_wp1,illuminant_groundtruth);
disp(["Angular error for White Patch with percentile=0: " num2str(err_wp1)])
Angular error for White Patch with percentile=0: 16.5381

chromadapt 함수를 사용하여 영상의 화이트 밸런스를 보정합니다. 추정된 광원을 지정하고, 색 값을 선형 RGB 컬러스페이스로 지정합니다.

B_wp1 = chromadapt(A,illuminant_wp1,ColorSpace="linear-rgb");

감마 보정된 화이트 밸런스 영상을 표시합니다.

B_wp1_sRGB = lin2rgb(B_wp1);

figure
imshow(B_wp1_sRGB)
title("White-Balanced Image using White Patch Retinex with percentile=0")

가장 밝은 픽셀 제외하기

픽셀이 과다 노출된 경우에는 White Patch Retinex 알고리즘이 좋은 성능을 보이지 못합니다. 알고리즘의 성능을 개선하기 위해 가장 밝은 픽셀 중 최상위 1%를 제외합니다.

percentileToExclude = 1;
illuminant_wp2 = illumwhite(A,percentileToExclude,Mask=mask_scene);

추정된 광원에 대한 각도 오차를 계산합니다. 모든 픽셀을 사용하여 광원을 추정한 경우보다 오차가 작습니다.

err_wp2 = colorangle(illuminant_wp2,illuminant_groundtruth);
disp(["Angular error for White Patch with percentile=1: " num2str(err_wp2)])
Angular error for White Patch with percentile=1: 5.0324

추정된 광원을 사용하여 선형 RGB 컬러스페이스에서 영상의 화이트 밸런스를 보정합니다.

B_wp2 = chromadapt(A,illuminant_wp2,ColorSpace="linear-rgb");

새 광원을 적용한 감마 보정된 화이트 밸런스 영상을 표시합니다.

B_wp2_sRGB = lin2rgb(B_wp2);
imshow(B_wp2_sRGB)
title("White-Balanced Image using White Patch Retinex with percentile=1")

Gray World

광원 추정을 위한 Gray World 알고리즘은 세상의 평균 색이 회색, 즉 무채색이라고 가정합니다. 따라서 장면의 광원을 영상의 평균 RGB 값으로 계산합니다. illumgray 함수를 사용하여 Gray World 알고리즘으로 조도를 추정합니다.

모든 장면 픽셀 포함하기

먼저 ColorChecker 차트에 속하는 픽셀을 제외한 영상의 모든 픽셀을 사용하여 장면 광원을 추정합니다. illumgray 함수는 제외할 최솟값 및 최댓값(밝기순)의 백분위수를 지정하는 파라미터를 제공합니다. 여기서는 백분위수를 0으로 지정합니다.

percentileToExclude = 0;
illuminant_gw1 = illumgray(A,percentileToExclude,Mask=mask_scene);

추정된 광원과 ground truth 광원 사이의 각도 오차를 계산합니다.

err_gw1 = colorangle(illuminant_gw1,illuminant_groundtruth);
disp(["Angular error for Gray World with percentiles=[0 0]: " num2str(err_gw1)])
Angular error for Gray World with percentiles=[0 0]: 5.0416

추정된 광원을 사용하여 선형 RGB 컬러스페이스에서 영상의 화이트 밸런스를 보정합니다.

B_gw1 = chromadapt(A,illuminant_gw1,ColorSpace="linear-rgb");

감마 보정된 화이트 밸런스 영상을 표시합니다.

B_gw1_sRGB = lin2rgb(B_gw1);
imshow(B_gw1_sRGB)
title("White-Balanced Image using Gray World with percentiles=[0 0]")

가장 밝은 픽셀과 가장 어두운 픽셀 제외하기

픽셀이 과소 노출되거나 과다 노출된 경우에는 Gray World 알고리즘이 좋은 성능을 보이지 못합니다. 알고리즘의 성능을 개선하기 위해 가장 밝은 픽셀과 가장 어두운 픽셀 중 최상위 1%를 제외합니다.

percentileToExclude = 1;
illuminant_gw2 = illumgray(A,percentileToExclude,Mask=mask_scene);

추정된 광원에 대한 각도 오차를 계산합니다. 모든 픽셀을 사용하여 광원을 추정한 경우보다 오차가 작습니다.

err_gw2 = colorangle(illuminant_gw2,illuminant_groundtruth);
disp(["Angular error for Gray World with percentiles=[1 1]: " num2str(err_gw2)])
Angular error for Gray World with percentiles=[1 1]: 5.1094

추정된 광원을 사용하여 선형 RGB 컬러스페이스에서 영상의 화이트 밸런스를 보정합니다.

B_gw2 = chromadapt(A,illuminant_gw2,ColorSpace="linear-rgb");

새 광원을 적용한 감마 보정된 화이트 밸런스 영상을 표시합니다.

B_gw2_sRGB = lin2rgb(B_gw2);
imshow(B_gw2_sRGB)
title("White-Balanced Image using Gray World with percentiles=[1 1]")

Cheng의 주성분 분석(PCA, Principal Component Analysis) 방법

Cheng의 광원 추정 방법은 영상의 기울기가 무채색이라고 가정하는 Grey Edge [4]와 같은 공간 영역 방법에서 영감을 얻었습니다. 이 방법에 따르면 영상 블록을 섞어 강한 기울기를 인위적으로 개입시키는 방식으로 Grey Edge를 개선할 수 있으며, 이때 가장 강한 기울기는 광원의 방향을 따릅니다. 이 방법에서는 평균 영상 색의 방향을 따라 투영의 노름을 기준으로 픽셀을 정렬하고 최하위 및 최상위 백분위수를 보존합니다. 이 두 그룹이 영상의 강한 기울기에 해당합니다. 마지막으로, 보존된 픽셀에 대해 주성분 분석(PCA)을 수행하여 첫 번째 성분을 추정된 광원으로 반환합니다. illumpca 함수를 사용하여 Cheng의 PCA 알고리즘으로 조도를 추정합니다.

디폴트 최하위 및 최상위 3.5%의 픽셀 포함하기

먼저 ColorChecker 차트에 속하는 픽셀을 제외하고 Cheng의 PCA 방법의 디폴트 백분율 값을 사용하여 광원을 추정합니다.

illuminant_ch2 = illumpca(A,Mask=mask_scene);

추정된 광원과 ground truth 광원 사이의 각도 오차를 계산합니다.

err_ch2 = colorangle(illuminant_ch2,illuminant_groundtruth);
disp(["Angular error for Cheng with percentage=3.5: " num2str(err_ch2)])
Angular error for Cheng with percentage=3.5: 5.0162

추정된 광원을 사용하여 선형 RGB 컬러스페이스에서 영상의 화이트 밸런스를 보정합니다.

B_ch2 = chromadapt(A,illuminant_ch2,ColorSpace="linear-rgb");

감마 보정된 화이트 밸런스 영상을 표시합니다.

B_ch2_sRGB = lin2rgb(B_ch2);
imshow(B_ch2_sRGB)
title("White-Balanced Image using Cheng with percentile=3.5")

최하위 및 최상위 5%의 픽셀 포함하기

이제 평균 색의 방향을 따라 최하위 및 최상위 5%의 픽셀을 사용하여 장면 광원을 추정합니다. illumpca 함수의 두 번째 인수는 제외할 최하위 및 최상위 값(밝기순)의 백분위수를 지정합니다.

illuminant_ch1 = illumpca(A,5,Mask=mask_scene);

추정된 광원과 ground truth 광원 사이의 각도 오차를 계산합니다. 디폴트 백분율을 사용하여 광원을 추정한 경우보다 오차가 작습니다.

err_ch1 = colorangle(illuminant_ch1,illuminant_groundtruth);
disp(["Angular error for Cheng with percentage=5: " num2str(err_ch1)])
Angular error for Cheng with percentage=5: 4.7454

추정된 광원을 사용하여 선형 RGB 컬러스페이스에서 영상의 화이트 밸런스를 보정합니다.

B_ch1 = chromadapt(A,illuminant_ch1,ColorSpace="linear-rgb");

감마 보정된 화이트 밸런스 영상을 표시합니다.

B_ch1_sRGB = lin2rgb(B_ch1);
imshow(B_ch1_sRGB)
title("White-Balanced Image using Cheng with percentage=5")

최적의 파라미터 찾기

각 알고리즘에 가장 좋은 파라미터를 찾기 위해 한 범위를 스윕하고 각각의 각도 오차를 계산할 수 있습니다. 이 3가지 알고리즘의 파라미터는 의미가 서로 다르지만, 파라미터의 범위가 비슷하므로 손쉽게 프로그래밍 방식으로 각 알고리즘에 가장 좋은 파라미터를 찾아낼 수 있습니다.

param_range = 0:0.25:5;
err = zeros(numel(param_range),3);
for k = 1:numel(param_range)
    % White Patch
    illuminant_wp = illumwhite(A,param_range(k),Mask=mask_scene);
    err(k,1) = colorangle(illuminant_wp,illuminant_groundtruth);
    % Gray World
    illuminant_gw = illumgray(A,param_range(k),Mask=mask_scene);
    err(k,2) = colorangle(illuminant_gw,illuminant_groundtruth);
    % Cheng
    if (param_range(k) ~= 0)
        illuminant_ch = illumpca(A,param_range(k),Mask=mask_scene);
        err(k,3) = colorangle(illuminant_ch,illuminant_groundtruth);
    else
        % Cheng's algorithm is undefined for percentage=0
        err(k,3) = NaN;
    end
end

heatmap 함수를 사용하여 각도 오차의 히트맵을 표시합니다. 진한 파란색은 낮은 각도 오차를 나타내며 노란색은 높은 각도 오차를 나타냅니다. 최적의 파라미터는 가장 작은 각도 오차를 갖습니다.

heatmap(err,Title="Angular Error",Colormap=parula(length(param_range)), ...
    XData=["White Patch" "Gray World" "Cheng's PCA"], ...
    YLabel="Parameter Value",YData=string(param_range));

각 알고리즘에 대한 최상의 파라미터를 찾습니다.

[~,idx_best] = min(err);
best_param_wp = param_range(idx_best(1));
best_param_gw = param_range(idx_best(2));
best_param_ch = param_range(idx_best(3));

fprintf("The best parameter for White Patch is %1.2f with angular error %1.2f degrees\n", ...
    best_param_wp,err(idx_best(1),1));
The best parameter for White Patch is 0.25 with angular error 3.35 degrees
fprintf("The best parameter for Gray World is %1.2f with angular error %1.2f degrees\n", ...
    best_param_gw,err(idx_best(2),2));
The best parameter for Gray World is 0.00 with angular error 5.04 degrees
fprintf("The best parameter for Cheng is %1.2f with angular error %1.2f degrees\n", ...
    best_param_ch,err(idx_best(3),3));
The best parameter for Cheng is 0.50 with angular error 1.74 degrees

최상의 파라미터를 사용하여 각 알고리즘에 대해 추정된 광원을 계산합니다.

best_illum_wp = illumwhite(A,best_param_wp,Mask=mask_scene);
best_illum_gw = illumgray(A,best_param_gw,Mask=mask_scene);
best_illum_ch = illumpca(A,best_param_ch,Mask=mask_scene);

각 최상의 광원의 각도 오차를 RGB 컬러스페이스에 표시합니다.

p = plot3([0 1],[0 1],[0,1],LineStyle=":",Color="k");
ax = p.Parent;
hold on
plotColorAngle(illuminant_groundtruth,ax)
plotColorAngle(best_illum_wp,ax)
plotColorAngle(best_illum_gw,ax)
plotColorAngle(best_illum_ch,ax)
title("Best Illuminants in RGB space")
view(28,36)
legend("Achromatic Line","Ground Truth","White Patch","Gray World","Cheng")
grid on
axis equal

최상의 광원을 사용하여 각 알고리즘에 대해 최적의 화이트 밸런스 영상을 계산합니다.

B_wp_best = chromadapt(A,best_illum_wp,ColorSpace="linear-rgb");
B_wp_best_sRGB = lin2rgb(B_wp_best);
B_gw_best = chromadapt(A,best_illum_gw,ColorSpace="linear-rgb");
B_gw_best_sRGB = lin2rgb(B_gw_best);
B_ch_best = chromadapt(A,best_illum_ch,ColorSpace="linear-rgb");
B_ch_best_sRGB = lin2rgb(B_ch_best);

각 알고리즘에 대해 최적의 화이트 밸런스 영상을 몽타주에 표시합니다.

figure
montage({B_wp_best_sRGB,B_gw_best_sRGB,B_ch_best_sRGB},Size=[1 3])
title("Montage of Best White-Balanced Images: White Point, Gray World, Cheng")

결론

고전적인 광원 추정 알고리즘 2가지와 비교적 최근에 나온 알고리즘 1가지를 비교해본 결과, 최상위 및 최하위 0.75%에 해당하는 가장 어두운 픽셀 및 가장 밝은 픽셀을 사용한 Cheng의 방법이 이 영상에는 가장 효과적입니다. 하지만 이 결과를 액면 그대로 받아들여서는 곤란합니다.

첫째, ground truth 광원은 ColorChecker 차트를 사용하여 측정했으며 산탄 잡음(shot noise) 및 센서 잡음에 민감합니다. 장면의 ground truth 광원은 분광 광도계를 사용하여 더 효과적으로 추정할 수 있습니다.

둘째, ground truth 광원을 무채색 패치들의 평균 색으로 추정했습니다. 평균 대신 중앙값을 사용하는 경우도 많습니다. 그러면 ground truth 결과가 크게 바뀔 수 있습니다. 예를 들어, 이번 연구에서 사용한 영상의 경우 같은 픽셀을 사용할 때 무채색 픽셀의 중앙값 색상과 평균 색상은 0.5도 떨어져 있습니다. 이 값이 각기 다른 알고리즘으로 추정한 광원의 각도 오차를 초과할 수도 있습니다.

셋째, 광원 추정 알고리즘을 종합적으로 비교하려면 각기 다른 조건에서 촬영한 다양한 영상을 사용해야 합니다. 어떤 영상에서 어느 한 알고리즘이 나머지보다 낫더라도 전체 데이터 세트를 놓고 보면 그리 효과적이지 않을 수도 있습니다.

지원 함수

plotColorAngle 함수는 3차원 RGB 컬러스페이스에 광원의 단위 벡터를 플로팅합니다. 입력 인수 illum은 광원을 RGB 색으로 지정하고 입력 인수 ax는 단위 벡터를 플로팅할 좌표축을 지정합니다.

function plotColorAngle(illum,ax)
    R = illum(1);
    G = illum(2);
    B = illum(3);
    magRGB = norm(illum);
    plot3([0 R/magRGB],[0 G/magRGB],[0 B/magRGB], ...
        Marker=".",MarkerSize=10,Parent=ax)
    xlabel("R")
    ylabel("G")
    zlabel("B")
    xlim([0 1])
    ylim([0 1])
    zlim([0 1])
end

참고 문헌

[1] Ebner, Marc. White Patch Retinex, Color Constancy. John Wiley & Sons, 2007. ISBN 978-0-470-05829-9.

[2] Ebner, Marc. The Gray World Assumption, Color Constancy. John Wiley & Sons, 2007. ISBN 978-0-470-05829-9.

[3] Cheng, Dongliang, Dilip K. Prasad, and Michael S. Brown. "Illuminant estimation for color constancy: why spatial-domain methods work and the role of the color distribution." JOSA A 31.5 (2014): 1049-1058.

[4] Van De Weijer, Joost, Theo Gevers, and Arjan Gijsenij. "Edge-based color constancy." IEEE Transactions on image processing 16.9 (2007): 2207-2214.

참고 항목

| | | | | | | |

관련 항목