OpenCV2在圖像中寫漢字

通過以往文獻的閱讀,中文等字符可以藉助OpenCV在圖像中寫入。但是往往都停留在OpenCV 1.0版本的調用上。本文在前人的基礎上,修改完善了CvxText.h、CvxText.cpp兩個文件,支持單通道圖像、三通道圖像的中文等字符的顯示,支持OpenCV 2.0的調用。所用參考文獻一一列舉)!


1.創建一個控制檯工程,配置OpenCV(OpenCV初學者請詳讀參考文獻)。


2.配置freetype:下載freetype。編譯相應的版本得到對應lib文件(debug、release, win32、×64),並配置(這裏我用的是freetype-2.6.5),其配置過程跟OpenCV有些類似,參考文獻中有詳細的測試過程,不贅述。


注意:使用freetype時,一定不要忘記將C:\WINDOWS\Fonts下的某一中文字符庫拷貝到工程目錄中!!我用的是微軟雅黑字符庫msyh.ttf。


3.將CvxText.h、CvxText.cpp文件複製到工程文件夾下。該類可以成功在單通道圖像、3通道圖像中添加中文、英文、字符等文本,可以替換不同的字體庫,觀察效果。


 


幾個知識點:


1、關於Scalar


OpenCV 1.0用的是cvScalar,2.0用的是Scalar。表示了具有4個元素的數組。次類型在OpenCV中被大量用於傳遞像素值。用它來表示RGB顏色值(三個參數)。如果用不到第四個參數,則無需定義。


如果給出以下顏色參數表達式:


Scalar( a, b, c )


那麼定義的RGB顏色值爲:Red = c,Green= bandBlue=a


也就是說如果用


Mat__I =img;


那麼在數值上_I(r,c)[0] = a, _I(r,c)[1] = b, _I(r,c)[2] = c.


因此在程序中可以用遍歷圖像數據的方法來進行文本與圖像色彩的融合(詳見函數putWChar


)。而字體定義用的也是Scalar,只是用到了他是多元組的關係,跟色彩沒有啥關聯。


2、 關於opencv1.0中img->origin


在使用opencv1.0顯示圖像時會出現圖像倒立的情況,IplImage的origin屬性有關係。origin爲0表示頂左結構,即圖像的原點是左上角,如果爲1爲左下角。一般從硬盤讀入的圖片或者通過cvCreateImage方法創建的IplImage圖片默認的origin爲0,即顯示的時候都是正的。


因此在修改先前代碼時,直接將該句賦爲0.


//====================================================================
//
// 文件: CvxText.h
//
// 說明: OpenCV漢字輸出
//
//====================================================================
 
#ifndef OPENCV_CVX_TEXT_2016_NEW_H
#define OPENCV_CVX_TEXT_2016_NEW_H
 
/**
* \file CvxText.h
* \brief OpenCV漢字輸出接口
*
* 實現了漢字輸出功能。
*/
 
#include <ft2build.h>
#include FT_FREETYPE_H
 
#include "opencv2/opencv.hpp"
using namespace cv;
 
class CvxText  
{
    CvxText& operator=(const CvxText&);
 
public:
    CvxText(const char *freeType);
    virtual ~CvxText();
 
    /**
    * 獲取字體。目前有些參數尚不支持。
    *
    * \param font        字體類型, 目前不支持
    * \param size        字體大小/空白比例/間隔比例/旋轉角度
    * \param underline   下畫線
    * \param diaphaneity 透明度
    *
    * \sa setFont, restoreFont
    */
    void getFont(int *type, Scalar *size=NULL, bool *underline=NULL, float *diaphaneity=NULL);
 
    /**
    * 設置字體。目前有些參數尚不支持。
    *
    * \param font        字體類型, 目前不支持
    * \param size        字體大小/空白比例/間隔比例/旋轉角度
    * \param underline   下畫線
    * \param diaphaneity 透明度
    *
    * \sa getFont, restoreFont
    */
    void setFont(int *type, Scalar *size=NULL, bool *underline=NULL, float *diaphaneity=NULL);
 
    /**
    * 恢復原始的字體設置。
    *
    * \sa getFont, setFont
    */
    void restoreFont();
 
 
    /**
    * 輸出漢字(顏色默認爲黑色)。遇到不能輸出的字符將停止。
    *
    * \param img  輸出的影象
    * \param text 文本內容
    * \param pos  文本位置
    *
    * \return 返回成功輸出的字符長度,失敗返回-1。
    */
    int putText(Mat img, const char *text, Point pos);
 
