Using cv::putText is cumbersome and placing your text at the correct position with the correct size is hard. Here is a wrapper function dealing with all of this for you. The text is fitted inside the given image, even multiple lines are possible and everything is nicely centered.
void ioxp::putText(cv::Mat imgROI, const std::string &text, const int fontFace = cv::FONT_HERSHEY_PLAIN,
const cv::Scalar color = cv::Scalar::all(255), const int thickness = 1, const int lineType = cv::LINE_8)
{
/*
* Split the given text into its lines
*/
std::vector<std::string> textLines;
std::istringstream f(text);
std::string s;
while (std::getline(f, s, '\n')) {
textLines.push_back(s);
}
/*
* Calculate the line sizes and overall bounding box
*/
std::vector<cv::Size> textLineSizes;
cv::Size boundingBox(0,0);
int baseline = 0;
for (std::string line : textLines) {
cv::Size lineSize = cv::getTextSize(line, fontFace, 1, thickness, &baseline);
baseline += 2 * thickness;
lineSize.width += 2 * thickness;
lineSize.height += baseline;
textLineSizes.push_back(lineSize);
boundingBox.width = std::max(boundingBox.width, lineSize.width);
boundingBox.height += lineSize.height;
}
const double scale = std::min(imgROI.rows / static_cast<double>(boundingBox.height),
imgROI.cols / static_cast<double>(boundingBox.width));
boundingBox.width *= scale;
boundingBox.height *= scale;
baseline *= scale;
for (size_t i = 0; i < textLineSizes.size(); i++) {
textLineSizes.at(i).width *= scale;
textLineSizes.at(i).height *= scale;
}
/*
* Draw the text line-by-line
*/
int y = (imgROI.rows - boundingBox.height + baseline) / 2;
for (size_t i = 0; i < textLines.size(); i++) {
y += textLineSizes.at(i).height;
// center the text horizontally
cv::Point textOrg((imgROI.cols - textLineSizes.at(i).width) / 2, y - baseline);
cv::putText(imgROI, textLines.at(i), textOrg, fontFace, scale, color, thickness, lineType);
}
}
This is how you use it and how the results look like:
cv::Mat outputImage(360, 640, CV_8UC3);
outputImage.setTo(0);
ioxp::putText(outputImage, "Short text");
cv::imshow("text", outputImage);
cv::waitKey(0);
cv::Mat outputImage(360, 640, CV_8UC3);
outputImage.setTo(0);
ioxp::putText(outputImage,
"Some longer text, even with\nmultiple lines spread over the whole image");
cv::imshow("text", outputImage);
cv::waitKey(0);
cv::Mat outputImage(360, 640, CV_8UC3);
outputImage.setTo(0);
ioxp::putText(outputImage, "\n\n\nEmpty\n\n\nLines\n\n\n");
cv::imshow("text", outputImage);
cv::waitKey(0);
By using the Rectangle accessor you can define exactly, which part of the image the text should be placed in:
cv::Mat outputImage(360, 640, CV_8UC3);
outputImage.setTo(0);
ioxp::putText(outputImage(cv::Rect(0, outputImage.rows / 10, outputImage.cols, outputImage.rows / 10)),
"Text placed in the upper\n10 percent of the image");
cv::imshow("text", outputImage);
cv::waitKey(0);