Python 영상처리 기초 with OpenCV

[Python 영상처리 기초 4] 이미지의 픽셀값 다루기 - Crop, 선형연산

toyprojects 2023. 7. 31. 19:53

 

 

Cells from cervical cancer - National Cancer Institute (출처1)

 

 

본 글에서는 이미지의 특정부분을 잘라내기(Crop) 혹은 추출하여 이미지 프로세싱을 수행후

본래 이미지에 붙여넣기 하는 작업을 해보겠습니다.

자동으로 이미지상의 ROI (Region of Interest) 를 추출하여 연산을 수행하는것은 중급과정에서 다룰

예정입니다.

 

우선 위의 이미지 에서 보이는 특정부분 (흰색 네모상자)을 잘라내기(Crop) 하여 새로운 변수로

지정하여 보겠습니다. 임의로 지정한 좌표 - tuple ( x좌표, y좌표 ) 형식 ( 753, 1210 ) 부터 ( 2115, 2110 ) -

를 입력 파라메터로 하는 cv2.rectangle( ) 명령어를 이용하였습니다.

 

여기에서 주의할 점은 cv2.rectangle( ) 명령어에서는 ( x좌표, y좌표 ) 순서의 입력 파라메터를 갖지만,

일반적으로 python에서 이미지 데이터는 행 과 열을 갖는 하나의 NumPy 데이터 매트릭스(Matrix)

형태를 갖으므로 픽셀에 접근하기 위해 반드시 ( y좌표, x좌표 ) 순서로 지정하며, 범위 지정을 위한

slicing이 가능 합니다.

 

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

 

import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

img_color = cv2.imread('national-cancer-institute-jdfn7Z03Qa4-unsplash.jpg',cv2.IMREAD_COLOR)
img_gray = cv2.cvtColor(img_color,cv2.COLOR_BGR2GRAY)
img_color_copy = img_color.copy()

# tuple (x,y-axis)
# start point: (753,1210), end point: (2115,2110), 
# thickness 30, white color rectangular line 
cv2.rectangle(img_color_copy,(753,1210),(2115,2110),(255,255,255),30)

 

 

cv2.rectangle( ) 는 작업수행후 새로운 변수에 결과를 리턴하는 것이 아닌, 

입력 파라메터의 이미지에 작업결과를 덧붙이는 방식이기 때문에  "img_color_copy" 라는 새로운

변수에 기존의 "img_color" 를 복사( .copy( ) )  한 것입니다.

 

이제는 gray scale 의 대상 이미지에 지정된 좌표로 numpy slicing을 이용하여 잘라내기(Crop) 를

하겠습니다. 지정된 좌표의 시작점과 끝점은 각각 start_pt 와 end_pt 변수에 [y좌표, x좌표] 를 지정

하였습니다. 잘라내기(Crop)한 새로운 이미지의 너비와 높이는 끝점과 시작점의 차이에 +1 픽셀을

더해야 합니다. 예를들어, 좌표 1에서 좌표 100 까지 지정한다면 길이는 100 이며, 이는 간단한 수식

(100 - 1) + 1  으로 표현됩니다.

 

 

start_pt = [1210, 753]     # [y, x-axis] or (y, x-axis), both are fine
end_pt = [2110, 2115]      # [y, x-axis]
img_gray_crop = img_gray[start_pt[0]:end_pt[0], start_pt[1]:end_pt[1]]     # numpy slicing

fig = plt.figure(figsize=(9, 7))

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_gray_crop, cmap='gray')
plt.title("Cropped Image")

plt.show()

 

 

다른 방법으로, 시작점에서 너비와 높이 (= 가로 x 세로 픽셀) 를 지정하면 잘라내기(Crop)가 가능합니다.

예를들어 시작점 start_pt = [ 1210, 753 ] - [y, x-axis] - 너비가 W, 높이가 H 픽셀로 지정되었다면

 

height_range = start_pt[0] + H + 1 
wide_range =  start_pt[1] + W + 1 

img_gray_crop = img_gray[start_pt[0]:height_range, start_pt[1]:wide_range]     # slicing with height & wide

 

