5.19. 톤 보정

톤 보정은 캡처된 이미지에서 밝기와 색상이 분포되는 방식을 변경합니다. 프레임이 너무 어둡거나, 너무 밝거나, 너무 평탄하거나, 잘못된 색상으로 치우쳤을 때 애플리케이션이 적용하는 수정 작업입니다.

이 보정은 두 가지 계열로 나뉩니다. 밝기를 재분포시키는 밝기-대비 조정과, 각 픽셀이 어떤 색상으로 읽히는지를 변경하는 색상 조정입니다. 둘 다 센서의 ISP에 대응하는 기능이 있으며, ISP는 들어오는 각 프레임을 보정합니다. 여기서 다루는 메서드는 이미 캡처된 Image에 사후적으로 적용되며, 프레임이 ISP가 제공한 것보다 더 많은 보정이 필요한 경우를 위한 것입니다.

5.19.1. 히스토그램 평활화

가장 단순한 대비 확장 연산은 히스토그램 평활화입니다. 그 아이디어는 출력의 히스토그램이 가능한 한 평탄해지도록 픽셀 값을 다시 매핑하는 것, 즉 모든 값이 대략 똑같은 빈도로 나타나도록 하는 것입니다. 시각적 효과는 저대비 이미지(히스토그램이 좁은 대역에 집중된 이미지)가 픽셀이 전체 0255 범위에 걸쳐 분포하는 고대비 이미지로 바뀌는 것입니다.

histeq()는 평활화를 실행합니다:

img.histeq()

그 동작 방식은 직관적입니다. 원본 히스토그램의 누적 분포 함수(CDF)가 계산되고, 각 입력 픽셀 값이 CDF상의 위치로 매핑되어 출력 범위에 맞게 스케일링됩니다. 픽셀이 이미 고르게 퍼져 있던 곳에서는 매핑이 항등 함수에 가깝고, 픽셀이 한 밝기에 몰려 있던 곳에서는 그 밝기를 더 넓은 출력 값 범위에 걸쳐 늘려 픽셀을 퍼뜨립니다.

저대비 장면에서는 그 결과가 극적입니다. 어두운 실내 사진과 histeq를 적용한 같은 사진의 차이는 흔히 “판독 불가”와 “완벽히 판독 가능”의 차이가 됩니다. 절충점은 이 연산이 센서 노이즈를 포함해 모든 것을 증폭한다는 것입니다. 복원할 실제 저대비 세부가 있는 장면에서는 histeq가 정답이지만, 그것이 전혀 필요 없는 깔끔하고 잘 노출된 장면에서는 histeq가 눈에 띄는 노이즈를 만들어냅니다.

5.19.2. CLAHE: 적응형 평활화

히스토그램 평활화는 전역적입니다. 즉 전체 이미지에서 계산된 하나의 CDF를 사용해 어디에나 적용합니다. 이는 밝기 범위가 대략 균일한 이미지에서는 잘 작동하지만, 국소적으로 어둡거나 밝은 영역이 있는 장면에서는 실패합니다. CDF가 픽셀이 더 많은 쪽으로 끌려가고, 반대쪽은 과도하게 보정됩니다.

적응형 변형은 대비 제한 적응형 히스토그램 평활화(Contrast Limited Adaptive Histogram Equalisation)이며, 흔히 CLAHE라고 불립니다. 하나의 전역 CDF 대신, CLAHE는 이미지의 작은 타일마다 별도의 CDF를 계산하고, 각 타일을 자체 CDF에 대해 평활화한 다음, 타일 경계를 함께 혼합합니다. 그 결과 밝기 조정이 국소적으로 일어납니다. 그늘진 구석은 밝은 구석이 잘못된 방향으로 끌어당기는 일 없이 자체적인 평활화를 받게 됩니다.

adaptive=True 플래그는 histeq()를 CLAHE 모드로 전환합니다:

img.histeq(adaptive=True, clip_limit=10)

clip_limit 매개변수는 CLAHE의 이름에 있는 “대비 제한”이 가리키는 부분입니다. 국소 평활화는 CDF에 서로 다른 값이 거의 없는 평탄한 영역에서 노이즈를 과도하게 증폭할 수 있습니다. 클립 제한은 단일 빈이 얼마나 공격적으로 재분포될 수 있는지를 제한하여, 중요한 곳에서는 여전히 대비 확장을 허용하면서도 노이즈 증폭을 방지합니다. 10 정도의 값이 합리적인 출발점입니다. 더 큰 값은 더 많은 눈에 띄는 노이즈를 대가로 CLAHE가 더 강하게 작동하게 하고, 더 작은 값은 더 부드럽게 만듭니다.

CLAHE는 전역 histeq보다 비용이 많이 들지만, 밝기가 고르지 않게 분포된 장면(즉 대부분의 실제 장면)에서 더 깨끗한 결과를 만들어냅니다.

5.19.3. 감마, 대비, 밝기

히스토그램 평활화는 밝기를 다시 매핑하는 데이터 기반 방식입니다. 데이터 비의존적인 방식은 몇 개의 조정하기 쉬운 노브로 매개변수화된, 선택한 곡선을 적용하는 것입니다. gamma()는 세 가지를 제공합니다:

