简体   繁体   English

如何根据OpenCV、Python中的图像大小调整cv2.putText的文本大小?

[英]How to resize text for cv2.putText according to the image size in OpenCV, Python?

fontScale = 1
fontThickness = 1

# make sure font thickness is an integer, if not, the OpenCV functions that use this may crash
fontThickness = int(fontThickness)

upperLeftTextOriginX = int(imageWidth * 0.05)
upperLeftTextOriginY = int(imageHeight * 0.05)

textSize, baseline = cv2.getTextSize(resultText, fontFace, fontScale, fontThickness)
textSizeWidth, textSizeHeight = textSize

# calculate the lower left origin of the text area based on the text area center, width, and height
lowerLeftTextOriginX = upperLeftTextOriginX
lowerLeftTextOriginY = upperLeftTextOriginY + textSizeHeight

# write the text on the image
cv2.putText(openCVImage, resultText, (lowerLeftTextOriginX, lowerLeftTextOriginY), fontFace, fontScale, Color,
            fontThickness)

It seems fontScale does not scale text according to the image width and height because the text is almost in the same size for different sized images.似乎fontScale不会根据图像的宽度和高度缩放文本,因为对于不同大小的图像,文本的大小几乎相同。 So how can I resize the text according to the image size so that all the text could fit in the image?那么如何根据图像大小调整文本大小,使所有文本都能适应图像呢?

Here is the solution that will fit the text inside your rectangle.这是适合矩形内文本的解决方案。 If your rectangles are of variable width, then you can get the font scale by looping through the potential scales and measuring how much width (in pixels) would your text take.如果您的矩形具有可变宽度,那么您可以通过遍历潜在比例并测量文本需要多少宽度(以像素为单位)来获得字体比例。 Once you drop below your rectangle width you can retrieve the scale and use it to actually putText :一旦低于矩形宽度,您就可以检索比例并将其用于实际putText

def get_optimal_font_scale(text, width):
    for scale in reversed(range(0, 60, 1)):
          textSize = cv.getTextSize(text, fontFace=cv.FONT_HERSHEY_DUPLEX, fontScale=scale/10, thickness=1)
          new_width = textSize[0][0]
          print(new_width)
    if (new_width <= width):
        return scale/10
    return 1

for this worked!为此工作!

scale = 1 # this value can be from 0 to 1 (0,1] to change the size of the text relative to the image
fontScale = min(imageWidth,imageHeight)/(25/scale)

just keep in mind that the font type can affect the 25 constant请记住,字体类型会影响 25 常量

If you take fontScale = 1 for images with size approximately 1000 x 1000, then this code should scale your font correctly.如果您将fontScale = 1用于大小约为 1000 x 1000 的图像,则此代码应正确缩放您的字体。

fontScale = (imageWidth * imageHeight) / (1000 * 1000) # Would work best for almost square images

If you are still having any problem, do comment.如果您仍有任何问题,请发表评论。

I implemented a function to find best fitted centered location for text.我实现了一个函数来为文本找到最合适的居中位置。

Take a look if these codes help you.看看这些代码是否对您有帮助。

def findFontLocate(s_txt, font_face, font_thick, cv_bgd):
    best_scale = 1.0
    bgd_w = cv_bgd.shape[1]
    bgd_h = cv_bgd.shape[0]
    txt_rect_w = 0
    txt_rect_h = 0
    baseline = 0
    for scale in np.arange(1.0, 6.0, 0.2):
        (ret_w, ret_h), tmp_bsl = cv2.getTextSize(
            s_txt, font_face, scale, font_thick)
        tmp_w = ret_w + 2 * font_thick
        tmp_h = ret_h + 2 * font_thick + tmp_bsl
        if tmp_w >= bgd_w or tmp_h >= bgd_h:
            break
        else:
            baseline = tmp_bsl
            txt_rect_w = tmp_w
            txt_rect_h = tmp_h
            best_scale = scale
    lt_x, lt_y = round(bgd_w/2-txt_rect_w/2), round(bgd_h/2-txt_rect_h/2)
    rb_x, rb_y = round(bgd_w/2+txt_rect_w/2), round(bgd_h/2+txt_rect_h/2)-baseline
    return (lt_x, lt_y, rb_x, rb_y), best_scale, baseline

Note that, the function accept four arguments: s_txt (string to render), font_face , font_thick and cv_bgd (background image in ndarray format)请注意,该函数接受四个参数: s_txt (要渲染的字符串)、 font_facefont_thickcv_bgd (ndarray 格式的背景图像)


When you putText() , write codes as following:当你putText()时,编写如下代码:

cv2.putText(
    cv_bgd, s_txt, (lt_x, rb_y), font_face,
    best_scale, (0,0,0), font_thick, cv2.LINE_AA)

You can use get_optimal_font_scale function as bellow, to adjust font size according to the image size:您可以使用get_optimal_font_scale函数如下所示,根据图像大小调整字体大小:

def get_optimal_font_scale(text, width):

    for scale in reversed(range(0, 60, 1)):
        textSize = cv2.getTextSize(text, fontFace=cv2.FONT_HERSHEY_DUPLEX, fontScale=scale/10, thickness=1)
        new_width = textSize[0][0]
        if (new_width <= width):
            return scale/10
    return 1

