IT/AI\ML

[python/OpenCV] 이미지 처리의 기본

개발자 두더지 2020. 4. 20. 12:53
728x90

0. 이미지란

 OpenCV로 읽어 들인 이미지는 Numpy 배열로 보존된다. 아래에서도 나오겠지만 세로 N, 가로 M 픽셀의 이미지는 흑백 이미지인 경우 N x M의 행렬로 보존되고, 컬러 이미지는 채널 수(색상 수) N x M x 3의 행렬로 보존된다. 이미지를 취급하는 데 있어 가장 중요한 것은 왼쪽 최상단의 좌표가 (0, 0)이 되는 것이다. 오른쪽으로 가면 갈 수록 x좌표가 점점 증가하는 것은 당연하지만, 아래로 갈 수록 y좌표가 증가하는 것에 대해 위화감을 느낄 수 있다.  그러나 이것은 이미지 처리 라이브러리 전반에 적용되는 공통 규칙이다.


1. 화소값의 접근 또는 변경방법

처음으로는 컬러 이미지를 읽어 들여 보자.

import cv2
import numpy as np

img = cv2.imread('messi5.jpg')

어떤 화소의 행또는 열의 좌표를 지정하는 것으로 화소치에 접근할 수 있다.

BGR이미지의 화소값은 파랑, 녹색, 빨강색의 값 배열로 구성되어 있으며,

그레이스케일화된 이미지의 화소값을 명도를 리턴한다.

px = img[100,100]
print px
>>> [157 166 200]

# accessing only blue pixel
blue = img[100,100,0]
print blue
>>> 157

아래와 같은 접근 방법을 사용하면, 화소치의 변경이 가능하다.

img[100,100] = [255,255,255]
print img[100,100]
>>> [255 255 255]

<주의 사항>

numpy는 배열을 고속으로 계산하는 것에 최적화된 라이브러리이다. 그러므로 화소값의 취득이나 변경은 각 화소에 대한 처리는 처리 시간이 꽤 걸려버리기 때문에 권장하지 않는다.

<노트>

위의 방법은 배열의 영역(예를 들면, 처음의 5열과 마지막 3행)을 선택할 때에 잘 사용된다. 각 화소에 접근에 관해서는 numpy의 배열을 다루는 array.item()이나 array.itemset()을 사용하면 될 것 처럼 느껴질 수 있지만, 위의 함수들은 스칼라값을 리턴하기 때문에 청색, 녹색, 빨강색의 모든 화소값에 접근하기 위해서는 array.item()를 모든 색 성분에 대해 적용할 필요가 있다.

화소에의 접근 방법과 변경 방법:

# accessing RED value
img.item(10,10,2)
>>> 59

# modifying RED value
img.itemset((10,10,2),100)
img.item(10,10,2)
>>> 100

2. 이미지의 속성 정보의 취득

배열의 속성 정보로는 열의 수, 행의 수, 채널 수(색상 수), 이미지 데이터의 형, 화소 수 등이 있다.

이미지의 형상은 img.shape로 취득 가능하다.

리턴값은 행수, 열수, (컬러 이미지 라면)채널 수의 튜플이다.

print img.shape
>>> (342, 548, 3)

<노트>

흑백 이미지의 리턴값의 튜플은 행의 수와 열의 수만 존재한다. 그러므로 위의 방법을 통해 이미지가 컬러인지 흑백인지 알아보는 데에 잘 사용된다.

총 화소의 수는 img.size로 알 수 있다.

print img.size
>>> 562248

이미지 데이터의 데이터형은 img.dtype을 사용하면 알 수 있다.

print img.dtype
>>> uint8

<노트>

img.dtype은 디버깅할 때 매우 중요하다. 그 이유는 OpenCV-Python을 사용할 때 자주 발새아는 에러는 맞지 않은 데이터형에 이해 발생하기 때문이다.


3. 이미지 중 주목 영역(ROI)의 설정

