[Learn opencv by examples] 3. 기본적인 드로잉 예제들

코딩/C, C++|2017. 5. 16. 00:51

728x90

오늘은 http://opencvexamples.blogspot.com/p/learning-opencv-functions-step-by-step.html에 있는 세번째 예제를 따라해보도록 하겠습니다. 

 

3. Basic drawing examples

1) 선 그리기

소스코드부터 보시죠. 

 

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

 

using namespace cv;

 

int main()

{

// Create black empty images

Mat image = Mat::zeros(400, 400, CV_8UC3); // 세로, 가로 각각 400, 400 사이즈의 검정 이미지를 만든다. 

 

// Draw a line 

line(image, Point(15, 20), Point(300, 350), Scalar(0, 255, 0), 10, 8); 

imshow("Image", image);

imwrite("result.bmp", image);

 

waitKey(0);

return(0);

}

 

링크의 예제에서는 #include <opencv2/imgproc/imgproc.hpp>가 없는데, 이걸 포함해줘야 line 함수를 사용할 수 있습니다. line 함수에 대해서 아는 한 설명을 해보겠습니다. 

 

line(image, Point(15, 20), Point(300, 350), Scalar(0, 255, 0), 10, 8); 

  • 1번째 인수: 도화지가 되어줄 이미지를 넣어줍니다. 
  • 2번째, 3번째 인수: 어디서부터 어디까지 선을 그릴지 픽셀 위치들을 뜻합니다. 즉 (15, 20)부터 (300, 350)을 잇는 선을 그리겠다는 것입니다. 

  • 4번째 인수: 선의 색을 결정지어줍니다. Scalar(0, 255, 0)은 녹색 선을 그리겠다는 것입니다. BGR순서로 되어 있습니다. 만약 Scalar(0, 0, 255)라고 세팅-하면 빨간색 선이 될 것입니다. 
  • 5번째 인수: 선의 굵기를 선택합니다. 클수록 굵어집니다.
  • 6번째 인수: 라인의 타입을 결정해줍니다. (이것의 역할은 잘 몰라서, 일단 그냥 default값으로 놔두겠습니다.)

 

실행결과 아래와 같은 이미지가 전시되고, 저장됩니다.

 

 

2) 원 그리기

이번에는 원을 그려보도록 하겠습니다. 소스코드는 아래와 같습니다. 

 

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

 

using namespace cv;

 

int main()

{

// Create black empty images

Mat image = Mat::zeros(400, 400, CV_8UC3); // 세로, 가로 각각 400, 400 사이즈의 검정 이미지를 만든다. 

 

// Draw a circle 

circle(image, Point(200, 200), 32.0, Scalar(0, 0, 255), 5, 8); 

  circle(image, Point(100, 300), 40.0, Scalar(255, 0, 255), 10, 8);

imshow("Image", image);

imwrite("result.bmp", image);

 

waitKey(0);

return(0);

}

 

circle 함수 

  • 1번째 인수: 역시 도화지가 되어줄 이미지입니다. 여기서는 400 x 400의 검정 이미지이겠지요? 
  • 2번째 인수: 원의 중심의 픽셀 위치입니다. 
  • 3번째 인수: 원의 반지름입니다. 
  • 4번째 인수: 원의 색깔로 역시 BGR순서로 되어 있습니다. 첫번째 원은 빨간 색이 되게 Scalar(0, 0, 255)로 설정했습니다. 
  • 5번째 인수: 원의 두께를 결정해주는 것입니다. 클수록 두꺼워집니다. 
  • 6번째 인수: 라인의 타입을 결정해주는 것입니다. 

 

line 함수의 인수들의 진행패턴과 크게 다르지 않습니다. 아래 그림은 결과로 그려진 두 개의 원을 보여줍니다. 

 

 

3) 타원 그리기

이번에는 타원을 그리겠습니다. 

 

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

 

using namespace cv;

 

int main()

{

// Create black empty images

Mat image = Mat::zeros(400, 400, CV_8UC3); // 세로, 가로 각각 400, 400 사이즈의 검정 이미지를 만든다. 

 

// Draw a ellipse

        ellipse(image, Point(200, 200), Size(100.0, 160.0), 45, 0, 360, Scalar(255, 0, 0), 1, 8);

ellipse(image, Point(200, 200), Size(100.0, 160.0), 0, 0, 360, Scalar(255, 0, 0), 5, 8);

ellipse(image, Point(200, 200), Size(100.0, 160.0), 135, 0, 360, Scalar(255, 0, 0), 10, 8);

imshow("Image", image);

imwrite("result.bmp", image);

 

waitKey(0);

return(0);

}

 