fontScale = 3*(img.shape[1]//6)
font_size = get_optimal_font_scale(text, fontScale)
cv2.putText(img, text, org, font, font_size, color, thickness, cv2.LINE_AA)

You can change fontScale for your image.您可以更改图片的fontScale

Approach方法

One way to approach this is to scale the font size proportionally to the size of the image.解决此问题的一种方法是将字体大小与图像大小成比例缩放。 In my experience, more natural results are obtained when applying this not only to fontScale , but also to thickness .根据我的经验,不仅将其fontScale ,还应用于thickness ,会获得更自然的结果。 For example:例如:

import math

import cv2

FONT_SCALE = 2e-3  # Adjust for larger font size in all images
THICKNESS_SCALE = 1e-3  # Adjust for larger thickness in all images

img = cv2.imread("...")
height, width, _ = img.shape

font_scale = min(width, height) * FONT_SCALE
thickness = math.ceil(min(width, height) * THICKNESS_SCALE)

Example例子

Let's take this free-to-use stock photo as an example.让我们以这张免费使用的库存照片为例。 We create two versions of the base image by rescaling to a width of 2000px and 600px (keeping the aspect ratio constant).我们通过重新缩放到 2000px 和 600px 的宽度(保持纵横比不变)来创建两个版本的基本图像。 With the approach above, text looks appropriately sized to the image size in both cases (here shown in an illustrative use case where we label bounding boxes):使用上述方法,在两种情况下,文本看起来都适合图像大小(此处显示在我们标记边界框的说明性用例中):

2000px 2000像素

在此处输入图像描述

600px 600像素

在此处输入图像描述

Full code to reproduce ( but note: input images have to be preprocessed ):重现的完整代码(但请注意:必须对输入图像进行预处理):

import math

import cv2

FONT_SCALE = 2e-3  # Adjust for larger font size in all images
THICKNESS_SCALE = 1e-3  # Adjust for larger thickness in all images
TEXT_Y_OFFSET_SCALE = 1e-2  # Adjust for larger Y-offset of text and bounding box

img_width_to_bboxes = {
    2000: [
        {"xywh": [120, 400, 1200, 510], "label": "car"},
        {"xywh": [1080, 420, 790, 340], "label": "car"},
    ],
    600: [
        {"xywh": [35, 120, 360, 155], "label": "car"},
        {"xywh": [325, 130, 235, 95], "label": "car"},
    ],
}


def add_bbox_and_text() -> None:
    for img_width, bboxes in img_width_to_bboxes.items():
        # Base image from https://www.pexels.com/photo/black-suv-beside-grey-auv-crossing-the-pedestrian-line-during-daytime-125514/
        # Two rescaled versions of the base image created with width of 600px and 2000px
        img = cv2.imread(f"pexels-kaique-rocha-125514_{img_width}.jpg")
        height, width, _ = img.shape
        for bbox in bboxes:
            x, y, w, h = bbox["xywh"]
            cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
            cv2.putText(
                img,
                bbox["label"],
                (x, y - int(height * TEXT_Y_OFFSET_SCALE)),
                fontFace=cv2.FONT_HERSHEY_TRIPLEX,
                fontScale=min(width, height) * FONT_SCALE,
                thickness=math.ceil(min(width, height) * THICKNESS_SCALE),
                color=(0, 255, 0),
            )
        cv2.imwrite(f"pexels-kaique-rocha-125514_{img_width}_with_text.jpg", img)


if __name__ == "__main__":
    add_bbox_and_text()

It`s work for me.这对我有用。

double calc_scale_rectbox(const char *txt, int box_width, int box_height, 
                          cv::Size &textSize, int &baseline)

{
       if (!txt) return 1.0;
       double scale = 2.0;
       double w_aprx = 0;
       double h_aprx = 0;
       do
       {
           textSize = cv::getTextSize(txt, FONT_HERSHEY_DUPLEX, scale, 2, 
                                      &baseline);
           w_aprx = textSize.width * 100 / box_width;
           h_aprx = textSize.height * 100 / box_height;
           scale -= 0.1;
        } while (w_aprx > 50 || h_aprx > 50);
        return scale;
 }

...... ……

cv::Size textSize;

int baseline = 0;

double scale = calc_scale_rectbox(win_caption.c_str(), width, 
                                 height, textSize, baseline);

cv::putText(img, win_caption, Point(width / 2 - textSize.width / 2, 
           (height + textSize.height - baseline + 2) / 2), 
            FONT_HERSHEY_DUPLEX, scale, CV_RGB(255, 255, 255), 2);

A simple utility function:一个简单的实用程序 function:

def optimal_font_dims(img, font_scale = 2e-3, thickness_scale = 5e-3):
    h, w, _ = img.shape
    font_scale = min(w, h) * font_scale
    thickness = math.ceil(min(w, h) * thickness_scale)
    return font_scale, thickness

Usage:用法:

font_scale, thickness = optimal_font_dims(image)
cv2.putText(image, "LABEL", (x, y), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255,0,0), thickness)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM