1. 程式人生 > 實用技巧 >為CV::Mat新增中文

為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#definemax(a,b) (((a) > (b)) ? (a) : (b))#endif#ifndef min#definemin(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操作已經正常了。





來自為知筆記(Wiz)

附件列表