ellipse 함수가 타원을 그릴 때 사용하는 함수입니다. 

  • 1번째 인수: 도화지가 될 이미지입니다. 
  • 2번째 인수: 타원의 중심위치입니다. 
  • 3번째 인수: 타원축들의 길이를 의미합니다. 즉, 세번째 인수가 Size(100,0, 160,0)라면 x축으로는 100만큼, y축으로는 160만큼 긴 모양의 타원을 만들겠다는 것입니다. 
  • 4번째 인수: 타원이 회전된 각도입니다. 첫번째 타원의 경우 45도만큼 오른쪽으로 회전시키겠다는 것입니다. 
  • 5번째, 6번째 인수: 타원이 시작하는 각도와 끝나는 각도를 결정지어주는 것인데 완전한 타원을 그리고 싶다면 0, 360으로 설정해주면 됩니다. 
  • 7번째 인수: 색을 결정해주는 것입니다. 세 타원 모두 파란색으로 설정해줬습니다. 
  • 8번째 인수: 타원의 굵기를 결정해주는 것입니다. 크면 클수록 굵어집니다. 
  • 9번째 인수: 역시 선의 타입을 결정해주는 것입니다. 

 

아래 그림은 결과적으로 그려진 3개의 타원을 보여줍니다. 뭔가 멋있네요. 

 

 

4) 직사각형 그리기

직사각형 그리는 코드입니다.

 

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

 

using namespace cv;

 

int main()

{

// Create black empty images

Mat image = Mat::zeros(400, 400, CV_8UC3); // 세로, 가로 각각 400, 400 사이즈의 검정 이미지를 만든다. 

 

// Draw a rectangle

rectangle(image, Point(15, 20), Point(70, 50), Scalar(0, 55, 255), +1, 4); 

rectangle(image, Point(200, 300), Point(300, 350), Scalar(100, 100, 0), -1, 8); 

imshow("Image", image);

imwrite("result.bmp", image);

 

waitKey(0);

return(0);

}

 

직사각형을 그려주는 rectangle 함수를 살펴봅시다. 

  • 1번째 인수: 도화지가 되어줄 이미지입니다.
  • 2번째 인수: 직사각형의 왼쪽 상단 모서리 픽셀 위치입니다.
  • 3번째 인수: 직사각형의 오른쪽 하단 모서리 픽셀 위치입니다.
  • 4번째 인수: 색을 결정해줍니다.
  • 5번째 인수: 양수면 빈 직사각형, 음수면 채워진 직사각형을 만듭니다.
  • 6번째 인수: 라인의 타입을 결정해줍니다. 

 

구현된 결과이미지입니다. 하나의 빈 직사각형과 하나의 채워진 직사각형을 보실 수 있습니다.

 

 

 

5) 다각형 그리기

이번에는 채워져있는 다각형을 그려보겠습니다. 

 

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

 

using namespace cv;

 

int main()

{

// Create black empty images

Mat image = Mat::zeros(400, 400, CV_8UC3); // 세로, 가로 각각 400, 400 사이즈의 검정 이미지를 만든다. 

 

int w = 400;

/** Create some points */

Point rook_points[1][20]; // 여기서 rook는 체스의 말의 이름입니다.

rook_points[0][0] = Point(w / 4.0, 7 * w / 8.0);

rook_points[0][1] = Point(3 * w / 4.0, 7 * w / 8.0);

rook_points[0][2] = Point(3 * w / 4.0, 13 * w / 16.0);

rook_points[0][3] = Point(11 * w / 16.0, 13 * w / 16.0);

rook_points[0][4] = Point(19 * w / 32.0, 3 * w / 8.0);

rook_points[0][5] = Point(3 * w / 4.0, 3 * w / 8.0);

rook_points[0][6] = Point(3 * w / 4.0, w / 8.0);

rook_points[0][7] = Point(26 * w / 40.0, w / 8.0);

rook_points[0][8] = Point(26 * w / 40.0, w / 4.0);

rook_points[0][9] = Point(22 * w / 40.0, w / 4.0);

rook_points[0][10] = Point(22 * w / 40.0, w / 8.0);

rook_points[0][11] = Point(18 * w / 40.0, w / 8.0);

rook_points[0][12] = Point(18 * w / 40.0, w / 4.0);

rook_points[0][13] = Point(14 * w / 40.0, w / 4.0);

rook_points[0][14] = Point(14 * w / 40.0, w / 8.0);

rook_points[0][15] = Point(w / 4.0, w / 8.0);

rook_points[0][16] = Point(w / 4.0, 3 * w / 8.0);

rook_points[0][17] = Point(13 * w / 32.0, 3 * w / 8.0);

rook_points[0][18] = Point(5 * w / 16.0, 13 * w / 16.0);

rook_points[0][19] = Point(w / 4.0, 13 * w / 16.0);

 

const Point* ppt[1] = { rook_points[0] };

int npt[] = { 20 };

 

fillPoly(image, ppt, npt, 1, Scalar(255, 255, 255), 8);

imshow("Image", image);

imwrite("result.bmp", image);

 

waitKey(0);

return(0);

}

 

