CvxText.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871
  1. #include "pch.h"
  2. #include "CvxText.h"
  3. #include <json/json.h>
  4. #include "BaseUtility.h"
  5. #include <iostream>
  6. #include <fstream>
  7. using namespace std;
  8. using namespace cv;
  9. #if defined(GNUC)
  10. #pragma GCC diagnostic push
  11. #pragma GCC diagnostic ignored “-Wdeprecated-declarations”
  12. #elif defined(_MSC_VER)
  13. #pragma warning(disable : 4996)
  14. #endif
  15. void GetStringSize(HDC hDC, const char* str, int* w, int* h)
  16. {
  17. SIZE size;
  18. GetTextExtentPoint32A(hDC, str, strlen(str), &size);
  19. if (w != 0) *w = size.cx;
  20. if (h != 0) *h = size.cy;
  21. }
  22. cv::Size GetTextSize(const char* str, int fontSize, const char* fn, bool italic, bool underline)
  23. {
  24. HDC hDC = CreateCompatibleDC(0);
  25. LOGFONTA lf;
  26. lf.lfHeight = -fontSize;
  27. lf.lfWidth = 0;
  28. lf.lfEscapement = 0;
  29. lf.lfOrientation = 0;
  30. lf.lfWeight = 5;
  31. lf.lfItalic = italic; //斜体
  32. lf.lfUnderline = underline; //下划线
  33. lf.lfStrikeOut = 0;
  34. lf.lfCharSet = DEFAULT_CHARSET;
  35. lf.lfOutPrecision = 0;
  36. lf.lfClipPrecision = 0;
  37. lf.lfQuality = PROOF_QUALITY;
  38. lf.lfPitchAndFamily = 0;
  39. strcpy_s(lf.lfFaceName, fn);
  40. HFONT hf = CreateFontIndirectA(&lf);
  41. HFONT hOldFont = (HFONT)SelectObject(hDC, hf);
  42. int strBaseW = 0, strBaseH = 0;
  43. int singleRow = 0;
  44. char buf[1 << 12];
  45. strcpy_s(buf, str);
  46. char *bufT[1 << 12]; // 这个用于分隔字符串后剩余的字符,可能会超出。
  47. //处理多行
  48. {
  49. int nnh = 0;
  50. int cw, ch;
  51. const char* ln = strtok_s(buf, "\n", bufT);
  52. while (ln != 0)
  53. {
  54. GetStringSize(hDC, ln, &cw, &ch);
  55. strBaseW = max(strBaseW, cw);
  56. strBaseH = max(strBaseH, ch);
  57. ln = strtok_s(0, "\n", bufT);
  58. nnh++;
  59. }
  60. singleRow = strBaseH;
  61. strBaseH *= nnh;
  62. }
  63. SelectObject(hDC, hOldFont);
  64. DeleteObject(hf);
  65. DeleteDC(hDC);
  66. cv::Size size;
  67. size.width = strBaseW;
  68. size.height = strBaseH;
  69. return size;
  70. }
  71. void putTextZH(cv::Mat &dst, cv::Size & rSize, const char* str, Point org, Scalar color, int fontSize, const char* fn, bool italic, bool underline)
  72. {
  73. CV_Assert(dst.data != 0 && (dst.channels() == 1 || dst.channels() == 3));
  74. int x, y, r, b;
  75. if (org.x > dst.cols || org.y > dst.rows) return;
  76. x = org.x < 0 ? -org.x : 0;
  77. y = org.y < 0 ? -org.y : 0;
  78. LOGFONTA lf;
  79. lf.lfHeight = -fontSize;
  80. lf.lfWidth = 0;
  81. lf.lfEscapement = 0;
  82. lf.lfOrientation = 0;
  83. lf.lfWeight = 5;
  84. lf.lfItalic = italic; //斜体
  85. lf.lfUnderline = underline; //下划线
  86. lf.lfStrikeOut = 0;
  87. lf.lfCharSet = DEFAULT_CHARSET;
  88. lf.lfOutPrecision = 0;
  89. lf.lfClipPrecision = 0;
  90. lf.lfQuality = PROOF_QUALITY;
  91. lf.lfPitchAndFamily = 0;
  92. strcpy_s(lf.lfFaceName, fn);
  93. HFONT hf = CreateFontIndirectA(&lf);
  94. HDC hDC = CreateCompatibleDC(0);
  95. HFONT hOldFont = (HFONT)SelectObject(hDC, hf);
  96. int strBaseW = 0, strBaseH = 0;
  97. int singleRow = 0;
  98. char buf[1 << 12];
  99. strcpy_s(buf, str);
  100. char *bufT[1 << 12]; // 这个用于分隔字符串后剩余的字符,可能会超出。
  101. //处理多行
  102. {
  103. int nnh = 0;
  104. int cw, ch;
  105. const char* ln = strtok_s(buf, "\n", bufT);
  106. while (ln != 0)
  107. {
  108. GetStringSize(hDC, ln, &cw, &ch);
  109. strBaseW = max(strBaseW, cw);
  110. strBaseH = max(strBaseH, ch);
  111. ln = strtok_s(0, "\n", bufT);
  112. nnh++;
  113. }
  114. singleRow = strBaseH;
  115. strBaseH *= nnh;
  116. }
  117. rSize.width = strBaseW;
  118. rSize.height = strBaseH;
  119. if (org.x + strBaseW < 0 || org.y + strBaseH < 0)
  120. {
  121. SelectObject(hDC, hOldFont);
  122. DeleteObject(hf);
  123. DeleteObject(hDC);
  124. return;
  125. }
  126. r = org.x + strBaseW > dst.cols ? dst.cols - org.x - 1 : strBaseW - 1;
  127. b = org.y + strBaseH > dst.rows ? dst.rows - org.y - 1 : strBaseH - 1;
  128. org.x = org.x < 0 ? 0 : org.x;
  129. org.y = org.y < 0 ? 0 : org.y;
  130. BITMAPINFO bmp = { 0 };
  131. BITMAPINFOHEADER& bih = bmp.bmiHeader;
  132. int strDrawLineStep = strBaseW * 3 % 4 == 0 ? strBaseW * 3 : (strBaseW * 3 + 4 - ((strBaseW * 3) % 4));
  133. bih.biSize = sizeof(BITMAPINFOHEADER);
  134. bih.biWidth = strBaseW;
  135. bih.biHeight = strBaseH;
  136. bih.biPlanes = 1;
  137. bih.biBitCount = 24;
  138. bih.biCompression = BI_RGB;
  139. bih.biSizeImage = strBaseH * strDrawLineStep;
  140. bih.biClrUsed = 0;
  141. bih.biClrImportant = 0;
  142. void* pDibData = 0;
  143. HBITMAP hBmp = CreateDIBSection(hDC, &bmp, DIB_RGB_COLORS, &pDibData, 0, 0);
  144. CV_Assert(pDibData != 0);
  145. HBITMAP hOldBmp = (HBITMAP)SelectObject(hDC, hBmp);
  146. //color.val[2], color.val[1], color.val[0]
  147. SetTextColor(hDC, RGB(255, 255, 255));
  148. SetBkColor(hDC, 0);
  149. //SetStretchBltMode(hDC, COLORONCOLOR);
  150. strcpy_s(buf, str);
  151. const char* ln = strtok_s(buf, "\n", bufT);
  152. int outTextY = 0;
  153. while (ln != 0)
  154. {
  155. TextOutA(hDC, 0, outTextY, ln, strlen(ln));
  156. outTextY += singleRow;
  157. ln = strtok_s(0, "\n", bufT);
  158. }
  159. uchar* dstData = (uchar*)dst.data;
  160. int dstStep = dst.step / sizeof(dstData[0]);
  161. unsigned char* pImg = (unsigned char*)dst.data + org.x * dst.channels() + org.y * dstStep;
  162. unsigned char* pStr = (unsigned char*)pDibData + x * 3;
  163. for (int tty = y; tty <= b; ++tty)
  164. {
  165. unsigned char* subImg = pImg + (tty - y) * dstStep;
  166. unsigned char* subStr = pStr + (strBaseH - tty - 1) * strDrawLineStep;
  167. for (int ttx = x; ttx <= r; ++ttx)
  168. {
  169. for (int n = 0; n < dst.channels(); ++n) {
  170. double vtxt = subStr[n] / 255.0;
  171. int cvv = vtxt * color.val[n] + (1 - vtxt) * subImg[n];
  172. subImg[n] = cvv > 255 ? 255 : (cvv < 0 ? 0 : cvv);
  173. }
  174. subStr += 3;
  175. subImg += dst.channels();
  176. }
  177. }
  178. SelectObject(hDC, hOldBmp);
  179. SelectObject(hDC, hOldFont);
  180. DeleteObject(hf);
  181. DeleteObject(hBmp);
  182. DeleteDC(hDC);
  183. }
  184. ///////////////////////////// 数据收集卡 ///////////////////////////////////////
  185. // 获取字符串的画布上的宽度
  186. int getLineStrWidth(string str, int fontSize, int ttBoxW, int tiSl, int backPix) {
  187. int iw = str.length()*fontSize*0.5;
  188. iw += ttBoxW;
  189. iw += tiSl;
  190. iw += backPix;
  191. iw += fontSize * 1.5; // 题号的宽度 最多不超过1.5个字体宽度 56.
  192. return iw;
  193. }
  194. // 转码 utf-8 2 ansi
  195. static void UTF82ANSI(LPCSTR lpBuff, int nLen, char* pData, int iMaxLen)
  196. {
  197. ZeroMemory(pData, sizeof(char)*iMaxLen);
  198. int nCont = MultiByteToWideChar(CP_UTF8, 0, lpBuff, nLen, NULL, 0);
  199. WCHAR* szTemp = new WCHAR[nCont + 1];
  200. ZeroMemory(szTemp, sizeof(WCHAR)*(nCont + 1));
  201. MultiByteToWideChar(CP_UTF8, 0, lpBuff, nLen, szTemp, nCont);
  202. //获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的
  203. int len = WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, NULL, 0, NULL, NULL);
  204. if (len < iMaxLen) {
  205. WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, pData, len, NULL, NULL);
  206. }
  207. else {
  208. char* buffer = new char[len + 1];
  209. //宽字节编码转换成多字节编码
  210. WideCharToMultiByte(CP_ACP, 0, szTemp, nCont, buffer, len, NULL, NULL);
  211. buffer[len] = '\0';
  212. //删除缓冲区并返回值
  213. strncpy(pData, buffer, iMaxLen - 1);
  214. delete[] buffer;
  215. buffer = NULL;
  216. }
  217. delete[]szTemp;
  218. szTemp = NULL;
  219. }
  220. // 二进制码生成获取
  221. void createBinString(Mat & img, int pageNum, int ptw, int pth, cv::Point ptStart, vector<cv::Rect>& vecBoxPages) {
  222. vector<int> vecBinStr; vecBinStr.resize(12);
  223. for (size_t i = 11; i > 0; i--) {
  224. int ys = pageNum % 2;
  225. vecBinStr[i] = ys;
  226. pageNum = pageNum / 2;
  227. }
  228. vecBoxPages.clear();
  229. int ptPgW = ptw / 2; int ptPgH = pth / 2;
  230. for (int j = 0; j < 3; j++) {
  231. int _startY = ptStart.y + 15 + 50 * j;
  232. for (int i = 0; i < 4; i++) {
  233. int _startX = ptStart.x + 18 + i * 72;
  234. vecBoxPages.push_back(cv::Rect(_startX, _startY, ptPgW, ptPgH));
  235. if (vecBinStr[j * 4 + i])
  236. rectangle(img, vecBoxPages[vecBoxPages.size() - 1], cv::Scalar(0), -1);
  237. else
  238. rectangle(img, vecBoxPages[vecBoxPages.size() - 1], cv::Scalar(200, 200, 200), 2, 8, 0);
  239. }
  240. }
  241. return;
  242. }
  243. void generatePageJson(vector<cv::Rect>& vecPts, vector<cv::Rect>& vecBoxPages)
  244. {
  245. Json::Value imageSize;
  246. imageSize["width"] = 1654;
  247. imageSize["height"] = 2344;
  248. Json::Value location(Json::arrayValue);
  249. for (auto& iter : vecPts)
  250. {
  251. Json::Value item;
  252. item["x"] = iter.x;
  253. item["y"] = iter.y;
  254. item["width"] = iter.width;
  255. item["height"] = iter.height;
  256. location.append(item);
  257. }
  258. Json::Value pagenumber(Json::arrayValue);
  259. int nSubVecSize = 4;// 每个小vector的容量
  260. for (size_t i = 0, j = 1; i < vecBoxPages.size(); i += nSubVecSize)
  261. {
  262. vector<cv::Rect> vecSmall;
  263. auto last = std::min(vecBoxPages.size(), i + nSubVecSize);
  264. vecSmall.insert(vecSmall.begin(), vecBoxPages.begin() + i, vecBoxPages.begin() + last);
  265. Json::Value opt(Json::arrayValue);
  266. std::string choice[] = { "A", "B", "C", "D" };
  267. for (int h = 0; h < vecSmall.size(); h++)
  268. {
  269. Json::Value bin;
  270. bin["x"] = vecSmall[h].x;
  271. bin["y"] = vecSmall[h].y;
  272. bin["width"] = vecSmall[h].width;
  273. bin["height"] = vecSmall[h].height;
  274. bin["optName"] = choice[h];
  275. opt.append(bin);
  276. }
  277. Json::Value item;
  278. item["id"] = j;
  279. item["opt"] = opt;
  280. pagenumber.append(item);
  281. j++;
  282. }
  283. Json::Value itemRoot;
  284. itemRoot["imageSize"] = imageSize;
  285. itemRoot["location"] = location;
  286. itemRoot["pagenumber"] = pagenumber;
  287. Json::FastWriter writer;
  288. std::string strJson = writer.write(itemRoot);
  289. CFile zip;
  290. zip.Open(_T("D:\\page.json"), CFile::modeCreate | CFile::modeWrite);
  291. DWORD wide_string_len = MultiByteToWideChar(CP_ACP, 0, strJson.c_str(), -1, NULL, 0);
  292. WCHAR* wide_string = new WCHAR[wide_string_len];
  293. wide_string_len = MultiByteToWideChar(CP_ACP, 0, strJson.c_str(), -1, wide_string, wide_string_len);
  294. DWORD utf8_string_len = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
  295. CHAR* utf8_string = new CHAR[utf8_string_len];
  296. utf8_string_len = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_string_len, NULL, NULL);
  297. zip.Write((void*)utf8_string, utf8_string_len - 1);
  298. // LOG4CPLUS_INFO_FMT(pTestLogger, LOG4CPLUS_TEXT("%s"), CString(json_str.c_str()));
  299. delete wide_string;
  300. delete utf8_string;
  301. zip.Close();
  302. }
  303. std::string JsonToString(const Json::Value & root)
  304. {
  305. static Json::Value def = []{
  306. Json::Value def;
  307. Json::StreamWriterBuilder::setDefaults(&def);
  308. def["emitUTF8"] = true;
  309. return def;
  310. }();
  311. std::ostringstream stream;
  312. Json::StreamWriterBuilder stream_builder;
  313. stream_builder.settings_ = def;//Config emitUTF8
  314. std::unique_ptr<Json::StreamWriter> writer(stream_builder.newStreamWriter());
  315. writer->write(root, &stream);
  316. return stream.str();
  317. }
  318. int dataCollectionPaper(int cols, int index, int fontSize, int lineGrayPix, bool engShow, std::vector<tuple<string, string>>& vecLines, CString dir, std::string& strPngPath) {
  319. /* 这里要做一些栏数 和 vecLines的对应值确认 */
  320. if (2 == cols && 14 * 2 == vecLines.size())
  321. ; // ok
  322. if (3 == cols && 14 * 3 == vecLines.size())
  323. ; // ok
  324. if (4 == cols && 14 * 4 == vecLines.size())
  325. ; // ok
  326. // 生成画布图像
  327. cv::Mat img(cv::Size(1654, 2344), CV_8UC3, cv::Scalar(255, 255, 255));
  328. int ptw = 80; int pth = 40;
  329. cv::Size fSize(0, 0);
  330. int leftPos = 100, rightPos = 1450;
  331. int topPos = 90;
  332. int fontW = rightPos - leftPos + ptw;
  333. vector<cv::Rect> vecPts = {
  334. cv::Rect(100,90,ptw,pth),
  335. cv::Rect(580,90,ptw,pth),
  336. cv::Rect(1450,90,ptw,pth),
  337. cv::Rect(100,2176,ptw,pth),
  338. cv::Rect(1450,2176,ptw,pth),
  339. };
  340. // 画定位点
  341. for (size_t i = 0; i < vecPts.size(); i++)
  342. {
  343. rectangle(img, vecPts[i], cv::Scalar(0, 0, 0), -1);
  344. }
  345. // 画示例框
  346. topPos += pth; topPos += 30;
  347. rectangle(img, cv::Rect(leftPos, topPos, fontW, 200), cv::Scalar(0, 0, 0), 2, 8, 0);
  348. // 画页码框
  349. cv::Rect boxPageNumber(leftPos + fontW - 300, topPos, 300, 200);
  350. rectangle(img, boxPageNumber, cv::Scalar(0, 0, 0), 2, 8, 0);
  351. // 生成二进制码流
  352. /* 二进制码流的坐标在下面函数里面实现,需要用书写获取 */
  353. vector<cv::Rect> vecBoxPages;
  354. createBinString(img, index, ptw, pth, cv::Point(boxPageNumber.x, boxPageNumber.y), vecBoxPages);
  355. string strPageNumInfo = "关联ID: " + to_string(index);
  356. putTextZH(img, fSize, strPageNumInfo.c_str(), cv::Point(boxPageNumber.x + 100, boxPageNumber.y + 50 * 2 + 20 + 15 + 15 + 15), Scalar(0), 20, "宋体");
  357. // 生成page.json
  358. //generatePageJson(vecPts, vecBoxPages);
  359. // 画警示信息
  360. string strMesInfo = "1、请将各题结果书写于横线上,勿写到线下!\
  361. \n\n2、请用黑色笔书写。☆\
  362. \n\n3、如果本题书写有误,进行了涂抹修改等操作,请将题号前矩形框用任意笔进行填涂!☆☆\n";
  363. putTextZH(img, fSize, strMesInfo.c_str(), cv::Point(leftPos + 20, topPos + 20), Scalar(0), 25, "宋体");
  364. int lineTop1 = topPos + 20 + fSize.height + 20;
  365. cv::line(img, cv::Point(leftPos, lineTop1), cv::Point(rightPos + ptw - 300, topPos + 20 + fSize.height + 20), Scalar(0), 2, 8, 0);
  366. // 正确示例
  367. string strEgInfo = "正\n确\n示\n例";
  368. putTextZH(img, fSize, strEgInfo.c_str(), cv::Point(leftPos + 20, topPos + 20 + fSize.height + 20 + 3), Scalar(0), 20, "宋体");
  369. 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);
  370. // 填涂示例
  371. string strtt = "正确填涂";
  372. cv::Point ptEgInfo(leftPos + 70, topPos + 20 + fSize.height + 20 + 30);
  373. putTextZH(img, fSize, strtt.c_str(), ptEgInfo, Scalar(0), 20, "宋体");
  374. cv::Rect rcttbox(ptEgInfo.x + 100, ptEgInfo.y - 5, 30, 30);
  375. rectangle(img, rcttbox, cv::Scalar(0), -1);
  376. rcttbox.x += 1;
  377. rcttbox.y += 1;
  378. rcttbox.width -= 2;
  379. rcttbox.height -= 2;
  380. rectangle(img, rcttbox, cv::Scalar(100, 100, 100), -1);
  381. // 贴图区域
  382. /* 暂不实现 */
  383. topPos += 200;
  384. // 文字区域
  385. static int maxLineNum = 14; // 最大行数14
  386. static int ttBoxWidth = 30; // 填涂框的边长
  387. static int tiSl = 10; // 题号和题干间的距离
  388. static int lineDis = 20; // 两行之间的距离
  389. int colWidth = fontW / cols; // 单栏的宽度
  390. fSize.width = 0; fSize.height = 0;
  391. Json::Value root(Json::arrayValue);
  392. //RNG rng;
  393. for (int col = 0; col < cols; col++)
  394. { // 逐栏画题
  395. int colTopPos = topPos;
  396. int colLeftPos = leftPos + col * colWidth;
  397. for (size_t i = 0; i < maxLineNum; i++)
  398. {
  399. size_t vecIndex = i + col * maxLineNum;
  400. if (vecIndex >= vecLines.size())
  401. {
  402. break;
  403. }
  404. colTopPos += lineDis;
  405. int lineLen = ttBoxWidth + tiSl;
  406. // 画填涂框 30*30 大小
  407. cv::Rect rcbox(colLeftPos, colTopPos, ttBoxWidth, ttBoxWidth);
  408. rectangle(img, rcbox, cv::Scalar(0), 1, 8, 0);
  409. int iQNum = i + 1 + col * maxLineNum;
  410. string strStinfo = to_string(iQNum); strStinfo += ".";
  411. cv::Point ptNum(colLeftPos + ttBoxWidth + tiSl, colTopPos);
  412. putTextZH(img, fSize, strStinfo.c_str(), ptNum, Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离
  413. lineLen += fSize.width;
  414. cv::Rect rcNum(ptNum.x, ptNum.y, fSize.width, fSize.height);
  415. strStinfo.clear();
  416. string strTgInfo = std::get<0>(vecLines[vecIndex]);
  417. string strSolution = std::get<1>(vecLines[vecIndex]);
  418. if (!engShow)
  419. ; /* 移除词性 */
  420. //char szTemp[2048];
  421. //memset(szTemp, 0, sizeof(char)*(2048));
  422. //UTF82ANSI(strTgInfo.c_str(), strTgInfo.length(), szTemp, 2048);
  423. //strTgInfo = szTemp;
  424. cv::Point ptQues(colLeftPos + ttBoxWidth + tiSl + fSize.width, colTopPos);
  425. putTextZH(img, fSize, strTgInfo.c_str(), ptQues, Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离
  426. lineLen += fSize.width;
  427. cv::Rect rcQues(ptQues.x, ptQues.y, fSize.width, fSize.height);
  428. cv::Rect rcAns(colLeftPos + 30, colTopPos + fSize.height, colWidth - ttBoxWidth, 1);
  429. colTopPos = colTopPos + fSize.height * 3.5; //两行之间两倍的距离用于书写
  430. //lineGrayPix = rng.operator ()(220);
  431. line(img, cv::Point(colLeftPos + 30, colTopPos), cv::Point(colLeftPos + colWidth - 1, colTopPos), Scalar(lineGrayPix, lineGrayPix, lineGrayPix), 1, 8, 0);
  432. rcAns.height = colTopPos - rcAns.y + lineDis;
  433. //生成识别信息
  434. Json::Value ttbox;
  435. ttbox["x"] = rcbox.x;
  436. ttbox["y"] = rcbox.y;
  437. ttbox["width"] = rcbox.width;
  438. ttbox["height"] = rcbox.height;
  439. Json::Value queNum;
  440. queNum["x"] = rcNum.x;
  441. queNum["y"] = rcNum.y;
  442. queNum["width"] = rcNum.width;
  443. queNum["height"] = rcNum.height;
  444. Json::Value queInfo;
  445. queInfo["x"] = rcQues.x;
  446. queInfo["y"] = rcQues.y;
  447. queInfo["width"] = rcQues.width;
  448. queInfo["height"] = rcQues.height;
  449. queInfo["info"] = strTgInfo.c_str();
  450. queInfo["solution"] = strSolution.c_str();
  451. Json::Value ansInfo;
  452. ansInfo["x"] = rcAns.x;
  453. ansInfo["y"] = rcAns.y;
  454. ansInfo["width"] = rcAns.width;
  455. ansInfo["height"] = rcAns.height;
  456. Json::Value item;
  457. item["id"] = iQNum;
  458. item["ttbox"] = ttbox;
  459. item["queNum"] = queNum;
  460. item["queInfo"] = queInfo;
  461. item["ansInfo"] = ansInfo;
  462. root.append(item);
  463. // test
  464. /*rectangle(img, rcbox, cv::Scalar(0, 0, 200), 1, 8, 0);
  465. rectangle(img, rcNum, cv::Scalar(0, 200, 0), 1, 8, 0);
  466. rectangle(img, rcQues, cv::Scalar(100, 0, 0), 1, 8, 0);
  467. rectangle(img, rcAns, cv::Scalar(200, 0, 0), 1, 8, 0);*/
  468. }
  469. }
  470. std::string strJson = JsonToString(root);
  471. CString strTemplatePath;
  472. strTemplatePath.Format(L"%s/json/%d.json", dir, index);
  473. CFile zip;
  474. zip.Open(strTemplatePath, CFile::modeCreate | CFile::modeWrite);
  475. zip.Write((void*)strJson.c_str(), strJson.length());
  476. zip.Close();
  477. wchar_t tempPath[MAX_PATH];
  478. DWORD dwSize = MAX_PATH;
  479. GetTempPath(dwSize, tempPath);//获取临时文件夹路径
  480. static int tmp_index = 1;
  481. wchar_t szPath[MAX_PATH];
  482. _stprintf(szPath, L"%stmp_%d.png", tempPath, tmp_index);
  483. tmp_index++;
  484. if (tmp_index > 10)
  485. {
  486. tmp_index = 1;
  487. }
  488. strPngPath = TstringToGB2312(szPath);
  489. cv::imwrite(strPngPath, img);
  490. /*cv::namedWindow("fuck", 1);
  491. cv::imshow("fuck", img);
  492. cv::waitKey(0);*/
  493. return 0;
  494. }
  495. // rect_label : 模板的切割区域 rect_ocr :检测到的手写区域
  496. double math_iou(cv::Rect rect_label, cv::Rect rect_ocr)
  497. {
  498. int endx = max(rect_label.x + rect_label.width, rect_ocr.x + rect_ocr.width);
  499. int startx = min(rect_label.x, rect_ocr.x);
  500. int width = rect_label.width + rect_ocr.width - (endx - startx);
  501. int endy = max(rect_label.y + rect_label.height, rect_ocr.y + rect_ocr.height);
  502. int starty = min(rect_label.y, rect_ocr.y);
  503. int height = rect_label.height + rect_ocr.height - (endy - starty);
  504. double area = 0.0, iou = 0.0;
  505. if (width <= 0 || height <= 0)
  506. return 0.0;
  507. area = width * height*1.0;
  508. iou = area / double(rect_ocr.area());
  509. return iou;
  510. }
  511. int get_gray_num(cv::Mat & img, cv::Rect & box, int &meanGray, int threshold) {
  512. uchar * ptr;
  513. int count = 0;
  514. long long totalGrayNum = 0;
  515. for (int h = box.y; h < box.y + box.height; h++) {
  516. ptr = img.ptr<uchar>(h);
  517. for (int w = box.x; w < box.x + box.width; w++) {
  518. int pix = static_cast<int>(ptr[w]);
  519. totalGrayNum += pix;
  520. if (pix < threshold)
  521. count++;
  522. }
  523. }
  524. meanGray = totalGrayNum / box.area();
  525. return count;
  526. }
  527. int get_around_box_gray(cv::Mat & img, cv::Rect & box) {
  528. int left = (box.x - box.width / 2) > 0 ? (box.x - box.width / 2) : 0;
  529. int top = (box.y - box.height / 2) > 0 ? (box.y - box.height / 2) : 0;
  530. int right = (box.x + box.width * 3 / 2) < (img.cols - 1) ? (box.x + box.width * 3 / 2) : (img.cols - 1);
  531. int bottom = (box.y + box.height * 3 / 2) < (img.rows - 1) ? (box.y + box.height * 3 / 2) : (img.rows - 1);
  532. uchar * ptr;
  533. std::int64_t total = 0;
  534. int number = 0;
  535. for (int h = top; h < bottom; h++) {
  536. ptr = img.ptr<uchar>(h);
  537. for (int w = left; w < right; w++) {
  538. if (w > box.x && w<(box.x + box.width) && h>box.y&&h < (box.y + box.height))
  539. continue;
  540. total += static_cast<std::int64_t>(ptr[w]);
  541. number++;
  542. }
  543. }
  544. return static_cast<int>(total / number);
  545. }
  546. #define USE_THRESHOLD_VAL 200
  547. bool analysis_ttbox_mark(cv::Mat & imgSrc, cv::Rect & rc) {
  548. int thre = min(200, max(get_around_box_gray(imgSrc, rc), 160));
  549. int gray = get_gray_num(imgSrc, rc, thre, USE_THRESHOLD_VAL);
  550. double ds = gray * 1.0 / rc.area()*1.0;
  551. if (ds > 0.50)
  552. return true;
  553. return false;
  554. }
  555. int cutPaper(int pageNum, std::string strJsonPath, std::string strPaperPath, std::string strSavePath, vector<std::tuple<cv::Rect, std::string>>& vecTranslate)
  556. {
  557. ifstream in(strJsonPath, ios::binary);
  558. if (!in.is_open())
  559. {
  560. return 1;
  561. }
  562. Json::Features features;
  563. Json::Reader reader(features);
  564. Json::Value root;
  565. if (!reader.parse(in, root))
  566. {
  567. return 1;
  568. }
  569. if (!root.isArray())
  570. {
  571. return 1;
  572. }
  573. cv::Mat src = cv::imread(strPaperPath);
  574. if (src.empty())
  575. {
  576. return 1;
  577. }
  578. auto pfGetPos = [](const Json::Value & value, cv::Rect& rc) {
  579. int x = 0, y = 0, w = 0, h = 0;
  580. if (value.isMember("x"))
  581. {
  582. x = value["x"].asInt();
  583. }
  584. if (value.isMember("y"))
  585. {
  586. y = value["y"].asInt();
  587. }
  588. if (value.isMember("width"))
  589. {
  590. w = value["width"].asInt();
  591. }
  592. if (value.isMember("height"))
  593. {
  594. h = value["height"].asInt();
  595. }
  596. rc = cv::Rect(x, y, w, h);
  597. };
  598. for (int i = 0; i < root.size(); i++)
  599. {
  600. Json::Value row = root[i];
  601. std::string id = row["id"].asString();
  602. std::string info = row["queInfo"]["info"].asString();
  603. std::string solution = row["queInfo"]["solution"].asString();
  604. Json::Value ttbox = row["ttbox"];
  605. Json::Value ansInfo = row["ansInfo"];
  606. cv::Rect rc_ttbox;
  607. cv::Rect rc_ansInfo;
  608. pfGetPos(ttbox, rc_ttbox);
  609. pfGetPos(ansInfo, rc_ansInfo);
  610. // 选择框灰度判断
  611. cv::Mat gray_src;
  612. cv::cvtColor(src, gray_src, COLOR_BGR2GRAY);
  613. bool ret = analysis_ttbox_mark(gray_src, rc_ttbox);
  614. char szTxtPath[MAX_PATH] = { 0 };
  615. char szStdPath[MAX_PATH] = { 0 };
  616. char szJpgPath[MAX_PATH] = { 0 };
  617. DWORD dwCount = GetTickCount();
  618. sprintf(szTxtPath, "%s\\%s\\%d_%s_%ld_yy.txt", strSavePath.c_str(), ret ? "abnormal" : "normal", pageNum, id.c_str(), dwCount);
  619. sprintf(szStdPath, "%s\\%s\\%d_%s_%ld_zz.txt", strSavePath.c_str(), ret ? "abnormal" : "normal", pageNum, id.c_str(), dwCount);
  620. sprintf(szJpgPath, "%s\\%s\\%d_%s_%ld.jpg", strSavePath.c_str(), ret ? "abnormal" : "normal", pageNum, id.c_str(), dwCount);
  621. CFile zip1;
  622. zip1.Open(CA2T(szTxtPath), CFile::modeCreate | CFile::modeWrite);
  623. zip1.Write((void*)info.c_str(), info.length());
  624. zip1.Close();
  625. CFile zip2;
  626. zip2.Open(CA2T(szStdPath), CFile::modeCreate | CFile::modeWrite);
  627. zip2.Write((void*)solution.c_str(), solution.length());
  628. zip2.Close();
  629. cv::Mat cut = src(rc_ansInfo);
  630. cv::imwrite(szJpgPath, cut);
  631. int index = 1;
  632. for (auto& iter : vecTranslate)
  633. {
  634. cv::Rect rcOcr = std::get<0>(iter);
  635. std::string words = std::get<1>(iter);
  636. if (math_iou(rc_ansInfo, rcOcr) > 0.5)
  637. {
  638. char szOcrJpgPath[MAX_PATH] = { 0 };
  639. char szOcrJpgText[MAX_PATH] = { 0 };
  640. sprintf(szOcrJpgPath, "%s\\%s\\%d_%s_%ld_%d_%d_%d_%d_%d.jpg", strSavePath.c_str(), ret ? "abnormal_small" : "normal_small", pageNum, id.c_str(), dwCount,
  641. rcOcr.x - rc_ansInfo.x, rcOcr.y - rc_ansInfo.y, rcOcr.width, rcOcr.height, index);
  642. sprintf(szOcrJpgText, "%s\\%s\\%d_%s_%ld_%d_%d_%d_%d_%d.txt", strSavePath.c_str(), ret ? "abnormal_small" : "normal_small", pageNum, id.c_str(), dwCount,
  643. rcOcr.x - rc_ansInfo.x, rcOcr.y - rc_ansInfo.y, rcOcr.width, rcOcr.height, index);
  644. CFile zip3;
  645. zip3.Open(CA2T(szOcrJpgText), CFile::modeCreate | CFile::modeWrite);
  646. zip3.Write((void*)words.c_str(), words.length());
  647. zip3.Close();
  648. cv::Mat cut = src(rcOcr);
  649. cv::imwrite(szOcrJpgPath, cut);
  650. index++;
  651. }
  652. }
  653. }
  654. return 0;
  655. }
  656. int PareseModeJson(preinfo::templatesInfo& temeplatInfo)
  657. {
  658. ifstream in("./page.json", ios::binary);
  659. if (!in.is_open())
  660. {
  661. return 1;
  662. }
  663. int uuid = 100000; /// 每个框子的一个独立ID 不会有重复的
  664. Json::Features features;
  665. Json::Reader reader(features);
  666. Json::Value root;
  667. if (!reader.parse(in, root))
  668. {
  669. return 1;
  670. }
  671. if (!root.isObject())
  672. {
  673. return 1;
  674. }
  675. //1.获取模板大小信息
  676. preinfo::PaperTemplateInfo page;
  677. Json::Value imageSize = root["imageSize"];
  678. page.height = imageSize["height"].asInt();
  679. page.width = imageSize["width"].asInt();
  680. //2.获取定位点信息
  681. Json::Value loction = root["location"];
  682. auto funGetPos = [&](Json::Value item) {
  683. preinfo::PaperRect<double> pb;
  684. int x = item["x"].asInt();
  685. int y = item["y"].asInt();
  686. int h = item["height"].asInt();
  687. int w = item["width"].asInt();
  688. pb.centerx = x + w / 2.0;
  689. pb.centery = y + h / 2.0;
  690. pb.width = w;
  691. pb.height = h;
  692. return pb;
  693. };
  694. double offsetx = page.width; double offsety = page.height;
  695. for (int i = 0; i < loction.size(); i++)
  696. {
  697. Json::Value row = loction[i];
  698. preinfo::LocationPoint lc;
  699. lc.id = uuid++;
  700. auto tm = funGetPos(row);
  701. lc.centerx = tm.centerx;
  702. lc.centery = tm.centery;
  703. lc.width = tm.width;
  704. lc.height = tm.height;
  705. if (offsetx > lc.centerx) offsetx = lc.getX();
  706. if (offsety > lc.centery) offsety = lc.getY();
  707. page.vecLocaltionPoints.push_back(lc);
  708. }
  709. //3.获取客观题信息
  710. Json::Value pagenumber = root["pagenumber"];
  711. for (int i = 0; i < pagenumber.size(); i++)
  712. {
  713. Json::Value item = pagenumber[i];
  714. string strId = item["id"].asString();
  715. preinfo::_QuestionChoice _qstc;
  716. _qstc.id = uuid++;
  717. _qstc.vecTiHao.push_back(atoi(strId.c_str()));
  718. double dBoundBox[4] = { 100000,100000,0,0 };
  719. _qstc.type = preinfo::BOX_QUESTION_CHOICE_M;
  720. auto itOpts = item["opt"];
  721. _qstc.number = itOpts.size();
  722. std::vector<preinfo::TtItem> _vctOpt;
  723. for (int i = 0; i < itOpts.size(); i++)
  724. {
  725. preinfo::TtItem opt;
  726. auto tm = funGetPos(itOpts[i]);
  727. opt.centerx = tm.centerx;
  728. opt.centery = tm.centery;
  729. opt.width = tm.width;
  730. opt.height = tm.height;
  731. _qstc.itemSize.width = opt.width;
  732. _qstc.itemSize.height = opt.height;
  733. dBoundBox[0] = dBoundBox[0] > opt.getX() ? opt.getX() : dBoundBox[0];
  734. dBoundBox[1] = dBoundBox[1] > opt.getY() ? opt.getY() : dBoundBox[1];
  735. dBoundBox[2] = dBoundBox[2] < (opt.centerx + opt.width / 2.0) ? (opt.centerx + opt.width / 2.0) : dBoundBox[2];
  736. dBoundBox[3] = dBoundBox[3] < (opt.centery + opt.height / 2.0) ? (opt.centery + opt.height / 2.0) : dBoundBox[3];
  737. auto subItem = itOpts[i];
  738. opt.optName = subItem["optName"].asString();
  739. _vctOpt.push_back(opt);
  740. }
  741. _qstc.width = (dBoundBox[2] - dBoundBox[0]);
  742. _qstc.height = (dBoundBox[3] - dBoundBox[1]);
  743. _qstc.centerx = dBoundBox[0] + _qstc.width / 2.0;
  744. _qstc.centery = dBoundBox[1] + _qstc.height / 2.0;
  745. _qstc.groups = _vctOpt;
  746. page._vecQtChoices.push_back(_qstc);
  747. }
  748. temeplatInfo.push_back(page);
  749. return 0;
  750. }