해당 포스트는 "열혈강의 영상처리 프로그래밍" 책의 내용을 요약한 것이다.



앞 포스트 "영상의 광학적 변환(4) - 히스토그램" 에서 회색조 영상에 대한 히스토그램 평활화를 구했다. 회색조 영상에는 256개의 밝기값이 있어서 256개의 히스토그램 공간만 있으면 되다. 하지만 RGB 컬러영상은 256*256*256 개의 공간이 필요하다. 그래서 단순하게 히스토그램을 구할 수가 없다. 이에 대한 해결 방법은 컬러 영상의 색 공간 변환을 이용하여 밝기 성분에 대해서만 히스토그램 평활화를 수행하는 것이다. 히스토그램의 목적은 영상의 밝기가 고르게 분포하게 하는 것이기에 컬러 영상의 히스토그램 평활화는 밝기 성분에 대해서만 수행하여도 충분하다.

그렇다면 영상의 밝기 성분만 분리하면 된다. 따라서 YIQ, YUV, YCbCr 색 공간에서는 Y 성분만 사용하면 되고 HSV는 V, HSL은 L 성분을 사용하면 된다. 

이렇게 컬러 영상을 HSV나 YIQ 영상으로 변환하고 밝기 성분만 추출 후 히스토그램 평활화를 수행한 다음, 그 결과를 원래의 색상 성분과 다시 결합하면 컬러영상에 대한 히스토그램 평활화가 수행된다. 다음은 이에 관한 그림이다. 


컬러 영상을 변환할 때 HSV나 YIQ 영상으로 변환하는 데 우리는 전 포스트에서 HSV로 변환하는 메서드를 구현했으므로 HSV로 변환한다. 하지만 실제 컬러 영상에 대해 히스토그램 평활화를 수행할 때는 YIQ 영상의 변환이 더 간단하므로 YIQ 로 변환하는 것이 더 좋다.

다음은 컬러 영상에 대한 히스토그램 함수인데 앞 포스트 "영상의 광학적 변환(4) - 히스토그램"에 나온 히스토그램 평활화 함수와 똑같이 구현했다. 다음의 코드가 이해 안 된다면 앞 포스트 "영상의 광학적 변환(4) - 히스토그램" 포스트를 보기를 추천한다.

void _HistogramEqualization()
{
        Rgb2hsv();   // HSV 영상으로 변환
        m_imageV = m_imageHSVAdj.GetChannelImg(0)*255.0 + 0.5; 
        //hsv 영상 값은 0에서 1사이의 값을 가지기 때문에 255를 곱하고 0.5를 더해 반올림후
        // 0에서 255까지의 값을 가진 회색조 영상으로 변환했다.

	int nWidth  = m_imageV.GetWidth();
	int nHeight = m_imageV.GetHeight();

	memset(m_histogram, 0, 256*sizeof(int));

	int r, c;
	for (r=0 ; r<nHeight ; r++)
	{
		BYTE* pIn = m_imageV.GetPtr(r);
		for (c=0 ; c<nWidth ; c++)
		{
			m_histogram[pIn[c]]++;
		}
	}

	double dNormFactor = 255.0 / (nWidth * nHeight);

	for (int i=0 ; i<256 ; i++)
	{
		m_histogramCdf[i] = m_histogram[i]*dNormFactor;
	}

	for (int i=1 ; i<256 ; i++)
	{
		m_histogramCdf[i] = m_histogramCdf[i-1] + m_histogramCdf[i];
	}

	for (r=0 ; r<nHeight ; r++)
	{
		BYTE* pIn = m_imageV.GetPtr(r);
		for (c=0 ; c<nWidth ; c++)
		{
			pIn[c] = (BYTE)(m_histogramCdf[pIn[c]]+0.5);
		}
	}
   
    m_imageHSVAdj.PutChannelImg(((CDoubleImage)m_imageV)/255.0, 0);
    Hsv2rgb();
}

GetChannelImg와 PutChannelImg 메서드 "영상의 채널단위 접근과 변환" 포스트에서 구현했고 Rgb2hsv와 Hsv2rgb 메서드는 "색 공간(3) - HSV, HSL" 포스트에서 구현했다. 해당 포스트에서 참조하면 될 것 같다.

+ Recent posts