OpenCV/OpenCV 강좌

OpenCV 강좌 4 - 도형을 그리자.

KALILIVE 2020. 10. 3. 23:43
반응형

이번 강좌에서는 OpenCV를 활용해 그림을 그려보도록 하자.

 

"도형을 왜 그려요?"

위와 같은 의문이 들 수도 있다.

백문불여일견[  ]

이라 하던가. 다음 그림을 보고 아! 하고 무릎을 탁 치며

"도형을 그려 어디다 사용하려고!!"라고 생각한 이전의 나를 반성해보도록 하자.

OpenCv를 이용해 얼굴인식 알고리즘을 통해 고양이의 얼굴을 추적했다.

이 그림을 보고 왜 우리가 도형을 그리는 법을 배워야 하는지 대부분이 알 것이다.

우리가 실제 코드를 작성하고 그 결과를 확인하기 위한 가장 좋은 방법은 영상위에 그 결과를 시각화하는 것이다.

단순히 x, y 좌표값을 주면 솔직히 코드가 제대로 작동하는지 잘 모른다.

단순한 도형 그리기 라도 기본기라고 생각하고 제대로 배워보자.

 

각종 도형 그리기 함수

함수 설명
cv2.line(img, pt1, pt2, color, thickness) img위에 pt1 부터 pt2를 지나는 직선을 그린다.
cv2.rectangle(img, pt1, pt2, color, thickness) img위에 pt1 부터 pt2를 지나는 사각형을 그린다.
cv2.clipLine(imgRect, pt1, pt2,) ->retval, pt1, pt2 pt1 부터 pt2를 까지의 직선이 imgRect 사각형에 의해 절단되는 좌표점을 계산하여 pt1과 pt2에 반환한다. 
직선이 사각 영역 밖에 있으면 retval에 False를 반환한다.
cv2.circle(img, center, radius, color, thickness) img위에 center을 중심으로 radius를 반지름으로 하는 원을
그린다.
cv2.ellipse(img, center, axes, angle, starAngle, endAngle, color, thickness) img위에 center을 중심점 axes를 주축 크기의 절반, angle을 수평축과 회전각도, 호(arc)의 시작과 끝의 각도는 startAngle, EndAngle인 타원을 그린다.
cv2.ellipse(img, box, color, thickness) img위에 회전된 사각형 box=(center,size,angle)에 내접하는 타원을 그린다.
cv2.ellipse2Poly(center, axes, angle, arcStart, arcEnd, delta)
-> pts
center 중심, axes 축의 크기, angle 각도, arc(호)의 시작과 끝의 각도 starAngle,endAngle인 타원 위의 좌표를 delta 각도 간격으로 계산한다.
cv2.polyline(img,pts,isClosed,color,thickness) 하나 이상의 다각형을 color색상으로 색칠한다.
pts는 다격형들의 np배열, isClosed=True이면 닫힌
다각형으로 그린다.
cv2.fillConvexPoly(img,points,color) points에 저장된 좌표로 이루어진 볼록다각형 or 일반 다각형을 color로 채운다. (fillPoly보다 빠르다.)
cv2.fillPoly(img,pts,color) 하나 이상의 다각형을 color 색상으로 칠한다. pts는 np 다각형 배열

※ pt는 point의 약자

※ thickness 가 -1이면 color색상으로 채운 사각형을 그린다.

 


직선 및 사각형 그리기

import cv2
import numpy as np

img = np.zeros(shape=(512, 512, 3), dtype=np.uint8) + 255  # White 배경 생성
# img=np.ones((512, 512, 3), np.uint8)*255 # 위 코드와 동일
# img = np.full(shape=(512,512,3),fill_value=255,dtype=np.uint8) 위코드와 동일
# img = np.zeros((512, 512, 3), np.uint8) <- Black 배경

pt1 = 100, 100
pt2 = 400, 400
cv2.rectangle(img, pt1, pt2, (0, 255, 0), 2)

cv2.line(img, (0, 0), (500, 0), (255, 0, 0), 5)
cv2.line(img, (0, 0), (0, 500), (0, 0, 255), 5)

cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()

opencv는 numpy를 이용하여 사용되기 때문에 직접 numpy모듈을 이용해 이미지를 편집할 수 있다. 그렇기 때문에

직접 배경을 생성하고 그 위에 cv2함수들을 이용하여 그림을 그리자 

위에서 dtype=np.int8를 볼 수가 있는데 이는 영상 화소가 부호 없는 8비트 정수를 설정하여준다.