잘라내기(Crop)할 범위지정시 너비와 높이에 +1 픽셀을 더해야 합니다.

Python의 slicing 은 A 부터 B까지가 아닌 A 부터 B-1 까지 이기 때문입니다.

 

위의 코드에 주의할 것은, 잘라내기(Crop) 하는 범위가 이미지 데이터의 범위를 넘어서지 않게

혹은 넘어설경우의 예외처리를 하는 코드가 추가적으로 필요합니다.

 

 

이번에는 잘라내기한 새로운 이미지에 임의로 픽셀값의 70%를 상향시켜 보겠습니다.

각각의 픽셀값에 70%를 상향시키는 작업이므로 element-wise 곱셈연산이지만 Python의

브로드캐스팅(Broadcasting) 기능으로 쉽게 계산이 가능합니다.

선형연산을 하기 전, 후를 비교하기 위해 histogram과 이미지를 나란히 표시하겠습니다.

 

img_gray_crop_UP = (img_gray_crop * 1.7).astype('uint8')    # intensity 70% UP
img_gray_crop_hist = cv2.calcHist([img_gray_crop], [0], None, [256], [0, 256])
img_gray_crop_UP_hist = cv2.calcHist([img_gray_crop_UP], [0], None, [256], [0, 256])

fig = plt.figure(figsize=(10, 6))     # histograms

fig.add_subplot(1, 2, 1)
plt.plot(img_gray_crop_hist)
plt.title('Original')
plt.xlim([0, 256])

fig.add_subplot(1, 2, 2)
plt.plot(img_gray_crop_UP_hist)
plt.title('Intensity 70% UP')
plt.xlim([0, 256])

plt.show()

 

Histogram (좌: Original, 우: Intensity 70% 상향)

 

 

fig = plt.figure(figsize=(9, 7))     # crop images

fig.add_subplot(1, 2, 1)
plt.axis("off")
plt.imshow(img_gray_crop, cmap='gray')
plt.title('Original')

fig.add_subplot(1, 2, 2)
plt.axis("off")
plt.imshow(img_gray_crop_UP, cmap='gray')
plt.title('Intensity 70% UP')

plt.show()

 

Cropped Image (좌: Original, 우: Intensity 70% 상향)

 

 

위의 histogram 에서 보듯이, 픽셀값 70% 상향시 픽셀값의 분포가 원래에 비해 우측으로 확장되었고,

이미지 또한 눈에띄게 밝아졌음을 알 수 있습니다. (이것은 이미지의 brightness 개선에 사용되는 기본적인

방법 입니다)

만일 cell(세포)의 형태를 추출, 가공하는것이 이미지 프로세싱의 목적이고 그 외의 것들

(예를들어 배경이 되는 어두운 부분)을 노이즈로 가정 한다면, 이미지의 일괄적인 선형연산으로 인한

노이즈 증가를 예상할 수 있습니다.

 

마지막으로 본래 이미지의 잘라내기(Crop)한 부분을 픽셀값 70% 상향한 이미지 조각으로 대체해 보겠습니다.

이처럼 특정부분의 픽셀값을 상향하여 대체하는 작업은 보편적으로 수행하는 테크닉은 아닙니다.

 

  

원래 이미지의 사본 (여기서는 "img_gray2")을 만들고, 픽셀값을 대체할 부분의 범위지정에 주의하십시오.

밑의 결과 이미지에서 알 수 있듯이 특정부분의 픽셀값만 70% 상향되었고 이로인해 특정부분만

눈에띄게 밝게 표현되고 있습니다. 

 

img_gray2 = img_gray.copy()
#start_pt = [1210,753]     # [y, x-axis]
#end_pt = [2110,2115]      # [y, x-axis]

img_gray2[start_pt[0]:end_pt[0],start_pt[1]:end_pt[1]] = img_gray_crop_UP

fig = plt.figure(figsize=(20, 18))
plt.axis("off")
plt.imshow(img_gray2, cmap='gray')
plt.show()

 

 

 

 

 

 

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

 

사진 작가: National Cancer Institute, Unsplash

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

unsplash.com