본문 바로가기

프로그래밍 강좌/C++ - OpenCV

[OpenCV][C++] 영상에 문자열 출력하기 cv::putText(), cv::getTextSize() 총정리 글씨 쓰기 글자 표현 image fontface fontscale

영상에 문자열을 이용하여 필요한 정보를 표시해야 하는 경우가 있습니다.

OpenCV에서는 영상에 문자열을 출력할 수 있는 cv::putText()를 제공합니다.

cv::putText는 다음과 같은 인자를 받을 수 있습니다.

src
문자열을 출력할 영상
text
출력할 문자열, const String&
pos
문자열 출력할 위치의 좌하단 좌표, cv::Point
fontFace
폰트 종류, cv::HersheyFonts에서 선택 가능
fontScale
폰트 크기 확대 축소 비율
color
문자열 색상
thickness
문자열 선 두께
lineType
선타입, LINE_4, LINE_8, LINE_AA
bottomleftOrigin
true: 영상 좌하단을 원점으로, false: 영상 좌상단이 원점으로

 

여기에서 cv::HERSHEY 에 어떠한 종류의 글씨체가 있는지 살펴보면,

 
FONT_HERSHEY_SIMPLEX
설명 보다는
직접 영상으로 확인하는 것이 좋을 것 같아요.
FONT_HERSHEY_PLAIN
FONT_HERSHEY_COMPLEX
FONT_HERSHEY_TIRPLEX
FONT_HERSHEY_COMPLEX_SMALL
FONT_HERSHEY_SCRIPT_SIMPLEX
FONT_HERSHEY_SCRIPT_COMPLEX
FONT_ITALIC

 

소스는 아래와 같습니다.

#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
    cv::Mat src(500, 800, CV_8UC3, cv::Scalar(255, 255, 255));
    cv::putText(src, "FONT_HERSHEY_SIMPLEX", cv::Point(20, 50), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255));
    cv::putText(src, "FONT_HERSHEY_PLAIN", cv::Point(20, 100), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 255, 0));
    cv::putText(src, "FONT_HERSHEY_DUPLEX", cv::Point(20, 150), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(255, 0, 0));
    cv::putText(src, "FONT_HERSHEY_COMPLEX", cv::Point(20, 200), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 0, 255));
    cv::putText(src, "FONT_HERSHEY_TRIPLEX", cv::Point(20, 250), cv::FONT_HERSHEY_TRIPLEX, 1, cv::Scalar(0, 255, 255));
    cv::putText(src, "FONT_HERSHEY_COMPLEX_SMALL", cv::Point(20, 300), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(255, 255, 0));
    cv::putText(src, "FONT_HERSHEY_SCRIPT_SIMPLEX", cv::Point(20, 350), cv::FONT_HERSHEY_SCRIPT_SIMPLEX, 1, cv::Scalar(0, 0, 255));
    cv::putText(src, "FONT_HERSHEY_SCRIPT_COMPLEX", cv::Point(20, 400), cv::FONT_HERSHEY_SCRIPT_COMPLEX, 1, cv::Scalar(100, 100, 100));
    cv::putText(src, "FONT_HERSHEY_TRIPLEX | FONT_ITALIC", cv::Point(20, 450), cv::FONT_HERSHEY_TRIPLEX | cv::FONT_ITALIC, 1, cv::Scalar(0, 0, 0));

    return 0;
}
 

결과 영상을 보면, 이런 글씨체들이 있습니다.

여러 문자열을 여러 줄로 출력할 경우 위의 예시처럼

pos 에 숫자를 넣어줄 수 없습니다. 이럴 때 어떻게 하느냐?

문자열 사각형의 크기를 알 수 있는 getTextSize()라는 함수를 제공하고 있습니다.

text
출력할 문자열
fontFace
폰트 종류
fontScale
폰트 크기 확대 축소 비율
thickness
문자열 선 두께
baseLine
가장 하단의 텍스트 위치 기준으로 기준선의 y좌표, 필요 없으면 0 입력
반환값
cv::Size 로 사각형의 크기 반환

이걸 응용하면 영상의 중앙에 글씨를 출력할 수 있습니다.

150 x 800 짜리 흰 배경 영상을 하나 만들고 그 위에 글씨를 적은 것 입니다.