    /**
    * 輸出漢字(顏色默認爲黑色)。遇到不能輸出的字符將停止。
    *
    * \param img  輸出的影象
    * \param text 文本內容
    * \param pos  文本位置
    *
    * \return 返回成功輸出的字符長度,失敗返回-1。
    */
    int putText(Mat img, const wchar_t *text, Point pos);
 
    /**
    * 輸出漢字。遇到不能輸出的字符將停止。
    *
    * \param img   輸出的影象
    * \param text  文本內容
    * \param pos   文本位置
    * \param color 文本顏色
    *
    * \return 返回成功輸出的字符長度,失敗返回-1。
    */
    int putText(Mat img, const char *text, Point pos, Scalar color);
 
    /**
    * 輸出漢字。遇到不能輸出的字符將停止。
    *
    * \param img   輸出的影象
    * \param text  文本內容
    * \param pos   文本位置
    * \param color 文本顏色
    *
    * \return 返回成功輸出的字符長度,失敗返回-1。
    */
    int putText(Mat img, const wchar_t *text, Point pos, Scalar color);
 
 
private:
    // 輸出當前字符, 更新m_pos位置
    void putWChar(Mat img, wchar_t wc, Point &pos, Scalar color);
 
 
private:
    FT_Library   m_library;   // 字庫
    FT_Face      m_face;      // 字體
 
    // 默認的字體輸出參數
    int          m_fontType;
    Scalar       m_fontSize;
    bool         m_fontUnderline;
    float        m_fontDiaphaneity;
};
 
#endif // OPENCV_CVX_TEXT_2007_08_31_H</ft2build.h>





#pragma once
#include <wchar.h>
#include 
#include <locale.h>
#include <ctype.h>
 
#include "CvxText.h"
using namespace cv;
 
//====================================================================
//====================================================================
 
// 打開字庫
CvxText::CvxText(const char *freeType)
{
    assert(freeType != NULL);
 
    // 打開字庫文件, 創建一個字體
    if(FT_Init_FreeType(&m_library)) throw;
    if(FT_New_Face(m_library, freeType, 0, &m_face)) throw;
 
    // 設置字體輸出參數
    restoreFont();
 
    // 設置C語言的字符集環境
    setlocale(LC_ALL, "");
}
 
// 釋放FreeType資源
CvxText::~CvxText()
{
    FT_Done_Face    (m_face);
    FT_Done_FreeType(m_library);
}
 
void CvxText::getFont(int *type, Scalar *size, bool *underline, float *diaphaneity)
{
    *type = m_fontType;
    *size = m_fontSize;
    *underline = m_fontUnderline;
    *diaphaneity = m_fontDiaphaneity;
}
 
void CvxText::setFont(int *type, Scalar *size, bool *underline, float *diaphaneity)
{
    // 參數合法性檢查
 
    if(type)
    {
        if(type >= 0) m_fontType = *type;
    }
    if(size)
    {
        m_fontSize.val[0] = fabs(size->val[0]);
        m_fontSize.val[1] = fabs(size->val[1]);
        m_fontSize.val[2] = fabs(size->val[2]);
        m_fontSize.val[3] = fabs(size->val[3]);
    }
    if(underline)
    {
        m_fontUnderline   = *underline;
    }
    if(diaphaneity)
    {
        m_fontDiaphaneity = *diaphaneity;
    }
       
    FT_Set_Pixel_Sizes(m_face, (int)m_fontSize.val[0], 0);
}
 
// 恢復原始的字體設置
void CvxText::restoreFont()
{
    m_fontType = 0;            // 字體類型(不支持)
 
    m_fontSize.val[0] = 20;      // 字體大小
    m_fontSize.val[1] = 0.5;     // 空白字符大小比例
    m_fontSize.val[2] = 0.1;     // 間隔大小比例
    m_fontSize.val[3] = 0;       // 旋轉角度(不支持)
 
    m_fontUnderline   = false;   // 下畫線(不支持)
 
    m_fontDiaphaneity = 1.0;   // 色彩比例(可產生透明效果)
 
    // 設置字符大小
    FT_Set_Pixel_Sizes(m_face, (int)m_fontSize.val[0], 0);
}
 
// 輸出函數(顏色默認爲黑色)
int CvxText::putText(Mat img, const char *text, Point pos)
{
    if (3==img.channels())
    {
        return putText(img, text, pos, CV_RGB(255,255,255));
    } 
    else
    {
        return putText(img, text, pos, Scalar(255));
    }
     
}
int CvxText::putText(Mat img, const wchar_t *text, Point pos)
{
    return putText(img, text, pos, CV_RGB(255,255,255));
}
 