이미지의 특정 영역에 대해 무엇인가를 처리할 때가 반드시 생긴다. 예를 들어 이미지에서 눈을 검출할 때, 먼저 이미지의 전체에서 사람의 얼굴을 검출하고 그 다음 얼굴 안에서 눈을 검출한다. 위와 같이 영역을 좁혀가며 검출하는 이유는 효율성이 높기 때문이다. 주목 영역(ROI)의 지정은 numpy의 인덱스를 사용한다. 여기서는 이미지 중에서 공의 위치를 선별해 다른 장소에 복사한다.

ball = img[280:340, 330:390]
img[273:333, 100:160] = ball

결과는 아래와 같다.


4. 이미지의 분할 또는 통합

이미지의 청색, 녹색, 빨강색 성분은 필요하다면 독립적으로 각각의 성분을 분리할 수 있다.

분리시킨 색 성분을 다시 통합하여 BGR이미지를 한번 더 만드는 것도 가능하다:

b,g,r = cv2.split(img)
img = cv2.merge((b,g,r))

아래의 코드로, 어떤 색 성분만을 추출하는 것도 가능하다.

b = img[:,:,0]

이미지 중에 다른 색 성분과 분할하지 않고 빨강색 성분만을 0으로 설정하고 싶다고 가정하자.

numpy의 인덱스를 사용하면 아래와 같이 표현 가능하다.

img[:,:,2] = 0

<주의 사항>

cv2.split()은 (처리속도의 관점에서) 계산 비용이 큰 처리이므로, 필요할 때만 사용하는 것이 좋다.

numpy의 인덱스는 효율적이기 때문에 가능하다면 인덱스를 사용하자.


5. 이미지의 경계 만들기 (패딩)

이미지에 포토 프레임같은 경계선을 만들 때는 cv2.copyMakeBorder()함수를 사용한다.

cv2.copyMakeBorder()함수의 인자는 다음과 같다.

- src ; 입력 이미지

- top, bottom, left, right ; 경계의 각 방향에 대한 선의 폭

- borderType ; 추가하는 경계의 종류를 지정하기 위한 플래그, 아래와 같은 타입이 존재

1) cv2.BORDER_CONSTANT ; 단색의 경계를 추가하는 것, value에서 색의 지정을 한다.

2) cv2.BORDER_REFLECT ; 거울에 비춘 것 처럼 경계를 지정한다. 예를 들면, 아래와 같은 경계를 만들 수 있다.

; fedcba|abcdefgh|hgfedcb

3) cv2.BORDER_REFLECT_101 / cv2.BORDER_DEFAULT ; cv2.BORDER_REFLECT와 같지만 미세한 차이가 있다

; gfedcb|abcdefgh|gfedcba

4) cv2.BORDER_REPLICATE ; 마지막 요소를 반복해서 표시한다.

; aaaaaa|abcdefgh|hhhhhhh

5) cv2.BORDER_WRAP ; 잘 설명할 수 없지만, 아래와 같이 표시된다.

; cdefgh|abcdefgh|abcdefg

- value ; 플래그가 cv2.BORDER_CONSTANT일 경우 경계 색을 지정할 때 사용한다.

참고로, 이미지의 convolution이나 제로 패딩과 같은 처리도 있다.

아래의 코드는 위에서 설명한 전 패턴을 표시하는 코드이다.

import cv2
import numpy as np
from matplotlib import pyplot as plt

BLUE = [255,0,0]

img1 = cv2.imread('opencv_logo.png')

replicate = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_WRAP)
constant= cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT,value=BLUE)

plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')

plt.show()

결과는 아래의 이미지와 같다.

이미지를 matplotlib를 이용해 표시하기 이해 파랑과 빨강색 성분의 위치를 바꿨다.


참고자료

http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_core/py_basic_ops/py_basic_ops.html

https://cvtech.cc/py-opencv/

728x90