為CV::Mat添加中文


opencv自帶的puttext函數,能夠很方便地在Mat中添加英文字母。但是在實際項目中,甲方往往希望能夠添加中文標識。解決的方法,總的來說有兩種,一種是基於基礎庫的,比如我使用MFC,那么所有的顯示最后都是在OnPaint一類的函數中進行處理,這個地方可以使用GDI等技術添加中文;另一類就是基於OpenCV的,也就是在Mat中添加中文。為了實現這個目標,往往需要一些第三方庫的支持。比如下圖,已經在ImageWatch中能夠看到“輸出漢字”等中文,而采用的就是基於FreeType的CvxText類。
關於這些方法,https://www.cnblogs.com/carle-09/  已經進行了很好的整理,我在這里再進行進一步的歸納梳理,方便自己和他人使用。
一、在圖片上打印文字  windows+GDI+TrueType字體
//====================================================================
//
// 文件: textTrueType.h
//
// 說明: OpenCV漢字輸出
//
//====================================================================

# ifndef PUTTEXT_H_
# define PUTTEXT_H_

# include <windows.h >
# include <string >
# include <opencv2 /opencv.hpp >

using namespace cv;

void GetStringSize(HDC hDC, const char * str, int * w, int * h);
void putTextZH(Mat &dst, const char * str, Point org, Scalar color, int fontSize,
    const char *fn = "Arial", bool italic = false, bool underline = false);

# endif // PUTTEXT_H_

//====================================================================
//
// 文件: textTrueType.cpp
//
// 說明: OpenCV漢字輸出
//
//====================================================================
# include "textTrueType.h"

void GetStringSize(HDC hDC, const char * str, int * w, int * h)
{
    SIZE size;
    GetTextExtentPoint32A(hDC, str, strlen(str), &size);
    if (w != 0) *w = size.cx;
    if (h != 0) *h = size.cy;
}

void putTextZH(Mat &dst, const char * str, Point org, Scalar color, int fontSize, const char * fn, bool italic, bool underline)
{
    CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));

    int x, y, r, b;
    if (org.x > dst.cols || org.y > dst.rows) return;
    x = org.x < 0 ? -org.x : 0;
    y = org.y < 0 ? -org.y : 0;

    LOGFONTA lf;
    lf.lfHeight = -fontSize;
    lf.lfWidth = 0;
    lf.lfEscapement = 0;
    lf.lfOrientation = 0;
    lf.lfWeight = 5;
    lf.lfItalic = italic;   //斜體
    lf.lfUnderline = underline; //下划線
    lf.lfStrikeOut = 0;
    lf.lfCharSet = DEFAULT_CHARSET;
    lf.lfOutPrecision = 0;
    lf.lfClipPrecision = 0;
    lf.lfQuality = PROOF_QUALITY;
    lf.lfPitchAndFamily = 0;
    strcpy_s(lf.lfFaceName, fn);

    HFONT hf = CreateFontIndirectA( &lf);
    HDC hDC = CreateCompatibleDC( 0);
    HFONT hOldFont = (HFONT)SelectObject(hDC, hf);

    int strBaseW = 0, strBaseH = 0;
    int singleRow = 0;
    char buf[ 1 << 12];
    strcpy_s(buf, str);
    char *bufT[ 1 << 12];   // 這個用於分隔字符串后剩余的字符,可能會超出。
    //處理多行
    {
        int nnh = 0;
        int cw, ch;

        const char * ln = strtok_s(buf, "\n",bufT);
        while (ln != 0)
        {
            GetStringSize(hDC, ln, &cw, &ch);
            strBaseW = max(strBaseW, cw);
            strBaseH = max(strBaseH, ch);

            ln = strtok_s( 0, "\n",bufT);
            nnh ++;
        }
        singleRow = strBaseH;
        strBaseH *= nnh;
    }

    if (org.x + strBaseW < 0 || org.y + strBaseH < 0)
    {
        SelectObject(hDC, hOldFont);
        DeleteObject(hf);
        DeleteObject(hDC);
        return;
    }

    r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
    b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
    org.x = org.x < 0 ? 0 : org.x;
    org.y = org.y < 0 ? 0 : org.y;

    BITMAPINFO bmp = { 0 };
    BITMAPINFOHEADER & bih = bmp.bmiHeader;
    int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));

    bih.biSize = sizeof(BITMAPINFOHEADER);
    bih.biWidth = strBaseW;
    bih.biHeight = strBaseH;
    bih.biPlanes = 1;
    bih.biBitCount = 24;
    bih.biCompression = BI_RGB;
    bih.biSizeImage = strBaseH * strDrawLineStep;
    bih.biClrUsed = 0;
    bih.biClrImportant = 0;

    void * pDibData = 0;
    HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);

    CV_Assert(pDibData != 0);
    HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);

    //color.val[2], color.val[1], color.val[0]
    SetTextColor(hDC, RGB( 255, 255, 255));
    SetBkColor(hDC, 0);
    //SetStretchBltMode(hDC, COLORONCOLOR);

    strcpy_s(buf, str);
    const char * ln = strtok_s(buf, "\n",bufT);
    int outTextY = 0;
    while (ln != 0)
    {
        TextOutA(hDC, 0, outTextY, ln, strlen(ln));
        outTextY += singleRow;
        ln = strtok_s( 0, "\n",bufT);
    }
    uchar * dstData = (uchar *)dst.data;
    int dstStep = dst.step / sizeof(dstData[ 0]);
    unsigned char * pImg = ( unsigned char *)dst.data + org.x * dst.channels() + org.y * dstStep;
    unsigned char * pStr = ( unsigned char *)pDibData + x * 3;
    for ( int tty = y; tty < = b; ++tty)
    {
        unsigned char * subImg = pImg + (tty - y) * dstStep;
        unsigned char * subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
        for ( int ttx = x; ttx < = r; ++ttx)
        {
            for ( int n = 0; n < dst.channels(); ++n){
                double vtxt = subStr[n] / 255. 0;
                int cvv = vtxt * color.val[n] + ( 1 - vtxt) * subImg[n];
                subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
            }

            subStr += 3;
            subImg += dst.channels();
        }
    }

    SelectObject(hDC, hOldBmp);
    SelectObject(hDC, hOldFont);
    DeleteObject(hf);
    DeleteObject(hBmp);
    DeleteDC(hDC);
}

