Skip to content

Drawing Functions in OpenCV

Goal

OpenCV 를 이용하여 선,원, 사각형, 글자 등을 그리는 방법을 배운다.

실질적으로 다음의 함수들을 사용하는 법을 배운다. :

  • cv2.line()
  • cv2.circle()
  • cv2.rectangle()
  • cv2.ellipse()
  • cv2.putText()

Common Parameters

우리가 다루게 될 function들에서 자주 등장하는 common argument는 다음과 같음.

img
실제로 우리가 그리는 도형이나 글자가 그려질 image 객체
color
도형이나 글자의 색.
칼러 이미지의 경우 BGR로 지정되고, gray-scale인 경우는 scalar값으로 처리 가능함.
thickness
도형 등의 line(선)의 두께. 만일 -1이 넘겨질 경우 채워줘서 그려짐 (또는 cv2.FILLED 로 지정). default thickness = 1
lineType
선의 형태, cv2.LINE_4(4-connected), cv2.LINE_8(8-connected), cv2.LINE_AA (anti-aliased line) 중 에 고름. default line type = cv2.LINE_8.

Drawing Line

cv2.line을 이용하여 라인을 그린다.

  • 시작점,
  • 끝점,
  • 선의 두께,

등을 argument로 넘겨줌.

다음 예제를 보자.

import numpy as np
import cv2

# Create a black image
img = np.zeros((512,512,3), np.uint8)

# Draw a diagonal blue line with thickness of 5 px
img = cv2.line(img,
               (0,0),       # pnt0
               (511,511),   # pnt1
               (255,0,0),   # color
               5            # thickness
               )

다음을 통해 선을 확인 가능함.

from matplotlib import pyplot as plt

img2 = img[:,:,::-1] # faster version.
# plt.imshow(img2,cmap='gray', interpolation='bicubic')
plt.imshow(img2,cmap='gray', interpolation='bicubic')
plt.xticks([]); plt.yticks([]) # to hide tick values on X and Y axis
plt.show()

Drawing Rectangle

사각형을 그리기 위해서,

  • **top-left corner**와
  • bottom-right corner of rectangle

argument로 넘겨줌.

다음 예제를 참고하라.

img = cv2.rectangle(img,       # 그려지는 대상. 
                    (384,0),   # top-left
                    (510,128), # bottom-right
                    (0,255,0), # color, BGR
                    3          # thickness
                    )

다음 조각코드로 오른쪽 상단에 사각형을 확인할 수 있음.

from matplotlib import pyplot as plt

# b,g,r = cv2.split(img)
# img2 = cv2.merge([r,g,b])
img2 = img[:,:,::-1] # faster version.
plt.imshow(img2,cmap='gray', interpolation='bicubic')
plt.xticks([]); plt.yticks([]) # to hide tick values on X and Y axis
plt.show()
  • 계속 같은 image 객체 img에 그리고 있기 때문에 이전 선과 같이 보인다.

Drawing Circle

원을 그리는 경우,

  • center의 좌표와
  • radius

argument로 넘겨줌.

다음 예제를 보자.

img = cv2.circle(img,         # target image
                 (447,63),    # center
                 63,          # radius
                 (0,0,255),   # color
                 cv2.FILLED,  # thickness : -1 means cv2.FILLED
                 )
print(f'{cv2.FILLED:d}')                 

다음 코드로 확인하면 오른쪽 상단의 사각형에 내접하는 원을 확인 가능함.

from matplotlib import pyplot as plt

# b,g,r = cv2.split(img)
# img2 = cv2.merge([r,g,b])
img2 = img[:,:,::-1] # faster version.
plt.imshow(img2,cmap='gray', interpolation='bicubic')
plt.xticks([]); plt.yticks([]) # to hide tick values on X and Y axis
plt.show()

Drawing Ellipse

타원(ellipse)을 그리기 위한 arguments는 다음과 같음.

  • 타원의 중점 좌표 : (x,y).
  • 타원의 major axis과 minor axis의 길이 : (major axis length, minor axis length).
  • 타원의 회전 각도 : angle (clockwise direction and degrees).
    • major axis와 x-axis의 각도. 아래 그림 참조할 것.
    • major axis를 장축으로도 지칭하는 경우도 있는데 엄밀히는 조금 틀린 명칭임(major axis가 minor aixs보다 길이가 짧을 수 있음)
  • 타원의 arc (호)를 그리기 위한 시작각과 끝각을 argument로 받음 : startAngle and endAngle (clockwise direction, degree).

For more details, check the documentation of cv2.ellipse().

다음 예제를 살펴보자.

# img = np.zeros((512,512,3), np.uint8)
img = cv2.ellipse(img,
                  (256,256), # center
                  (100,50),  # major axis length, minor axis length
                  0,         # rotaion angle (CW)
                  45,        # start angle (CW)
                  180,       # end angle (CW)
                  255,       # color (gray-scale or [255,0,0]과 같음.) 
                  -1         # filled 
                  )

arc를 그리지 않는 경우는 다음의 같이 사용하기도 함.

