CvxText.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. #include "pch.h"
  2. #include "CvxText.h"
  3. using namespace std;
  4. using namespace cv;
  5. void GetStringSize(HDC hDC, const char* str, int* w, int* h)
  6. {
  7. SIZE size;
  8. GetTextExtentPoint32A(hDC, str, strlen(str), &size);
  9. if (w != 0) *w = size.cx;
  10. if (h != 0) *h = size.cy;
  11. }
  12. cv::Size GetTextSize(const char* str, int fontSize, const char* fn, bool italic, bool underline)
  13. {
  14. HDC hDC = CreateCompatibleDC(0);
  15. LOGFONTA lf;
  16. lf.lfHeight = -fontSize;
  17. lf.lfWidth = 0;
  18. lf.lfEscapement = 0;
  19. lf.lfOrientation = 0;
  20. lf.lfWeight = 5;
  21. lf.lfItalic = italic; //斜体
  22. lf.lfUnderline = underline; //下划线
  23. lf.lfStrikeOut = 0;
  24. lf.lfCharSet = DEFAULT_CHARSET;
  25. lf.lfOutPrecision = 0;
  26. lf.lfClipPrecision = 0;
  27. lf.lfQuality = PROOF_QUALITY;
  28. lf.lfPitchAndFamily = 0;
  29. strcpy_s(lf.lfFaceName, fn);
  30. HFONT hf = CreateFontIndirectA(&lf);
  31. HFONT hOldFont = (HFONT)SelectObject(hDC, hf);
  32. int strBaseW = 0, strBaseH = 0;
  33. int singleRow = 0;
  34. char buf[1 << 12];
  35. strcpy_s(buf, str);
  36. char *bufT[1 << 12]; // 这个用于分隔字符串后剩余的字符,可能会超出。
  37. //处理多行
  38. {
  39. int nnh = 0;
  40. int cw, ch;
  41. const char* ln = strtok_s(buf, "\n", bufT);
  42. while (ln != 0)
  43. {
  44. GetStringSize(hDC, ln, &cw, &ch);
  45. strBaseW = max(strBaseW, cw);
  46. strBaseH = max(strBaseH, ch);
  47. ln = strtok_s(0, "\n", bufT);
  48. nnh++;
  49. }
  50. singleRow = strBaseH;
  51. strBaseH *= nnh;
  52. }
  53. SelectObject(hDC, hOldFont);
  54. DeleteObject(hf);
  55. DeleteDC(hDC);
  56. cv::Size size;
  57. size.width = strBaseW;
  58. size.height = strBaseH;
  59. return size;
  60. }
  61. void putTextZH(cv::Mat &dst, cv::Size & rSize, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline)
  62. {
  63. CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));
  64. int x, y, r, b;
  65. if (org.x > dst.cols || org.y > dst.rows) return;
  66. x = org.x < 0 ? -org.x : 0;
  67. y = org.y < 0 ? -org.y : 0;
  68. LOGFONTA lf;
  69. lf.lfHeight = -fontSize;
  70. lf.lfWidth = 0;
  71. lf.lfEscapement = 0;
  72. lf.lfOrientation = 0;
  73. lf.lfWeight = 5;
  74. lf.lfItalic = italic; //斜体
  75. lf.lfUnderline = underline; //下划线
  76. lf.lfStrikeOut = 0;
  77. lf.lfCharSet = DEFAULT_CHARSET;
  78. lf.lfOutPrecision = 0;
  79. lf.lfClipPrecision = 0;
  80. lf.lfQuality = PROOF_QUALITY;
  81. lf.lfPitchAndFamily = 0;
  82. strcpy_s(lf.lfFaceName, fn);
  83. HFONT hf = CreateFontIndirectA(&lf);
  84. HDC hDC = CreateCompatibleDC(0);
  85. HFONT hOldFont = (HFONT)SelectObject(hDC, hf);
  86. int strBaseW = 0, strBaseH = 0;
  87. int singleRow = 0;
  88. char buf[1 << 12];
  89. strcpy_s(buf, str);
  90. char *bufT[1 << 12]; // 这个用于分隔字符串后剩余的字符,可能会超出。
  91. //处理多行
  92. {
  93. int nnh = 0;
  94. int cw, ch;
  95. const char* ln = strtok_s(buf, "\n", bufT);
  96. while (ln != 0)
  97. {
  98. GetStringSize(hDC, ln, &cw, &ch);
  99. strBaseW = max(strBaseW, cw);
  100. strBaseH = max(strBaseH, ch);
  101. ln = strtok_s(0, "\n", bufT);
  102. nnh++;
  103. }
  104. singleRow = strBaseH;
  105. strBaseH *= nnh;
  106. }
  107. rSize.width = strBaseW;
  108. rSize.height = strBaseH;
  109. if (org.x + strBaseW < 0 || org.y + strBaseH < 0)
  110. {
  111. SelectObject(hDC, hOldFont);
  112. DeleteObject(hf);
  113. DeleteObject(hDC);
  114. return;
  115. }
  116. r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
  117. b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
  118. org.x = org.x < 0 ? 0 : org.x;
  119. org.y = org.y < 0 ? 0 : org.y;
  120. BITMAPINFO bmp = { 0 };
  121. BITMAPINFOHEADER& bih = bmp.bmiHeader;
  122. int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));
  123. bih.biSize = sizeof(BITMAPINFOHEADER);
  124. bih.biWidth = strBaseW;
  125. bih.biHeight = strBaseH;
  126. bih.biPlanes = 1;
  127. bih.biBitCount = 24;
  128. bih.biCompression = BI_RGB;
  129. bih.biSizeImage = strBaseH * strDrawLineStep;
  130. bih.biClrUsed = 0;
  131. bih.biClrImportant = 0;
  132. void* pDibData = 0;
  133. HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);
  134. CV_Assert(pDibData != 0);
  135. HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);
  136. //color.val[2], color.val[1], color.val[0]
  137. SetTextColor(hDC, RGB(255, 255, 255));
  138. SetBkColor(hDC, 0);
  139. //SetStretchBltMode(hDC, COLORONCOLOR);
  140. strcpy_s(buf, str);
  141. const char* ln = strtok_s(buf, "\n", bufT);
  142. int outTextY = 0;
  143. while (ln != 0)
  144. {
  145. TextOutA(hDC, 0, outTextY, ln, strlen(ln));
  146. outTextY += singleRow;
  147. ln = strtok_s(0, "\n", bufT);
  148. }
  149. uchar* dstData = (uchar*)dst.data;
  150. int dstStep = dst.step / sizeof(dstData[0]);
  151. unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;
  152. unsigned char* pStr = (unsigned char*)pDibData + x * 3;
  153. for (int tty = y; tty <= b; ++tty)
  154. {
  155. unsigned char* subImg = pImg + (tty - y) * dstStep;
  156. unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
  157. for (int ttx = x; ttx <= r; ++ttx)
  158. {
  159. for (int n = 0; n < dst.channels(); ++n) {
  160. double vtxt = subStr[n] / 255.0;
  161. int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];
  162. subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
  163. }
  164. subStr += 3;
  165. subImg += dst.channels();
  166. }
  167. }
  168. SelectObject(hDC, hOldBmp);
  169. SelectObject(hDC, hOldFont);
  170. DeleteObject(hf);
  171. DeleteObject(hBmp);
  172. DeleteDC(hDC);
  173. }
  174. ///////////////////////////// 数据收集卡 ///////////////////////////////////////
  175. // 获取字符串的画布上的宽度
  176. int getLineStrWidth(string str, int fontSize, int ttBoxW,int tiSl, int backPix) {
  177. int iw = str.length()*fontSize*0.5;
  178. iw += ttBoxW;
  179. iw += tiSl;
  180. iw += backPix;
  181. iw += fontSize * 1.5; // 题号的宽度 最多不超过1.5个字体宽度 56.
  182. return iw;
  183. }
  184. // 转码 utf-8 2 ansi
  185. static void UTF82ANSI(LPCSTR lpBuff, int nLen, char* pData, int iMaxLen)
  186. {
  187. ZeroMemory(pData, sizeof(char)*iMaxLen);
  188. int nCont = MultiByteToWideChar(CP_UTF8, 0, lpBuff, nLen, NULL, 0);
  189. WCHAR* szTemp = new WCHAR[nCont + 1];
  190. ZeroMemory(szTemp, sizeof(WCHAR)*(nCont + 1));
  191. MultiByteToWideChar(CP_UTF8, 0, lpBuff, nLen, szTemp, nCont);
  192. //获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的
  193. int len = WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, NULL, 0, NULL, NULL);
  194. if (len < iMaxLen) {
  195. WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, pData, len, NULL, NULL);
  196. }
  197. else {
  198. char* buffer = new char[len + 1];
  199. //宽字节编码转换成多字节编码
  200. WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, buffer, len, NULL, NULL);
  201. buffer[len] = '\0';
  202. //删除缓冲区并返回值
  203. strncpy(pData, buffer, iMaxLen - 1);
  204. delete[] buffer;
  205. buffer = NULL;
  206. }
  207. delete[]szTemp;
  208. szTemp = NULL;
  209. }
  210. // 二进制码生成获取
  211. void createBinString(Mat & img, int pageNum, int ptw, int pth, cv::Point ptStart) {
  212. vector<int> vecBinStr; vecBinStr.resize(12);
  213. for (size_t i = 11; i > 0; i--) {
  214. int ys = pageNum % 2;
  215. vecBinStr[i] = ys;
  216. pageNum = pageNum / 2;
  217. }
  218. vector<cv::Rect> vecBoxPages; vecBoxPages.resize(12);
  219. int ptPgW = ptw / 2; int ptPgH = pth / 2;
  220. for (int j = 0; j < 3; j++) {
  221. int _startY = ptStart.y + 15 + 50 * j;
  222. for (int i = 0; i < 4; i++) {
  223. int _startX = ptStart.x + 18 + i * 72;
  224. vecBoxPages.push_back(cv::Rect(_startX, _startY, ptPgW, ptPgH));
  225. if (vecBinStr[j * 4 + i])
  226. rectangle(img, vecBoxPages[vecBoxPages.size() - 1], cv::Scalar(0), -1);
  227. else
  228. rectangle(img, vecBoxPages[vecBoxPages.size() - 1], cv::Scalar(200, 200, 200), 2, 8, 0);
  229. }
  230. }
  231. return;
  232. }
  233. int dataCollectionPaper(int cols, int index,int fontSize, int lineGrayPix, bool engShow, std::vector<string> & vecLines) {
  234. /* 这里要做一些栏数 和 vecLines的对应值确认 */
  235. if (2 == cols && 14*2 == vecLines.size())
  236. ; // ok
  237. if (3 == cols && 14*3 == vecLines.size())
  238. ; // ok
  239. if (4 == cols && 14*4 == vecLines.size())
  240. ; // ok
  241. // 生成画布图像
  242. cv::Mat img(cv::Size(1654, 2344), CV_8UC3, cv::Scalar(255, 255, 255));
  243. int ptw = 80; int pth = 40;
  244. cv::Size fSize(0, 0);
  245. int leftPos = 100, rightPos = 1450;
  246. int topPos = 90;
  247. int fontW = rightPos - leftPos + ptw;
  248. vector<cv::Rect> vecPts = {
  249. cv::Rect(100,90,ptw,pth),
  250. cv::Rect(580,90,ptw,pth),
  251. cv::Rect(1450,90,ptw,pth),
  252. cv::Rect(100,2176,ptw,pth),
  253. cv::Rect(1450,2176,ptw,pth),
  254. };
  255. // 画定位点
  256. for (size_t i = 0; i < vecPts.size(); i++)
  257. {
  258. rectangle(img, vecPts[i], cv::Scalar(0, 0, 0), -1);
  259. }
  260. // 画示例框
  261. topPos += pth; topPos += 30;
  262. rectangle(img, cv::Rect(leftPos, topPos, fontW, 200), cv::Scalar(0, 0, 0), 2, 8, 0);
  263. // 画页码框
  264. cv::Rect boxPageNumber(leftPos + fontW - 300, topPos, 300, 200);
  265. rectangle(img, boxPageNumber, cv::Scalar(0, 0, 0), 2, 8, 0);
  266. // 生成二进制码流
  267. /* 二进制码流的坐标在下面函数里面实现,需要用书写获取 */
  268. createBinString(img, index, ptw, pth, cv::Point(boxPageNumber.x, boxPageNumber.y));
  269. string strPageNumInfo = "关联ID: " + to_string(index);
  270. putTextZH(img, fSize, strPageNumInfo.c_str(), cv::Point(boxPageNumber.x + 100, boxPageNumber.y + 50 * 2 + 20 + 15 + 15 + 15), Scalar(0), 20, "宋体");
  271. // 画警示信息
  272. string strMesInfo = "1、请在每题下方的横线上书写本题的内容!\
  273. \n\n2、题号不需要书写,标点符号需要原样书写。☆\
  274. \n\n3、如果本题书写有误,进行了涂抹修改等操作,请将题号前矩形框用任意笔进行填涂!☆☆\n";
  275. putTextZH(img, fSize, strMesInfo.c_str(), cv::Point(leftPos + 20, topPos + 20), Scalar(0), 25, "宋体");
  276. int lineTop1 = topPos + 20 + fSize.height + 20;
  277. cv::line(img, cv::Point(leftPos, lineTop1), cv::Point(rightPos + ptw - 300, topPos + 20 + fSize.height + 20), Scalar(0), 2, 8, 0);
  278. // 正确示例
  279. string strEgInfo = "正\n确\n示\n例";
  280. putTextZH(img, fSize, strEgInfo.c_str(), cv::Point(leftPos + 20, topPos + 20 + fSize.height + 20 + 3), Scalar(0), 20, "宋体");
  281. 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);
  282. // 贴图区域
  283. /* 暂不实现 */
  284. topPos += 200;
  285. // 文字区域
  286. static int maxLineNum = 14; // 最大行数14
  287. static int colWidth = fontW / cols; // 单栏的宽度
  288. static int ttBoxWidth = 30; // 填涂框的边长
  289. static int tiSl = 10; // 题号和题干见的距离
  290. static int lineDis = 20; // 两行之间的距离
  291. fSize.width = 0; fSize.height = 0;
  292. for (int col = 0; col < cols; col++)
  293. { // 逐栏画题
  294. int colTopPos = topPos;
  295. int colLeftPos = leftPos + col * colWidth;
  296. for (size_t i = 0; i < maxLineNum; i++)
  297. {
  298. colTopPos += lineDis;
  299. int lineLen = ttBoxWidth + tiSl;
  300. // 画填涂框 30*30 大小
  301. rectangle(img, cv::Rect(colLeftPos, colTopPos, ttBoxWidth, ttBoxWidth), cv::Scalar(0), 1, 8, 0);
  302. int iQNum = i+1 + col * maxLineNum;
  303. string strStinfo = to_string(iQNum); strStinfo += ".";
  304. putTextZH(img, fSize, strStinfo.c_str(), cv::Point(colLeftPos + ttBoxWidth + tiSl, colTopPos), Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离
  305. lineLen += fSize.width;
  306. strStinfo.clear();
  307. size_t vecIndex = i + col * maxLineNum;
  308. string strTgInfo = vecLines[vecIndex];
  309. if (!engShow)
  310. ; /* 移除词性 */
  311. char szTemp[2048];
  312. memset(szTemp, 0, sizeof(char)*(2048));
  313. UTF82ANSI(strTgInfo.c_str(), strTgInfo.length(), szTemp, 2048);
  314. strTgInfo = szTemp;
  315. putTextZH(img, fSize, strTgInfo.c_str(), cv::Point(colLeftPos + ttBoxWidth + tiSl + fSize.width , colTopPos), Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离
  316. lineLen += fSize.width;
  317. colTopPos = colTopPos + fSize.height * 3.5; //两行之间两倍的距离用于书写
  318. line(img, cv::Point(colLeftPos+30, colTopPos), cv::Point(colLeftPos + lineLen, colTopPos), Scalar(lineGrayPix, lineGrayPix, lineGrayPix), 1, 8, 0);
  319. }
  320. }
  321. cv::imwrite(R"(G:\英译汉手写识别\数据收集\template-2.png)", img);
  322. cv::namedWindow("fuck", 1);
  323. cv::imshow("fuck", img);
  324. cv::waitKey(0);
  325. return 0;
  326. }