I want to write persian (Farsi) text on an image in C++, preferably in OpenCV.
I tried cv::putText
which leads to writing ????...?
instead the text. Also I tested cv::addText
but I got not implemented exception
. Additional I also checked opencv docs here which seems problematic. When I install opencv 4.8 via "VC package manager" there is no freetype class
on it.
Finally I find a solution using this link. It writes persian strings 1 by 1 (characters are divided and are in reverse order).
#include "opencv2/opencv.hpp"
#include "../UTL/Util.h"
#include "ft2build.h"
#include FT_FREETYPE_H
FT_Library library;
FT_Face face;
using namespace cv;
using namespace std;
//-----------------------------------------------------------------------
void my_draw_bitmap(Mat& img, FT_Bitmap* bitmap, int x, int y, Scalar color)
{
Scalar src_col, dst_col;
for (int i = 0; i < bitmap->rows; i++)
{
for (int j = 0; j < bitmap->width; j++)
{
unsigned char val = bitmap->buffer[j + i * bitmap->pitch];
float mix = (float)val / 255.0;
if (val != 0)
{
src_col = Scalar(img.at<Vec3b>(i + y, j + x));
dst_col = mix * color + (1.0 - mix) * src_col;
img.at<Vec3b>(i + y, j + x) = Vec3b(dst_col[0], dst_col[1], dst_col[2]);
}
}
}
}
//-----------------------------------------------------------------------
float PrintString(Mat& img, std::wstring str, int x, int y, Scalar color)
{
FT_Bool use_kerning = 0;
FT_UInt previous = 0;
use_kerning = FT_HAS_KERNING(face);
float prev_yadv = 0;
float posx = 0;
float posy = 0;
float dx = 0;
for (int k = 0; k < str.length(); k++)
{
int glyph_index = FT_Get_Char_Index(face, str.c_str()[k]);
FT_GlyphSlot slot = face->glyph; // a small shortcut
if (k > 0) { dx = slot->advance.x / 64; }
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
prev_yadv = slot->metrics.vertAdvance / 64;
if (use_kerning && previous && glyph_index)
{
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
posx += (delta.x / 64);
}
posx += (dx);
my_draw_bitmap(img, &slot->bitmap, posx + x + slot->bitmap_left, y - slot->bitmap_top + posy, color);
previous = glyph_index;
}
return prev_yadv;
}
//-----------------------------------------------------------------------
void PrintText(Mat& img, std::wstring str, int x, int y, Scalar color)
{
float posy = 0;
for (int pos = str.find_first_of(L'\n'); pos != wstring::npos; pos = str.find_first_of(L'\n'))
{
std::wstring substr = str.substr(0, pos);
str.erase(0, pos + 1);
posy += PrintString(img, substr, x, y + posy, color);
}
PrintString(img, str, x, y + posy, color);
}
//-----------------------------------------------------------------------
int main(int argc, char* argv[])
{
FT_Init_FreeType(&library);
auto path_to_font_file = "arial.ttf";
FT_New_Face(library, path_to_font_file, 0, &face);
FT_Set_Pixel_Sizes(face, 36, 0);
FT_Select_Charmap(face, FT_Encoding::FT_ENCODING_UNICODE);
Mat im(100, 300, CV_8UC3, Scalar(0,0,0));
wstring str = L"خلیج فارس";
PrintText(im, str, 100, 50, Scalar(255, 255, 255));
cv::imshow("win", im);
cv::waitKey(0);
return 0;
}
but the result is:
which should be:
how can fix the problem in the above? any other approach will be appreciated.