How to draw different language text on the image? - python

I am doing a project where I need to translate the text into other Indian languages
I have tried few codes but the result I get for languages other than english is empty boxes
Original image:
this is the variable output
[([[217, 25], [1061, 25], [1061, 61], [217, 61]],
'He was in his bedroom: He smelled food, The food smelled',
0.7254911849154941),
([[181, 70], [1006, 70], [1006, 114], [181, 114]],
'like chicken: It smelled good. A neighbor must be cooking',
0.773367181175143),
I am using the output coordinates for drawing a rectangle around the text
1 - first code
image = cv2.imread("/content/drive/MyDrive/image_text_3.jpg")
color = (0, 0, 255)
# Line thickness
thickness = 1
fontpath = "/content/VANAVIL-AvvaiyarRegular.otf"
TEXT_Font = ImageFont.truetype(fontpath, 20)
TEXT_SCALE = 0.85
TEXT_THICKNESS = 1
TEXT = "இனிய பிறந்தநாள் வாழ்த்துக்கள் யோஷினி குட்டி"
for bound in output:
tl, tr, br, bl = bound[0]
tl = (int(tl[0]), int(tl[1]))
tr = (int(tr[0]), int(tr[1]))
br = (int(br[0]), int(br[1]))
bl = (int(bl[0]), int(bl[1]))
cv2.rectangle(image, tl, br, color, thickness)
text_size, _ = cv2.getTextSize(TEXT, TEXT_Font, TEXT_THICKNESS)
text_origin = (tl[0] - text_size[0] / 2, br[1] + text_size[1] / 2)
cv2.putText(image, TEXT, (tl[0], br[1]), TEXT_Font, TEXT_SCALE, (0,255,0), TEXT_THICKNESS, cv2.LINE_AA)
cv2.imwrite('centertext_out.png', image)
This is the error I get for the above code for tamil language
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-103-1d8c09103798> in <module>()
26 cv2.rectangle(image, tl, br, color, thickness)
27
---> 28 text_size, _ = cv2.getTextSize(TEXT, TEXT_SCALE, TEXT_THICKNESS)
29
30 text_origin = (tl[0] - text_size[0] / 2, br[1] + text_size[1] / 2)
TypeError: an integer is required (got type FreeTypeFont)
2 - second code for marathi
fontpath = "/content/Devnew.ttf"
img=cv2.imread('/content/drive/MyDrive/image_text_3.jpg')
b,g,r,a = 0,255,0,0
font = ImageFont.truetype(fontpath, 32)
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)
draw.text((50, 80), "जन्मदिन मुबारक", font = font, fill = (b, g, r, a))
img = np.array(img_pil)
cv2.imwrite('result.png', img)
Result of code 2:
3 - Third code
image = cv2.imread("/content/drive/MyDrive/image_text_3.jpg")
TEXT = "जन्मदिन मुबारक हो योशिनी कुट्टी"
color = (0, 0, 255)
# Line thickness
thickness = 1
b,g,r,a = 0,255,0,0
TEXT_SCALE = 0.9
TEXT_THICKNESS = 1
fontpath = "/content/drive/MyDrive/Devnew.ttf"
font = ImageFont.truetype(fontpath, 20)
img_pil = Image.fromarray(image)
draw = ImageDraw.Draw(img_pil)
draw.text((50, 80), TEXT, font = font, fill =(b,g,r,a))
image = np.array(img_pil)
cv2_imshow(image)
Result of code 3:
I don't understand whether there is a problm with the font file or the code or the image. kindly help in resolving. Thankyou!
My main concern is to put the text inside the rectangle box created in the initial step({cv2.rectangle(image, tl, br, color, thickness} from code 1))

