Reputation: 675
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. So how can I resize the text according to the image size so that all the text could fit in the image?
Upvotes: 22
Views: 50523
Reputation: 51
Here's a C# implementation:
public static void PutText(Mat mat, Rect rect, double scale, string text)
{
var textBound = Cv2.GetTextSize(text, HersheyFonts.HersheySimplex, 1, 1, out int baseline);
var widthScale = (double)textBound.Width / rect.Width;
var heightScale = (double)textBound.Height / rect.Height;
var finalScale = scale / Math.Max(widthScale, heightScale);
textBound = Cv2.GetTextSize(text, HersheyFonts.HersheySimplex,
finalScale, 1, out int baselineScaled);
var widthDiff = rect.Width - textBound.Width;
var heightDiff = rect.Height - textBound.Height;
mat.PutText(text, new Point(rect.Left + widthDiff / 2, rect.Bottom - heightDiff / 2),
HersheyFonts.HersheySimplex, finalScale, Scalar.Black);
}
Where rect
is the place where text should be painted, scale
is the scale of the text inside the rect
. Text is centered inside the given rect
. For example, rect
is 100x100 and the text is completely squared. Then it is painted inside the 80x80 rectangle, shifted by 10x10.
Upvotes: 1
Reputation: 519
A simple utility 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)
Upvotes: 0
Reputation: 849
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
:
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]
if (new_width <= width):
print(new_width)
return scale/10
return 1
Upvotes: 7
Reputation: 1
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);
Upvotes: 0
Reputation: 3333
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
. 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)
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). 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
600px
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()
Upvotes: 3
Reputation: 61
You can use get_optimal_font_scale
function as bellow, to adjust font size according to the image size:
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.
Upvotes: 0
Reputation:
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
andcv_bgd
(background image in ndarray format)
When you putText()
, write codes as following:
cv2.putText(
cv_bgd, s_txt, (lt_x, rb_y), font_face,
best_scale, (0,0,0), font_thick, cv2.LINE_AA)
Upvotes: 0
Reputation: 31
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
Upvotes: 3
Reputation: 941
If you take fontScale = 1
for images with size approximately 1000 x 1000, then this code should scale your font correctly.
fontScale = (imageWidth * imageHeight) / (1000 * 1000) # Would work best for almost square images
If you are still having any problem, do comment.
Upvotes: 0