为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操作已经正常了。

 

附件列表

    posted on 2022-12-03 15:29  jsxyhelu  阅读(29)  评论(0编辑  收藏  举报

    导航