Most likely the problem is with the .otf and .ttf files you are using. Try using the default lohit-tamil fonts from /usr/share/fonts/truetype. They can be installed with sudo apt install fonts-indic if they are not installed already. Use Unicode fonts only or download other Unicode fonts online - e.g. Latha.ttf
import cv2
import numpy as np
from PIL import ImageFont, ImageDraw, Image
img = np.zeros((200,1200,3),np.uint8)
fontpath = "tamil/Latha.ttf"
text = "இனிய பிறந்தநாள் வாழ்த்துக்கள் யோஷினி குட்டி"
font = ImageFont.truetype(fontpath, 32)
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)
draw.text((50, 80),text, font = font)
img_tamil = np.array(img_pil)
cv2.imwrite('tamil.jpg', img_tamil)

Related

How to set anchor to fit the text at center of image with PIL.ImageDraw.Draw.text?

Drawn text on an image with Pillow library, tried to fit the text anchored at center of image by option anchor='mm', but it looks not exactly as center of image.
Demo code
from PIL import Image, ImageDraw, ImageFont
im = Image.new("RGBA", (500, 500), (255, 255, 255, 255))
font = ImageFont.truetype(font='arial.ttf', size=320)
draw = ImageDraw.Draw(im)
draw.text((250, 250), "123", font=font, fill='black', anchor='mm')
im.show()
result:
expectation:
The reason why the text looks misaligned is because there is a little margin at the left of the 1 and pillow will align the text including this margin.
Adding 0 at both ends will visualize that it is correctly centered and that there is a large margin at 1.
If you want to align the text excluding margins, ImageFont.getmask is helpful.
def get_offset_for_true_mm(text, draw, font):
anchor_bbox = draw.textbbox((0, 0), text, font=font, anchor='lt')
anchor_center = (anchor_bbox[0] + anchor_bbox[2]) // 2, (anchor_bbox[1] + anchor_bbox[3]) // 2
mask_bbox = font.getmask(text).getbbox()
mask_center = (mask_bbox[0] + mask_bbox[2]) // 2, (mask_bbox[1] + mask_bbox[3]) // 2
return anchor_center[0] - mask_center[0], anchor_center[1] - mask_center[1]
im = Image.new("RGBA", (500, 500), (255, 255, 255, 255))
font = ImageFont.truetype(font='arial.ttf', size=320)
draw = ImageDraw.Draw(im)
text = "123"
offset = get_offset_for_true_mm(text, draw, font)
draw.text((250 + offset[0], 250 + offset[1]), text, font=font, fill='black', anchor='mm')
im.show()
Result:

Text within image in OpenCV

I was trying to get some text on an image using OpenCV. But the text is too long for one line and goes out of the image, instead of the next line. I could hardcode the spaces, but I was looking for a more dynamic solution. How do I work around this issue?
def get_text(img, text):
sh = img.shape
txt = np.ones(shape=sh)
fontface = cv2.FONT_HERSHEY_SIMPLEX
fontscale = 1
thickness = 2
color = (0, 0, 0)
orig = (10, 100)
linetype = cv2.LINE_AA
txt = cv2.putText(txt, text, orig, fontface, fontscale, color, thickness, linetype)
txt = txt.astype("uint8")
return txt
import textwrap
def get_text(img, text):
sh = img.shape
txt = np.ones(shape=sh)
fontface = cv2.FONT_HERSHEY_SIMPLEX
fontscale = 1
thickness = 2
color = (0, 0, 0)
orig = (10, 100)
linetype = cv2.LINE_AA
wrapped_text = textwrap.wrap(text, width=35)
x, y = 10, 40
font_size = 1
font_thickness = 2
i = 0
for line in wrapped_text:
textsize = cv2.getTextSize(line, font, font_size, font_thickness)[0]
gap = textsize[1] + 10
y = int((img.shape[0] + textsize[1]) / 2) + i * gap
x = int((img.shape[1] - textsize[0]) / 2)
cv2.putText(img, line, (x, y), font,
font_size,
(0,0,0),
font_thickness,
lineType = cv2.LINE_AA)
txt = txt.astype("uint8")
return txt
Try this, may require some adjustment, but the idea is to use textwrap.wrap(text, width=35).
in this code i simply split string into parts according image width.
# Python program to explain cv2.putText() method
import cv2
import math
import textwrap
path = r'YOUR PATH OF IMAGE'
image = cv2.imread(path)
window_name = 'Image'
font = cv2.FONT_HERSHEY_SIMPLEX
zero= 5
one =50
org = (zero, one)
fontScale = 1
color = (255, 0, 0)
thickness = 2
imageHeight = image.shape[0]
imageWidth = image.shape[1]
print(f"width:",imageWidth)
sizeofnumpix=min(imageWidth,imageHeight)/(25/fontScale)
stringputt = 'YOUR STRING'
i=len(stringputt)
print(i)
if i*sizeofnumpix > imageWidth*2:
n=math.ceil(i*sizeofnumpix/(imageWidth*2))
part_size = math.ceil(i/n)
txt = textwrap.wrap(stringputt, part_size)
for l in txt:
image = cv2.putText(image, l, org, font,fontScale, color, thickness, cv2.LINE_AA)
zero= 5
one = one+math.ceil(sizeofnumpix)
org = (zero, one)
else:
image = cv2.putText(image, stringputt, org, font,fontScale, color, thickness, cv2.LINE_AA)
# Displaying the image
cv2.imwrite('IMGWITHTXT.png',image)