#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
    int width = 800;
    int height = 150;
    cv::Mat src(height, width, CV_8UC3, cv::Scalar(255, 255, 255));
    std::string str = "JK Garden, Landscape Tree";
    int fontFace = cv::FONT_HERSHEY_SIMPLEX;
    double fontScale = 1.3;
    int thickness = 2;

    cv::Size szText = cv::getTextSize(str, fontFace, fontScale, thickness, 0);

    cv::Point pos((width - szText.width) / 2, (height - szText.height) / 2);
    cv::putText(src, str, pos, fontFace, fontScale, cv::Scalar(0, 0, 255), thickness);
    
    return 0;
}
 

결과 영상은 아래와 같습니다.

여기서 꿀팁 대 방출~!!

font color를 매번 cv::Scalar(0,0,255) 이런 식으로 설정한다면 복잡하게 됩니다.

따라서 아래와 같이 미리 color 값을 정의 해 놓는다든지,

아니면 각 값을 0~255 값을 가지는 random 변수를 생성하면 편리해집니다.

cv::Scalar color[] = { {0, 0, 255},		    // red
                       {0, 255, 0},		    // green
                       {255, 0, 0},		    // blue
                       {255, 255, 255},     // white
                       {255, 0, 255},		// magenta
                       {255, 255, 0},		// cyan							
                       {0, 165, 255},		// orange
                       {0, 255, 255},		// yellow							
                       {128, 0, 255},
                       {255, 128, 64} };
 

만약 영상의 color와 font color가 유사하다면? 문자열의 시인성이 떨어집니다.

그럴 경우 글상자에 background color를 넣으면 해결 될꺼에요.

cv::rectangle()을 사용하여 글자 크기에 맞도록 사각박스를 그린 후 글자를 출력하면

시인성 문제는 없어질 꺼에요. 이 것들을 조합하여 아래와 같은 함수 하나를 만들어봤어요.

이 함수를 이용하여 간단히 영상에 문자열을 출력해 보면, 전체 소스는 아래와 같고,

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>

cv::Scalar color[] = { {0, 0, 255},		    // red
                       {0, 255, 0},		    // green
                       {255, 0, 0},		    // blue
                       {255, 255, 255},     // white 
                       {255, 0, 255},		// magenta
                       {0, 165, 255},		// orange
                       {0, 255, 255},		// yellow                       
                       {255, 255, 0},		// cyan
                       {128, 0, 255},
                       {255, 128, 64} };

void putTextOnImage(cv::Mat& src, const std::string& label_in, const cv::Point& pt_in, const cv::Scalar& textColor, 
    const cv::Scalar& bgColor, const int& textLineNum = 0)
{
    int fontFace = cv::FONT_HERSHEY_SIMPLEX;
    double fontScale = 0.5;
    int fontThickness = 1;
    int baseline = 0;
    cv::Point text_pos = pt_in;
    cv::Size text = cv::getTextSize(label_in, fontFace, fontScale, fontThickness, &baseline);
    if (textLineNum > 0)
        text_pos += cv::Point(0, text.height * textLineNum + 7 * textLineNum);

    if (bgColor.val[0] >= 0 && bgColor.val[1] >= 0 && bgColor.val[2] >= 0) {        
        cv::rectangle(src, cv::Point(0 + text_pos.x, text_pos.y - text.height - 1), 
            cv::Point(text.width + text_pos.x, text_pos.y + baseline + 1), bgColor, cv::FILLED);
    }

    cv::putText(src, label_in, text_pos, fontFace, fontScale, textColor, fontThickness, 8);
}

int main()
{
    cv::Mat lena_color = cv::imread("./lena_color.bmp", cv::IMREAD_UNCHANGED);
    std::vector<std::string> vecStr{ "1. Lena Color Image", "2. JK Garden", "3. Landscape Tree" };

    for (int i = 0; i < static_cast<int>(vecStr.size()); ++i) {
        putTextOnImage(lena_color, vecStr[i], cv::Point(10, 10), color[i], color[i + 3], i);
    }
    
    return 0;
}
 

결과는 아래와 같습니다.

여기서 잠깐~~

한글이 출력되는지 확인해봐야겠죠?

위 소스에 한글을 하나 넣어봤습니다. 그리고 출력해봤어요.

출력해보니 한글이 '????' 로 깨지는 것을 볼 수 있습니다.

한글은 유니코드 이고, OpenCV는 유니코드를 지원하지 않기 때문이죠.

그럼 어떻게 해결할 수 있을까요?

다음 번에 시간내어 설명 드릴께요~!