Favicon

Contrast enhancement

Peponi3/24/20258m

C#
NugetPackageOpenCvSharp4HistogramNormalizationEqualization

1. Introduction

이미지의 대비 (명암비) 는 밝은 부분과 어두운 부분의 차이로, 대비가 높을 수록 이미지의 세부 사항이 더 뚜렷하게 나타날 수 있다. 주로 histogram을 통해 픽셀 값 분포를 분석하며 이미지가 전체적으로 얼마나 밝은지, 대비가 어느 정도인지 등을 평가한다.

Normalization, equalization은 이미지의 대비를 개선할 수 있는 방법으로, 각각 다음과 같은 특징을 가진다.

  1. Normalization
    • 이미지의 픽셀 값을 특정 범위로 조정한다. 보통 0 ~ 1, 0 ~ 255 사이로 조정한다.
    • 이미지의 전체적인 대비가 향상되며 픽셀 값의 분포를 넓혀 세부 사항을 더 잘 드러나게 한다.
    • 정규화된 히스토그램은 원본 히스토그램의 형태를 유지한다.
  2. Equalization
    • 이미지의 히스토그램을 균등하게 분포시킨다. 저조도 또는 저대비 이미지에 효과적이다.
    • 이미지 처리 과정에서 대비가 극대화되어 눈에 띄지 않는 세부 사항을 드러내는 데 유리하다.
    • 정규화된 히스토그램은 원본 히스토그램과는 다른 형태를 가질 수 있다.

이 문서에서는 histogram, normalization, equalization과 함께 equalization의 문제를 보완할 수 있는 CLAHE (Contrast Limited Adaptive Histogram Equalization) 를 소개한다.

실습에 사용할 이미지는 다음과 같다.

Image by TRANG NGUYEN from Pixabay

2. Histogram

이미지 프로세싱에서는 픽셀 값 (밝기 또는 색상) 분포를 주로 histogram으로 표현한다. 일반적으로 X축은 밝기 또는 색상, Y축은 빈도로 표현된다. 여기서는 그레이스케일 이미지를 사용해 히스토그램을 그리는 예시를 보여준다.

grayscaleGrayscale
histogramHistogram
private void GrayscaleHistogram(Mat image)
{
    // Grayscale 변환
    using var grayscale = image.CvtColor(ColorConversionCodes.BGR2GRAY);
    using var histogram = GetHistogram(grayscale);
}
 
private Mat GetHistogram(Mat grayscale)
{
    using var histogram = new Mat();
 
    // Calculate histogram
    // Cv2.CalcHist(image, channel, mask, histogram, dims, bins, range)
    Cv2.CalcHist([grayscale], [0], null, histogram, 1, [256], [[0, 256]]);
 
    Mat histogramImage = Mat.Zeros(256, 256, MatType.CV_8UC3);
 
    // Normalize histogram's Y axis for display
    histogram.MinMaxLoc(out double min, out double max);
 
    if (max != 0)
    {
        Cv2.Multiply(histogram, 256 / max, histogram);
    }
 
    // Draw histogram
    for (int i = 0; i < 256; i++)
    {
        histogramImage.Rectangle(new OpenCvSharp.Point(i, histogramImage.Rows - (int)histogram.Get<float>(i)), new((i + 1), histogramImage.Rows), Scalar.White);
    }
 
    return histogramImage;
}

3. Normalization

grayscaleGrayscale
grayscale histogramGrayscale histogram
normalizedNormalized
normalized histogramNormalized histogram
private void NormalizeHistogram(Mat image)
{
    // 도수 분포에 최대, 최소값이 있는 경우에는 normalize 잘 수행되지 않음
    // Grayscale 변환
    using var grayscale = image.CvtColor(ColorConversionCodes.BGR2GRAY);
 
    // Normalize
    using var normalized = grayscale.Normalize(0, 255, NormTypes.MinMax);
    using var normalizedHistogram = GetHistogram(normalized);
}

3.1. Color image

Color image에 normalization을 적용하는 경우 색상 공간을 HSV color로 변경, 밝기 채널 (Value) 에 대해 normalization 수행 후 다시 합성한다.