How to wrap text in OpenCV when I print it on an image and it exceeds the frame of the image?

I have a 1:1 ratio image and I want to make sure that if the text exceeds the frame of the image, it gets wrapped to the next line.
How would I do it?
I am thinking of doing an if-else block, where "if sentence exceeds x characters->new line" but I'm not sure how to implement it.
import numpy as np
import cv2
img = cv2.imread('images/1.png')
print(img.shape)
height, width, channel = img.shape
text_img = np.ones((height, width))
print(text_img.shape)
font = cv2.FONT_HERSHEY_SIMPLEX
text = "Lorem Ipsum "
textsize = cv2.getTextSize(text, font, 2, 2)[0]
font_size = 1
font_thickness = 2
for i, line in enumerate(text.split('\n')):
textsize = cv2.getTextSize(line, font, font_size, font_thickness)[0]
gap = textsize[1] + 10
y = int((img.shape[0] + textsize[1]) / 2) + i * gap
x = int((img.shape[1] - textsize[0]) / 2)
cv2.putText(img, line, (x, y), font,
font_size,
(0,0,0),
font_thickness,
lineType = cv2.LINE_AA)
cv2.imshow("Result Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
You can use textwrap to wrap text in OpenCV.
import numpy as np
import cv2
import textwrap
img = cv2.imread('apple.png')
print(img.shape)
height, width, channel = img.shape
text_img = np.ones((height, width))
print(text_img.shape)
font = cv2.FONT_HERSHEY_SIMPLEX
text = "Lorem Ipsum dgdhswjkclyhwegflhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhvhasvcxsbvfajhskvfgsdj"
wrapped_text = textwrap.wrap(text, width=35)
x, y = 10, 40
font_size = 1
font_thickness = 2
for i, line in enumerate(wrapped_text):
textsize = cv2.getTextSize(line, font, font_size, font_thickness)[0]
gap = textsize[1] + 10
y = int((img.shape[0] + textsize[1]) / 2) + i * gap
x = int((img.shape[1] - textsize[0]) / 2)
cv2.putText(img, line, (x, y), font,
font_size,
(0,0,0),
font_thickness,
lineType = cv2.LINE_AA)
cv2.imshow("Result Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Below is the output image without using textwrap(running your code):
Below is the output image using textwrap(my code):
There are many other ways you can achieve the same but textwrap is certainly one way of doing so in OpenCV and is simple too.

How to trace a path generated by openCV findContours() using ReportLab PDFgen?

Im working with openCV for a project that traces patterns and allows me to manipulate that pattern as a path in Adobe Illustrator with the image of the pattern in the background.
Below is the solution that I and a friend have come up with.
The output of drawContours() doesn't match the vector path I am creating with PDFgen.
drawContours() output has a blue stroke
generated path has black stroke
My source code is below.
How can I trace a path generated by findContours() using ReportLab PDFgen?
import numpy as np
import cv2
import math
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
print(cv2.__version__)
im = cv2.imread('img.jpg')
#Orient the image properly
# grab the dimensions of the image and calculate the center
# of the image
(h, w) = im.shape[:2]
center = (w / 2, h / 2)
# rotate the image by 180 degrees
M = cv2.getRotationMatrix2D(center, 180, 1.0)
rotated = cv2.warpAffine(im, M, (w, h))
#flip the image across
flippedColor = cv2.flip(rotated, 1) #used to display the result of drawContours in color
imgray = cv2.cvtColor(rotated, cv2.COLOR_BGR2GRAY)
blur = cv2.flip(imgray, 1)
#thresholding
(thresh, binRed) = cv2.threshold(blur, 200, 255, cv2.THRESH_BINARY)
_, Rcontours, hier_r = cv2.findContours(binRed,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_NONE)
r_areas = [cv2.contourArea(c) for c in Rcontours]
max_rarea = np.argmax(r_areas)
CntExternalMask = np.ones(binRed.shape[:2], dtype="uint8") * 255
contourMax= Rcontours[max_rarea]
# calc arclentgh
arclen = cv2.arcLength(contourMax, True)
# approx the contour
epsilon = arclen * 0.0005
contour = cv2.approxPolyDP(contourMax, epsilon, True)
cv2.drawContours(flippedColor,[contour],-1,(255,0,0),1)
'''
#display the image for testing
cv2.imshow('image',flippedColor)
cv2.waitKey(0)
cv2.destroyAllWindows()
'''
#set up the pdf with proper size and stuff
canvas = canvas.Canvas("output.pdf", pagesize=letter)
canvas.bottomup = 1
canvas.setLineWidth(0.1)
h,w = im.shape[:2]
canvas.setPageSize((h, w))
canvas.drawImage("img.jpg", 0, 0)
#draw the path
last_angle = 0
last_angle1 = 0
p1 = contour[0][0]
ccount = 0
pathman = canvas.beginPath()
for index,p in enumerate(contour):
if index==len(contour)-1:
break
s1 = contour[index+1][0]
p = p[0]
dist1 = get_dist(p1,p)
dist2 = get_dist(s1,p)
dist = get_dist(s1,p1)
ccount+=1
cv2.circle(CntExternalMask, (p[0], p[1]), 5, (0,255,0), -1)
cv2.circle(CntExternalMask, (p1[0], p1[1]), 5, (0,255,0), -1)
cv2.line(CntExternalMask, (p[0], p[1]), (p1[0], p1[1]), (100, 200, 0), 5)
pathman.moveTo(p[0],p[1])
pathman.lineTo(p[0],p[1])
canvas.line(p[0], p[1],p1[0], p1[1])
p1 = p
p = contour[0][0]
cv2.line(CntExternalMask, (p[0], p[1]), (p1[0], p1[1]), (100, 200, 0), 5)
print ("count = ",ccount)
canvas.line(p[0], p[1],p1[0], p1[1])
canvas.drawPath(pathman, fill=1, stroke=1)
canvas.save()
#save image
cv2.imwrite("output.jpg", CntExternalMask)
cv2.waitKey(0)
cv2.destroyAllWindows()

How to get the font pixel height using PIL's ImageFont class?

I am using PIL' ImageFont module to load fonts to generate text images.
I want the text to tightly bound to the edge, however, when using the ImageFont to get the font height, It seems that it includes the character's padding. As the red rectangle indicates.
c = 'A'
font = ImageFont.truetype(font_path, font_size)
width = font.getsize(c)[0]
height = font.getsize(c)[1]
im = Image.new("RGBA", (width, height), (0, 0, 0))
draw = ImageDraw.Draw(im)
draw.text((0, 0), 'A', (255, 255, 255), font=font)
im.show('charimg')
If I can get the actual height of the character, then I could skip the bounding rows in the bottom rectangle, could this info got from the font?
Thank you.
Exact size depends on many factors. I'll just show you how to calculate different metrics of font.
font = ImageFont.truetype('arial.ttf', font_size)
ascent, descent = font.getmetrics()
(width, baseline), (offset_x, offset_y) = font.font.getsize(text)
Height of red area: offset_y
Height of green area: ascent - offset_y
Height of blue area: descent
Black rectangle: font.getmask(text).getbbox()
Hope it helps.
The top voted answer is outdated. There is a new function in Pillow 8.0.0: ImageDraw.textbbox.
See the release notes for other text-related functions added in Pillow 8.0.0.
Note that ImageDraw.textsize, ImageFont.getsize and ImageFont.getoffset are broken, and should not be used for new code. These have been effectively replaced by the new functions with a cleaner API. See the documentaion for details.
To get a tight bounding box for a whole string you can use the following code:
from PIL import Image, ImageDraw, ImageFont
image = Image.new("RGB", (200, 80))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("arial.ttf", 30)
draw.text((20, 20), "Hello World", font=font)
bbox = draw.textbbox((20, 20), "Hello World", font=font)
draw.rectangle(bbox, outline="red")
print(bbox)
# (20, 26, 175, 48)
image.show()
You can combine it with the new ImageDraw.textlength to get individual bounding boxes per letter:
from PIL import Image, ImageDraw, ImageFont
image = Image.new("RGB", (200, 80))
draw = ImageDraw.Draw(image)
font = ImageFont.truetype("arial.ttf", 30)
xy = (20, 20)
text = "Example"
draw.text(xy, text, font=font)
x, y = xy
for c in text:
bbox = draw.textbbox((x, y), c, font=font)
draw.rectangle(bbox, outline="red")
x += draw.textlength(c, font=font)
image.show()
Note that this ignores the effect of kerning. Kering is currently broken with basic text layout, but could introduce a slight inaccuracy with Raqm layout. To fix it you would add the text length of pairs of letters instead:
for a, b in zip(text, text[1:] + " "):
bbox = draw.textbbox((x, y), a, font=font)
draw.rectangle(bbox, outline="red")
x += draw.textlength(a + b, font=font) - draw.textlength(b, font=font)
from PIL import Image, ImageDraw, ImageFont
im = Image.new('RGB', (400, 300), (200, 200, 200))
text = 'AQj'
font = ImageFont.truetype('arial.ttf', size=220)
ascent, descent = font.getmetrics()
(width, height), (offset_x, offset_y) = font.font.getsize(text)
draw = ImageDraw.Draw(im)
draw.rectangle([(0, 0), (width, offset_y)], fill=(237, 127, 130)) # Red
draw.rectangle([(0, offset_y), (width, ascent)], fill=(202, 229, 134)) # Green
draw.rectangle([(0, ascent), (width, ascent + descent)], fill=(134, 190, 229)) # Blue
draw.rectangle(font.getmask(text).getbbox(), outline=(0, 0, 0)) # Black
draw.text((0, 0), text, font=font, fill=(0, 0, 0))
im.save('result.jpg')
print(width, height)
print(offset_x, offset_y)
print('Red height', offset_y)
print('Green height', ascent - offset_y)
print('Blue height', descent)
print('Black', font.getmask(text).getbbox())
result
Calculate area pixel
from PIL import Image, ImageDraw, ImageFont
im = Image.new('RGB', (400, 300), (200, 200, 200))
text = 'AQj'
font = ImageFont.truetype('arial.ttf', size=220)
ascent, descent = font.getmetrics()
(width, height), (offset_x, offset_y) = font.font.getsize(text)
draw = ImageDraw.Draw(im)
draw.rectangle([(0, offset_y), (font.getmask(text).getbbox()[2], ascent + descent)], fill=(202, 229, 134))
draw.text((0, 0), text, font=font, fill=(0, 0, 0))
im.save('result.jpg')
print('Font pixel', (ascent + descent - offset_y) * (font.getmask(text).getbbox()[2]))
result

Categories