해당 포스팅은 Mastering OpenCV 4 with Python 원서 내용을 기반으로 작성되었습니다.
이번 포스팅에서는 다음의 내용들을 포함하고 있습니다.
- image basics에 대한 이론적인 소개
- OpenCV에서의 좌표계
- OpenCV에서 픽셀에 접근하고 계산하는 방법
- OpenCV에서의 RGB 순서
1. 이미지 처리에서의 문제점
컴퓨터 비전에서 이미지 처리 기술을 다룰 때, 몇 가지 문제점들이 존재합니다. 첫 번째, 이미지는 관점에 의해 영향을 받습니다. 같은 물체라고 해도, 다른 관점으로 보면 다른 이미지로 인식할 수 있습니다. 두 번째, 이미지는 빛, 날씨, 반사, 움직임 등 다양한 요소로부터 영향을 받습니다. 세 번째, 다른 물체에 의해서 가려지는 경우 발견하거나 분류하기 어렵습니다.
예를 들어, 얼굴 위치를 감지하는 모델을 만든다고 해보면, 비교적 빛이나 날씨에 영향을 받지 않아야 하고, 머리의 움직임은 인식을 방해할 것입니다. 또한 정면의 얼굴은 잘 인식하겠지만 측면의 얼굴이나 안경이나 선글라스를 쓴 얼굴도 인식할 수 있어야 합니다. 그래서 우리는 프로젝트를 진행할 때, 이러한 요소들을 반드시 고려해야 합니다.
2. 이미지 처리 과정
이미지 처리 과정은 아래의 3개의 과정을 포함하고 있습니다.
1) 카메라, 비디오, 온라인 등 다양한 곳에서 사용할 이미지를 얻습니다.
2) 필요한 기능을 얻어내기 위해 이미지 처리 기술을 적용해서 이미지 처리합니다.
3) 처리의 결과를 표시합니다.
위의 과정 중 2번은 Low-level, Mid-level, High-level process로 구분할 수 있습니다. Low-level process에서는 노이즈 제거, 선명한 이미지로 변경, 조명에 대한 정규화, 관점을 변경해서 처음 이미지와 다른 새로운 이미지들을 만드는 과정을 포함합니다. Mid-level process에서는 사전 처리된 이미지를 사용해서 output을 얻을 수 있습니다. 예를 들어, face-detection이라고 했을 때, 얼굴을 나타내는 사각형 포인트, 너비와 높이 등을 말합니다. high-level process에서는 최종 결과 output을 얻어냅니다.
3. 파일 확장자
OpenCV 라이브러리에서는 다양한 종류의 이미지 확장자를 지원합니다.
- Windows bitmaps: *.bmp, *.dib
- JPEG files: *.jpeg, *.jpg, *.jpe
- JPEG 2000 files: *.jp2
- Portable Network Graphics: *.png
- Protable image format: *.pbm, *.pgm, *.ppm
- TIFF files: *.tiff, *.tif
4. OpenCV 좌표계
위의 그림처럼 OpenCV 좌표계는 좌측 상단을 원점(0, 0)으로 기준을 잡아서 사용합니다. x값은 오른쪽으로 갈수록 커지며, y값은 아래로 갈수록 큰 값을 얻습니다.
5. OpenCV에서 실습하기
1) 컬러 이미지 불러오고 출력하기
OpenCV 명령어로 이미지를 불러와서 확인하는 방법에 대해서 알아보겠습니다. 저는 local jupyter notebook 환경에서 진행했습니다. 혹시 colab으로 진행하실 때에는 일부 명령어가 다릅니다. 참고하시면 될 것 같습니다.
# jupyter notebook
import cv2
img = cv2.imread('dog1.jpg') # 이미지 불러오기(파일 위치는 상대경로, 절대경로 활용)
dimensions = img.shape
print(dimensions) # (4000, 6000, 3) = (rows, columns, channels)
resize_img = cv2.resize(img, (600, 400)) # 이미지 리사이즈(columns, rows)
cv2.imshow("dog", resize_img)
# 아무키나 입력하면 이미지 창을 제거(colab에서는 안써도 상관없습니다)
cv2.waitKey()
cv2.destroyAllWindows()
# colab
from google.colab.patches import cv2_imshow
cv2_imshow(img) #cv2.imshow('title', img) 대신 사용
위의 그림처럼 cv2.imshow에서 첫 번째 인자는 이미지 출력 창의 title을 설정할 수 있고, imread로 읽어온 img를 두 번째 인자에 넣어주면 됩니다. 이미지 크기가 너무 커서 중간에 저는(600, 400)으로 resize 해서 진행했습니다. colab환경에서는 waitKey, destroyAllWindows를 해줄 필요는 없고, cv2.imshow 대신 cv2_imshow를 활용하면 구현 가능합니다.
2) 컬러 이미지를 회색 이미지로 출력하기
회색 이미지로 불러오는 것은 어렵지 않습니다. 위에 있었던 imread 명령어만 아래의 코드 블럭으로 변경해주면 됩니다.
# gray로 이미지 불러오기
gray_img = cv2.imread('dog1.jpg', cv2.IMREAD_GRAYSCALE)
resize_gray_img = cv2.resize(gray_img, (600, 400))
cv2.imshow("gray_img", resize_gray_img)
cv2.waitKey()
cv2.destroyAllWindows()
3) 이미지의 일부분을 잘라서 표현하기
이미지의 일부분을 자르기 위해서는 슬라이싱을 활용하면 쉽게 표현할 수 있습니다.
# 슬라이싱으로 img의 일부 잘라내기
dog = resize_gray_img[150:350, 250:450]
cv2.imshow("cropped_img", dog)
cv2.waitKey()
cv2.destroyAllWindows()
6. matplotlib과 OpenCV Channel 비교
1) Matplotlib으로 표현하기
# 채널별로 값을 b, g, r로 넣어서 merge를 r, g, b 순으로 저장
b, g, r = cv2.split(img)
img_matplotlib = cv2.merge([r, g, b])
import matplotlib.pyplot as plt
plt.subplot(1,2,1)
plt.title('BGR')
plt.imshow(img)
plt.subplot(1,2,2)
plt.title('RGB')
plt.imshow(img_matplotlib)
plt.show()
img의 경우 이미지를 cv2.imread로 받은 것으로 BGR 채널 순으로 정보가 담겨있습니다. 이 이미지를 matplotlib에서 imshow를 하면, matplotlib은 RGB 채널 순으로 정보가 담겨있다고 생각하기 때문에 기존의 이미지와 다른 형태를 가집니다. 우측 이미지는 cv2.imread로 얻어낸 img 파일의 채널을 R과 B를 바꿔준 다음 imshow를 한 것입니다. 원본사진과 동일한 것을 확인하실 수 있습니다.
2) OpenCV로 표현하기
b, g, r = cv2.split(resize_img)
img_matplotlib = cv2.merge([r, g, b])
import numpy as np
img_concats = np.concatenate((resize_img, img_matplotlib), axis=1)
cv2.imshow('BGR, RGB', img_concats)
cv2.waitKey()
cv2.destroyAllWindows()
이번에는 OpenCV에서 resize_img와 채널 바꾼 img_matplotlib을 concat 한 다음 표현했습니다. 리사이즈해준 것을 사용한 이유는 matplotlib은 이미지 크기가 아무리 커도 jupyter notebook 상에서 조절해주지만 OpenCV는 따로 조절해주지 않습니다. 위의 사진을 보면, 좌측은 BGR 순으로 이뤄진 사진이고, 우측은 RGB 순으로 이뤄진 사진입니다. 아까 matplotlib과 반대의 결과를 보실 수 있습니다.
7. 요약
- 이미지 처리할 때, 관점, 주변 환경(명도, 날씨, 빛 반사, 움직임), 다른 객체에 의해 가려지는 현상 등 문제점이 많습니다.
- 데이터를 구성할 때, 위에서 언급된 문제점들을 충분히 고려해야 합니다.
- OpenCV 좌표계는 좌측 상단(0, 0)이 기준입니다.
- 라이브러리의 이미지 채널 차이가 존재합니다. OpenCV는 BGR, matplotlib은 RGB 순서