지난 시간에 영상을 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++ 문서를 참조하시면 됩니다.
[OpenCV][C++] 영상에 문자열 출력하기 cv::putText(), cv::getTextSize() 총정리 글씨 쓰기 글자 표현 image font
영상에 문자열을 이용하여 필요한 정보를 표시해야 하는 경우가 있습니다. OpenCV에서는 영상에 문자열...
blog.naver.com
위에서 설명한 모든 소스를 한번에 작성한 소스를 확인해보겠습니다.
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()
위 소스의 결과는 아래와 같습니다.
