Python 영상처리 기초 with OpenCV

[Python 영상처리 기초 3] 이미지의 픽셀값 다루기 - Gray scale, Histogram, Binarization

toyprojects 2023. 7. 29. 05:15

 

 

픽셀 (pixel)은 이미지를 구성하는 기본요소로써 한 그룹의 픽셀들이 점, 선, 면 등을 나타낼 수 있고,

각 픽셀 값에 의해 이미지의 밝기, 명암 등을 나타낼 수 있습니다.  (모자이크가 좋은 예 입니다.)

이미지 데이터는 높이(세로) x 폭(가로)의 2차원, 혹은 높이(세로) x 폭(가로) x 색상채널의 3차원

픽셀값 모임이라고 볼 수 있습니다. 

 

3차원 이미지 데이터의 구조는 아래의 오른쪽 그림에 묘사되어 있으며, 앞선 글에서 대상이 되는

3차원의 BGR컬러 이미지 파일을 Jupyter Notebook 에 불러들여 Blue, Green, Red 각각의

색상채널로 분리하는것을 수행하였습니다. 

 

대상 이미지의 픽셀값은 uint8 형식을 따르며 이는 최소 0 부터 최대 255까지 256가지 범위를

갖고, 픽셀값이 0이면 검정색, 255는 흰색을 나타냅니다.

 

 

Colors components - (R, G, B 순서) (출처 1)

 

 

Gray scale  이미지로 변환 - cv2.COLOR_BGR2GRAY

 

이번 글에서는 3차원의 컬러가 아닌 2차원의 gray scale 이미지 데이터를 다룹니다.

앞선 글에서와 같이 컬러 이미지 파일은 이미 "img_color" 라는 변수에 저장되어 있습니다.

본 글에 쓰인 이미지는 본 블로그 맨밑 출처 2에 가셔서 다운로드 받을 수 있습니다.

 

 

img_gray = cv2.cvtColor(img_color, cv2.COLOR_BGR2GRAY)

fig = plt.figure(figsize=(8,6))

fig.add_subplot(1, 2, 1)
plt.axis("off")
plt.imshow(cv2.cvtColor( img_color, cv2.COLOR_BGR2RGB))
plt.title("Color Image")

fig.add_subplot(1, 2, 2)
plt.axis("off")
plt.imshow(img_gray, cmap='gray')
plt.title("Gray Image")

plt.show()

 

Color Image: Cells from cervical cancer - National Cancer Institute (출처2)

 

 

우측의 gray scale 이미지에서 보이듯이 cell(세포)의 특정 염색 색상정보는 사라졌고,

픽셀값에 의해 cell(세포)의 윤곽과 어두운 명암비가 나타남을 알수 있습니다. 

 

Gray scale 이미지의 바탕이 되는 배경은 검은색으로 보이지만 실제로도 픽셀값 0을 갖는 검은색일까?

특정 픽셀값을 알고자 하면 print( ) 문에서 값을 출력할 수 있습니다.

 

예를들어, gray scale 이미지의 1269행 813열의 픽셀값은 uint8 형식의 9 입니다.

 

 

print("Pixel value of 1269x813: ", img_gray[1269,813])
print("Data type of 1269x813: ", type(img_gray[1269,813]))

 

 

 

Gray scale 이미지의 픽셀값 중 최소(min), 최대(max), 중간값(median) 은 

 

import numpy as np

print("Min value of img_gray: ", np.min(img_gray))
print("Max value of img_gray: ", np.max(img_gray))
print("Median value of img_gray: ", np.median(img_gray))

 

 

 

Histogram - cv2.calcHist( )

 

앞서 언급한 바와 같이 위의 gray scale 이미지 데이터는 uint8 형식 이므로 픽셀값은 최소 0 부터

최대 255 까지 256 가지의 범위를 갖습니다.

이미지상의 픽셀값 분포를 알고자 한다면 Histogram이 유용할 것입니다.

 

img_gray_hist = cv2.calcHist([img_gray], [0], None, [256], [0,256])

plt.plot(img_gray_hist)
plt.xlim([0, 256])
plt.ylim([0, np.max(img_gray_hist)])
plt.show()

 

 

 

Histogram의 x축은 픽셀값 범위 0 부터 255를 나타내며, y축은 각 픽셀값(혹은 bin 이라고도 함)

에 해당하는 픽셀의 갯수를 나타냅니다.

 