# img = np.zeros((512,512,3), np.uint8)
img = cv2.ellipse(img,
                  [
                    (256,256), # center
                    (100,50),  # major axis length, minor axis length
                    0,         # rotaion angle (CW)
                  ],
                  [0,0,255],   # color  
                  -1         # filled (-1) or thickness (0 초과 양수) 
                  )
  • 이 경우, 2nd argument가 ellipse에 대한 정보를 다 가지고 있음.
  • cv2.fitEllipse 로 contour를 둘러싸는 타원을 계산하여 얻어지는 반환값이 바로 2nd argument로 들어가는 정보임. ref. : fitEllipse

다음 조각코드 수행시 중앙에 타원이 보인다. (arc가 일부만 되도록 한 부분 주의할 것.)

from matplotlib import pyplot as plt

img2 = img[:,:,::-1] # faster version.
plt.imshow(img2,
        #    cmap='gray', 
        #    interpolation='bicubic'
           )
plt.xticks([]); plt.yticks([]) # to hide tick values on X and Y axis
plt.show()

Drawing Polygon

다각형을 그리는 방법은 다음과 같음.

  • 우선 각 vertex의 좌표들의 ndarray를 생성. (rows x 2)
  • 해당 ndarray를 rows x 1 x 2 로 reshape를 시킨다. rows는 vertex들의 숫자에 해당.
  • 해당 ndarray는 int32 를 dtype로 가짐.

사실, 위의 내용은 tutorial의 내용이나, 실제로 reshape를 하지 않고도 동작함.

다음의 예제를 참고.

pts = np.array([[10,5],[20,30],[70,20],[50,10]], 
               np.int32)
print(pts.shape)

# pts = pts.reshape((-1,1,2)) #openCV 튜토리얼에서 권하는 구현이나 생략해도 그려짐.
print(pts.shape)

img = cv2.polylines(img,        # target image
                    [pts],      # vertices
                    True,       # isClosed (닫혔는지여부)
                    (0,255,255),# color
                    1,          # thickness
                    )

다음의 조각코드를 수행하여 확인 가능함. 4개의 점이 연결된 다각형이 그려짐.

from matplotlib import pyplot as plt

img2 = img[:,:,::-1] # faster version.
plt.imshow(img2,cmap='gray', interpolation='bicubic')
plt.xticks([]); plt.yticks([]) # to hide tick values on X and Y axis
plt.show()

Note0 : cv2.polylines

cv2.polylines의 세번째 argument 가 False이면 닫힌 다각형이 아닌 끝점과 시작점이 연결이 안 된 상태로 그려짐을 확인할 수 있음.

test_img = np.zeros((50,80,3), np.uint8)
test_img = cv2.polylines(test_img,[pts],False,(0,255,255))

img2 = test_img[:,:,::-1] # faster version.
plt.imshow(img2,
        #    cmap='gray', 
        #    interpolation='bicubic'
           )
plt.xticks([]); plt.yticks([]) # to hide tick values on X and Y axis
plt.show()

Note1 : cv2.polylines 의 용도

cv2.polylines() 은 실제로 여러 개의 라인을 그리는데 사용된다.

여러 선에 해당하는 list를 argument로 넘기면 해당 선들이 그려진다.
cv2.line() 을 여러번 호출하는 것보다 처리 효율이 좋다.

다음 코드를 통해 사용 방법을 익혀보자.

pts0 = np.array(
    [[3,3],[3,13]]
    ,np.int32).reshape(-1,1,2)
pts1 = np.array(
    [[10,3],[10,13]]
    , np.int32).reshape(-1,1,2)
pts2 = np.array(
    [[17,3],[17,13]]
    , np.int32).reshape(-1,1,2)
print(np.array([pts0,pts1,pts2]).shape) # (3, 2, 1, 2)

test_img = np.zeros((50,80,3), np.uint8)
test_img = cv2.polylines(test_img,[pts0,pts1,pts2],False,(0,255,255))

img2 = test_img[:,:,::-1] # faster version.
plt.imshow(img2,
           #cmap='gray',
           #interpolation='bicubic'
           )
plt.xticks([]); plt.yticks([]) # to hide tick values on X and Y axis
plt.show()

꽉 찬 다각형 그리기.

보통 다음 두가지 function을 사용한다.

cv2.fillConvexPoly(img,points,color)
points에 저장된 좌표로 이루어진 볼록다각형을 color로 채운다. fillPoly보다 빠르지만, 오목한 경우 다르게 칠해진다.
cv2.fillPoly(img,contours,color)
하나 이상의 다각형 을 color 색상으로 칠한다. countours=[points_of_contour0, ...]

일반적으로 contour등을 구한 경우, 경계좌표들로 라벨링을 해야할 때 위 2개의 함수가 이용되는데 fillConvexPoly는 하나의 볼록 다각형을 가정하기 때문에 오목한 경우 제대로 그려지지 않는 경우가 많다.

가급적 fillPoly 또는 cv2.drawContours를 사용하는 것이 좋다.