originalOriginal
normalizedNormalized
normalized value channelNormalized value channel
private void NormalizeColorHistogram(Mat image)
{
    using var hsvImage = image.CvtColor(ColorConversionCodes.BGR2HSV);
 
    // H, S, V 순으로 분리
    var split = hsvImage.Split();
 
    // Normalize
    using var valueChannel = split[2].Normalize(0, 255, NormTypes.MinMax);
 
    // Color image로 다시 합성
    using var merge = new Mat();
    Cv2.Merge([split[0], split[1], valueChannel], merge);
    using var normalized = merge.CvtColor(ColorConversionCodes.HSV2BGR);
}

4. Equalization

grayscaleGrayscale
grayscale histogramGrayscale histogram
equalizedEqualized
equalized histogramEqualized histogram
private void EqualizeHistogram(Mat image)
{
    // Grayscale 변환
    using var grayscale = image.CvtColor(ColorConversionCodes.BGR2GRAY);
 
    // 전역 histogram 평준화
    // Mat.EqualizeHist()
    using var equalized = grayscale.EqualizeHist();
    using var equalizedHistogram = GetHistogram(equalized);
}

4.1. Color image

Color image에 equalization을 적용하는 경우 색상 공간을 HSV color로 변경, 밝기 채널 (Value) 에 대해 equalization 수행 후 다시 합성한다.

originalOriginal
equalizedEqualized
equalized value channelEqualized value channel
private void EqualizeColorHistogram(Mat image)
{
    using var hsvImage = image.CvtColor(ColorConversionCodes.BGR2HSV);
 
    // H, S, V 순으로 분리
    var split = hsvImage.Split();
 
    // 밝기 채널에 대해 전역 histogram 평준화
    using var valueChannel = split[2].EqualizeHist();
 
    // Color image로 다시 합성
    using var merge = new Mat();
    Cv2.Merge([split[0], split[1], valueChannel], merge);
    using var equalized = merge.CvtColor(ColorConversionCodes.HSV2BGR);
}

5. CLAHE

앞서 CLAHE는 equalization의 문제를 보완할 수 있다 기술하였다. 앞의 예제와 같이, Equalization은 이미지 전역의 대비를 증가시키지만 지역적으로 과도한 대비가 발생하는 경우가 있다. 이를 해결하기 위해 CLAHE는 지역적으로 equalization을 수행하며, 각 커널의 대비 한계가 존재하여 이를 초과하는 경우 다른 픽셀로 분배된다.

equalizedEqualized
equalized histogramEqualized histogram
claheCLAHE
clahe histogramCLAHE histogram
private void CLAHEHistogram(Mat image, double clipLimit = 4, OpenCvSharp.Size? gridSize = null)
{
    // Grayscale 변환
    using var grayscale = image.CvtColor(ColorConversionCodes.BGR2GRAY);
    using var claheEqualized = new Mat();
 
    // 지역 histogram 평준화
    using var clahe = Cv2.CreateCLAHE(clipLimit, gridSize);
    clahe.Apply(grayscale, claheEqualized);
 
    using var claheHistogram = GetHistogram(claheEqualized);
}

5.1. Color image

Color image에 CLAHE를 적용하는 경우 색상 공간을 HSV color로 변경, 밝기 채널 (Value) 에 대해 CLAHE 수행 후 다시 합성한다.

originalOriginal
CLAHECLAHE
clahe value channelCLAHE value channel
private void CLAHEColorHistogram(Mat image, double clipLimit = 4, OpenCvSharp.Size? gridSize = null)
{
    using var hsvImage = image.CvtColor(ColorConversionCodes.BGR2HSV);
 
    // H, S, V 순으로 분리
    var split = hsvImage.Split();
 
    using var valueChannel = new Mat();
 
    // 밝기 채널에 대해 지역 histogram 평준화
    using var clahe = Cv2.CreateCLAHE(clipLimit, gridSize);
    clahe.Apply(split[2], valueChannel);
 
    // Color image로 합성
    using var merge = new Mat();
    Cv2.Merge([split[0], split[1], valueChannel], merge);
    using var claheEqualized = merge.CvtColor(ColorConversionCodes.HSV2BGR);
}

6. 참조 자료