지난 시간에 영상을 window에 표시하고 창의 속성들을 설정하는 방법에 대해 알아봤습니다.
이번에는 영상에 도형을 그리고 글씨를 쓰는 방법에 대해 알아보겠습니다.
도형을 그리는 것은 영상에서 물체나 코너를 검출했을 때 결과를 표시하여 시각적으로 확인할 때 많이 사용하기도 합니다.
그리기 함수는 위치, 두께 ,색상, 선 타입, 비트 시프트 등의 인수를 사용하여 도형을 그리게 됩니다. 위치, 두께, 색상은 직관적이기 때문에 별도 설명을 드리지 않을께요. 먼저 선 타입과 비트 시프트에 대해 알아보겠습니다.
(1) 선 타입 (line type)
선 타입은 도형을 그릴 때 어떤 유형으로 그릴지를 결정하게 됩니다. 선을 그릴 때 특히 대각선을 그릴 때 이 직선의 소수점에 매칭되는 위치를 digitizing 할 때 어디를 그려야 하는지에 대한 알고리즘이 있는데, 브레젠험 직선 알고리즘(Bresenham's line algorithm)이 대표적입니다. (https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm)
브레젠험 알고리즘은 아래 그림 한장으로 설명이 가능한 간단한 알고리즘입니다. 이는 1962년 IBM에서 근무하던 Jack Elton Bresenham 이라는 분이 만들었다고 합니다.
그 이후에 Wu's line algorithm이 있는데, 이는 1991년에 antialiasing을 지원하는 알고리즘 입니다. 이는 높은 해상도 신호를 낮은 해상도에서 표현할 때 발생하는 계단현상을 제거하는 기법으로 다른 두 공간의 평균색을 표시하는 방법입니다.
OpenCV에서는 4연결, 8연결 그리고 anti-aliasing 방식이 있습니다.
(2) 비트 시프트
도형 그리기 함수에서 입력으로 사용되는 값은 일반적으로 정수입니다. 하지만 비트 시프트를 사용하면 소수점 이하의 값이 포함된 실수값 좌표로도 도형 그리기 함수를 사용할 수 있습니다. 비트 시프트는 서브 픽셀 정렬을 지원하기 때문에 소수점 이하 자리수를 표현할 수 있습니다. 비트 시프트는 오른쪽 시프트 연산으로 간주하면 쉽게 이해하실 수 있습니다. 예를 들이 0010(2) 를 오른쪽 비트 시프트 하게 되면 0001(2)가 됩니다. 십진수로 생각했을 때 2로 나누는 값이라고 생각하셔도 됩니다.
예를 들면 (2,2)에서 (7,4)로 이어지는 직선이 있다고 생각한다면(원래는 좌상단이 (0,0)이지만 편의상 (1,1)로 그렸습니다.) 직선은 아래 첫번째 그림처럼 그려지게 됩니다. 이 선분에 1-bit shift를 적용하면 (1,1)에서 (3.5, 2)로 이어지는 선분이 됩니다. 디지털 영상에서는 소수점이 없기 때문에 반올림하여 (4,2)로 그립니다. 만약 제일 왼쪽의 선분을 2-bit shift 를 적용하면 (0.5, 0.5)에서 ((1.75, 1)로 이어지는 선분이 되고, 이를 반올림 하면 (1, 1)에서 (2,1)로 이어지는 선분으로 나타낼 수 있습니다.
위 그림은 4연결 방법으로 그린 그림이며, 만약 8연결 또는 anti-aliasing 을 적용하면 가우스 필터링이 적용되어 gray 색의 픽셀도 존재하게 되겠죠.
이제부터 도형을 그려볼텐데, 하나씩 설명드리겠습니다.
(1) 직선 그리기
OpenCV에서 직선을 그리는 함수는 아래와 같습니다.
cv2.line(img, pt1, pt2, color, thickness = 1, lineType = cv2.LINE_8, Shift = 0)
img
|
도형을 그릴 영상
|
pt1
|
선분의 시작 점
|
pt2
|
선분의 끝 점
|
color
|
선분 색상 (BGR 형식)
|
thickness
|
선분의 두께
|
lineType
|
선 타입 (cv2.LINE_4 or 4, cv2.LINE_8 or 8, cv2.LINE_AA)
|
Shift
|
실수 값 처리할 좌표의 비트 값 할당
|
만약 pt1과 pt2의 위치가 같다면 1점을 그릴 수 있습니다.
(2) 화살표 그리기
OpenCV에서 화살표를 그릴 수 있습니다. 함수 원형은 아래와 같습니다.
cv2.arrowedLine(img, pt1, pt2, color, thickness=1, lineType=cv2.LINE_8, Shift=0, tipLength=0.1)
img
|
도형을 그릴 영상
|
pt1
|
화살표의 시작 점
|
pt2
|
화살표의 끝 점
|
color
|
화살표 색상 (BGR 형식)
|
thickness
|
화살표의 두께
|
lineType
|
화살표 선 타입 (cv2.LINE_4 or 4, cv2.LINE_8 or 8, cv2.LINE_AA)
|
Shift
|
실수 값 처리할 좌표의 비트 값 할당
|
tipLength
|
화살표 크기 (float)
|
(3) 사각형 그리기
OpenCV에서 사각형을 그리는 함수는 아래와 같습니다.
cv2.rectangle(img, pt1, pt2, color, thickness = 1, lineType = cv2.Line_8, Shift = 0)
cv2.rectangle(img, rect, color, thickness = 1, lineType = cv2.Line_8, Shift = 0)
img
|
도형을 그릴 영상
|
pt1
|
사각형의 좌상단 점
|
pt2
|
사각형의 우하단 점
|
rect
|
pt1과 pt2 대신 (x,y,width,height) 타입의 사각형 정보 입력
|
color
|
사각형 선 색상 (BGR 형식)
|
thickness
|
사각형 선의 두께 (cv2.FILLED, -1 사용 가능)
|
lineType
|
사각형 선 타입(cv2.LINE_4, cv2.LINE_8, cv2.LINE_AA)
|
Shift
|
실수 값 처리할 모서리 좌표의 비트 값 할당
|
(3-1) 사각형과 직선이 만나는 점 구하기
OpenCV에서는 사각형과 직선을 주어주면 사각형과 직선이 만나는 점을 계산해 줍니다.
ret, rpt1, rpt2 = cv2.clipLine(imgRect, pt1, pt2)
ret
|
결과값 여부, 선분이 사각형 안에 있으면 True, 없으면 False
|
rpt1
|
사각형과 선분이 만나는 점 1
|
rpt2
|
사각형과 선분이 만나는 점 2
|
imgRect
|
사각형 정보(x,y,width, height)
|
pt1
|
선분 시작점
|
pt2
|
선분 끝점
|
만약 선분이 사각형 안에 있으면 ret은 True를 반환하고, 선분의 시작점과 끝점을 반환합니다. 만약 선분이 사각형과 만나는 점이 있다면 ret 는 역시 True를 반환하고 선분과 사각형이 만나는 점을 반환해 줍니다. 만약 선분이 사각형 밖에 있다면 ret 은 False를 반환합니다.
(4) 원 그리기
OpenCV에서 사각형과 함께 가장 많이 그리게 될 것이 바로 원 입니다. 점을 표현할 때에도 많이 사용을 하기 때문입니다. 제공하는 함수 원형은 아래와 같습니다.
cv2.circle(img, center, radius, color, thickness=1, lineType=8, shift=0)
img
|
원을 그릴 영상
|
center
|
원의 중심
|
radius
|
원의 반지름
|
color
|
원의 선 색상
|
thickness
|
원의 선 두께
|
lineType
|
원의 선 타입
|
shift
|
실수값 처리할 중심점과 반지름의 비트값 할당
|
(5) 타원 그리기
타원을 그리는 함수의 원형은 아래와 같습니다. 위쪽 함수는 타원을 그리거나 호를 그릴 때 사용되며, 아래쪽 함수는 사각형 내부에 선을 그리는 형태로 타원을 그리는 방식 입니다.
cv2.ellipse(img, center, axes, angle, startAngle, endAngle, color, thickness=1, lineType=8, shift=0)
cv2.ellipse(img, box, color, thickness=1, lineType=8, shift=0)
img
|
타원 또는 호를 그릴 영상
|
center
|
타원의 중심 좌표(x,y)
|
axes
|
(장축, 단축)
|
angle
|
장축이 기울어진각도
|
startAngle
|
타원 그리는 시작 각도
|
endAngle
|
타원 그리는 마지막 각도
|
box
|
회전 사각형((x,y),(width,height), angle)
|
color
|
타원의 선 색상
|
thickness
|
타원의 선 두께
|
lineType
|
타원의 선 종류
|
shift
|
실수값으로 처리할 중심점과 장축, 단축 비트 값 할당
|
(6) 다각형 그리기
OpenCV 에서 다각형을 그리는 함수 원형은 아래와 같습니다.
다각형 그리기
cv2.polylines(img, pts, isClosed, color, thickness=1, lineType=8, shift=0)
볼록 다각형 채우기
cv2.fillConvexPoly(img, pts, color, thickness=1, lineType=8, shift=0)
다각형 채우기
cv2.fillPoly(img, pts, color, thickness=1, lineType=8, shift=0)
img
|
다각형을 그릴 영상
|
pts
|
다각형의 꼭지점들
|
isClosed
|
다각형의 폐곡선 여부
|
color
|
다각형의 선 색상
|
thickness
|
다각형의 선 두께
|
lineType
|
다각형의 선 타입(채울 색깔)
|
shift
|
실수값으로 처리할 좌표(pts)의 비트값 할당
|
(6-1) 타원 위의 점들을 잇는 다각형
타원을 이루는 점들을 일정 각도 간격으로 계산하여 다각형의 point 들을 계산해주는 함수를 제공합니다.
pts = cv2.ellipse2Poly(center, axes, angle, arcStart, arcEnd, delta)
pts
|
타원을 이루는 다각형 꼭지점들
|
center
|
타원의 중심 위치 (x, y)
|
axes
|
(장축, 단축)
|
angle
|
회전 각도
|
arcStart
|
호의 시작 각도
|
arcEnd
|
호의 마지막 각도
|
delta
|
좌표를 delta 간격으로 계산
|
(7) 글씨 쓰기
영상에 글씨를 쓰는 함수 원형은 아래와 같습니다.
cv2.putText(img, text, org, fontFace, fontScale, color, thickness=1, lineType=8, bottomLeftOrigin=False)
img
|
문자를 출력할 영상
|
text
|
문자열
|
org
|
좌상단 모서리 좌표
|
fontFace
|
글꼴
|
fontScale
|
문자 크기
|
color
|
문자 색상
|
thickness
|
문자 두께
|
lineType
|
문자 선 타입
|
bottomLeftOrigin
|
기준 좌표로 좌상단이 아닌 텍스트의 좌하단 모서리 사용할 경우 True
|
fontFace 에 대해서는 아래 C++과 같으므로 C++ 문서를 참조하시면 됩니다.
위에서 설명한 모든 소스를 한번에 작성한 소스를 확인해보겠습니다.
import cv2
import numpy as np
img = np.zeros((1000, 1000, 3), dtype=np.uint8)
# line 그리기
cv2.line(img, (100, 100), (700, 100), (0,0,255), 2, cv2.LINE_AA)
# 점 그리기
cv2.line(img, (200, 150), (200,150), (0,0,255), 3)
# 화살표 그리기
cv2.arrowedLine(img, (800, 100), (850, 300), (0, 255,0), 3, 4, 0, 0.3)
# 사각형 그리기
cv2.rectangle(img, (400, 200), (550, 500), (255,255,0), 4, cv2.LINE_8)
# 사각형 그리기 rect 사용 (x, y, width, height)
rect = (100, 800, 100, 150)
cv2.rectangle(img, rect, (0, 100, 100), 3)
# line 그리기
pt1 = 150, 780
pt2 = 120, 900
cv2.line(img, pt1, pt2, (100, 100, 255), 3)
#clipLine 사용하기
retval, rpt1, rpt2 = cv2.clipLine(rect, pt1, pt2)
# 사각형과 선분이 만난다면 만나는 점 그리기
if retval:
cv2.circle(img, rpt1, radius=5, color=(255, 100, 0), thickness=-1)
cv2.circle(img, rpt2, radius=8, color=(100, 255,100), thickness=-1)
# 원 그리기
cv2.circle(img, (200, 250), 70, (255, 0, 0), cv2.FILLED, cv2.LINE_4)
# 타원 그리기
cv2.ellipse(img, (700, 700), (100, 50), 0, 30, 180, (0, 255,255), 2)
cv2.ellipse(img, (350, 350), (90, 20), 0, 90, 360, (0,0,255), -1)
cv2.ellipse(img, ((400, 850), (200, 100), 45), (255, 100, 100), -1)
#다각형 그리기
pts1=np.array([[[100, 500],[300, 500], [200, 600]], [[400, 600], [600, 800],[400, 700]]])
pts2=np.array([[700, 500], [800, 500],[700, 600]])
pts3=np.array([[800, 800], [9000, 800],[900, 900],[800, 1000],[800,900]])
cv2.polylines(img, pts1, True, (255,100,100), 2)
cv2.fillPoly(img, [pts2, pts3], (255,0,255), cv2.LINE_AA)
# ellipse 에서 꼭지점 좌표 계산하여 다각형 그리기
pts4 = cv2.ellipse2Poly((800, 400), (90, 50), 45, 0, 360, delta=45)
cv2.polylines(img, [pts4], isClosed=True, color=(0,0,255), thickness=3)
# 글씨 쓰기
cv2.putText(img, "Python", (500, 900), cv2.FONT_HERSHEY_COMPLEX | cv2.FONT_ITALIC, 2, (255,255,255), 3)
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
위 소스의 결과는 아래와 같습니다.