참고 : cv2.drawContours

  • fillPolycv2.drawContours여러 개 의 꽉 찬 다각형을 처리하도록 구현됨.
  • fillConvexPoly하나의 꽉찬 볼록 다각형을 그리는 것으로 구현됨.

다음 예제를 보자.

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

src = np.zeros((300, 300, 3), dtype=np.uint8)

img = src.copy()
points = np.array(
    [[100, 50], [200, 100], 
     [200, 140], [50, 250], [130,100], 
     [270, 120], [220, 50],
     [100, 60]
     ])
points_a = np.array([[5,5], [10,10], [5,30]])

cv2.fillPoly(img, [points,points_a], color=(0, 255, 0))
plt.imshow(img)

img = src.copy()
cv2.fillConvexPoly(img, points, color=(0, 255, 0))
plt.figure()
plt.imshow(img)
  • cv2.fillPoly는 여러 개의 다각형을 그리므로 넘겨지는 것이 다각형들의 꼭지점을 가진 list들의 list임 : [points,points_a].
  • cv2.fillConvexPoly는 하나의 다각형에 대한 꼭지점들의 list가 넘겨진다.
  • 이 예제에서는 오목형 다각형 이라 cv2.fillConvexPoly는 제대로 그려지지 않음을 확인할 수 있음.

다음 코드는 cv2.fillConvexPoly의 사용법을 같이 보여준다.

src = np.zeros((3000, 3000, 3), dtype=np.uint8)
img = src.copy()

points = np.array([
   [2148,  687],[2120,  658],[2100,  650],[2062,  631],
   [2028,  596],[1994,  580],[1978,  580],[1938,  557],[1914,  519],
   [1877,  491],[1845,  485],[1825,  468],[1785,  466],[1747,  470],
   [1716,  481],[1687,  494],[1648,  535],[1626,  573],[1598,  604],
   [1597,  640],[1597,  687],[1574,  727],[1578,  782],[1582,  816],
   [1593,  849],[1597,  866],[1605,  895],[1598,  947],[1589,  978],
   [1566, 1043],[1546, 1067],[1518, 1080],[1506, 1104],[1482, 1148],
   [1481, 1227],[1484, 1251],[1498, 1271],[1498, 1289],[1514, 1310],
   [1544, 1331],[1554, 1350],[1546, 1433],[1536, 1481],[1521, 1504],
   [1518, 1548],[1510, 1579],[1508, 1606],[1512, 1647],[1493, 1698],
   [1493, 1739],[1504, 1752],[1525, 1784],[1548, 1836],[1557, 1853],
   [1574, 1872],[1567, 1889],[1581, 1917],[1563, 1946],[1566, 1971],
   [1577, 1978],[1577, 1998],[1585, 2014],[1602, 2032],[1621, 2112],
   [1631, 2147],[1642, 2184],[1647, 2213],[1663, 2271],[1688, 2305],
   [1720, 2339],[1763, 2366],[1821, 2394],[1846, 2399],[1888, 2376],
   [1930, 2185],[1946, 2136],[1951, 2117],[1974, 1993],[1805, 1662],
   [2010, 1562],[1910, 1425],[1993, 1387],[1933, 1255],[1951,  970],
   [2035, 1215],[2163, 1273],[2230, 1109],[2468, 1104],[2581, 1306],
   [2675, 1226],[2609, 1118],[2588, 1040],[2561, 1000],[2528,  976],
   [2484,  976],[2456,  984],[2384,  960],[2347,  834],[2306,  824],
   [2269,  794],[2242,  789],[2198,  744],[2176,  736],[2173,  731]])

print(points.shape)
img = cv2.drawContours(img, [points], -1, (255,255,255), thickness=-1)
plt.figure()
plt.imshow(img)

cv2.fillPoly(img, [points], color=(0, 255, 0))
plt.figure()
plt.imshow(img)

img = src.copy()
cv2.fillConvexPoly(img, points, color=(0, 255, 0))
plt.figure()
plt.imshow(img)

Adding Text to Images:

image에 글자를 추가하려면 다음과 같이 처리한다.

  • 우선 글자에 해당하는 문자열 데이터 생성.
  • bottom-left corner로 해당 text 가 놓일 위치를 지정.
  • Font type 을 지정. (Check cv2.putText() docs for supported fonts)
  • Font Scale 을 지정. (specifies the size of font)
  • color, thickness, lineType 등을 지정.

For better look, lineType = cv2.LINE_AA is recommended.

다음 예제를 참고하라.

font = cv2.FONT_HERSHEY_SIMPLEX
img = cv2.putText(img,
                  'OpenCV',       # text
                  (10,500),       # location
                  font,           # font Type
                  4,              # font size
                  (255,255,255),  # color
                  2,              # thickness
                  cv2.LINE_AA     # lineType
                  )

다음 조각코드로 결과를 확인할 수 있다.

from matplotlib import pyplot as plt

img2 = img[:,:,::-1] # faster version.
plt.imshow(img2,cmap='gray', interpolation='bicubic')
plt.xticks([]); plt.yticks([]) # to hide tick values on X and Y axis
plt.show()

관련 자료