Smorodov 578 Report post Posted August 24, 2011 Предлагаю поразмыслить на досуге над выводом русских букв и произвольных шрифтов. Привожу ниже код, использующий библиотеку freetype, но результат, надо сказать не очень радует. В первую очередь огорчает прорисовка контуров средствами opencv. Если кто решил вопрос человеческой отрисовки шрифтов, прошу поделиться опытом Вот еще полезная ссылка в тему: http://www.codeproject.com/KB/GDI/hersheyfont.aspx // OpenCVFont.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "opencv2/opencv.hpp" #include <ft2build.h> #include FT_FREETYPE_H FT_Library library; FT_Face face; /* handle to face object */ using namespace cv; // all the new API is put into "cv" namespace. Export its content using namespace std; typedef vector<Point> cont; vector<cont> contours; vector<Vec4i> hierarchy; //---------------------------------------------------------- void cubic_bezier(cont& points, const Point2f & from, const Point2f & cp1, const Point2f & cp2, const Point2f & to, int curveResolution ) { points.push_back(Point2f(from.x,from.y)); if (points.size() > 0) { float x0 = points[points.size()-1].x; float y0 = points[points.size()-1].y; float ax, bx, cx; float ay, by, cy; float t, t2, t3; float x, y; // polynomial coefficients cx = 3.0f * (cp1.x - x0); bx = 3.0f * (cp2.x - cp1.x) - cx; ax = to.x - x0 - cx - bx; cy = 3.0f * (cp1.y - y0); by = 3.0f * (cp2.y - cp1.y) - cy; ay = to.y - y0 - cy - by; for (int i = 0; i < curveResolution; i++) { t = (float)i / (float)(curveResolution-1); t2 = t * t; t3 = t2 * t; x = (ax * t3) + (bx * t2) + (cx * t) + x0; y = (ay * t3) + (by * t2) + (cy * t) + y0; points.push_back(Point(x,y)); } } } //---------------------------------------------------------- void quad_bezier(cont& points, float x1, float y1, float x2, float y2, float x3, float y3, int curveResolution) { points.push_back(Point2f(x1,y1)); for(int i=0; i <= curveResolution; i++) { double t = (double)i / (double)(curveResolution); double a = (1.0 - t)*(1.0 - t); double b = 2.0 * t * (1.0 - t); double c = t*t; double x = a * x1 + b * x2 + c * x3; double y = a * y1 + b * y2 + c * y3; points.push_back(Point(x, y)); } } //--------------------------------------------------------------------- static void makeContoursForCharacter(vector<cont> &result,FT_Face &face) { result.clear(); cont Cont; int nContours = face->glyph->outline.n_contours; int startPos = 0; char * tags = face->glyph->outline.tags; FT_Vector * vec = face->glyph->outline.points; for(int k = 0; k < nContours; k++) { Cont.clear(); if( k > 0 ) { startPos = face->glyph->outline.contours[k-1]+1; } int endPos = face->glyph->outline.contours[k]+1; Point2f lastPoint; for(int j = startPos; j < endPos; j++) { if( FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_ON ) { lastPoint.x=(float)vec[j].x; lastPoint.y=(float)-vec[j].y; Cont.push_back(Point(lastPoint.x, lastPoint.y)); } else { if( FT_CURVE_TAG(tags[j]) == FT_CURVE_TAG_CUBIC ) { int prevPoint = j-1; if( j == 0) { prevPoint = endPos-1; } int nextIndex = j+1; if( nextIndex >= endPos) { nextIndex = startPos; } Point2f nextPoint( (float)vec[nextIndex].x, -(float)vec[nextIndex].y ); //we need two control points to draw a cubic bezier bool lastPointCubic = ( FT_CURVE_TAG(tags[prevPoint]) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG(tags[prevPoint]) == FT_CURVE_TAG_CUBIC); if( lastPointCubic ) { Point2f controlPoint1((float)vec[prevPoint].x, (float)-vec[prevPoint].y); Point2f controlPoint2((float)vec[j].x, (float)-vec[j].y); Point2f nextPoint((float) vec[nextIndex].x, -(float) vec[nextIndex].y); cubic_bezier(Cont, lastPoint, controlPoint1, controlPoint2, nextPoint, 8); } } else { Point2f conicPoint( (float)vec[j].x, -(float)vec[j].y ); //If the first point is connic and the last point is connic then we need to create a virutal point which acts as a wrap around if( j == startPos ) { bool prevIsConnic = ( FT_CURVE_TAG( tags[endPos-1] ) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG( tags[endPos-1]) != FT_CURVE_TAG_CUBIC ); if( prevIsConnic ) { Point2f lastConnic((float)vec[endPos - 1].x, (float)-vec[endPos - 1].y); lastPoint.x = (conicPoint.x + lastConnic.y) / 2; lastPoint.y = (conicPoint.x + lastConnic.y) / 2; } } //bool doubleConic = false; int nextIndex = j+1; if( nextIndex >= endPos) { nextIndex = startPos; } Point2f nextPoint( (float)vec[nextIndex].x, -(float)vec[nextIndex].y ); bool nextIsConnic = ( FT_CURVE_TAG( tags[nextIndex] ) != FT_CURVE_TAG_ON ) && ( FT_CURVE_TAG( tags[nextIndex]) != FT_CURVE_TAG_CUBIC ); //create a 'virtual on point' if we have two connic points if( nextIsConnic ) { nextPoint.x = (conicPoint.x + nextPoint.x) / 2; nextPoint.y = (conicPoint.y + nextPoint.y) / 2; } quad_bezier(Cont, lastPoint.x, lastPoint.y, conicPoint.x, conicPoint.y, nextPoint.x, nextPoint.y, 8); if( nextIsConnic ) { lastPoint = nextPoint; } } } //end for } result.push_back(Cont); } } //----------------------------------------------------------------------- void PrintString(Mat& img,std::wstring str,int x,int y) { FT_Bool use_kerning=0; FT_UInt previous=0; use_kerning = FT_HAS_KERNING( face ); float posx=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 */ float dx=slot->advance.x/64; FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT ); makeContoursForCharacter(contours,face); if ( use_kerning && previous && glyph_index ) { FT_Vector delta; FT_Get_Kerning( face, previous, glyph_index, FT_KERNING_DEFAULT, &delta ); posx += (delta.x/64); } for(int i=0;i<contours.size();i++) { for(int j=0;j<contours[i].size();j++) { contours[i][j].x=(float)contours[i][j].x/64.0; contours[i][j].y=(float)contours[i][j].y/64.0; } } posx+=(dx); drawContours(img,contours,-1, Scalar(255,255,255), -1,CV_AA,noArray(),INT_MAX,cv::Point(posx+x,y)); contours.clear(); previous = glyph_index; } } //----------------------------------------------------------------------- int _tmain(int argc, _TCHAR* argv[]) { FT_Init_FreeType( &library ); FT_New_Face( library,"arial.ttf",0,&face ); FT_Set_Pixel_Sizes(face,40,0); FT_Select_Charmap(face, FT_ENCODING_UNICODE); Mat img(480,640,CV_8UC3); PrintString(img,L"Привет!",100,100); // http://freetype.org/freetype2/docs/reference/ft2-outline_processing.html#FT_Outline //printf("%d",x); cv::imshow("win",img); cv::waitKey(0); //CvPoint2D32f return 0; } Share this post Link to post Share on other sites
Nuzhny 243 Report post Posted August 25, 2011 Одной из самых мощных библиотек считается AGG. Но я не пробовал. Было дело, рисовал весь алфавит средствами GDI на картинке, а после сам рисовал текст: вырезал каждую букву, учитывая отступы. Но подход тяжёлый: на разных версиях Windows шрифты с одними и теми же флагами рисуются по-разному. 1 Share this post Link to post Share on other sites
mrgloom 242 Report post Posted August 25, 2011 http://freetype.sourceforge.net/freetype1/image/freetest.png ну чо то не впечатляет, сглаживания то нет наверно. http://freetype.sourceforge.net/freetype1/image/scrshot3.png а может и есть) а так то наверно, можно брать картинку из opencv и на ней рисовать сторонними средствами. а QT тоже не умеет русский? http://opencv.willowgarage.com/documentation/cpp/highgui_qt_new_functions 1 Share this post Link to post Share on other sites
Smorodov 578 Report post Posted August 25, 2011 Спасибо. AGG - действительно мощная штука. Хотелось бы рисовать векторным способом. При рендеринге шрифта средствами библиотеки freetype (она умеет сама делать из букв битмапы), нужны будут дополнительные танцы с бубном для наложения букв с эффектом прозрачности, при этом, скорее всего возникнут проблемы с переходом на границах. Вероятно, здесь действительно наиболее подходящим способом является адаптация шрифтов Hershey (плоттерный тип шрифта) который, кстати говоря, содержится в файле, лежащем в директории modules/core/tables.cpp (принцип описания шрифтов стандартный можно найти например здесь: http://paulbourke.net/dataformats/hershey/ ). Там же есть и русские буквы, только пока не сообразил как прикрутить. Сама функция отрисовки текста содержится в файле drawing.cpp в той же директории. ЗЫ: QT надо посмотреть, может правда получится оттуда содрать что нибудь. ЗЫЗЫ: Посмотрел, как реализовано в QT. Там рисуется средствами класса QPainter. Можно применить похожий подход для VS - захватить в BITMAP вывести текст обычным способом или через AGG и вернуть BITMAP обратно. При этом можно включить прозрачный фон. Share this post Link to post Share on other sites
Smorodov 578 Report post Posted August 25, 2011 Решил вопрос попиксельным копированием (вроде не очень грузит проц.) результатом вполне доволен. вот код: // OpenCVFont.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "opencv2/opencv.hpp" #include <ft2build.h> #include FT_FREETYPE_H FT_Library library; FT_Face face; /* handle to face object */ using namespace cv; // all the new API is put into "cv" namespace. Export its content using namespace std; //----------------------------------------------------------------------- void my_draw_bitmap(Mat& img,FT_Bitmap* bitmap,int x,int y) { 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]; if(val!=0) { img.at<Vec3b>(i+y,j+x)=Vec3b(val,val,val); } } } } //----------------------------------------------------------------------- void PrintString(Mat& img,std::wstring str,int x,int y) { 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 ); previous = glyph_index; } } //----------------------------------------------------------------------- int _tmain(int argc, _TCHAR* argv[]) { FT_Init_FreeType( &library ); FT_New_Face( library,"arial.ttf",0,&face ); FT_Set_Pixel_Sizes(face,40,0); FT_Select_Charmap(face, FT_ENCODING_UNICODE); Mat img(480,640,CV_8UC3); PrintString(img,L"Привет!",100,100); cv::imshow("win",img); cv::waitKey(0); return 0; } 2 Share this post Link to post Share on other sites
Smorodov 578 Report post Posted August 25, 2011 Добавил многострочный вывод. // OpenCVFont.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "opencv2/opencv.hpp" #include <ft2build.h> #include FT_FREETYPE_H FT_Library library; FT_Face face; /* handle to face object */ using namespace cv; // all the new API is put into "cv" namespace. Export its content using namespace std; //----------------------------------------------------------------------- void my_draw_bitmap(Mat& img,FT_Bitmap* bitmap,int x,int y) { 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]; if(val!=0) { img.at<Vec3b>(i+y,j+x)=Vec3b(val,val,val); } } } } //----------------------------------------------------------------------- float PrintString(Mat& img,std::wstring str,int x,int y) { 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 ); previous = glyph_index; } return prev_yadv; } //----------------------------------------------------------------------- void PrintText(Mat& img,std::wstring str,int x,int y) { 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); } PrintString(img,str,x,y+posy); } //----------------------------------------------------------------------- int _tmain(int argc, _TCHAR* argv[]) { FT_Init_FreeType( &library ); FT_New_Face( library,"arial.ttf",0,&face ); FT_Set_Pixel_Sizes(face,12,0); FT_Select_Charmap(face, FT_ENCODING_UNICODE); Mat img(480,640,CV_8UC3); std::wstring str= L"Мой дядя самых честных правил,\n\ Когда не в шутку занемог,\n\ Он уважать себя заставил \n\ И лучше выдумать не мог.\n\ Его пример другим наука;\n\ Но, боже мой, какая скука\n\ С больным сидеть и день и ночь,\n\ Не отходя ни шагу прочь!\n\ Какое низкое коварство\n\ Полу-живого забавлять,\n\ Ему подушки поправлять,\n\ Печально подносить лекарство,\n\ Вздыхать и думать про себя:\n\ Когда же чёрт возьмет тебя!\n"; PrintText(img,str,100,50); cv::imshow("win",img); cv::waitKey(0); return 0; } С больным сидеть и день и ночь,\n\ Не отходя ни шагу прочь!\n\ Какое низкое коварство\n\ Полу-живого забавлять,\n\ Ему подушки поправлять,\n\ Печально подносить лекарство,\n\ Вздыхать и думать про себя:\n\ Когда же чёрт возьмет тебя!\n"; PrintText(img,str,100,50); cv::imshow("win",img); cv::waitKey(0); return 0; } Share this post Link to post Share on other sites
dl23 0 Report post Posted May 31, 2017 Данный способ актуален? Или имеются уже варианты по лучше? Share this post Link to post Share on other sites
Smorodov 578 Report post Posted May 31, 2017 К сожалению актуален. Share this post Link to post Share on other sites