Fork me on GitHub

为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 @ 2020-10-18 11:30  jsxyhelu  阅读(298)  评论(0编辑  收藏  举报