This is a continuation of Last time.
The function cv2.putText ()
that draws text in OpenCV cannot use Japanese. Since it is possible with PIL, there are many methods on the web that use PIL only for that part, but I have published a unique function that can be an alternative to cv2.putText ()
just because I tried it for the time being. There was no place.
So I made it myself. I'm sorry if there is a standard method just because my search skill is low.
version: OpenCV 4.1.2.30 Pillow 7.0
-* img * Image. Needless to say, OpenCV images.
-* Text * Text. ** Japanese is not available . It is not possible to start a new line with a line feed code.
- org * Specify the ** coordinates at the bottom left of the text with (x, y)
.
- FontFace * font. Only a few types such as cv2.FONT_HERSHEY_SIMPLEX
can be specified.
-* FontScale * Font size. Essential for troublesome things. The size of "1" depends on the font.
-* Color * Text color. Needless to say, if it is an RGB image, the order is (b, g, r)
instead of RGB.
-* Thickness * Line thickness. Optional and default value is 1.
The target is an ImageDraw.Draw object created from a PIL image.
-* xy * Specify the coordinates of ** upper left ** of the text with (x, y)
.
-* Text * Text. Japanese is OK. Line breaks using \ n
are also OK.
-* Fill * Text color. Needless to say, for RGB images, (r, g, b)
.
-* Font * Font. Specify the font and size with ImageFont.truetype.
cv2.putText ()
is a meaningful argument because it is a vector font. It doesn't matter if it's not in PIL.
Other than that, it seems possible to replace cv2.putText ()
with ʻImageDraw.text ()`.cv2.imshow ()
cannot be used Google Colab has its own function ** cv2_imshow () **.
Following that, we named the original function that describes Japanese fonts in OpenCV ** cv2_putText () **.
First, prepare two functions. This is a function to convert OpenCV and PIL, which I wrote in this article.
python
import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont
def pil2cv(imgPIL):
imgCV_RGB = np.array(imgPIL, dtype = np.uint8)
imgCV_BGR = np.array(imgPIL)[:, :, ::-1]
return imgCV_BGR
def cv2pil(imgCV):
imgCV_RGB = imgCV[:, :, ::-1]
imgPIL = Image.fromarray(imgCV_RGB)
return imgPIL
python
def cv2_putText_1(img, text, org, fontFace, fontScale, color):
x, y = org
b, g, r = color
colorRGB = (r, g, b)
imgPIL = cv2pil(img)
draw = ImageDraw.Draw(imgPIL)
fontPIL = ImageFont.truetype(font = fontFace, size = fontScale)
draw.text(xy = (x,y), text = text, fill = colorRGB, font = fontPIL)
"""
I'll add it here later
"""
imgCV = pil2cv(imgPIL)
return imgCV
It is the same as many predecessors to make a PIL image, draw Japanese text on it, and then return it to an OpenCV image, but cv2.putText (img, text, org, fontFace, fontScale, color, thickness) The feature is that it can be used in the same way as
.
Of course, if you say "I want to use Japanese, but the font and size are fixed", you can set the default arguments.
Let's compare the output results of cv2.putText ()
and cv2_putText ()
.
python
import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont
def pil2cv(imgPIL):
#The ones listed above
def cv2pil(imgCV):
#The ones listed above
def cv2_putText_1(img, text, org, fontFace, fontScale, color):
#The ones listed above
def main():
img = np.full((200,400,3), (160,160,160), dtype=np.uint8)
#Ordinarily cv2.putText()Draw text with
text = "OpenCV"
x, y = 50, 100
fontCV = cv2.FONT_HERSHEY_SIMPLEX
fontScale = 1
colorBGR = (255,0,0)
thickness = 1
cv2.putText(img = img,
text = text,
org = (x,y),
fontFace = fontCV,
fontScale = fontScale,
color = colorBGR,
thickness = thickness)
"""
I'll add it here later
"""
#Draw Japanese text with your own function
text = "Japanese too\n possible"
x, y = 200,100
fontPIL = "Dflgs9.TTC" #DF Reiga Song
size = 40
colorBGR = (255,0,0) # cv2.putText()As with, defined in the order of BGR
img = cv2_putText_1(img = img,
text = text,
org = (x,y),
fontFace = fontPIL,
fontScale = size,
color = colorBGR)
cv2.imshow("cv2_japanese_test", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
if __name__ == "__main__":
main()
The result is this.
The y coordinate should be the same value, but the height is off. This is because, as I wrote in the review section, * org * in cv2.putText ()
specifies the lower left, and * xy * in ʻImageDraw.text ()` specifies the upper right.
Let's process it so that you can understand it.
python
# cv2_putText_1()Add the following to the comment line of
w, h = draw.textsize(text, font = fontPIL)
draw.rectangle([(x,y), (x+w,y+h)], outline = (255,0,0), width = 1)
draw.ellipse([(x-3,y-3), (x+3,y+3)], None, (255,0,0), 1)
# main()Add the following to the comment line of
(w, h), b = cv2.getTextSize(text = text,
fontFace = fontCV,
fontScale = fontScale,
thickness = thickness)
pt1 = (x, y - h)
pt2 = (x + w, y + b)
cv2.rectangle(img, pt1, pt2, (0,0,255), 1)
cv2.circle(img, (x,y), 5, (0,0,255))
The result is this.
In the previous article, I didn't use the baseline obtained by cv2.getTextSize ()
, but this time I tried to draw a rectangle including it.
I'm not sure why the PIL rectangle doesn't fit the Japanese text subtly. Perhaps the bug mentioned by this person is related.
Anyway, to make your own function compatible with * org * in cv2.putText ()
, you need to modify this part.
Here is a modified version of the height standard.
python
def cv2_putText_2(img, text, org, fontFace, fontScale, color):
x, y = org
b, g, r = color
colorRGB = (r, g, b)
imgPIL = cv2pil(img)
draw = ImageDraw.Draw(imgPIL)
fontPIL = ImageFont.truetype(font = fontFace, size = fontScale)
w, h = draw.textsize(text, font = fontPIL)
draw.text(xy = (x,y-h), text = text, fill = colorRGB, font = fontPIL)
imgCV = pil2cv(imgPIL)
return imgCV
You can get an image like this by changing cv2_putText_1
inmain ()
to cv2_putText_2
.
Similar to cv2.putText ()
, I was able to draw the text by specifying the lower left.
However, I don't like the specification that * org * specifies the lower left in cv2.putText ()
, so I prefer cv2_putText_1 ()
.
If you specify the font and font size with cv2.putText ()
, the size of the text to be drawn is determined. If you calculate the appropriate size with the specified font based on it, you can modify the existing program as shown in here with minimal modification. You can change to TrueType fonts! I thought, but I stopped because it was troublesome. ..
If you make it a function, you can use Japanese fonts in the same way as cv2.putText ()
. It was a story.
However, of course, note that Japanese is not available in the window name of cv2.imshow ()
or the file name of cv2.imwrite ()
.
By the way, how can we create (b, g, r)
from the tuple (r, g, b)
?
In my function this time, I take out each element and change the order to make a new tuple, but it is not an elegant solution.
You could convert the tuples to a list, reverse the order, and then convert them back to tuples, but that's far from elegant.
python
r, g, b = 0, 128, 255
RGB = (r, g, b)
BGR = tuple(list(RGB)[::-1])
print (RGB) # (0, 128, 255)
print (BGR) # (255, 128, 0)
Please let me know if there is a nice way to experience aha, such as ʻimg [:,:, :: -1] `when converting an RGB image to BGR.