#include "pch.h" #include "CvxText.h" using namespace std; using namespace cv; 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(cv::Mat &dst, cv::Size & rSize, 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; } rSize.width = strBaseW; rSize.height = strBaseH; 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); } ///////////////////////////// 数据收集卡 /////////////////////////////////////// // 获取字符串的画布上的宽度 int getLineStrWidth(string str, int fontSize, int ttBoxW,int tiSl, int backPix) { int iw = str.length()*fontSize*0.5; iw += ttBoxW; iw += tiSl; iw += backPix; iw += fontSize * 1.5; // 题号的宽度 最多不超过1.5个字体宽度 56. return iw; } // 转码 utf-8 2 ansi static void UTF82ANSI(LPCSTR lpBuff, int nLen, char* pData, int iMaxLen) { ZeroMemory(pData, sizeof(char)*iMaxLen); int nCont = MultiByteToWideChar(CP_UTF8, 0, lpBuff, nLen, NULL, 0); WCHAR* szTemp = new WCHAR[nCont + 1]; ZeroMemory(szTemp, sizeof(WCHAR)*(nCont + 1)); MultiByteToWideChar(CP_UTF8, 0, lpBuff, nLen, szTemp, nCont); //获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的 int len = WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, NULL, 0, NULL, NULL); if (len < iMaxLen) { WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, pData, len, NULL, NULL); } else { char* buffer = new char[len + 1]; //宽字节编码转换成多字节编码 WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, buffer, len, NULL, NULL); buffer[len] = '\0'; //删除缓冲区并返回值 strncpy(pData, buffer, iMaxLen - 1); delete[] buffer; buffer = NULL; } delete[]szTemp; szTemp = NULL; } // 二进制码生成获取 void createBinString(Mat & img, int pageNum, int ptw, int pth, cv::Point ptStart) { vector vecBinStr; vecBinStr.resize(12); for (size_t i = 11; i > 0; i--) { int ys = pageNum % 2; vecBinStr[i] = ys; pageNum = pageNum / 2; } vector vecBoxPages; vecBoxPages.resize(12); int ptPgW = ptw / 2; int ptPgH = pth / 2; for (int j = 0; j < 3; j++) { int _startY = ptStart.y + 15 + 50 * j; for (int i = 0; i < 4; i++) { int _startX = ptStart.x + 18 + i * 72; vecBoxPages.push_back(cv::Rect(_startX, _startY, ptPgW, ptPgH)); if (vecBinStr[j * 4 + i]) rectangle(img, vecBoxPages[vecBoxPages.size() - 1], cv::Scalar(0), -1); else rectangle(img, vecBoxPages[vecBoxPages.size() - 1], cv::Scalar(200, 200, 200), 2, 8, 0); } } return; } int dataCollectionPaper(int cols, int index,int fontSize, int lineGrayPix, bool engShow, std::vector & vecLines) { /* 这里要做一些栏数 和 vecLines的对应值确认 */ if (2 == cols && 14*2 == vecLines.size()) ; // ok if (3 == cols && 14*3 == vecLines.size()) ; // ok if (4 == cols && 14*4 == vecLines.size()) ; // ok // 生成画布图像 cv::Mat img(cv::Size(1654, 2344), CV_8UC3, cv::Scalar(255, 255, 255)); int ptw = 80; int pth = 40; cv::Size fSize(0, 0); int leftPos = 100, rightPos = 1450; int topPos = 90; int fontW = rightPos - leftPos + ptw; vector vecPts = { cv::Rect(100,90,ptw,pth), cv::Rect(580,90,ptw,pth), cv::Rect(1450,90,ptw,pth), cv::Rect(100,2176,ptw,pth), cv::Rect(1450,2176,ptw,pth), }; // 画定位点 for (size_t i = 0; i < vecPts.size(); i++) { rectangle(img, vecPts[i], cv::Scalar(0, 0, 0), -1); } // 画示例框 topPos += pth; topPos += 30; rectangle(img, cv::Rect(leftPos, topPos, fontW, 200), cv::Scalar(0, 0, 0), 2, 8, 0); // 画页码框 cv::Rect boxPageNumber(leftPos + fontW - 300, topPos, 300, 200); rectangle(img, boxPageNumber, cv::Scalar(0, 0, 0), 2, 8, 0); // 生成二进制码流 /* 二进制码流的坐标在下面函数里面实现,需要用书写获取 */ createBinString(img, index, ptw, pth, cv::Point(boxPageNumber.x, boxPageNumber.y)); string strPageNumInfo = "关联ID: " + to_string(index); putTextZH(img, fSize, strPageNumInfo.c_str(), cv::Point(boxPageNumber.x + 100, boxPageNumber.y + 50 * 2 + 20 + 15 + 15 + 15), Scalar(0), 20, "宋体"); // 画警示信息 string strMesInfo = "1、请在每题下方的横线上书写本题的内容!\ \n\n2、题号不需要书写,标点符号需要原样书写。☆\ \n\n3、如果本题书写有误,进行了涂抹修改等操作,请将题号前矩形框用任意笔进行填涂!☆☆\n"; putTextZH(img, fSize, strMesInfo.c_str(), cv::Point(leftPos + 20, topPos + 20), Scalar(0), 25, "宋体"); int lineTop1 = topPos + 20 + fSize.height + 20; cv::line(img, cv::Point(leftPos, lineTop1), cv::Point(rightPos + ptw - 300, topPos + 20 + fSize.height + 20), Scalar(0), 2, 8, 0); // 正确示例 string strEgInfo = "正\n确\n示\n例"; putTextZH(img, fSize, strEgInfo.c_str(), cv::Point(leftPos + 20, topPos + 20 + fSize.height + 20 + 3), Scalar(0), 20, "宋体"); cv::line(img, cv::Point(leftPos + 20 + fSize.width + 10, lineTop1), cv::Point(leftPos + 20 + fSize.width + 10, topPos + 200), Scalar(0, 0, 0), 2, 8, 0); // 贴图区域 /* 暂不实现 */ topPos += 200; // 文字区域 static int maxLineNum = 14; // 最大行数14 static int colWidth = fontW / cols; // 单栏的宽度 static int ttBoxWidth = 30; // 填涂框的边长 static int tiSl = 10; // 题号和题干见的距离 static int lineDis = 20; // 两行之间的距离 fSize.width = 0; fSize.height = 0; for (int col = 0; col < cols; col++) { // 逐栏画题 int colTopPos = topPos; int colLeftPos = leftPos + col * colWidth; for (size_t i = 0; i < maxLineNum; i++) { colTopPos += lineDis; int lineLen = ttBoxWidth + tiSl; // 画填涂框 30*30 大小 rectangle(img, cv::Rect(colLeftPos, colTopPos, ttBoxWidth, ttBoxWidth), cv::Scalar(0), 1, 8, 0); int iQNum = i+1 + col * maxLineNum; string strStinfo = to_string(iQNum); strStinfo += "."; putTextZH(img, fSize, strStinfo.c_str(), cv::Point(colLeftPos + ttBoxWidth + tiSl, colTopPos), Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离 lineLen += fSize.width; strStinfo.clear(); size_t vecIndex = i + col * maxLineNum; string strTgInfo = vecLines[vecIndex]; if (!engShow) ; /* 移除词性 */ char szTemp[2048]; memset(szTemp, 0, sizeof(char)*(2048)); UTF82ANSI(strTgInfo.c_str(), strTgInfo.length(), szTemp, 2048); strTgInfo = szTemp; putTextZH(img, fSize, strTgInfo.c_str(), cv::Point(colLeftPos + ttBoxWidth + tiSl + fSize.width , colTopPos), Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离 lineLen += fSize.width; colTopPos = colTopPos + fSize.height * 3.5; //两行之间两倍的距离用于书写 line(img, cv::Point(colLeftPos+30, colTopPos), cv::Point(colLeftPos + lineLen, colTopPos), Scalar(lineGrayPix, lineGrayPix, lineGrayPix), 1, 8, 0); } } cv::imwrite(R"(G:\英译汉手写识别\数据收集\template-2.png)", img); cv::namedWindow("fuck", 1); cv::imshow("fuck", img); cv::waitKey(0); return 0; }