img.gamma(gamma=1.0, contrast=1.0, brightness=0.0)

각 매개변수는 모든 픽셀에 하나의 특정 변환을 적용합니다:

gamma는 각 픽셀 값을 거듭제곱 함수 output = input ** (1 / gamma)에 통과시킵니다. 표준적인 의미는 다음과 같습니다. 1.0보다 큰 값은 이미지를 밝게 하고 중간 톤을 끌어올립니다(고전적인 “모니터 감마” 보정). 1.0보다 작은 값은 어둡게 합니다. 이 매개변수는 비선형적입니다. 흑점과 백점은 보존하고 그 사이의 분포만 다시 형성하는데, 이는 기존 극단을 망가뜨리지 않으면서 그림자나 하이라이트 영역의 세부를 복원하는 것이 목표일 때 올바른 동작입니다.

contrast는 각 픽셀을 중간 회색 지점을 중심으로 한 단순 곱셈에 통과시킵니다. 1.0보다 큰 값은 대비를 높이고(어두운 곳은 더 어둡게, 밝은 곳은 더 밝게, 중간 회색은 그대로), 1.0보다 작은 값은 대비를 줄입니다.

brightness는 모든 픽셀 값에 상수를 더합니다. 양수 값은 밝게 하고, 음수 값은 어둡게 합니다. 이 이동은 균일하여 아무것도 보존되지 않으므로 그 자체만으로 애플리케이션이 원하는 경우는 드물지만, 결과를 다시 중심에 맞추기 위한 대비 처리와 잘 어울립니다.

세 매개변수는 조합됩니다. 한 번의 gamma() 호출로 감마 곡선, 그다음 대비 곱셈, 그다음 밝기 이동을 모두 한 번의 처리로 적용할 수 있습니다. 순서는 감마가 먼저, 그다음 대비, 그다음 밝기이며, 이는 세 가지가 모두 기본값이 아닐 때 가장 직관적인 결과를 주는 순서와 일치합니다.

5.19.4. 자동 화이트 밸런스

톤 보정의 색상 계열은 자동 화이트 밸런스로 시작합니다. 센서의 ISP가 이미징 파이프라인의 일부로 실행하는 것과 동일한 메커니즘, 즉 평균적인 회색 패치가 실제 회색으로 읽히도록 빨강, 초록, 파랑 채널의 상대적 게인을 조정하는 메커니즘이 완성된 Image에 대한 캡처 후 연산으로도 제공됩니다:

img.awb()

기본값은 그레이 월드(gray-world) 알고리즘을 사용합니다. 전체 이미지의 평균 색상이 중립 회색이라고 가정하고, 그렇게 되도록 채널별 게인을 조정합니다. 대안인 max=True 형태는 화이트 패치(white-patch) 알고리즘을 사용합니다. 가장 밝은 픽셀이 중립 흰색이라고 가정하고, 그렇게 되도록 게인을 조정합니다. 둘 다 RGB565와 raw Bayer에서 작동합니다. 어느 것도 그레이스케일(균형을 맞출 색상이 없는 경우)이나 YUV(이 알고리즘이 작동하는 색상 표현이 아닌 경우)에서는 작동하지 않습니다.

ISP의 자동 화이트 밸런스 대신 캡처 후 형태를 사용해야 할 때는 다음과 같습니다. ISP의 선택이 특정 장면에 잘 맞지 않았을 때, 애플리케이션이 다른 조건에서 캡처된 참조 프레임을 디스크에서 불러오는 경우, 또는 색상 판단이 충분히 중요해서 애플리케이션이 자체적인 알고리즘 선택으로 다시 실행하고 싶을 때입니다.

5.19.5. 색상 보정 행렬

이미지에 필요한 색상 보정이 화이트 밸런스가 제공하는 채널별 스케일링이 아니라 더 일반적인 채널 혼합일 때, 사용할 연산은 색상 보정 행렬입니다. ccm() 메서드는 모든 픽셀의 (r, g, b) 벡터에 곱해 새로운 (r, g, b) 벡터를 생성하는 3x3(또는 오프셋이 있는 3x4) 행렬을 적용합니다:

img.ccm([[1.1, -0.05, -0.05],
        [-0.05, 1.1, -0.05],
        [-0.05, -0.05, 1.1]])

이 행렬을 사용하면 애플리케이션이 색상 채널 간의 크로스토크를 보정할 수 있습니다. 예를 들어 빨강 센서의 응답에 일부 초록 빛이 포함된 경우, 행렬은 보상을 위해 초록 채널의 일부를 빨강 출력에서 뺄 수 있습니다. 채널별 오프셋과 결합하면, 3x4 형태는 애플리케이션이 각 채널을 다시 영점화할 수도 있게 합니다.

ISP 파이프라인 자료는 색상 보정 행렬의 이유를 다룹니다. Image에 대한 캡처 후 형태는 사후에 적용되는 동일한 연산일 뿐입니다.