Opencv310图片Mat中叠加汉字(freetype+VS2015)

相信putText不能给图片Mat写入中文汉字给小伙伴们带来了很多苦恼,之前也一直忍着用英文了,到今天忍无可忍终于花了半天把这个问题解决啦~

这里用到开源的Freetype,利用Freetype以及特定字体生成矢量图叠加到原图上。

//是不是听着很麻烦,确实挺麻烦,花了一下午呢,哈哈。

字体一般存在C:\windows\fonts目录下,本文用的仿宋simfang.ttf

所有用到的文件下载链接:http://download.csdn.net/detail/zmdsjtu/9680510


具体流程如下:

第一步:配置Freetype和opencv环境(opencv环境想必你肯定配置过了)

友情链接:
Opencv环境一键安装方法:http://blog.csdn.net/zmdsjtu/article/details/52794260
Opencv环境配置方法:http://blog.csdn.net/zmdsjtu/article/details/52235056

下面是freetype一键安装方法(类似于一键安装opencv):
项目-管理NuGet程序包—搜索freetype安装图中所示即可
这里写图片描述

这里写图片描述
同理,freetype和opencv310(其他241以上的版本都可以)都安装完成之后,就可以看第二步了。

第二步:粘代码。//就是这么简单粗暴

本文代码是在网上找的08年的写的旧版opencv的基础之上改写成了新版Mat可用的形式。

主要分为Cv310Text.cpp,Cv310Text.h 以及使用方法Main.cpp

详细讲解一下使用方法
Main.cpp

#include <opencv2/opencv.hpp>
#include "Cv310Text.h"

using namespace cv;

int main()
{

	cv::Mat img = imread("1.jpg");

	Cv310Text text("simfang.ttf");
	const char *msg = "最英俊的人";
	float p = 0.5;
	text.setFont(NULL, NULL, NULL, &p);   // 透明处理 
	text.putText(img, msg, cvPoint(20, 30), CV_RGB(255, 0, 0));
	imshow("最英俊的人", img);
	waitKey(-1);
	return 0;
}

Cv310Text为今天下午改写的用以给一个Mat图片叠加中文汉字的类。
首先给img这个Mat读入一张图片
接着调用构造函数给text初始化,这里的simfagn.ttf为我从系统里找的仿宋字体,大家可以自行寻找合适的字体。
注意把图片和TTF字体文件放到程序调试目录或者EXE所在目录即可。

集成到程序里的时候把下面.h文件和.cpp文件添加到项目并#include "Cv310Text.h"即可。

下面是我下午改写的一个类分为.h文件和.cpp文件
具体如下:

Cv310Text.h

//====================================================================  
//====================================================================  
// 文件: Cv310Text.h   
// 说明: OpenCV310汉字输出    
// 时间:  2016-11-11 
// 改写作者: zmdsjtu@163.com  
//博客地址:http://blog.csdn.net/zmdsjtu/article/category/6371625

//原作者: chaishushan#gmail.com   2007-8-21
//====================================================================  
//====================================================================  

#ifndef OPENCV_Cv310Text_2016_11_11_ZMD 
#define OPENCV_Cv310Text_2016_11_11_ZMD 


#include <ft2build.h>  
#include FT_FREETYPE_H  
#include<opencv\cv.h>
#include <opencv\highgui.h>  


class Cv310Text
{


	//================================================================  
	//================================================================  

public:

	/**
	* 装载字库文件
	*/

	Cv310Text(const char *freeType);
	virtual ~Cv310Text();

	//================================================================  
	//================================================================  

	/**
	* 获取字体。目前有些参数尚不支持。
	*
	* \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(cv::Mat &frame, const char    *text, CvPoint pos);

	/**
	* 输出汉字(颜色默认为黑色)。遇到不能输出的字符将停止。
	*
	* \param img  输出的影象
	* \param text 文本内容
	* \param pos  文本位置
	*
	* \return 返回成功输出的字符长度,失败返回-1。
	*/

	int putText(cv::Mat &frame, const wchar_t *text, CvPoint pos);

	/**
	* 输出汉字。遇到不能输出的字符将停止。
	*
	* \param img   输出的影象
	* \param text  文本内容
	* \param pos   文本位置
	* \param color 文本颜色
	*
	* \return 返回成功输出的字符长度,失败返回-1。
	*/

	int putText(cv::Mat &frame, const char    *text, CvPoint pos, CvScalar color);

