해당 포스팅은 Mastering OpenCV 4 with Python 원서를 바탕으로 작성했습니다. 원서를 옮기는 과정에서 부자연스러운 부분이 있을 수 있습니다. 잘못 작성되거나 어색한 부분에 대해서 알려주시면 감사하겠습니다! 코드 정보는 여기를 클릭하시면 확인하실 수 있습니다.
OpenCV 라이브러리는 선, 원, 사각형, 타원 등 다양한 기본 도형들을 그릴 수 있는 함수들을 제공합니다. 보통 컴퓨터 비전 프로젝트를 진행하다 보면, image에 추가적인 도형을 그릴 필요가 있습니다. 예를 들어, 얼굴 인식 알고리즘을 개발했다고 가정하면, 알고리즘이 예측한 사각형을 image 위에 표시해야 합니다. 또한 필요한 정보에 대해 text를 넣어야 할 수도 있습니다. 이번 장에서는 기본 도형과 고급 도형을 OpenCV 라이브러리를 활용해서 그리는 방법을 소개합니다.
이번 글에서는 다음의 내용 중 Bold체로 된 부분만 포함하고 있습니다.
- OpenCV를 활용한 그리기 이론적 소개
- 기본 도형 그리기 - 선, 사각형, 원
- 기본 도형 2 - 클립, 화살표, 타원, 폴리라인
- 텍스트 그리기
- 마우스 Event에 따른 Dynamic Drawing
- 그리기 고급편
1. OpenCV를 활용한 그리기 이론적 소개
OpenCV 라이브러리는 기초적인 도형들을 그리는 함수들을 제공하고 있습니다. 일반적으로 선, 사각형, 원이 포함되지만 OpenCV를 사용하면 더 많은 기본적인 도형들을 그릴 수 있습니다. 앞서 설명했듯이 다음을 수행하기 위해서 이미지에 도형을 추가합니다.
- 알고리즘의 결과를 즉시 확인하고 싶을 때
- 일부 디버깅 정보를 표시하고 싶을 때
위의 그림을 보면, 사람의 얼굴을 boundary를 나름 잘 예측하고 있으며, 해당 사람이 누구인지 맞추고 있습니다. 이처럼 이미지 내부의 도형을 통해 face detection과 face recognition에서의 유용한 정보를 알려주고 있습니다. 이러한 측면에서 도형은 우리의 알고리즘이 얼굴을 잘 찾았는지 아니면 찾지 못했는지에 대한 정보를 확인할 수 있습니다.
4장에서는 다른 색상으로 기본적인 도형들과 텍스트를 그리는 방법에 대해서 소개합니다. 이전의 챕터에서 소개했었던 color에 대한 내용을 한번 더 보시는 것을 추천드립니다.
위의 그림처럼 colors라는 딕셔너리를 생성한 다음 텍스트나 도형에 적용해볼 수 있습니다.
# constant.py
"""
Common colors triplets (BGR space) to use in OpenCV
"""
BLUE = (255, 0, 0)
GREEN = (0, 255, 0)
RED = (0, 0, 255)
YELLOW = (0, 255, 255)
MAGENTA = (255, 0, 255)
CYAN = (255, 255, 0)
DARK_GRAY = (50, 50, 50)
또한 위의 코드 블록처럼 constant.py에 정보를 미리 입력해놓고 불러와서 사용할 수 있습니다.
import constant
# Getting red color:
print(f"red: {constant.RED}") #result - red : (0, 0, 255)
constant를 활용해서 다양한 색상의 선을 표시하는 형태를 만들 수 있습니다. 위의 형태를 만드는 코드는 아래와 같습니다.
"""
Example for testing predefined colors and matplotlib output
"""
# Import required packages:
import cv2
import numpy as np
import matplotlib.pyplot as plt
import constant
# Getting red color:
print("red: '{}'".format(constant.RED))
def show_with_matplotlib(img, title):
"""Shows an image using matplotlib capabilities"""
# Convert BGR image to RGB:
img_RGB = img[:, :, ::-1]
# Show the image using matplotlib:
plt.imshow(img_RGB)
plt.title(title)
plt.show()
# Dictionary containing some colors:
colors = {'blue': (255, 0, 0), 'green': (0, 255, 0), 'red': (0, 0, 255), 'yellow': (0, 255, 255),
'magenta': (255, 0, 255), 'cyan': (255, 255, 0), 'white': (255, 255, 255), 'black': (0, 0, 0),
'gray': (125, 125, 125), 'rand': np.random.randint(0, high=256, size=(3,)).tolist(),
'dark_gray': (50, 50, 50), 'light_gray': (220, 220, 220)}
# We create the canvas to draw: 500 x 500 pixels, 3 channels, uint8 (8-bit unsigned integers)
# We set background to black using np.zeros():
image = np.zeros((500, 500, 3), dtype="uint8")
# If you want another background color you can do the following:
image[:] = colors['light_gray']
# We draw all the colors to test the dictionary
# We draw some lines each one in one color. To get the color use 'colors[key]'
separation = 40
for key in colors:
# Draw a line using the function cv2.line():
cv2.line(image, (0, separation), (500, separation), colors[key], 10)
separation += 40
# Show image:
show_with_matplotlib(image, 'Dictionary with some predefined colors')
show_with_matplotlib은 BGR 형태의 이미지를 RGB형태로 변환하여 보여주는 함수입니다. colors라는 dictionary는 color 정보를 담고 있으며, cv2.line으로 선을 그릴 때, 선의 색상으로 사용됩니다. image는 (500, 500, 3) 짜리의 배경을 만들어서 light_gray 색상으로 채웁니다. separation은 선과 선의 간격을 주기 위해 지정한 부분으로 for 문을 돌면서 40씩 커지게 됩니다. cv2.line(선을 넣고 싶은 객체, (시작점), (끝점), 색상, 굵기)로 작성할 수 있습니다. 위의 예시를 보면, 시작점과 끝점의 x는 고정시키고 separation만 움직이기 때문에 평행한 선이 만들어지게 됩니다.
cv2.line 내부의 x좌표와 y좌표를 변경하면 세로 형태의 결과가 나옵니다.
2. 기본 도형 그리기 - 선, 사각형, 원
이번에는 선, 사각형, 원을 그려보려고 합니다. 그리기 전에는 항상 그림을 그릴 공간이 필요합니다. 위에서 정의했던 것처럼 동일하게 정의합니다.
import numpy as np
# We create the canvas to draw: 400 x 400 pixels, 3 channels, uint8 (8-bit unsigned integers)
# We set the background to black using np.zeros()
image = np.zeros((400, 400, 3), dtype="uint8")
# If you want another background color, you can do the following:
image[:] = colors['light_gray']
# Show image:
show_with_matplotlib(image, '')
위처럼 image 객체를 생성하면 회색 바탕의 한 변의 길이가 400으로 이뤄진 3 채널짜리 이미지가 생성됩니다.
기본적으로 cv2.line은 parameter로 img, color, thickness, lineType, shift을 사용합니다. img는 위의 캔버스처럼 그리고자 하는 객체이고, color는 BGR형태로 입력해줘야 합니다. thickness는 선의 두께이며, lineType은 LINE_4, LINE_8, LINE_AA가 존재합니다.
"""
Example to show the lineType argument in OpenCV
"""
# Import required packages:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# We create the canvas to draw: 20 x 20 pixels, 3 channels, uint8 (8-bit unsigned integers)
# We set the background to black using np.zeros():
image = np.zeros((20, 20, 3), dtype="uint8")
# If you want another background color you can do the following:
image[:] = colors['light_gray']
# We are going to see how cv2.line() works modifying the parameter lineType:
cv2.line(image, (5, 0), (20, 15), colors['yellow'], 1, cv2.LINE_4)
cv2.line(image, (0, 0), (20, 20), colors['red'], 1, cv2.LINE_AA)
cv2.line(image, (0, 5), (15, 20), colors['green'], 1, cv2.LINE_8)
# Show image:
show_with_matplotlib(image, 'LINE_4 LINE_AA LINE_8')
접은 글에는 위의 LINE_4, LINE_8, LINE_AA를 비교하는 코드가 있습니다. 혹시 궁금하시면 추가적으로 보시면 좋을 것 같습니다. 기본적으로 앞에서 작성했던 코드와 유사하고 cv2.line 부분만 일부 다릅니다.
책에서는 LINE_AA 옵션이 가장 좋은 퀄리티로 그릴 수 있다고 합니다. 하지만, 다른 옵션에 비하면 그리는데 살짝 시간이 더 오래 걸린다고 합니다. LINE_4, LINE_8은 브레젠험 직선 알고리즘을 사용해서 그려지고, LINE_AA는 가우시안 필터링 알고리즘을 사용하고 있습니다. shift 파라미터는 도형 그리기에서는 일반적으로 정수 값을 사용하기 때문에 실수 값을 좌표로 활용하기 어렵습니다. 그래서 shift는 서브 픽셀 정렬을 지원해서 소수점 이하 자릿수를 표현할 수 있도록 해줍니다.
# 1. We are going to see how cv2.line() works:
cv2.line(image, (0, 0), (400, 400), colors['green'], 3)
cv2.line(image, (0, 400), (400, 0), colors['blue'], 3)
cv2.line(image, (200, 0), (200, 400), colors['red'], 10)
cv2.line(image, (0, 200), (400, 200), colors['yellow'], 10)
# Show image:
show_with_matplotlib(image, 'cv2.line()')
다른 부분은 위에서 불러왔던 것들과 동일하기 때문에 선을 그리는 부분만 가져왔습니다. cv2.line은 그리고자 하는 image와 시작점 x, y 좌표값, 끝점 x,y 좌표값, 선의 색상, 선의 굵기를 입력으로 넣어줘야 합니다. 그래서 위의 결과를 실행하면 다음과 같은 결과를 얻을 수 있습니다.
그다음으로는 사각형을 그려볼 텐데, 사각형은 좌상단점과 우하단점을 입력으로 넣어주면 되고, thickness를 주면 해당 굵기만큼 내부가 비어있는 사각형을 그립니다. 이때, 꽉 찬 사각형을 그리고 싶다면 -1을 입력으로 넣어주시면 됩니다.
# 2. We are going to see how cv2.rectangle() works:
cv2.rectangle(image, (10, 50), (60, 300), colors['green'], 3)
cv2.rectangle(image, (80, 50), (130, 300), colors['blue'], -1)
cv2.rectangle(image, (150, 50), (350, 100), colors['red'], -1)
cv2.rectangle(image, (150, 150), (350, 300), colors['cyan'], 10)
# Show image:
show_with_matplotlib(image, 'cv2.rectangle()')
마지막으로 원의 경우에는 원의 중심과 반지름의 정보가 필요합니다. thickness는 사각형을 그릴 때와 동일한 방법으로 적용됩니다.
# 3. We are going to see how cv2.circle() works:
cv2.circle(image, (50, 50), 20, colors['green'], 3)
cv2.circle(image, (100, 100), 30, colors['blue'], -1)
cv2.circle(image, (200, 200), 40, colors['magenta'], 10)
cv2.circle(image, (300, 300), 40, colors['cyan'], -1)
# Show image:
show_with_matplotlib(image, 'cv2.circle()')
3. 3줄 요약
- 이미지 위에 다양한 도형을 그리는 것은 우리에게 모델이 얼마나 잘 동작하는지를 알아볼 수 있는 정보를 제공합니다.
- 색상은 기본적으로 BGR 형태를 입력으로 받습니다.
- 선, 사각형, 원은 각각에 맞게 옵션을 넣어줌으로써 image 위에 작성할 수 있습니다.