2^8 = 256이므로 0~255까지의 RGB 값을 저장할 수 있기 때문이다.

<실행 결과>

이 예제에서 확인할 수 있듯이 color 함수는 BGR순서임을 확인할 수 있다.

[Python/OpenCV 관련 잡다한 지식] - OpenCV이 BGR 포맷을 쓰는 이유 글을 참고하면 왜 color함수가 BGR순으로 

작동하는지 알 수 있다.

 

 

OpenCV이 BGR 포맷을 쓰는 이유

이미지를 불러오거나 우리가 도형을 그리기 위해 color에 입력하는 순서는 흔히 접하는 RGB 순서가 아니라 BGR 순서이다. 왜 그런지 가끔씩 호기심이 들 때가 있어 이번 포스팅에서는 그 이유에 대�

kali-live.tistory.com

예를 들어 color 파라미터가 (255,0,0) 이면 blue

(0,255,0)는 Green (0,0,255) 면 Red색을 띤다.

네이버 RGB 색상표

네이버에 rgb 색상표라고 검색하면 자신이 원하는 색상을 쉽게 찾아볼 수 있으니 나중에 필요할 때 사용해보길 바란다.

 


직선-사각형 교차점

물론 수학적 공식에 의해 구할 수도 있다. 하지만 언제 그 많은 코드와 도형 사이에서 저런 함수를 만들시간이 있을까?

그래서 opencv는 교차점을 구할 수 있는 고마운 함수를 제공한다.

import cv2
import numpy as np

img = np.zeros(shape = (512, 512, 3), dtype = np.uint8) + 255

x1, x2 = 100, 400
y1, y2 = 100, 400
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255))

pt1 = 120, 50
pt2 = 300, 500
cv2.line(img, pt1, pt2, (255, 0, 0), 2)

imgRect = (x1, y1, x2-x1, y2-y1)
retval, rpt1, rpt2 = cv2.clipLine(imgRect, pt1, pt2)
if retval:
    cv2.circle(img, rpt1, radius= 5, color = (0, 255, 0),thickness=-1)
    cv2.circle(img, rpt2, radius=5, color=(0, 255, 0), thickness=-1)

cv2.imshow('img',img)
cv2.waitKey()
cv2.destroyAllWindows()

 

imgRect변수에 사각형의 왼쪽 꼭짓점의 좌표와 가로 세로의 길이를 저장하여

cv2.clipLine(사각형 정보, 직선1 좌표, 직선2 좌표) 형태로 함수의 파라미터로 사용하면 된다.

또한 retval로 직선이 사각형 내에 올 때 True, 접하지 않을 때 False로 반환되기 때문에

만약 접하지 않으면 False로 if문이 실행되지 않아 접하는 부분에 circle을 생성하는 것을 정할 수 있다.

실행 결과


원 그리기

import cv2
import numpy as np

img = np.zeros(shape=(512, 512, 3), dtype=np.uint8) + 255
cx = img.shape[0]//2  # x축화면크기의 절반을 저장
cy = img.shape[1]//2  # y축화면크기의 절반을 저장

for r in range(200, 0, -100):
    cv2.circle(img, (cx, cy), r, color=(255, 0, 0))
    
cv2.circle(img, (cx, cy), radius=50, color=(0, 0, 255), thickness=-1)

cv2.imshow("img", img)
cv2.waitKey()
cv2.destroyAllWindows()

원은 위 표에 나온 대로 설명이 간단하다 보니 따로 설명은 안 하겠다. (처음으로 글을 많이 써봐서 조금 피곤하다...ㅋ)

참고로 thickness 값이 -1 이면 동일한 색상으로 내부를 채운다.

실행 결과 (옆의 숫자는 반지름(radius)을 의미한다.)

원 옆의 숫자는 내가 따로 코드를 작성한 것이라 위 코드를 실행하면 여러분의 코드에서는 표시되지 않습니다.

혹시 원하는 분들이 계실 수도 있으니 아래 코드를 추가하겠습니다.

import cv2
import numpy as np

img = np.zeros(shape=(512, 512, 3), dtype=np.uint8) + 255
cx = img.shape[0]//2  # x축화면크기의 절반을 저장
cy = img.shape[1]//2  # y축화면크기의 절반을 저장

for r in range(200, 0, -100):
    cv2.circle(img, (cx, cy), r, color=(255, 0, 0))
    cv2.putText(img,str(r),(cx+r,cy),cv2.FONT_HERSHEY_SIMPLEX,1,(255,0,0),2)