	/**
	* 输出汉字。遇到不能输出的字符将停止。
	*
	* \param img   输出的影象
	* \param text  文本内容
	* \param pos   文本位置
	* \param color 文本颜色
	*
	* \return 返回成功输出的字符长度,失败返回-1。
	*/
	int putText(cv::Mat &frame, const wchar_t *text, CvPoint pos, CvScalar color);

	//================================================================  
	//================================================================  

private:

	// 输出当前字符, 更新m_pos位置  

	void putWChar(cv::Mat &frame, 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_Cv310Text_2016_11_11_ZMD 

Cv310Text.cpp

#include <wchar.h>  
#include <assert.h>  
#include <locale.h>  
#include <ctype.h>  
#include "Cv310Text.h"  



Cv310Text::Cv310Text(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资源  

Cv310Text::~Cv310Text()
{
	FT_Done_Face(m_face);
	FT_Done_FreeType(m_library);
}

// 设置字体参数:  
//  
// font         - 字体类型, 目前不支持  
// size         - 字体大小/空白比例/间隔比例/旋转角度  
// underline   - 下画线  
// diaphaneity   - 透明度  

void Cv310Text::getFont(int *type, CvScalar *size, bool *underline, float *diaphaneity)
{
	if (type) *type = m_fontType;
	if (size) *size = m_fontSize;
	if (underline) *underline = m_fontUnderline;
	if (diaphaneity) *diaphaneity = m_fontDiaphaneity;
}

void Cv310Text::setFont(int *type, CvScalar *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;
	}
}

// 恢复原始的字体设置  

void Cv310Text::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 Cv310Text::putText(cv::Mat &frame, const char    *text, CvPoint pos)
{
	return putText(frame, text, pos, CV_RGB(255, 255, 255));
}
int Cv310Text::putText(cv::Mat &frame, const wchar_t *text, CvPoint pos)
{
	return putText(frame, text, pos, CV_RGB(255, 255, 255));
}

//  

int Cv310Text::putText(cv::Mat &frame, const char    *text, CvPoint pos, CvScalar color)
{



	if (frame.empty()) return -1;
	if (text == NULL) return -1;

	//  

	int i;
	for (i = 0; text[i] != '\0'; ++i)
	{
		wchar_t wc = text[i];

		// 解析双字节符号  

		if (!isascii(wc)) mbtowc(&wc, &text[i++], 2);

		// 输出当前的字符  

		putWChar(frame, wc, pos, color);
	}
	return i;
}
int Cv310Text::putText(cv::Mat &frame, const wchar_t *text, CvPoint pos, CvScalar color)
{

	if (frame.empty()) return -1;
	if (text == NULL) return -1;

	//  

	int i;
	for (i = 0; text[i] != '\0'; ++i)
	{
		// 输出当前的字符  

		putWChar(frame, text[i], pos, color);
	}
	return i;
}

// 输出当前字符, 更新m_pos位置  

void Cv310Text::putWChar(cv::Mat &frame, wchar_t wc, CvPoint &pos, CvScalar color)
{
	// 根据unicode生成字体的二值位图  
	IplImage* img=NULL;
	img = &(IplImage)frame;
	


	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;

	//  

	for (int i = 0; i < rows; ++i)
	{
		for (int j = 0; j < cols; ++j)
		{
			int off = ((img->origin == 0) ? i : (rows - 1 - i))* slot->bitmap.pitch + j / 8;

			if (slot->bitmap.buffer[off] & (0xC0 >> (j % 8)))
			{
				int r = (img->origin == 0) ? pos.y - (rows - 1 - i) : pos.y + i;;
				int c = pos.x + j;

				if (r >= 0 && r < img->height
					&& c >= 0 && c < img->width)
				{
					CvScalar 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);
}

最后运行结果如下:

这里写图片描述

祝大家使用opencv3愉快~~摆脱蛋疼的旧版Ip1Image*

开发愉快~

=2020.01.09补充=
windows用户的话还有下面一种选项:


#include <opencv2/opencv.hpp>
#include <windows.h>
#include <string>
using namespace cv;

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

	cv::Mat img = cv::imread("1.jpg");
	const char *msg = "最英俊的人";
	putTextZH(img, msg, cv::Point(50, 50), cv::Scalar(0, 0, 255), 20);

	imshow("最英俊的人", img);
	cv::waitKey(-1);
	return 0;
}


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);
}
  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朱铭德

五毛也是爱٩(●´৺`●)૭

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值