본문 바로가기
AI/데이터 시각화

[데이터 시각화] 픽셀을 조각하다: 대비, 휘도, 노이즈 필터링으로 이미지 마스터하기

by Yoon_estar 2025. 10. 6.
728x90
반응형

2025.10.01 - [AI/데이터 시각화] - [데이터 시각화] 픽셀을 해부하다: 파이썬으로 배우는 이미지의 다차원 구조 (Part 1)

 

 

이전에 이미지를 NumPy 배열로 다루고 기본적인 슬라이싱을 통해 채널을 분리하는 방법을 배웠습니다. 이번에는 더 흥미로운 이미지 처리 기법인 밝기/대비 조절과 노이즈 필터링을 구현해 보겠습니다.

1. 밝기 조절 : 클리핑이 핵심

픽셀 밝기는 RGB 구성요소 값에 비례합니다. (0 : 빛 없음, 255 : 최대 밝기) 밝기를 조절할 때는 값이 범위를 벗어나지 않도록 주의해야합니다.

예를 들어 G = 200 인 픽셀의 밝기를 두 배로 늘리면 G = 400이 되는데, 최대값인 255를 초과하므로 이 값을 255로 클리핑(Clipping) 해야합니다. 

# NumPy의 clip 함수를 사용하면 밝기 연산 후 자동으로 범위(0~255)를 보장할 수 있습니다.
new_image = np.clip(image_array * factor, 0, 255)

 

2. 흑백 변환 : 단순 평균은 틀렸다.(Luminance)

컬러 이미지를 흑백(Grayscale)으로 바꿀 때 R, G, B 값을 평균하면 안 됩니다. 왜냐하면 인간의 망막은 각 색상의 밝기를 다르게 인지하기 때문입니다. (녹색 > 빨간색 > 파란색)

따라서 우리는 지각적 기여도를 고려한 휘도(Luminance, Luma) 개념을 사용해야합니다. 고화질 TV 표준인 ITU-R BT.709에서 권장하는 공식은 다음과 같습니다.

# Luminance 계산 및 2차원 흑백 배열 생성
R, G, B = img[:,:,0], img[:,:,1], img[:,:,2]
img_bw = (R * 0.2126 + G * 0.7152 + B * 0.0722).astype(np.uint8)

plt.imshow(img_bw, cmap='gray')
plt.show()

 

3. 대비 조절 : 극단적인 이진화와 부드러운 시그모이드

극단적인 대비(이진화)

가장 극단적인 대비 조절은 이미지를 흑백 두 가지 색상만으로 표현하는 임계값 이진화(Thresholding)입니다.

# 흑백 이미지에서 임계값(T)보다 밝은 픽셀은 255, 아니면 0
threshold = 100
binary_image = np.zeros_like(img_bw)
binary_image[img_bw >= threshold] = 255

 

부드러운 대비(시그모이드 함수)

너무 극단적인 대비는 디테일을 잃게 합니다. 좀 더 자연스럽게 대비를 높이려면 시그모이드(Sigmoid) 함수를 사용합니다.

시그모이드 곡선은 중간 밝기(mid-tones)의 차이를 증폭시키고, 극단적인 밝기(하이라이트/로우라이트)의 변화를 압축하여 정보 손실 없이 대비를 효과적으로 높입니다.

# 0-1로 정규화 후 시그모이드 적용
img_normalized = img_bw.astype(float) / 255.0
alpha, beta = 10.0, 0.5 
img_sigmoid = 1 / (1 + np.exp(-alpha * (img_normalized - beta)))
# ... (0-255로 역정규화 및 표시)

 

 

4. 필름 그레인 효과(Dither)

아날로그 필름의 특징인 그레인 노이즈를 추가하는 작업은 NumPy의 정규 분포 난수 생성을 통해 쉽게 시뮬레이션 할 수 있습니다. 

def filmgrainNormal(image, g):
    # 0-1로 정규화
    im = image.copy() / 255. 
    x, y = im.shape
    
    # 정규 분포 노이즈 생성 후 강도 g를 곱함
    noise = np.random.randn(x,y) * g
    im = im + noise
    
    # 0-1 클리핑
    im[im<0]=0; im[im>1]=1 
    return im
    
plt.imshow(filmgrainNormal(img_bw, 0.15), cmap='gray')

 

Dither 효과 : 이 필름 그레인 노이즈를 극단적인 이진화 이미지에 결합하면, 노이즈가 무작위로 흑백 픽셀을 흩뿌려 추가적인 회색 톤이 시각적으로 나타납니다. 이 효과를 디더링(Dither)이라고 하며, 제한된 색상으로 풍부한 명암을 표현하는 데 사용됩니다. 

 

5. 미묘한 색조(Nuance) 조절 : 채널별 보정

마지막으로 이미지의 색상 톤을 조절해 봅시다. 이는 RGB 각 채널에 보정 개수를 곱한 다음 결과 값이 0 ~ 255 범위를 벗어나지 않도록 클리핑하는 방식으로 구현됩니다.

def color_correction(image, r_factor, g_factor, b_factor):
    im = image.copy().astype(float)
    
    # 채널별 곱셈
    im[:, :, 0] *= r_factor
    im[:, :, 1] *= g_factor
    im[:, :, 2] *= b_factor
    
    # 클리핑 (0과 255 사이로 제한)
    im_clipped = np.clip(im, 0, 255)
    return im_clipped.astype(np.uint8)
    
# 예: 녹색 톤을 낮추고 빨간색 톤을 높임
# img_tone = color_correction(img, 1.1, 0.9, 1.0)

 

마무리

Python 과 NumPy의 강력한 배열 연산 기능 덕분에 복잡해 보이는 이미지 처리 기법들을 간단한 수학적 조작으로 구현할 수 있었습니다. 이미지 처리는 결국 데이터 조작이라는 것을 기억한다면 더 많은 필터와 효과를 직접 만들어 볼 수 있을 것입니다.

반응형