LoginSignup
8
5

More than 5 years have passed since last update.

FreeTypeでOpenCV上に文字を描画しよう

Last updated at Posted at 2016-10-30

Advent Calendar にするほどでもないので、FreeTypeをOpenCV上で使うサンプルを載せておく。


材料

  1. TrueType Font
  2. OpenCV
  3. FreeType

今回は、http://mplus-fonts.osdn.jp/ 様の https://ja.osdn.net/projects/mplus-fonts/releases/62344 から mplus-TESTFLIGHT-062.tar.xz を使いました。


したごしらえ

(やらなくてもいい)
フォントデータを毎回ロードするロジックを作ってもいい。
だけど、将来的にOpenCVのライブラリに組み込む前提でライブラリに取り込むことも試作しておこうと思う。

$ xxd -i mplus-1c-thin.ttf > fontdata.c


実行結果

<修正後>
a.png

<修正前>
a.png


修正(2016/11/7)

        gPos.y = gPos.y - ( mface->glyph->metrics.horiBearingY >> 6) ;
+       gPos.x = gPos.x + ( mface->glyph->metrics.horiBearingX >> 6) ;

-                      ptr[ org.x + col * 8 + (7 - bit) ][0] = color[0];
-                      ptr[ org.x + col * 8 + (7 - bit) ][1] = color[1];
-                      ptr[ org.x + col * 8 + (7 - bit) ][2] = color[2];
+                      ptr[ gPos.x + col * 8 + (7 - bit) ][0] = color[0];
+                      ptr[ gPos.x + col * 8 + (7 - bit) ][1] = color[1];
+                      ptr[ gPos.x + col * 8 + (7 - bit) ][2] = color[2];

-           c  = ( ((unsigned char)text[i  ] << 18 ) & 0x700C0 ) |
+           c  = ( ((unsigned char)text[i  ] << 18 ) & 0x70000 ) |
                 ( ((unsigned char)text[i+1] << 12 ) &  0xF000 ) |
                 ( ((unsigned char)text[i+2] <<  6 ) &  0x0FC0 ) |
                 ( ((unsigned char)text[i+3] <<  0 ) &  0x003F ) ;

こんな感じ

all : a.out

fontdata.cpp.o : fontdata.h fontdata.cpp Makefile
    g++ -c fontdata.cpp -o fontdata.cpp.o

main.cpp.o : fontdata.h main.cpp Makefile
    g++ -g -c main.cpp -o main.cpp.o `pkg-config freetype2 opencv --cflags --libs`

a.out : fontdata.cpp.o main.cpp.o
    g++ fontdata.cpp.o main.cpp.o -o a.out `pkg-config freetype2 opencv --cflags --libs`
fontdata.h
#ifndef FONTDATA_H
#define FONTDATA
extern unsigned int mplus_1c_thin_ttf_len;
extern unsigned char mplus_1c_thin_ttf[];
#endif
main.c
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include "fontdata.h"
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftoutln.h>

#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>

using namespace cv;

class FreeTypeWrapper{
private:
    FT_Library mlibrary;
    FT_Face    mface;

