본문 바로가기
강의 (Lecture)/OpenCV 마스터 with Python (초급)

OpenCV + Python 이미지의 밝기와 명암 조절

by codingwalks 2024. 10. 5.
728x90
반응형

 

 

안녕하세요. 코딩산책입니다.

이미지 처리에서 밝기와 명암을 조절하는 작업은 기본적이면서도 매우 중요한 과정입니다. 특히 사진을 보정하거나 영상의 퀄리티를 높이는 작업에서 밝기와 명암은 핵심적인 요소로 작용합니다. 이번 글에서는 OpenCVPython을 사용하여 이미지의 밝기와 명암을 조절하는 방법을 소개합니다. 이 글에서는 Trackbar를 이용해 실시간으로 밝기와 명암을 조절하는 방법과 각 픽셀을 직접 조작하는 방법을 설명하고, 나아가 cv2.addWeighted 함수를 사용해 보다 직관적으로 명암 대비와 밝기를 동시에 조절하는 방법을 다룹니다. 또한 이미지 파일 형식에서 비손실 이미지인 BMP 포맷의 사용 이유에 대해서도 설명합니다.

 

1. 컬러 영상을 그레이스케일로 변환하기

이미지를 처리할 때, 컬러 영상(3채널, RGB)을 그레이스케일(1채널)로 변환하는 경우가 자주 있습니다. 그레이스케일 이미지는 색상 정보를 제거하고 밝기 정보만을 담고 있기 때문에, 이미지의 구조적 특징을 분석하거나 처리할 때 매우 유용합니다. OpenCV에서는 cv2.cvtColor 함수를 이용하여 쉽게 컬러 이미지를 그레이스케일로 변환할 수 있습니다.

1.1 cv2.imread 함수에서 직접 변환하기

cv2.imread 함수는 이미지를 읽어들일 때 여러 가지 플래그를 사용할 수 있습니다. 이 플래그 중 cv2.IMREAD_GRAYSCALE 옵션을 사용하면, 이미지를 불러오는 순간 바로 그레이스케일로 변환하여 읽어들일 수 있습니다. 이 방법은 cv2.cvtColor를 따로 호출하지 않아도 되므로 코드가 간결해집니다.

import cv2

# Load image in grayscale
img_gray = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)

cv2.imshow('Gray Image', img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()

이 코드는 이미지가 로드되는 순간 이미 그레이스케일로 변환된 상태로 메모리에 저장됩니다. 별도의 변환 과정이 필요 없으므로 간단하게 그레이스케일 이미지를 처리하고자 할 때 유용합니다.

1.2 cv2.cvtColor 함수를 사용하여 변환하기

cv2.cvtColor 함수는 OpenCV에서 다양한 색상 공간 변환을 지원하는 함수로, OpenCV에서 기본으로 사용되는 색상 공간인 BGR에서 그레이스케일로 변환하는 가장 기본적인 방법 중 하나입니다.

import cv2

# Load a color image
img_color = cv2.imread('resources/lena.bmp')

# Convert color image to grayscale
img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)

cv2.imshow('Gray Image', img_gray)
cv2.waitKey(0)
cv2.destroyAllWindows()

이 방법은 이미 로드된 컬러 이미지를 필요에 따라 그레이스케일로 변환할 때 적합합니다. 특히, 컬러 이미지를 유지한 채 별도의 그레이스케일 버전을 생성하는 경우에 유리합니다.

칼라 이미지, 그레이스케일 이미지

 

2. 이미지의 밝기(brightness) 조절

이미지의 밝기를 조절하는 것은 각 픽셀의 색상 값을 일정한 양만큼 증가시키거나 감소시키는 것을 의미합니다. 밝기를 증가시키면 이미지 전체가 더 밝아지며, 반대로 감소시키면 더 어두워집니다. 이때 각 픽셀의 RGB 값에 일정한 상수를 더하거나 빼는 방식으로 구현할 수 있습니다. 또한, 밝기 값의 증가 혹은 감소로 인하여 0보다 작아지거나 255보다 커지는 경우에는 아래의 조건으로 처리가 되어야 합니다. 그 이유는 byte (unsigned char)의 자료형은 0~255의 값만 사용할 수 있기 때문에 saturate(x)의 처리가 필요하지만, 예외적으로 signed의 경우에는 필요하지 않습니다. 

\[ saturate(x)=\left\{\begin{matrix}
0 & if(x<0) \\
255 & elif(x>255) \\
x & else \\
\end{matrix}\right. \]

이미지의 밝기 조절을 수식으로 표현하면 다음과 같습니다.

\[ dst(x,y)=saturate(src(x,y)+n) \]

밝기 증가, 밝기 감소

import cv2
import numpy as np

# Load image in grayscale
gray = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)
gray_minus = np.clip(gray-100, 0, 255).astype(np.uint8)
gray_plus = np.clip(gray+100, 0, 255).astype(np.uint8)

result = cv2.hconcat([gray, gray_minus, gray_plus])

