Golang : Put UTF8 text on OpenCV video capture image frame
The HERSHEY font use by OpenCV does not support UTF-8 characters. If you use my previous tutorial on how to put text on OpenCV video capture image with PutText() function with Chinese, Russian, Greek, Japanese or Korean characters(for example), it will not be able to display those characters properly. Instead it will show up as ????
.
For this tutorial, we will learn how to place UTF-8 characters onto OpenCV video capture. We will use Nigel Tao's example on how to load True Type Font and draw the UTF8 strings on an image and paste the image onto the video capture image frames. Such as :
To get the code example below running, first download the True Type Font at http://www.slackware.com/~alien/slackbuilds/wqy-zenhei-font-ttf/build/wqy-zenhei-0.4.23-1.tar.gz, unzip and place the wqy-zenhei.ttf
file in the same folder.
Read the notes after the source code too. Especially on the languages covered by the wqy-zenhei.ttf
.
Execute go get -u
on these packages if you haven't done so.
"github.com/lazywei/go-opencv/opencv"
"github.com/mattn/go-gtk/glib"
"github.com/mattn/go-gtk/gtk"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
Here you go!
package main
import (
"fmt"
"image"
"image/color"
"image/draw"
"io/ioutil"
"log"
"runtime"
"strconv"
"time"
"github.com/lazywei/go-opencv/opencv"
"github.com/mattn/go-gtk/glib"
"github.com/mattn/go-gtk/gtk"
"github.com/golang/freetype"
"github.com/golang/freetype/truetype"
)
var (
win = new(opencv.Window)
webCamera = new(opencv.Capture)
statusbar = new(gtk.Statusbar)
snapshotFileName string
width, height int
sliderPosX, sliderPosY int
stopCamera = false // to prevent segmentation fault
backgroundWidth = 650
backgroundHeight = 150
horizontalScale = float32(1.0)
verticalScale = float32(1.0)
shear = float32(1.0)
thickness = 3
lineType = 8
textFont = opencv.InitFont(opencv.CV_FONT_HERSHEY_SIMPLEX, horizontalScale, verticalScale, shear, thickness, lineType)
utf8FontFile = "wqy-zenhei.ttf"
utf8FontSize = float64(30.0)
spacing = float64(1.5)
dpi = float64(72)
ctx = new(freetype.Context)
utf8Font = new(truetype.Font)
IplImgFrame, utf8TextImg *opencv.IplImage
redColor = opencv.NewScalar(0, 0, 255, 0) // red - (blue, green, red, alpha)
cyanColor = opencv.NewScalar(255, 255, 0, 0) // cyan - (blue, green, red, alpha)
red = color.RGBA{255, 0, 0, 255}
blue = color.RGBA{0, 0, 255, 255}
white = color.RGBA{255, 255, 255, 255}
black = color.RGBA{0, 0, 0, 255}
background *image.RGBA
// more color at https://github.com/golang/image/blob/master/colornames/table.go
)
func opencvImageBGRToBGRA(img *opencv.IplImage) opencv.IplImage {
// The image frames from camera is in RGB (3 channels )
// We need to convert the frames to RGBA (4 channels )
// so that we can perform copy and paste the UTF8 strings
// into the region of interest.
// Using the ToImage() function will work, but will cause delay in refresh rate.
// Use CvtColor() function for the best result
//fmt.Println("img channels : ", img.Channels())
w := img.Width()
h := img.Height()
// create a IplImage with 4 channels
tmp := opencv.CreateImage(w, h, opencv.IPL_DEPTH_8U, 4)
// upgrade BGR to BGRA ( 3 to 4 channels)
opencv.CvtColor(img, tmp, opencv.CV_BGR2BGRA)
//fmt.Println("tmp channels : ", tmp.Channels())
return *tmp
}
func BGRAToBGR(img *opencv.IplImage) opencv.IplImage {
w := img.Width()
h := img.Height()
// create a IplImage with 3 channels
tmp := opencv.CreateImage(w, h, opencv.IPL_DEPTH_8U, 3)
// downgrade BGRA to BGR ( 4 to 3 channels)
opencv.CvtColor(img, tmp, 1)
// why use integer value of 1?
// see http://docs.opencv.org/3.1.0/df/d4e/group__imgproc__c.html
return *tmp
}
func processFrameAndUpdate() {
var UTF8text = []string{
`©, 世界,Мир,World,세계,Dunia,κόσμος`,
`全世界 愛Go語, Весь мир любовь golang`,
`Όλος ο κόσμος αγάπη golang,전세계 Go언어`,
}
// Draw the text to the background
pt := freetype.Pt(10, 10+int(ctx.PointToFixed(utf8FontSize)>>6))
// not all utf8 fonts are supported by wqy-zenhei.ttf
// use your own language true type font file if your language cannot be printed
for _, str := range UTF8text {
_, err := ctx.DrawString(str, pt)
if err != nil {
fmt.Println(err)
return
}
pt.Y += ctx.PointToFixed(utf8FontSize * spacing)
}
// convert background from image.Image type to opencv.IplImage
utf8TextImg = opencv.FromImage(background)
// after converting to opencv IplImage...the transparency is gone!
// test := utf8TextImg.ToImage()
// any way to make the UTF8 string to have transparent background ??
// maybe http://stackoverflow.com/questions/32350604/create-a-transparent-image-in-opencv
for {
if webCamera.GrabFrame() && !stopCamera {
IplImgFrame = webCamera.RetrieveFrame(1)
if IplImgFrame != nil {
// we need to convert IplImgFrame number of
// channels to 4 instead of 3.
// otherwise the opencv.Copy() function below will crash...
*IplImgFrame = opencvImageBGRToBGRA(IplImgFrame)
// see https://www.socketloop.com/tutorials/golang-get-current-time
currentTime := time.Now().Local().Format("2006-01-02 15:04:05 +0800")
// set ROI(Region Of Interest) in IplImageFrame
rect := opencv.NewRect(sliderPosX, sliderPosY, backgroundWidth, backgroundHeight)
IplImgFrame.SetROI(rect)
// copy and paste our UTF8 runes into ROI via Copy
opencv.Copy(utf8TextImg, IplImgFrame, nil)
IplImgFrame.ResetROI() // don't forget this!
opencv.Rectangle(IplImgFrame,
opencv.Point{sliderPosX + backgroundWidth, sliderPosY},
opencv.Point{sliderPosX, sliderPosY + backgroundHeight},
opencv.ScalarAll(0.0), 2, 2, 0)
//position := opencv.Point{sliderPosX, sliderPosY}
//textFont.PutText(IplImgFrame, "Hello World!", position, redColor)
textFont.PutText(IplImgFrame, currentTime, opencv.Point{sliderPosX, sliderPosY + int(verticalScale*200.0)}, cyanColor)
win.ShowImage(IplImgFrame)
}
}
}
}
func main() {
cores := runtime.NumCPU()
fmt.Printf("This machine has %d CPU cores. Using all cores. \n", cores)
// maximize CPU usage for maximum performance
runtime.GOMAXPROCS(cores)
// download font from http://www.slackware.com/~alien/slackbuilds/wqy-zenhei-font-ttf/build/wqy-zenhei-0.4.23-1.tar.gz
// extract wqy-zenhei.ttf to the same folder as this program
// Read the font data - for this example, we load the Chinese fontfile wqy-zenhei.ttf,
// but it will display any utf8 fonts such as Russian, Japanese, Korean, etc as well.
// some utf8 fonts cannot be displayed. You need to use your own language .ttf file
fontBytes, err := ioutil.ReadFile(utf8FontFile)
if err != nil {
log.Println(err)
return
}
utf8Font, err = freetype.ParseFont(fontBytes)
if err != nil {
log.Println(err)
return
}
// we use red on white to make our text more visible in the camera video feed
// if you plan to use the fontForeGroundColor only. Setting background to transparent with image.Transparent
// will cause it to appear as black on camera feed. Why? because the OpenCV camera video capture images' color model cannot
// support transparency. See https://blog.golang.org/go-imagedraw-package.
//fontForeGroundColor, fontBackGroundColor := image.NewUniform(white), image.Transparent
fontForeGroundColor, fontBackGroundColor := image.NewUniform(red), image.NewUniform(white)
background = image.NewRGBA(image.Rect(0, 0, backgroundWidth, backgroundHeight))
draw.Draw(background, background.Bounds(), fontBackGroundColor, image.ZP, draw.Src)
ctx = freetype.NewContext()
ctx.SetDPI(dpi) //screen resolution in Dots Per Inch
ctx.SetFont(utf8Font)
ctx.SetFontSize(utf8FontSize) //font size in points
ctx.SetClip(background.Bounds())
ctx.SetDst(background)
ctx.SetSrc(fontForeGroundColor)
// the rest will be inside processFrameAndUpdate() function
// a new OpenCV window
win = opencv.NewWindow("Display UTF8 text or runes on Go-OpenCV camera feed")
defer win.Destroy()
// activate webCamera
webCamera = opencv.NewCameraCapture(opencv.CV_CAP_ANY) // autodetect
if webCamera == nil {
panic("Unable to open camera")
}
defer webCamera.Release()
// get some data from camera
width = int(webCamera.GetProperty(opencv.CV_CAP_PROP_FRAME_WIDTH))
height = int(webCamera.GetProperty(opencv.CV_CAP_PROP_FRAME_HEIGHT))
fmt.Println("Camera width : ", width)
fmt.Println("Camera height : ", height)
// open up a new "pure" OpenCV window first
go processFrameAndUpdate() // goroutine to update feed from camera
// then our "floating" GTK toolbar
gtk.Init(nil)
window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
window.SetPosition(gtk.WIN_POS_CENTER)
window.SetTitle("Example of writing UTF8 text on Go-OpenCV video capture!")
window.SetIconName("gtk-dialog-info")
window.Connect("destroy", func(ctx *glib.CallbackContext) {
println("got destroy!", ctx.Data().(string))
gtk.MainQuit()
}, "Happy coding!")
vbox := gtk.NewVBox(false, 1)
//--------------------------------------------------------
// GtkVPaned
//--------------------------------------------------------
vpaned := gtk.NewVPaned()
vbox.Add(vpaned)
//--------------------------------------------------------
// GtkFrame
//--------------------------------------------------------
frame1 := gtk.NewFrame("Adjust X & Y co-ordinates to place the text location :")
framebox1 := gtk.NewVBox(false, 1)
frame1.Add(framebox1)
//--------------------------------------------------------
// GtkScale
//--------------------------------------------------------
scaleXHBox := gtk.NewHBox(false, 1)
scaleX := gtk.NewHScaleWithRange(0, float64(width), 1)
scaleX.Connect("value-changed", func() {
//println("scale:", int(scale.GetValue()))
sliderPosX = int(scaleX.GetValue())
statusbar.Push(statusbar.GetContextId("go-gtk"), "X : "+strconv.Itoa(sliderPosX)+" Y : "+strconv.Itoa(sliderPosY))
})
scaleXHBox.Add(scaleX)
framebox1.PackStart(scaleXHBox, false, false, 0)
scaleYHBox := gtk.NewHBox(false, 1)
scaleY := gtk.NewHScaleWithRange(0, float64(height), 1)
scaleY.Connect("value-changed", func() {
//println("scale:", int(scale.GetValue()))
sliderPosY = int(scaleY.GetValue())
statusbar.Push(statusbar.GetContextId("go-gtk"), "X : "+strconv.Itoa(sliderPosX)+" Y : "+strconv.Itoa(sliderPosY))
})
scaleYHBox.Add(scaleY)
framebox1.PackStart(scaleYHBox, false, false, 0)
vpaned.Pack1(frame1, false, false)
//--------------------------------------------------------
// GtkHBox
//--------------------------------------------------------
buttons := gtk.NewHBox(false, 1)
//--------------------------------------------------------
// GtkButton
//--------------------------------------------------------
quitButton := gtk.NewButtonWithLabel("Quit")
quitButton.Clicked(func() {
stopCamera = true
webCamera.Release() // don't forget to release !!
gtk.MainQuit()
})
buttons.Add(quitButton)
framebox1.PackStart(buttons, false, false, 0)
//--------------------------------------------------------
// GtkVSeparator
//--------------------------------------------------------
vsep := gtk.NewVSeparator()
framebox1.PackStart(vsep, false, false, 0)
statusbar = gtk.NewStatusbar()
//context_id := statusbar.GetContextId("go-gtk")
//--------------------------------------------------------
// GtkStatusbar
//--------------------------------------------------------
framebox1.PackStart(statusbar, false, false, 0)
//--------------------------------------------------------
// Event
//--------------------------------------------------------
window.Add(vbox)
window.SetSizeRequest(600, 128)
window.ShowAll()
gtk.Main()
}
NOTES:
If the link for wqy-zenhei.ttf
is not available, you can download it here.
Is there another way to use PutText() with UTF-8 characters? Yeah, see http://blog.csdn.net/fengbingchun/article/details/8029337 ( in Chinese language and C++ ) by [email protected] - the original developer of Go-OpenCV package.
One problem that I still could not solve is how to make the UTF-8 strings to have a transparent background. At this moment, it is good enough for me because I will use the UTF-8 characters as a label on top of a rectangle. Maybe I'll solve this next in future.
Do not scroll the copy and paste image beyond the border of the video capture. Otherwise, the program will crash because of the opencv.Copy() function! :P
Extracted from wqy-zenhei's
README on language coverage.
----------------------------------------------------------
III. Language Coverage
The following table is based on the locale data provided by fontconfig
(generated by langcover.pl from Dejavu Project
http://dejavu.sourceforge.net/wiki/index.php/Font_utilities).
ZenHei
aa Afar 100% (62/62)
ab Abkhazia 75% (68/90)
af Afrikaans 100% (69/69)
am Amharic (0/264)
ar Arabic (0/125)
as (0/89)
ast Asturian 100% (72/72)
ava Avaric 100% (67/67)
ay Aymara 100% (60/60)
az Azerbaijani 85% (127/148)
az-ir Azerbaijani in Iran (0/130)
ba Bashkir 78% (64/82)
bam Bambara 90% (54/60)
be Byelorussian 100% (68/68)
bg Bulgarian 100% (60/60)
bh Bihari (Devanagari script) (0/68)
bho Bhojpuri (Devanagari script) (0/68)
bi Bislama 100% (58/58)
bin Edo or Bini 92% (72/78)
bn Bengali (0/89)
bo Tibetan (0/95)
br Breton 100% (64/64)
bs Bosnian 85% (53/62)
bua Buriat (Buryat) 94% (66/70)
ca Catalan 100% (74/74)
ce Chechen 100% (67/67)
ch Chamorro 100% (58/58)
chm Mari (Lower Cheremis / Upper Cheremis) 86% (66/76)
chr Cherokee (0/85)
co Corsican 100% (85/85)
cs Czech 80% (66/82)
cu Old Church Slavonic 71% (74/103)
cv Chuvash 89% (66/74)
cy Welsh 87% (68/78)
da Danish 100% (70/70)
de German 100% (60/60)
dz Dzongkha (0/95)
el Greek 100% (70/70)
en English 100% (73/73)
eo Esperanto 81% (52/64)
es Spanish 100% (67/67)
et Estonian 93% (60/64)
eu Basque 100% (56/56)
fa Persian (0/129)
fi Finnish 93% (59/63)
fj Fijian 100% (52/52)
fo Faroese 100% (68/68)
fr French 100% (85/85)
ful Fulah (Fula) 87% (54/62)
fur Friulian 100% (66/66)
fy Frisian 100% (75/75)
ga Irish 77% (62/80)
gd Scots Gaelic 100% (70/70)
gez Ethiopic (Geez) (0/218)
gl Galician 100% (66/66)
gn Guarani 94% (66/70)
gu Gujarati (0/78)
gv Manx Gaelic 100% (54/54)
ha Hausa 86% (52/60)
haw Hawaiian 92% (58/63)
he Hebrew (0/27)
hi Hindi (Devanagari script) (0/68)
ho Hiri Motu 100% (52/52)
hr Croatian 85% (53/62)
hu Hungarian 94% (66/70)
hy Armenian (0/77)
ia Interlingua 100% (52/52)
ibo Igbo 89% (52/58)
id Indonesian 100% (54/54)
ie Interlingue 100% (52/52)
ik Inupiaq (Inupiak, Eskimo) 100% (68/68)
io Ido 100% (52/52)
is Icelandic 100% (70/70)
it Italian 100% (73/73)
iu Inuktitut (0/161)
ja Japanese 100% (6538/6538)
ka Georgian (0/33)
kaa Kara-Kalpak (Karakalpak) 84% (66/78)
ki Kikuyu 92% (52/56)
kk Kazakh 84% (65/77)
kl Greenlandic 95% (77/81)
km Khmer (0/70)
kn Kannada (0/80)
ko Korean 100% (2443/2443)
kok Kokani (Devanagari script) (0/68)
ks Kashmiri (Devanagari script) (0/68)
ku Kurdish 90% (58/64)
ku-ir Kurdish in Iran (0/32)
kum Kumyk 100% (66/66)
kv Komi (Komi-Permyak/Komi-Siryan) 97% (68/70)
kw Cornish 89% (57/64)
ky Kirgiz 94% (66/70)
la Latin 83% (57/68)
lb Luxembourgish (Letzeburgesch) 100% (75/75)
lez Lezghian (Lezgian) 100% (67/67)
ln Lingala 90% (73/81)
lo Lao (0/65)
lt Lithuanian 75% (53/70)
lv Latvian 73% (57/78)
mg Malagasy 100% (56/56)
mh Marshallese 88% (55/62)
mi Maori 89% (57/64)
mk Macedonian 90% (38/42)
ml Malayalam (0/78)
mn Mongolian (0/130)
mo Moldavian 95% (122/128)
mr Marathi (Devanagari script) (0/68)
mt Maltese 91% (66/72)
my Burmese (Myanmar) (0/48)
nb Norwegian Bokmal 100% (70/70)
nds Low Saxon 100% (59/59)
ne Nepali (Devanagari script) (0/68)
nl Dutch 100% (83/83)
nn Norwegian Nynorsk 100% (76/76)
no Norwegian (Bokmal) 100% (70/70)
ny Chichewa 100% (54/54)
oc Occitan 100% (70/70)
om Oromo or Galla 100% (52/52)
or Oriya (0/79)
os Ossetic 100% (66/66)
pa Punjabi (Gurumukhi script) (0/63)
pl Polish 81% (57/70)
ps-af Pashto in Afghanistan (0/49)
ps-pk Pashto in Pakistan (0/49)
pt Portuguese 100% (83/83)
rm Rhaeto-Romance (Romansch) 100% (66/66)
ro Romanian 90% (56/62)
ru Russian 100% (66/66)
sa Sanskrit (Devanagari script) (0/68)
sah Yakut 86% (66/76)
sco Scots 92% (52/56)
se North Sami 89% (59/66)
sel Selkup (Ostyak-Samoyed) 100% (66/66)
sh Serbo-Croatian 100% (76/76)
si Sinhala (Sinhalese) (0/77)
sk Slovak 80% (69/86)
sl Slovenian 85% (53/62)
sm Samoan 100% (53/53)
sma South Sami 100% (60/60)
smj Lule Sami 100% (60/60)
smn Inari Sami 89% (61/68)
sms Skolt Sami 78% (63/80)
so Somali 100% (52/52)
sq Albanian 100% (56/56)
sr Serbian 100% (76/76)
sv Swedish 100% (68/68)
sw Swahili 100% (52/52)
syr Syriac (0/45)
ta Tamil (0/48)
te Telugu (0/80)
tg Tajik 84% (66/78)
th Thai (0/87)
ti-er Eritrean Tigrinya (0/256)
ti-et Ethiopian Tigrinya (0/282)
tig Tigre (0/221)
tk Turkmen 89% (66/74)
tl Tagalog (0/19)
tn Tswana 100% (56/56)
to Tonga 100% (53/53)
tr Turkish 92% (65/70)
ts Tsonga 100% (52/52)
tt Tatar 86% (66/76)
tw Twi 79% (58/73)
tyv Tuvinian 94% (66/70)
ug Uighur (0/125)
uk Ukrainian 97% (70/72)
ur Urdu (0/145)
uz Uzbek 88% (60/68)
ven Venda 83% (52/62)
vi Vietnamese 43% (85/194)
vo Volapuk 100% (54/54)
vot Votic 93% (58/62)
wa Walloon 100% (70/70)
wen Sorbian languages (lower and upper) 76% (58/76)
wo Wolof 100% (66/66)
xh Xhosa 100% (52/52)
yap Yapese 100% (58/58)
yi Yiddish (0/27)
yo Yoruba 77% (92/119)
zh-936 GBK Chinese national standard 100% (21921/21920)
zh-cn Chinese (simplified) 100% (6765/6765)
zh-hk Chinese Hong Kong Supplementary Character Set 100% (2213/2213)
zh-mo Chinese in Macau 100% (2213/2213)
zh-sg Chinese in Singapore 100% (6765/6765)
zh-tw Chinese (traditional) 100% (13063/13063)
zu Zulu 100% (52/52)
----------------------------------------------------------
Happy coding!
References:
http://docs.opencv.org/3.1.0/df/d4e/groupimgprocc.html (for the enumeration integers, something that go-opencv doesn't cover )
https://godoc.org/golang.org/x/image/colornames
https://github.com/golang/freetype/blob/master/example/freetype/main.go
https://godoc.org/github.com/golang/freetype
https://www.socketloop.com/references/golang-image-paletted-set-and-setcolorindex-functions-example
http://blog.csdn.net/fengbingchun/article/details/8029337
http://stackoverflow.com/questions/10991523/opencv-draw-an-image-over-another-image
See also : Golang : Add text to image and get OpenCV's X, Y co-ordinates example
By Adam Ng
IF you gain some knowledge or the information here solved your programming problem. Please consider donating to the less fortunate or some charities that you like. Apart from donation, planting trees, volunteering or reducing your carbon footprint will be great too.
Advertisement
Tutorials
+29k Golang : Save map/struct to JSON or XML file
+12.3k Golang : Send data to /dev/null a.k.a blackhole with ioutil.Discard
+6.9k Golang : Dealing with postal or zip code example
+8.7k Golang : Build and compile multiple source files
+31.2k Golang : Example for ECDSA(Elliptic Curve Digital Signature Algorithm) package functions
+5.2k Golang : If else example and common mistake
+9.4k Golang : interface - when and where to use examples
+31.1k Golang : Get local IP and MAC address
+13.7k Golang : Get current time
+20.4k Golang : Saving private and public key to files
+7.1k Golang : Check to see if *File is a file or directory
+13.9k Golang : Chunk split or divide a string into smaller chunk example