#include "pch.h" #include "CvxText.h" #include #include "BaseUtility.h" using namespace std; using namespace cv; #if defined(GNUC) #pragma GCC diagnostic push #pragma GCC diagnostic ignored “-Wdeprecated-declarations” #elif defined(_MSC_VER) #pragma warning(disable : 4996) #endif 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; } cv::Size GetTextSize(const char* str, int fontSize, const char* fn, bool italic, bool underline) { HDC hDC = CreateCompatibleDC(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); 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; } SelectObject(hDC, hOldFont); DeleteObject(hf); DeleteDC(hDC); cv::Size size; size.width = strBaseW; size.height = strBaseH; return size; } 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& vecBoxPages) { vector vecBinStr; vecBinStr.resize(12); for (size_t i = 11; i > 0; i--) { int ys = pageNum % 2; vecBinStr[i] = ys; pageNum = pageNum / 2; } vecBoxPages.clear(); 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; } void generatePageJson(vector& vecPts, vector& vecBoxPages) { Json::Value imageSize; imageSize["width"] = 1654; imageSize["height"] = 2344; Json::Value location(Json::arrayValue); for (auto& iter : vecPts) { Json::Value item; item["x"] = iter.x; item["y"] = iter.y; item["width"] = iter.width; item["height"] = iter.height; location.append(item); } Json::Value pagenumber(Json::arrayValue); int nSubVecSize = 4;// 每个小vector的容量 for (size_t i = 0, j = 1; i < vecBoxPages.size(); i += nSubVecSize) { vector vecSmall; auto last = std::min(vecBoxPages.size(), i + nSubVecSize); vecSmall.insert(vecSmall.begin(), vecBoxPages.begin() + i, vecBoxPages.begin() + last); Json::Value opt(Json::arrayValue); std::string choice[] = { "A", "B", "C", "D" }; for (int h = 0; h < vecSmall.size(); h++) { Json::Value bin; bin["x"] = vecSmall[h].x; bin["y"] = vecSmall[h].y; bin["width"] = vecSmall[h].width; bin["height"] = vecSmall[h].height; bin["optName"] = choice[h]; opt.append(bin); } Json::Value item; item["id"] = j; item["opt"] = opt; pagenumber.append(item); j++; } Json::Value itemRoot; itemRoot["imageSize"] = imageSize; itemRoot["location"] = location; itemRoot["pagenumber"] = pagenumber; Json::FastWriter writer; std::string strJson = writer.write(itemRoot); CFile zip; zip.Open(_T("D:\\page.json"), CFile::modeCreate | CFile::modeWrite); DWORD wide_string_len = MultiByteToWideChar(CP_ACP, 0, strJson.c_str(), -1, NULL, 0); WCHAR* wide_string = new WCHAR[wide_string_len]; wide_string_len = MultiByteToWideChar(CP_ACP, 0, strJson.c_str(), -1, wide_string, wide_string_len); DWORD utf8_string_len = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL); CHAR* utf8_string = new CHAR[utf8_string_len]; utf8_string_len = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, utf8_string, utf8_string_len, NULL, NULL); zip.Write((void*)utf8_string, utf8_string_len - 1); // LOG4CPLUS_INFO_FMT(pTestLogger, LOG4CPLUS_TEXT("%s"), CString(json_str.c_str())); delete wide_string; delete utf8_string; zip.Close(); } std::string JsonToString(const Json::Value & root) { static Json::Value def = []{ Json::Value def; Json::StreamWriterBuilder::setDefaults(&def); def["emitUTF8"] = true; return def; }(); std::ostringstream stream; Json::StreamWriterBuilder stream_builder; stream_builder.settings_ = def;//Config emitUTF8 std::unique_ptr writer(stream_builder.newStreamWriter()); writer->write(root, &stream); return stream.str(); } int dataCollectionPaper(int cols, int index, int fontSize, int lineGrayPix, bool engShow, std::vector& vecLines, CString dir, std::string& strPngPath) { /* 这里要做一些栏数 和 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); // 生成二进制码流 /* 二进制码流的坐标在下面函数里面实现,需要用书写获取 */ vector vecBoxPages; createBinString(img, index, ptw, pth, cv::Point(boxPageNumber.x, boxPageNumber.y), vecBoxPages); 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, "宋体"); // 生成page.json //generatePageJson(vecPts, vecBoxPages); // 画警示信息 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 ttBoxWidth = 30; // 填涂框的边长 static int tiSl = 10; // 题号和题干间的距离 static int lineDis = 20; // 两行之间的距离 int colWidth = fontW / cols; // 单栏的宽度 fSize.width = 0; fSize.height = 0; Json::Value root(Json::arrayValue); for (int col = 0; col < cols; col++) { // 逐栏画题 int colTopPos = topPos; int colLeftPos = leftPos + col * colWidth; for (size_t i = 0; i < maxLineNum; i++) { size_t vecIndex = i + col * maxLineNum; if (vecIndex >= vecLines.size()) { break; } colTopPos += lineDis; int lineLen = ttBoxWidth + tiSl; // 画填涂框 30*30 大小 cv::Rect rcbox(colLeftPos, colTopPos, ttBoxWidth, ttBoxWidth); rectangle(img, rcbox, cv::Scalar(0), 1, 8, 0); int iQNum = i + 1 + col * maxLineNum; string strStinfo = to_string(iQNum); strStinfo += "."; cv::Point ptNum(colLeftPos + ttBoxWidth + tiSl, colTopPos); putTextZH(img, fSize, strStinfo.c_str(), ptNum, Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离 lineLen += fSize.width; cv::Rect rcNum(ptNum.x, ptNum.y, fSize.width, fSize.height); strStinfo.clear(); 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; cv::Point ptQues(colLeftPos + ttBoxWidth + tiSl + fSize.width, colTopPos); putTextZH(img, fSize, strTgInfo.c_str(), ptQues, Scalar(0, 0, 0), fontSize, "宋体"); // 跟填涂框保持10px距离 lineLen += fSize.width; cv::Rect rcQues(ptQues.x, ptQues.y, fSize.width, fSize.height); cv::Rect rcAns(colLeftPos + 30, colTopPos + fSize.height, colWidth - ttBoxWidth, 1); colTopPos = colTopPos + fSize.height * 3.5; //两行之间两倍的距离用于书写 line(img, cv::Point(colLeftPos + 30, colTopPos), cv::Point(colLeftPos + colWidth - 1, colTopPos), Scalar(lineGrayPix, lineGrayPix, lineGrayPix), 1, 8, 0); rcAns.height = colTopPos - rcAns.y + lineDis; //生成识别信息 Json::Value ttbox; ttbox["x"] = rcbox.x; ttbox["y"] = rcbox.y; ttbox["width"] = rcbox.width; ttbox["height"] = rcbox.height; Json::Value queNum; queNum["x"] = rcNum.x; queNum["y"] = rcNum.y; queNum["width"] = rcNum.width; queNum["height"] = rcNum.height; Json::Value queInfo; queInfo["x"] = rcQues.x; queInfo["y"] = rcQues.y; queInfo["width"] = rcQues.width; queInfo["height"] = rcQues.height; queInfo["info"] = strTgInfo.c_str(); Json::Value ansInfo; ansInfo["x"] = rcAns.x; ansInfo["y"] = rcAns.y; ansInfo["width"] = rcAns.width; ansInfo["height"] = rcAns.height; Json::Value item; item["id"] = iQNum; item["ttbox"] = ttbox; item["queNum"] = queNum; item["queInfo"] = queInfo; item["ansInfo"] = queInfo; root.append(item); // test /*rectangle(img, rcbox, cv::Scalar(0, 0, 200), 1, 8, 0); rectangle(img, rcNum, cv::Scalar(0, 200, 0), 1, 8, 0); rectangle(img, rcQues, cv::Scalar(100, 0, 0), 1, 8, 0); rectangle(img, rcAns, cv::Scalar(200, 0, 0), 1, 8, 0);*/ } } std::string strJson = JsonToString(root); CString strTemplatePath; strTemplatePath.Format(L"%s/json/%d.json", dir, index); CFile zip; zip.Open(strTemplatePath, CFile::modeCreate | CFile::modeWrite); zip.Write((void*)strJson.c_str(), strJson.length()); zip.Close(); wchar_t tempPath[MAX_PATH]; DWORD dwSize = MAX_PATH; GetTempPath(dwSize, tempPath);//获取临时文件夹路径 static int tmp_index = 1; wchar_t szPath[MAX_PATH]; _stprintf(szPath, L"%stmp_%d.png", tempPath, tmp_index); tmp_index++; if (tmp_index > 10) { tmp_index = 1; } strPngPath = TstringToGB2312(szPath); cv::imwrite(strPngPath, img); /*cv::namedWindow("fuck", 1); cv::imshow("fuck", img); cv::waitKey(0);*/ return 0; }