3

I know how to pass putText position and font size:

void TextBox( cv::Mat & img, const std::string & text, const cv::Rect & bbox )
{
    cv::Point position;
    double size;
    int face = CV_FONT_HERSHEY_PLAIN;
    Trick( /*in:*/ text, bbox, face /*out:*/ position, size );
    cv::putText( img, text, position, face, size, cv::Scalar( 100, 255, 100 ) );
}

How to do the trick? I would like to scale text to fit its bounding box. (Font face might be unused input to that.)

renonsz
  • 571
  • 1
  • 4
  • 17

1 Answers1

6

It's a bit tricky, but you can play with cv::getTextSize(). There is a sample code in the description of the function but it only renders some text, the tight box (surrounding the text), and the baseline of it.

If you'd like to render the text in an arbitrary ROI of an image then you first need to render it into another image (which fits to the text size), resize it to the ROI desired and then put it over the image, such as below:

void PutText(cv::Mat& img, const std::string& text, const cv::Rect& roi, const cv::Scalar& color, int fontFace, double fontScale, int thickness = 1, int lineType = 8)
{
    CV_Assert(!img.empty() && (img.type() == CV_8UC3 || img.type() == CV_8UC1));
    CV_Assert(roi.area() > 0);
    CV_Assert(!text.empty());

    int baseline = 0;

    // Calculates the width and height of a text string
    cv::Size textSize = cv::getTextSize(text, fontFace, fontScale, thickness, &baseline);

    // Y-coordinate of the baseline relative to the bottom-most text point
    baseline += thickness;

    // Render the text over here (fits to the text size)
    cv::Mat textImg(textSize.height + baseline, textSize.width, img.type());

    if (color == cv::Scalar::all(0)) textImg = cv::Scalar::all(255);
    else textImg = cv::Scalar::all(0);

    // Estimating the resolution of bounding image
    cv::Point textOrg((textImg.cols - textSize.width) / 2, (textImg.rows + textSize.height - baseline) / 2);

    // TR and BL points of the bounding box
    cv::Point tr(textOrg.x, textOrg.y + baseline);
    cv::Point bl(textOrg.x + textSize.width, textOrg.y - textSize.height);

    cv::putText(textImg, text, textOrg, fontFace, fontScale, color, thickness);

    // Resizing according to the ROI
    cv::resize(textImg, textImg, roi.size());

    cv::Mat textImgMask = textImg;
    if (textImgMask.type() == CV_8UC3)
        cv::cvtColor(textImgMask, textImgMask, cv::COLOR_BGR2GRAY);

    // Creating the mask
    cv::equalizeHist(textImgMask, textImgMask);

    if (color == cv::Scalar::all(0)) cv::threshold(textImgMask, textImgMask, 1, 255, cv::THRESH_BINARY_INV);
    else cv::threshold(textImgMask, textImgMask, 254, 255, cv::THRESH_BINARY);

    // Put into the original image
    cv::Mat destRoi = img(roi);
    textImg.copyTo(destRoi, textImgMask);
}

And call it like:

cv::Mat image = cv::imread("C:/opencv_logo.png");
cv::Rect roi(5, 5, image.cols - 5, image.rows - 5);
cv::Scalar color(255, 0, 0);
int fontFace = cv::FONT_HERSHEY_SCRIPT_SIMPLEX;
double fontScale = 2.5;
int thickness = 2;

PutText(image, "OpenCV", roi, color, fontFace, fontScale, thickness);

As a result of the PutText() function you can render any text over an arbitrary ROI of the image, such as:

enter image description here enter image description here

Hope it helps and works :)


Update #1:

And keep in mind that text rendering (with or without this trick) in OpenCV is very expensive and can affect to the runtime of your applications. Other libraries may outperform the OpenCVs rendering system.

Kornel
  • 5,264
  • 2
  • 21
  • 28
  • can you recommend any text rendering libraries? – Micka Sep 25 '15 at 09:12
  • I have never used, but some of my team mate praise [Cairo library](http://cairographics.org/manual/cairo-text.html), or OpenGL ofcourse. – Kornel Sep 25 '15 at 09:17