위의 histogram에서 보이듯이 픽셀값의 분포가 낮은값에 편향되어 있음을 알 수 있으며

이것은 이미지 밝기(brightness)가 어두운 부분이 밝은 부분보다 상대적으로 더 많이 분포해 있음을

알 수 있습니다. Histogram의 입력 데이터는 반드시 gray scale 또는 2차원의 이미지 데이터 이어야

함에 주의하십시오.

 

 

위의 코드중 핵심인 cv2.calcHist( ) 의 5가지 입력 파라메터 지정을 보자면,

 

1) 첫번째 입력 파라메터인 이미지 데이터는 [ img_gray ] 와 같이 리스트로 감싸야 하며,

 

2) 두번째 파라메터는 이미지 데이터의 채널을 리스트 형식으로써 표현 합니다.

     예를 들어, 그레이스케일 이미지 라면 [0], 컬러 이미지의 모든 채널 이라면 [0, 1, 2] 으로 지정합니다.

 

3) 위의 히스토그램 연산에서 이미지 마스크를 사용하지 아니하였으므로 세번째 파라메터는

    'None' 으로 지정,

 

4) 네번째, 히스토그램 연산에 사용되는 bin 수를 리스트 형식으로 사용,

 

5) 마지막 다섯번째 파라메터는 픽셀값의 범위를 리스트로 감싼 형식으로 지정 하였습니다.

     범위의 최대값은 포함되지 않기 때문에 [0, 256]의 경우 최소 0 부터 최대 255의 범위를 갖습니다.

 

 

 

이미지의 이진화(Binarization) - cv2.threshold( )

 

Gray scale 이미지의 픽셀값이 특정 값(= threshold 혹은 임계값) 보다 작거나 같으면 0,

크면 1 을 갖는 이진화 이미지로 변환하는 것을 이미지 binarization 이라고 합니다. 

여기에서 변환의 기준이 되는 특정 값인 threshold 를 이용하는 것은 binarization의 가장

기초가 되는 방식입니다.

 

만일 원하는 threshold가 있다면 cv2.threshold() 함수의 입력 파라메터로 지정할 수 있습니다.

여기서는 위에서 구한 "img_gray"의 median 값을 threshold로 지정해 보겠습니다.

Gray scale 이미지의 픽셀값이 threshold 보다 크다면 255 (흰색), 작거나 같다면 0 (검정색)을

갖는 이진화 결과 데이터를 얻을것입니다. 

 

ret,img_binary = cv2.threshold(img_gray, np.median(img_gray), 255, cv2.THRESH_BINARY)

fig = plt.figure(figsize=(10,8))

fig.add_subplot(1, 2, 1)
plt.axis("OFF")
plt.imshow(img_gray, cmap='gray')
plt.title("Gray Image")

fig.add_subplot(1, 2, 2)
plt.axis("OFF")
plt.imshow(img_binary, cmap='gray')
plt.title("Binary Image")

plt.show()

 

 

 

 

cv2.threshold()의 첫번째 출력 변수인 "ret"는 단지 threshold 즉, "img_gray"의 median인

41.0을 나타냅니다.

 

Gray scale 이미지에서 보듯이 상대적으로 낮은 픽셀값을 갖는 검은 배경은 이진화 결과

이미지에서도 검정색 (0)을 갖고 상대적으로 높은 픽셀값을 갖는 cell(세포)과 주변부는

이진화 결과 이미지에서 흰색(255)을 갖는것을 알 수 있습니다.

 

단 하나의 threshold가 gray scale 이미지 전체를 binarization을 수행하기 때문에 global thresholding

이라고 합니다. 만일 이미지의 특정부분의 밝기, 명암 등이 전반적으로 차이가 난다면

adaptive thresholding을 적용할 수 있습니다.

이것은 이미지를 여러구역으로 나누어 별도의 thresholding을 구현하는 방법으로써 다음 포스트에서

설명 하겠습니다. 

 

 

 

 

출처1: https://pyimagesearch.com/2021/01/20/opencv-getting-and-setting-pixels/

 

OpenCV Getting and Setting Pixels - PyImageSearch

In this tutorial, you will learn how to get and set pixel values using OpenCV and Python.

pyimagesearch.com

 

 

출처2: https://unsplash.com/photos/jdfn7Z03Qa4

 

사진 작가: National Cancer Institute, Unsplash

Cells from cervical cancer – Splash에서 National Cancer Institute의 이 사진 다운로드

unsplash.com