cv2.imshow('Brightness Image', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

원본, 밝기 증가, 밝기 감소

 

3. 이미지의 명암(contrast) 조절

명암은 이미지의 밝고 어두운 부분 간의 차이를 나타내는 척도입니다. 명암을 높이면 밝은 부분은 더 밝아지고 어두운 부분은 더 어두워지며, 명암을 낮추면 이미지가 평탄해져서 중간 톤에 가까워집니다. 명암은 픽셀 값에 곱셈을 적용하여 조정하며, 가장 단순한 명암비 조절 방법으로 각 픽셀의 값에 일정한 배율을 곱하는 것입니다. 이를 통해 이미지의 어두운 부분은 더 어두워지고, 밝은 부분은 더 밝아지게 됩니다. 이 방법은 수식을 통해 다음과 같이 표현됩니다:

\[ dst(x,y)=saturate(src(x,y)*s) \]

명암 감소, 명암 증가
원본, 명암 감소(s=0.5), 명암 증가(s=2.0)

import cv2
import numpy as np

# Load image in grayscale
gray = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)

gray_dec = np.clip(gray*0.5, 0, 255).astype(np.uint8)
gray_inc = np.clip(gray*2.0, 0, 255).astype(np.uint8)
result = cv2.hconcat([gray, gray_dec, gray_inc])

