本程序是写在水星相机里的代码片,主要实现在图片上写汉字。因为字符宽度的问题,OpenCV自带的cvInitFont和cvPutText函数不支持向图像中写入中文,所以用GDI+和位图操作实现。
void GetStringSize(HDC hDC, const char* str, int* w, int* h)
{
SIZE size;//分cx和cy
GetTextExtentPoint32A(hDC, str, strlen(str), &size);
if (w != 0) *w = size.cx;
if (h != 0) *h = size.cy;
}
void paDrawString(Mat& dst, const char* str, Point org, Scalar color, int fontSize, bool italic, bool underline)
{
char *p = NULL;
//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,"宋体");//字体名
HFONT hf = CreateFontIndirectA(&lf);//创建字体
HDC hDC = CreateCompatibleDC(0);//创建一个內存设备上下文环境的DC
HFONT hOldFont = (HFONT)SelectObject(hDC, hf);//hdc:上下文环境句柄,被选择的对象句柄
int strBaseW = 0, strBaseH = 0;
int singleRow = 0;
char buf[1 << 12];
strcpy_s(buf, str);//把汉字写进buf里
//处理多行
{
int nnh = 0;//计算一共有几个字符
int cw, ch;//字体的长宽
const char* ln = strtok_s(buf, "\n", &p);//把buf数据按照换行给分割开
while (ln != 0)
{
GetStringSize(hDC, ln, &cw, &ch);//获得字体的大小(长宽cw ch表示)
strBaseW = max(strBaseW, cw);//字体的最大长度
strBaseH = max(strBaseH, ch);//字体的最大宽度
ln = strtok_s(0, "\n",&p);
nnh++;
}
singleRow = strBaseH;//每一个字符的长度
strBaseH *= nnh;//
}
if (org.x + strBaseW < 0 || org.y + strBaseH < 0)//如果输入的数字不合适,就释放画图画笔
{
SelectObject(hDC, hOldFont);//函数功能:该函数选择一对象到指定的设备上下文环境中,该新对象替换先前的相同类型的对象。
DeleteObject(hf);//DeleteObject,该函数删除一个逻辑笔、画笔、字体、位图、区域或者调色板,释放所有与该对象有关的系统资源,在对象被删除之后,指定的句柄也就失效了。
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 };//BITMAPINFO结构定义了Windows设备无关位图(DIB)的度量和颜色信息
BITMAPINFOHEADER& bih = bmp.bmiHeader;//位图信息头BITMAPINFOHEADER
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);//创建应用程序可以直接写入的、与设备无关的位图(DIB)的函数;HBITMAP是句柄; BITMAP是实例:
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));//功能:设置指定设备环境(HDC)的字体颜色
SetBkColor(hDC, 0);//该函数用指定的颜色值来设置当前的背景色,如果指定的颜色值超出了当前设备的表示范围,则设置为最近似的、设备可以表示的颜色。
//SetStretchBltMode(hDC, COLORONCOLOR);
strcpy_s(buf, str);
const char* ln = strtok_s(buf, "\n",&p);
int outTextY = 0;
while (ln != 0)
{
TextOutA(hDC, 0, outTextY, ln, strlen(ln));
outTextY += singleRow;
ln = strtok_s(0, "\n",&p);
}
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);
}
在主程序中,添加代码(由于直接从相机中得到的BAYE是上下颠倒的,因此,需要一步Flip翻转)
RECT objRect;
m_pWnd->GetClientRect(&objRect);
nWndWidth = objRect.right - objRect.left;
nWndHeight = objRect.bottom - objRect.top;
IplImage * frame;
ch = (LPSTR)(LPCTSTR)sum4;
frame = cvCreateImageHeader(cvSize(m_nImageWidth, m_nImageHeight), IPL_DEPTH_8U, 3);//添加画布
cvSetData(frame, m_pImgBuffer, m_nImageWidth * 3);
Mat img(frame,0);
paDrawString(img, ch, Point(20, 20), Scalar(0, 0, 255), 50, false, false);
frame = &IplImage(img);
for (int i = 0; i<(m_nFrameSize); i++)
m_pImgBuffer[i] = (frame->imageDataOrigin)[i];//IplImage转BAYE