//====================================================================
//
// 文件: test_main.cpp
//
// 說明: OpenCV漢字輸出,測試主函數
//
//====================================================================
# include "opencv2/opencv.hpp"
# include "textTrueType.h"

using namespace std;
using namespace cv;

void main()
{
    //Mat img(150,600,CV_8UC3,Scalar(255,255,255));//初始化圖像
    Mat img = imread( "D:\\005_test_4\\testImg\\road_6.png");
    putTextZH(img, "打印漢字,漢字,漢字!", Point( 30, 30), Scalar( 255, 0, 0), 45, "華文行楷");
    imwrite( "1.png", img);
    imshow( "", img);
    waitKey( 0);
}

二、在圖片上打印文字 FreeType庫
//====================================================================
//====================================================================
//
// 文件: CvxText.h
//
// 說明: OpenCV漢字輸出
//
//====================================================================
//====================================================================
# ifndef OPENCV_CVX_TEXT_2007_08_31_H
# define OPENCV_CVX_TEXT_2007_08_31_H
/**
* \file CvxText.h
* \brief OpenCV漢字輸出接口
*
* 實現了漢字輸出功能。
*/

# include <ft2build.h >
# include FT_FREETYPE_H
# include <opencv2\opencv.hpp >
//modified
//將CvxText.h中的#include<cv.h> #include <highgui.h>用#include<opnecv2/opencv.hpp>替代;
/**
* \class CvxText
* \brief OpenCV中輸出漢字
*
* OpenCV中輸出漢字。字庫提取采用了開源的FreeFype庫。由於FreeFype是
* GPL版權發布的庫,和OpenCV版權並不一致,因此目前還沒有合並到OpenCV
* 擴展庫中。
*
* 顯示漢字的時候需要一個漢字字庫文件,字庫文件系統一般都自帶了。
* 這里采用的是一個開源的字庫:“文泉驛正黑體”。
*
* 關於"OpenCV擴展庫"的細節請訪問
* http://code.google.com/p/opencv-extension-library/
*
* 關於FreeType的細節請訪問
* http://www.freetype.org/
*
* 例子:
*
* \code
int main(int argc, char *argv[])
{
   // 定義CvxApplication對象
   CvxApplication app(argc, argv);
   // 打開一個影象
   IplImage *img = cvLoadImage("test.jpg", 1);
   // 輸出漢字
   {
      // "wqy-zenhei.ttf"為文泉驛正黑體
      CvText text("wqy-zenhei.ttf");
      const char *msg = "在OpenCV中輸出漢字!";
      float p = 0.5;
      text.setFont(NULL, NULL, NULL, &p);   // 透明處理
      text.putText(img, msg, cvPoint(100, 150), CV_RGB(255,0,0));
   }
   // 定義窗口,並顯示影象
   CvxWindow myWin("myWin");
   myWin.showImage(img);
   // 進入消息循環
   return app.exec();
}
* \endcode
*/