fillPoly함수의 인수들을 살펴봅시다. 

  • 1번째 인수: 도화지가 될 이미지입니다.
  • 2번째 인수: 꼭지점들을 담고 있는 배열입니다. (Array of polygons where each polygon is represented as an array of points)
  • 3번째 인수: 꼭지점들의 갯수를 담고 있는 배열입니다. (Array of polygon vertex counters)
  • 4번째 인수: 색으로 채워진 지역을 감싸는 가장자리의 갯수를 설정해줍니다. (Number of contours that bind the filled region)
  • 5번째 인수: 다각형의 색을 설정합니다.
  • 6번째 인수: 라인의 타입을 결정해줍니다.

 

아래 그림과 같이 체스의 rook가 그려집니다.

 

 

이번에는 fillPoly 함수를 사용해서 다각형 중의 하나인 삼각형을 하나 그려보겠습니다. 코드는 아래와 같습니다.

 

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

 

using namespace cv;

 

int main()

{

// Create black empty images

Mat image = Mat::zeros(400, 400, CV_8UC3); // 세로, 가로 각각 400, 400 사이즈의 검정 이미지를 만든다. 

 

int w = 400;

/** Create some points */

Point rook_points[1][3]; // 여기서 rook는 체스의 말의 이름입니다.

rook_points[0][0] = Point(w / 2.0, w / 4.0); // Point(가로 픽셀 위치, 세로 픽셀 위치)

rook_points[0][1] = Point(w / 4.0, 3 * w / 4.0);

rook_points[0][2] = Point(3 * w / 4.0, 3 * w / 4.0);

 

const Point* ppt[1] = { rook_points[0] };

int npt[] = { 3 };

 

fillPoly(image, ppt, npt, 1, Scalar(255, 255, 255), 8);

imshow("Image", image);

imwrite("result.bmp", image);

 

waitKey(0);

return(0);

}

 

삼각형을 그리므로 세 점만 있으면 됩니다. 그려진 그림은 아래와 같습니다.

 

 

 

6) 이미지에 텍스트 넣기

이제 마지막으로 이미지에 텍스트 넣는 것을 따라해보겠습니다. 

 

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
 
using namespace cv;
 
int main()
{
// Create black empty images
Mat image = Mat::zeros(400, 400, CV_8UC3); // 세로, 가로 각각 400, 400 사이즈의 검정 이미지를 만든다. 
 
putText(image, "God is good!", Point(50, 100), FONT_HERSHEY_PLAIN, 2, Scalar(0, 200, 200), 3); //이미지에 텍스트 넣기
imshow("Image", image);
imwrite("result.bmp", image);
 
waitKey(0);
return(0);
}
 
putText 함수를 살펴봅시다.
  • 1번째 인수: 도화지가 될 이미지입니다.
  • 2번째 인수: 텍스트 내용을 적어줍니다.
  • 3번째 인수: 이미지에서 텍스트가 배치될 위치를 결정해줍니다. 텍스트 스트링의 왼쪽 아래 위치가 됩니다. 
  • 4번째 인수: 폰트를 선택합니다. FONT_HERSHEY_SIMPLEX, FONT_HERSHEY_PLAIN,FONT_HERSHEY_DUPLEX, FONT_HERSHEY_COMPLEX, FONT_HERSHEY_TRIPLEX,FONT_HERSHEY_COMPLEX_SMALL, FONT_HERSHEY_SCRIPT_SIMPLEX, FONT_HERSHEY_SCRIPT_COMPLEX
  • 5번째 인수: 글자의 크기를 결정합니다. 
  • 6번째 인수: 글자의 색을 선택합니다.
  • 7번째 인수: 글자의 두께를 선택합니다. 