    /** 
     *
     */
    void readNextCode(FT_Long &c, int &i, const String &text )
    {
        unsigned char t1 = (unsigned char) text[i];
        if( t1 >= 0xF0) //4 bytes utf
        {
            c  = ( ((unsigned char)text[i  ] << 18 ) & 0x70000 ) |
                 ( ((unsigned char)text[i+1] << 12 ) &  0xF000 ) |
                 ( ((unsigned char)text[i+2] <<  6 ) &  0x0FC0 ) |
                 ( ((unsigned char)text[i+3] <<  0 ) &  0x003F ) ;
            i+=4;
        }
        else if( t1 >= 0xE0) //3 bytes utf
        {
            c  = ( ((unsigned char)text[i  ] << 12 ) & 0xF000 ) |
                 ( ((unsigned char)text[i+1] <<  6 ) & 0x0FC0 ) |
                 ( ((unsigned char)text[i+2] <<  0 ) & 0x003F ) ;
            i+=3;
        }
        else if( t1 >= 0xC2) //2 bytes utf
        {
            c  = ( ((unsigned char)text[i  ] <<  6 ) & 0x0FC0 ) |
                 ( ((unsigned char)text[i+1] <<  0 ) & 0x003F ) ;
            i+=2;
        }
        else if(t1 > 0 )//1 bytes utf
        {
            c  = text[i];
            i+=1;
        }else{
            c = '?';
            i++;
        }
    }

public:
    FreeTypeWrapper(){
        FT_Init_FreeType(&mlibrary);
    FT_New_Memory_Face(mlibrary, 
                       mplus_1c_thin_ttf, 
                       mplus_1c_thin_ttf_len, 0, &mface ); 
    }
    ~FreeTypeWrapper(){
    // 後処理
    FT_Done_FreeType(mlibrary);
    }

public:
void putText(InputOutputArray _img, const String& text, Point org,
              double fontScale, Scalar color,
              bool bottomLeftOrigin )
{

    if ( text.empty() )
    {
        return;
    }

    FT_Set_Pixel_Sizes( mface, fontScale, fontScale );

    cv::Mat img = _img.getMat();

    for( int i = 0 ; text[i] != '\0' ; ){
        FT_Long c;
        readNextCode(c, i, text );
        FT_Load_Char(mface, c, 0 ); 
        FT_Render_Glyph( mface->glyph, FT_RENDER_MODE_MONO );
        FT_Bitmap    *bmp = &(mface->glyph->bitmap);

        Point gPos = org;
        gPos.y = gPos.y - ( mface->glyph->metrics.horiBearingY >> 6) ;
        gPos.x = gPos.x + ( mface->glyph->metrics.horiBearingX >> 6) ;
        int row, col, bit, cl;

        for (row = 0; row < bmp->rows; row ++) {
            if( gPos.y + row > img.rows ) { continue; }
            cv::Vec3b* ptr = img.ptr<cv::Vec3b>( gPos.y + row );
            for (col = 0; col < bmp->pitch; col ++) {
                cl = bmp->buffer[ row * bmp->pitch + col ];
                for(bit = 7; bit >= 0; bit -- ){
                    if ( gPos.x + col * 8 + (7 - bit) > img.cols ) { continue; }
                    if ( (cl >> bit) & 0x01 == 1 ) {
                       ptr[ gPos.x + col * 8 + (7 - bit) ][0] = color[0];
                       ptr[ gPos.x + col * 8 + (7 - bit) ][1] = color[1];
                       ptr[ gPos.x + col * 8 + (7 - bit) ][2] = color[2];
                    }
                }
            }
        }
        org.x += ( mface->glyph->advance.x ) >> 6;
        org.y += ( mface->glyph->advance.y ) >> 6;
    }

}

};
int main()
{
    cv::Mat src = cv::Mat( 480, 640, CV_8UC3 );
    FreeTypeWrapper ftw;
    ftw.putText(src, "ABCDEFGHIJKLMNOPRSTUVWXYZ",  Point(0,16), 16, Scalar(255,255,255), false );
    ftw.putText(src, "ABCDEFGHIJKLMNOPRSTUVWXYZ",  Point(0,40), 24, Scalar(255,255,255), false );
    ftw.putText(src, "ABCDEFGHIJKLMNOPRSTUVWXYZ,", Point(0,64), 32, Scalar(255,255,255), false );
    ftw.putText(src, "いろはにほへと",             Point(0,120), 16, Scalar(255,255,255), false );
    ftw.putText(src, "Happy New Year !!!!!!!!!!",  Point(0,150), 24, Scalar(255,255,255), false );
    ftw.putText(src, "パフォーマンスとかは任せた", Point(0,210), 32, Scalar(255,255,255), false );
    ftw.putText(src, "こんな感じで使えます",       Point(0,250), 32, Scalar(255,255,255), false );
    ftw.putText(src, "赤",                         Point(0,300), 32, Scalar(  0,  0,255), false );
    ftw.putText(src, "青",                         Point(32,300), 32, Scalar(255,  0, 0), false );
    ftw.putText(src, "緑",                         Point(64,300), 32, Scalar( 0, 255, 0), false );
    cv::imwrite("a.png", src);
}
fontdata.c
#include "fontdata.h"

unsigned char mplus_1c_thin_ttf[] = {
  0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x04, 0x00, 0x50,
...
...
...

あ、bottomのオプション作るの忘れてた、てへぺろ。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5