class CvxText  
{
    // 禁止copy
   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,
      CvScalar *size =NULL, bool *underline =NULL, float *diaphaneity =NULL);
    /**
    * 設置字體。目前有些參數尚不支持。
    *
    * \param font        字體類型, 目前不支持
    * \param size        字體大小/空白比例/間隔比例/旋轉角度
    * \param underline   下畫線
    * \param diaphaneity 透明度
    *
    * \sa getFont, restoreFont
    */

    void setFont( int *type,
      CvScalar *size =NULL, bool *underline =NULL, float *diaphaneity =NULL);
    /**
    * 恢復原始的字體設置。
    *
    * \sa getFont, setFont
    */

    void restoreFont();
    //================================================================
    //================================================================
    /**
    * 輸出漢字(顏色默認為黑色)。遇到不能輸出的字符將停止。
    *
    * \param img  輸出的影象
    * \param text 文本內容
    * \param pos  文本位置
    *
    * \return 返回成功輸出的字符長度,失敗返回-1。
    */

    int putText(IplImage *img, const char     *text, CvPoint pos);
    /**
    * 輸出漢字(顏色默認為黑色)。遇到不能輸出的字符將停止。
    *
    * \param img  輸出的影象
    * \param text 文本內容
    * \param pos  文本位置
    *
    * \return 返回成功輸出的字符長度,失敗返回-1。
    */

    int putText(IplImage *img, const wchar_t *text, CvPoint pos);
    /**
    * 輸出漢字。遇到不能輸出的字符將停止。
    *
    * \param img   輸出的影象
    * \param text  文本內容
    * \param pos   文本位置
    * \param color 文本顏色
    *
    * \return 返回成功輸出的字符長度,失敗返回-1。
    */

    int putText(IplImage *img, const char     *text, CvPoint pos, CvScalar color);
    /**
    * 輸出漢字。遇到不能輸出的字符將停止。
    *
    * \param img   輸出的影象
    * \param text  文本內容
    * \param pos   文本位置
    * \param color 文本顏色
    *
    * \return 返回成功輸出的字符長度,失敗返回-1。
    */

    int putText(IplImage *img, const wchar_t *text, CvPoint pos, CvScalar color);
    //================================================================
    //================================================================
private :
    // 輸出當前字符, 更新m_pos位置
    void putWChar(IplImage *img, wchar_t wc, CvPoint &pos, CvScalar color);
    //================================================================
    //================================================================
private :
   FT_Library   m_library;   // 字庫
   FT_Face      m_face;       // 字體
    //================================================================
    //================================================================
    // 默認的字體輸出參數
    int         m_fontType;
   CvScalar   m_fontSize;
    bool      m_fontUnderline;
    float      m_fontDiaphaneity;
    //================================================================
    //================================================================
};

# endif // OPENCV_CVX_TEXT_2007_08_31_H

//====================================================================
//====================================================================
//
// 文件: text_in_image.cpp
//
// 說明: OpenCV漢字輸出
//
//====================================================================
//====================================================================
# include <iostream >
# include "opencv2/opencv.hpp"
# include "CvxText.h"

using namespace std;
using namespace cv;
//--------------------------------IPLImage格式的圖像, 使用FreeType庫---------------------------------------
int main( int argc, char *argv[])
{
    IplImage *img = cvLoadImage( "D:\\005_test_4\\testImg\\road_6.png");
    {
        CvxText text( "simfang.ttf"); 
        const char *msg = "漢字!漢字!漢字!";
        float p = 1. 5;
        text.setFont(NULL, NULL, NULL, &p); 
        text.putText(img, msg, cvPoint( 100, 150), CV_RGB( 0, 255, 0));
    }
    cvShowImage( "test", img ); cvWaitKey( - 1);
    cvReleaseImage( &img);
    return 0;
}