실행결과를 보시죠.

 

이번 예제는 조금 길었네요. 그리기 함수들을 이용해서 집을 하나 그려보겠습니다. 코드는 아래와 같습니다.
 

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

#include <opencv2/imgproc/imgproc.hpp>

 

using namespace cv;

 

int main()

{

// Create black empty images

Mat image = Mat::zeros(400, 400, CV_8UC3); // 세로, 가로 각각 400, 400 사이즈의 검은색 이미지를 만든다. 

 

rectangle(image, Point(0, 0), Point(399, 399), Scalar(255, 255, 255), -1, 8); // 흰색 도화지로 바꿔준다. 

 

// 큰 지붕, 다각형 함수 활용

int w = 400;

Point roof1_points[1][4];

roof1_points[0][0] = Point(140, 40);   

roof1_points[0][1] = Point(260, 40);

roof1_points[0][2] = Point(360, 160);

roof1_points[0][3] = Point(40, 160);

 

const Point* ppt1[1] = { roof1_points[0] };

int npt1[] = { 4 };

fillPoly(image, ppt1, npt1, 1, Scalar(30, 30, 30), 8);

 

// 건물의 몸통. 직사각형 함수 활용

rectangle(image, Point(70, 161), Point(330, 340), Scalar(150, 150, 150), -1, 8);

rectangle(image, Point(70, 341), Point(330, 360), Scalar(30, 30, 30), -1, 8);

 

// 작은 지붕

Point roof2_points[1][4];

roof2_points[0][0] = Point(160, 100);

roof2_points[0][1] = Point(240, 100);

roof2_points[0][2] = Point(300, 172);

roof2_points[0][3] = Point(100, 172);

 

const Point* ppt2[1] = { roof2_points[0] };

int npt2[] = { 4 };

fillPoly(image, ppt2, npt2, 1, Scalar(60, 60, 60), 8);

 

// 중간에 회색 두 줄

rectangle(image, Point(150, 173), Point(160, 340), Scalar(100, 100, 100), -1, 8);

rectangle(image, Point(240, 173), Point(250, 340), Scalar(100, 100, 100), -1, 8);

 

//굴뚝

Point chimney_points[1][4];

chimney_points[0][0] = Point(70, 60);

chimney_points[0][1] = Point(92, 60);

chimney_points[0][2] = Point(92, 97);

chimney_points[0][3] = Point(70, 124);

 

const Point* ppt3[1] = { chimney_points[0] };

int npt3[] = { 4 };

fillPoly(image, ppt3, npt3, 1, Scalar(60, 60, 60), 8);

 

//굴뚝 덮개 

rectangle(image, Point(67, 48), Point(95, 59), Scalar(30, 30, 30), -1, 8);

 

//문

rectangle(image, Point(172, 252), Point(228, 340), Scalar(70, 70, 90), -1, 8);

 

//문에 있는 창문 두 개

rectangle(image, Point(180, 264), Point(190, 288), Scalar(255, 200, 150), -1, 8);

rectangle(image, Point(210, 264), Point(220, 288), Scalar(255, 200, 150), -1, 8);

 

//문에 있는 점 두 개, 원 그리기 활용

circle(image, Point(190, 300), 2, Scalar(30, 30, 30), 2, 8);

circle(image, Point(210, 300), 2, Scalar(30, 30, 30), 2, 8);

 

//중간 원형 창문의 테두리

circle(image, Point(200, 210), 18, Scalar(55, 55, 55), 10, 8);

 

//중간 원형 창문

circle(image, Point(200, 210), 8, Scalar(255, 200, 150), 18, 8);

 

//중간 원형 창문 십자

rectangle(image, Point(199, 190), Point(201, 230), Scalar(55, 55, 55), -1, 8);

rectangle(image, Point(180, 209), Point(220, 211), Scalar(55, 55, 55), -1, 8);

 

//왼쪽 상단 창문 회색 틀

rectangle(image, Point(85, 240), Point(135, 247), Scalar(55, 55, 55), -1, 8);

 

//왼쪽 상단 창문 검은 틀

Point win1_points[1][4];

win1_points[0][0] = Point(88, 248);

win1_points[0][1] = Point(132, 248);

win1_points[0][2] = Point(127, 255);

win1_points[0][3] = Point(93, 255);

 

const Point* win1_ppt[1] = { win1_points[0] };

int win1_npt[] = { 4 };

fillPoly(image, win1_ppt, win1_npt, 1, Scalar(30, 30, 30), 8);

 

//왼쪽 상단 창문

rectangle(image, Point(88, 190), Point(132, 239), Scalar(255, 200, 150), -1, 8);

 

//왼쪽 상단 창문 십자

rectangle(image, Point(88, 215), Point(132, 217), Scalar(55, 55, 55), -1, 8);

rectangle(image, Point(109, 190), Point(111, 239), Scalar(55, 55, 55), -1, 8);

 

//오른쪽 상단 창문 회색 틀

rectangle(image, Point(265, 240), Point(315, 247), Scalar(55, 55, 55), -1, 8);

 

//오른쪽 상단 창문 검은 틀

Point win2_points[1][4];

win2_points[0][0] = Point(268, 248);

win2_points[0][1] = Point(312, 248);

win2_points[0][2] = Point(307, 255);

win2_points[0][3] = Point(273, 255);

 

const Point* win2_ppt[1] = { win2_points[0] };

int win2_npt[] = { 4 };

fillPoly(image, win2_ppt, win2_npt, 1, Scalar(30, 30, 30), 8);

 

//오른쪽 상단 창문

rectangle(image, Point(268, 190), Point(312, 239), Scalar(255, 200, 150), -1, 8);

 

//오른쪽 상단 창문 십자

rectangle(image, Point(268, 215), Point(312, 217), Scalar(55, 55, 55), -1, 8);

rectangle(image, Point(289, 190), Point(291, 239), Scalar(55, 55, 55), -1, 8);

 

//왼쪽 하단 창문 회색 틀

rectangle(image, Point(85, 315), Point(135, 322), Scalar(55, 55, 55), -1, 8);

 

//왼쪽 하단 창문 검은 틀

Point win3_points[1][4];

win3_points[0][0] = Point(88, 323);

win3_points[0][1] = Point(132, 323);

win3_points[0][2] = Point(127, 330);

win3_points[0][3] = Point(93, 330);

 

const Point* win3_ppt[1] = { win3_points[0] };

int win3_npt[] = { 4 };

fillPoly(image, win3_ppt, win3_npt, 1, Scalar(30, 30, 30), 8);

 

//왼쪽 하단 창문

rectangle(image, Point(88, 265), Point(132, 314), Scalar(255, 200, 150), -1, 8);

 

//왼쪽 하단 창문 십자

rectangle(image, Point(88, 290), Point(132, 292), Scalar(55, 55, 55), -1, 8);

rectangle(image, Point(109, 265), Point(111, 314), Scalar(55, 55, 55), -1, 8);

 

//오른쪽 하단 창문 회색 틀

rectangle(image, Point(265, 315), Point(315, 322), Scalar(55, 55, 55), -1, 8);

 

//오른쪽 하단 창문 검은 틀

Point win4_points[1][4];

win4_points[0][0] = Point(268, 323);

win4_points[0][1] = Point(312, 323);

win4_points[0][2] = Point(307, 330);

win4_points[0][3] = Point(273, 330);

 

const Point* win4_ppt[1] = { win4_points[0] };

int win4_npt[] = { 4 };

fillPoly(image, win4_ppt, win4_npt, 1, Scalar(30, 30, 30), 8);

 

//오른쪽 하단 창문

rectangle(image, Point(268, 265), Point(312, 314), Scalar(255, 200, 150), -1, 8);

 

//오른쪽 하단 창문 십자

rectangle(image, Point(268, 290), Point(312, 292), Scalar(55, 55, 55), -1, 8);

rectangle(image, Point(289, 265), Point(291, 314), Scalar(55, 55, 55), -1, 8);

 

 

imshow("Image", image);

imwrite("result.bmp", image);

 

 

waitKey(0);

return(0);

}

 

구현된 이미지는 밑에 있습니다. 노가다였지만 나름 성취감이 있네요. ㅋㅋ