오늘은 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번째 인수: 글자의 두께를 선택합니다. 
실행결과를 보시죠.

이번 예제는 조금 길었네요. 그리기 함수들을 이용해서 간단한 이미지를 하나 그려보겠습니다. 재미를 위해서..ㅎㅎ 아래 그림 중에서 왼쪽 아래에 있는 집을 한번 따라 그려보겠습니다. 그림의 출처는 http://cafe.naver.com/logo12/1692719입니다.


사실 노가다지만, 그래도 함수들 확실히 익힐겸 해봤습니다. 코드는 아래와 같습니다.

#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);

}


구현된 이미지는 밑에 있습니다. 사실 그냥 노가다였지만 나름 성취감이 있네요. ㅋㅋ 창문에 햇살 비친 것까지 하긴 너무 귀찮아서 패스했습니다. 






+ Recent posts

티스토리 툴바