cv2.circle(img, (cx, cy), radius=50, color=(0, 0, 255), thickness=-1)

cv2.imshow("img", img)
cv2.waitKey()
cv2.destroyAllWindows()

 여기서 cv2.putText 함수가 글자를 입력하는 함수인데 이번 강좌가 꽤 기니 다음 강좌에서 문자열 출력에 대해 

설명하도록 하겠습니다.


타원 그리기

import cv2
import numpy as np

img = np.full(shape=(512,512,3),fill_value=255,dtype=np.uint8)
ptCenter = img.shape[0]//2,img.shape[1]//2
size = 200,100

cv2.ellipse(img,ptCenter,size,0,0,360,(255,0,0))
cv2.ellipse(img,ptCenter,size,45,0,360,(0,0,255))

box = (ptCenter,size,0)
cv2.ellipse(img,box,(255,0,0),5)

box = (ptCenter,size,45)
cv2.ellipse(img,box,(0,0,255),5)

cv2.imshow('Ha....',img)
cv2.waitKey()
cv2.destroyAllWindows()

이것도 위 표에서 설명을 다해서 딱히 설명할 게 없네요~~

참고로 cv2.ellipse2Poly 함수는 다음 예제에서 다뤄 볼게요

실행 결과


다각형 그리기

import cv2
import numpy as np

img = np.full(shape=(512,512,3),fill_value=255,dtype=np.uint8)

pts1 = np.array([[100,100],[200,100],[200,200],[100,200]])
pts2 = np.array([[300,200],[400,100],[400,200]])

cv2.polylines(img,[pts1,pts2],isClosed=True,color=(255,0,0))

cv2.imshow("KK",img)
cv2.waitKey()
cv2.destroyAllWindows()
#--second---#

img = np.full(shape=(512,512,3),fill_value=255,dtype=np.uint8)

ptCenter = img.shape[0]//2, img.shape[1]//2
size = 200,100

cv2.ellipse(img,ptCenter,size,0,0,360,(255,0,0))
pts1 = cv2.ellipse2Poly(ptCenter,size,0,0,360,delta=45)

cv2.ellipse(img,ptCenter,size,45,0,360,(255,0,0))
pts2 = cv2.ellipse2Poly(ptCenter,size,0,0,360,delta=45)

cv2.polylines(img,[pts1,pts2],isClosed=True,color=(0,0,255))

cv2.imshow("HH",img)
cv2.waitKey()
cv2.destroyAllWindows()

이번엔 첫 번째 실행결과 창이 뜨고 아무 키나 누르면 다음 창이 뜨도록 코드를 작성하였다.(생각보다 많이 귀찮다.)

첫번째 실행 화면

pts1 = np.array([[100,100],[200,100],[200,200],[100,200]]) -> 사각형
pts2 = np.array([[300,200],[400,100],[400,200]]) -> 삼각형

polylines 함수를 이용해 위 배열에 저장된 위치로 도형을 그려낸 것이다.

isClosed=True로 설정되어있기 때문에 자동으로 시작점과 끝점을 이어준다.

 

2번째 실행 화면

pts1 = cv2.ellipse2Poly(ptCenter,size,0,0,360,delta=45)

pts2 = cv2.ellipse2Poly(ptCenter,size,0,0,360,delta=45)

을 이용하여 45도씩 좌표값을 저장하여 360/45 = 8

즉 8개의 꼭짓점을 pts1, pts2에 각각 저장하였다.

 


다각형 채우기

import cv2
import numpy as np

img = np.full(shape=(512,512,3),fill_value=255,dtype=np.uint8)

pts1 = np.array([[100,100],[200,100],[200,200],[100,200]])
pts2 = np.array([[300,200],[400,100],[400,200]])

cv2.fillConvexPoly(img,pts1,color=(255,0,0))
cv2.fillPoly(img,[pts2],color=(0,0,255))

cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()

나도 fillConvexPoly와 fillPoly의 차이점이 궁금해 구글링 해도 아무리 정보가 안 나온다.

혹시 이 글을 읽고 있는 누군가 알고 있다면 댓글로 알려주길 바란다.

실행 결과

 


이번 강좌는 길었던 것 같다.

어쨌든 도형은 앞으로 우리가 원하는 영상처리를 한 뒤 빈번하게 사용되기 때문에 익숙해지는 게 좋을 것 같다.

다음 강좌는 위에서 언급한 대로 짧게 문자열 출력에 대해서 강의하도록 하겠다.

여기가지 읽느라 수고하셨습니다.

반응형