三、在圖片上打印文字 putText()
/*
作者:WP @20190626
功能:opencv在圖片中寫入文字
說明:
    (1)只在圖像上打印 數字、字母的話:
            1.Mat格式的圖像,可以使用opencv自帶的putText。
            2.IPLImage格式的圖像,可以使用自帶的cvInitFont和cvPutText函數。
    (2)在圖像上打印 漢字的話,可以使用FreeType庫。
            FreeType庫是一個完全免費(開源)的、高質量的且可移植的字體引擎,它提供統一的接口來訪問多種字體格式文件。
*/

# include <iostream >
# include "opencv2/opencv.hpp"

using namespace std;
using namespace cv;

//--------------------------------Mat格式的圖像,可以使用opencv自帶的putText()函數---------------------------------------
int main( )
{
    //Mat image = Mat::zeros(Size(640, 480), CV_8UC3);            // 創建空白圖用於繪制文字
    //image.setTo(Scalar(100, 0, 0));        //設置藍色背景
    Mat image = imread( "D:\\005_test_4\\testImg\\road_6.png", 1);     // 最后顯示,1---原圖,0---灰度圖

    //設置繪制文本的相關參數
    string text = "JILIN UNIVERSITY";
    int font_face = FONT_HERSHEY_COMPLEX; 
    double font_scale = 2;
    int thickness = 2;
    int baseline;
    //獲取文本框的長寬
    Size text_size = getTextSize(text, font_face, font_scale, thickness, &baseline);

    //將文本框居中繪制
    Point origin; 
    origin.x = image.cols / 2 - text_size.width / 2;
    origin.y = image.rows / 2 + text_size.height / 2;
    putText(image, text, origin, font_face, font_scale, Scalar( 0, 255, 255), thickness, 8, 0);
    putText(image, "This image is clear.", Point( 50, 100), FONT_HERSHEY_SIMPLEX, 1, Scalar( 0, 0, 255), 4, 8);

    //顯示繪制結果
    imshow( "image", image);
    waitKey( 0);
    return 0;
}

小結:第一種方法,基於現有的環境,一個函數解決“中文書寫”問題,簡單直接;但是其它方法 也需要了解掌握,以方便應對不同的情況和要求。
P.S方法1使用過程中,容易出現的錯誤和解決方法:來源: < https://www.cnblogs.com/rainbow70626/p/9073017.html>

std::max、std::min error C2589: “(”:“::”右邊的非法標記,error C2059: 語法錯誤:“::”

  在VC++種同時包含頭文件#include <windows.h>和#include <algorithm>后就會出現無法正常使用std標准庫中的min和max模板函數,經過查閱發現這是因為在Windows.h種也有min和max的定義,這樣就導致了algorithm中的min和max無法正常使用,這里給出兩種解決方案,來解決std命名空間無法使用min和max的問題。

解決方案一

使用std::min或者std::max的時候加上括號,避免與Windows.h中的min、max宏定義沖突。

#include <windows.h> #include <algorithm> (std::min)( 100, 2000
);
(std::max)(
10, 500);

解決方案二

禁用Windows.h中的min、max宏定義。

在Windows.h中可以查閱到min、max的定義為:

復制代碼
#ifndef NOMINMAX
 
#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b)) #endif
 
#ifndef min
#define min(a,b)            (((a) < (b)) ? (a) : (b)) #endif #endif  /* NOMINMAX */
復制代碼

看懂了定義就很簡單了,在包含Windows.h文件之前直接定義一個NOMINMAX宏定義就OK了,如下代碼所示:

#define NOMINMAX #include <windows.h> #include <algorithm> std::max( 100, 200);

解決方案三:

這個解決辦法與第二個本質是一樣的。具體方法為:打開工程屬性->C/C++->預處理器->預處理器定義->加入NOMINMAX

OK,經過上面的操作,min,max操作已經正常了。



 




附件列表

     


    免責聲明!

    本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



     
    粵ICP備18138465號   © 2018-2025 CODEPRJ.COM