cv2.imshow('Contrast Image', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

 

4. cv2.addWeighted 함수를 사용한 밝기 및 명암 조절

cv2.addWeighted 함수는 두 개의 이미지를 각각 가중치를 적용하여 합산하는 방식으로 이미지를 조정합니다. 이를 통해 명암 조절은 가중치 곱셈을 통해, 밝기 조절은 상수 추가를 통해 구현할 수 있습니다.

\[ dst(x,y)=saturate(src1(x,y)*\alpha + src2(x,y)*\beta + \gamma) \]

import cv2

# Load image in grayscale
gray = cv2.imread('resources/lena.bmp', cv2.IMREAD_GRAYSCALE)

# Contrast and brightness control (increase contrast)
alpha = 1.5  # Contrast ratio
beta = 0.0   # Contrast ratio (0 means no change)
gamma = 50   # Brightness to add

# Adjust brightness and contrast ratio with addWeighted function
adjusted_img = cv2.addWeighted(gray, alpha, gray, beta, gamma)

cv2.imshow('Brightness/Contrast Adjusted', adjusted_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

원본, 명암(1.5)/밝기(50) 조절

여기서는 동일한 이미지를 두 번 사용하되, 한 이미지는 \( \alpha \) 가중치를 적용하고, 다른 이미지는 가중치를 0으로 설정하여 명암비를 조정합니다. \( \gamma \)는 밝기를 조절하는 상수로, 필요시 값을 조정하여 밝기와 명암을 동시에 조절할 수 있습니다.

 

5. OpenCV에서 Trackbar를 사용한 밝기와 명암 조절

Trackbar는 OpenCV에서 제공하는 슬라이더 형태의 UI 요소로, 이를 이용해 사용자가 직접 이미지의 밝기와 명암을 실시간으로 조절할 수 있습니다. OpenCV의 createTrackbar 함수를 사용하여 각 값을 제어하며, Trackbar를 통해 변경된 값을 바탕으로 이미지를 즉시 업데이트할 수 있습니다.

5.1 기본적인 밝기와 명암 조절

import cv2
import numpy as np

# A function that adjusts the brightness and contrast of an image
def adjust_brightness_contrast(val):
    brightness = cv2.getTrackbarPos('Brightness', 'Image') - 50
    contrast = cv2.getTrackbarPos('Contrast', 'Image') / 50.0

    # Brightness and contrast control
    adjusted_img = np.int16(img)
    adjusted_img = adjusted_img * contrast + brightness
    adjusted_img = np.clip(adjusted_img, 0, 255)  # 값 범위를 0-255로 제한
    adjusted_img = np.uint8(adjusted_img)

    cv2.imshow('Image', adjusted_img)

img = cv2.imread('resources/lena.bmp')

cv2.namedWindow('Image', cv2.WINDOW_AUTOSIZE)

# Create a trackbar (for brightness and contrast adjustment)
cv2.createTrackbar('Brightness', 'Image', 50, 100, adjust_brightness_contrast)
cv2.createTrackbar('Contrast', 'Image', 50, 100, adjust_brightness_contrast)

# Display initial image
adjust_brightness_contrast(0)

while True:
    if cv2.waitKey(1) & 0xFF == 27:  # ESC 키를 누르면 종료
        break

cv2.destroyAllWindows()
1. 밝기 조절: brightness 변수는 슬라이더에서 선택된 값을 기준으로 계산됩니다. 트랙바의 기본값은 50으로 설정하고, 범위는 0에서 100까지입니다. 실제 밝기 조절은 슬라이더 값에서 50을 뺀 결과로 적용됩니다.
2. 명암 조절: contrast 변수는 트랙바의 값을 기준으로 50으로 나눠 1을 기준으로 설정됩니다. 명암은 곱셈 연산을 통해 조절되며, 값이 1에 가까울수록 원본 이미지와 유사합니다.
3. 픽셀 값 변환: 밝기와 명암을 조정하는 과정에서 np.int16 형식을 사용해 이미지 값을 확장하고, 이후 np.clip을 통해 값이 0에서 255 사이에 있도록 제한합니다.

5.2 cv2.addWeighted를 사용한 명암 및 밝기 조절

import cv2
import numpy as np

def adjust_brightness_contrast_addWeighted(val):
    brightness = cv2.getTrackbarPos('Brightness', 'Image') - 50
    contrast = cv2.getTrackbarPos('Contrast', 'Image') / 50.0
    
    # Adjust brightness and contrast using cv2.addWeighted
    adjusted_img = cv2.addWeighted(img, contrast, np.zeros_like(img), 0, brightness)
    
    cv2.imshow('Image', adjusted_img)

img = cv2.imread('resources/lena.bmp')

cv2.namedWindow('Image', cv2.WINDOW_AUTOSIZE)

# Create a trackbar (for brightness and contrast adjustment)
cv2.createTrackbar('Brightness', 'Image', 50, 100, adjust_brightness_contrast_addWeighted)
cv2.createTrackbar('Contrast', 'Image', 50, 100, adjust_brightness_contrast_addWeighted)

# Display initial image
adjust_brightness_contrast_addWeighted(0)

while True:
    if cv2.waitKey(1) & 0xFF == 27:
        break

cv2.destroyAllWindows()
1. 명암 조절: contrast 값은 cv2.addWeighted에서 첫 번째 이미지의 가중치로 적용됩니다. alpha 값은 명암 대비를 조절하는 데 사용됩니다.
2. 밝기 조절: gamma 값은 이미지의 밝기를 조절하는 데 사용되며, 각 픽셀에 일정한 상수를 더해줍니다.

결과 이미지

 

6. BMP를 사용하는 이유: 비손실 이미지 형식의 중요성

이미지 파일 형식에는 손실 압축비손실 압축이 있습니다. JPGPNG는 널리 사용되는 손실 압축 이미지 포맷입니다. 이러한 손실 압축 이미지는 저장 시 파일 크기를 줄이기 위해 일부 이미지 데이터가 제거되어 이미지의 품질이 손실될 수 있습니다. 특히 연속적인 이미지 처리 과정에서는 매번 압축이 적용되어 품질 저하가 누적될 수 있습니다.

반면에 BMP(Bitmap)는 비손실 압축 이미지 형식으로, 압축 없이 원본 데이터를 그대로 유지합니다. 비록 파일 크기는 JPG나 PNG보다 훨씬 크지만, 이미지 처리에서는 원본 데이터를 정확하게 유지하는 것이 매우 중요할 때 BMP 형식을 사용하는 것이 유리합니다. 이는 특히 딥러닝, 컴퓨터 비전, 정밀한 픽셀 조작 작업에서 중요한 요소입니다. BMP 형식을 사용하면 원본 데이터의 품질을 보장할 수 있어, 이미지의 정확성과 세부 정보를 보존할 수 있습니다.

 

7. 결론

이미지 처리에서 밝기와 명암 조절은 기본적이면서도 매우 중요한 작업입니다. 이 글에서는 OpenCV와 Python을 사용하여 이미지의 밝기와 명암을 조절하는 방법을 설명했습니다. 실시간으로 조절 가능한 Trackbar를 이용해 직관적으로 이미지를 변형하고, cv2.addWeighted 함수를 사용해 효과적으로 밝기와 명암을 동시에 조정할 수 있음을 보았습니다.

또한, 컬러 이미지의 그레이스케일 변환을 통해 이미지의 구조적 특징을 더 명확하게 처리할 수 있는 방법을 설명했습니다. 마지막으로 비손실 이미지 포맷인 BMP의 중요성에 대해서도 다루었는데, 이는 특히 이미지 품질 유지가 중요한 작업에서 필수적입니다.

Trackbarcv2.addWeighted를 적절히 활용하고 BMP와 같은 비손실 이미지 포맷을 사용함으로써 이미지 처리 응용 프로그램에서 사용자가 원하는 대로 이미지를 쉽게 조정하고, 높은 품질을 유지할 수 있습니다.

 

해당 포스트가 유용하셨다면 하단의 좋아요와 구독하기 부탁드립니다. ^^

Buy me a coffee

 

[Codingwalks]에게 송금하기 - AQR

[Codingwalks]에게 송금하기 - AQR

aq.gy

★ 모든 내용은 아래의 링크를 참조하였습니다. ★

 

OpenCV: OpenCV-Python Tutorials

Core Operations In this section you will learn basic operations on image like pixel editing, geometric transformations, code optimization, some mathematical tools etc.

docs.opencv.org

 

 

728x90
반응형