//
 
int CvxText::putText(Mat img, const char *text, Point pos, Scalar color)
{
    if(img.data == NULL) return -1;
    if(text == NULL)     return -1;
 
    int i(0);
    for(i = 0; text[i] != '\0'; ++i)
    {
        wchar_t wc = text[i];
 
        // 解析雙字節符號
        if(!isascii(wc)) mbtowc(&wc, &text[i++], 2);
 
        // 輸出當前的字符
        putWChar(img, wc, pos, color);
    }
    return i;
}
int CvxText::putText(Mat img, const wchar_t *text, Point pos, Scalar color)
{
    if(img.data == NULL) return -1;
    if(text == NULL)     return -1;
 
    int i;
    for(i = 0; text[i] != '\0'; ++i)
    {
        // 輸出當前的字符
        putWChar(img, text[i], pos, color);
    }
    return i;
}
 
 
// 輸出當前字符, 更新m_pos位置
void CvxText::putWChar(Mat img, wchar_t wc, Point &pos, Scalar color)
{
    // 根據unicode生成字體的二值位圖
    FT_UInt glyph_index = FT_Get_Char_Index(m_face, wc);
    FT_Load_Glyph(m_face, glyph_index, FT_LOAD_DEFAULT);
    FT_Render_Glyph(m_face->glyph, FT_RENDER_MODE_MONO);
 
    FT_GlyphSlot slot = m_face->glyph;
 
    // 行列數
    int rows = slot->bitmap.rows;
    int cols = slot->bitmap.width;
 
    // 圖像的高度、寬度
    int nImgHeight = img.rows;
    int nImgWidth  = img.cols;
 
    const int nOrigin = 0;
 
    for(int i = 0; i < rows; ++i)
    {
        for(int j = 0; j < cols; ++j)
        {
            int off  = ((nOrigin==0)? i: (rows-1-i)) * slot->bitmap.pitch + j/8;
 
            if(slot->bitmap.buffer[off] & (0xC0 >> (j%8)))
            {
                int r = (nOrigin==0)? pos.y - (rows-1-i): pos.y + i;;
                int c = pos.x + j;
 
                if(r >= 0 && r < nImgHeight  && c >= 0 && c < nImgWidth)
                {
                    switch(img.channels())
                    {
                    case 1: 
                        {
                            img.ptr<uchar>(r)[c] = uchar(img.ptr<uchar>(r)[c]*(1-m_fontDiaphaneity) + color.val[0]*m_fontDiaphaneity);  
                            break;
                        }
                    case 3: 
                        {
                            Mat_<vec3b> _I = img;
                            _I(r, c)[0] = uchar(_I(r, c)[0]*(1-m_fontDiaphaneity) + color.val[0]*m_fontDiaphaneity);
                            _I(r, c)[1] = uchar(_I(r, c)[1]*(1-m_fontDiaphaneity) + color.val[1]*m_fontDiaphaneity);
                            _I(r, c)[2] = uchar(_I(r, c)[2]*(1-m_fontDiaphaneity) + color.val[2]*m_fontDiaphaneity);
                            break;
                        }
                    }
//                  Scalar scalar = cvGet2D(img, r, c); 
//
//                  // 進行色彩融合
//                  float p = m_fontDiaphaneity;
//                  for(int k = 0; k < 4; ++k)
//                  {
//                      scalar.val[k] = scalar.val[k]*(1-p) + color.val[k]*p;
//                  }
// 
//                  cvSet2D(img, r, c, scalar);
                }
            }
        } // end for
    } // end for
 
    // 修改下一個字的輸出位置
    double space = m_fontSize.val[0]*m_fontSize.val[1];
    double sep   = m_fontSize.val[0]*m_fontSize.val[2];
 
    pos.x += (int)((cols? cols: space) + sep);
}</vec3b></uchar></uchar></ctype.h></locale.h></assert.h></wchar.h>

主函數

#pragma once
 
#include "opencv2/opencv.hpp"
#include "CvxText.h"
 
using namespace cv;
 
int main(int argc, char *argv[])
{
    Mat img = imread("lena.jpg", 0);
    CvxText text("msyh.ttf");
     
    Scalar size(15, 0.5, 0.1, 0);
    float p = 1.f; 
    const char *msg = "@OpenCV中文輸出測試!";
 
    text.setFont(NULL, &size, NULL, &p);
    text.putText(img, msg, Point(140, 250), CV_RGB(255,255,0));
     
    imwrite("1.bmp", img);
 
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章