OnLineCardPageIdentifier.cpp 162 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336
  1. #include "stdafx.h"
  2. #include "OnLineCardPageIdentifier.h"
  3. #include "OnLineCardSchemaStruct.h"
  4. #include "schema_struct.h"
  5. #include "schema_struct_auto.h"
  6. #include <iostream>
  7. #include <vector>
  8. #include <string>
  9. #include <sstream>
  10. #include <zxing\LuminanceSource.h>
  11. #include "GrayImageSource.h"
  12. #include <zxing\Exception.h>
  13. #include <zxing\qrcode\QRCodeReader.h>
  14. #include <zxing\qrcode\QRCodeReader.h>
  15. #include <zxing\BinaryBitmap.h>
  16. #include <zxing\common\GlobalHistogramBinarizer.h>
  17. #include <zxing\Binarizer.h>
  18. #include <zxing\qrcode\QRCodeReader.h>
  19. #include "schema_struct.h"
  20. #include <map>
  21. #include <..\zxinglib\zxing\ReaderException.h>
  22. #include "Util.h"
  23. #include "../lib/myqr/include/GetQRCode.h"
  24. #include "opencv2/opencv.hpp"
  25. #include <chrono>
  26. #include <ctime>
  27. #include "ParseQRInfo.h"
  28. #include "IdentifyArea.h"
  29. #include <atomic>
  30. #include <mutex>
  31. #define POSITION_JUGDMENT_THRESHOLD 0.6184074853897741
  32. std::atomic<int> g_examid_by_qr_code = 0;
  33. std::mutex g_mtx_examid;
  34. extern std::string g_appFilePathName;
  35. static std::string GetExePath()
  36. {
  37. HMODULE module = GetModuleHandle(0);
  38. char pFileName[MAX_PATH + 2] = { 0 };
  39. GetModuleFileNameA(module, pFileName, MAX_PATH);
  40. std::string csFullPath(pFileName);
  41. std::size_t nPos = csFullPath.find_last_of('\\');
  42. if (nPos == std::string::npos)
  43. return "";
  44. else
  45. return csFullPath.substr(0, nPos);
  46. }
  47. /********************************************************
  48. * @function :
  49. * @brief : 红色分析判断的客户端交互接口(阶段1:实现红色像素的数量统计 阶段2:实现红色的形状判断 目前只实现阶段1
  50. * @input : Mat img_input; // 切割好的打分框区域 精准区域
  51. int cols; // 该打分框的个数
  52. int type; // 打分框所属(非在线模板解答0 非在线模板填空1 在线模板解答2 在线模板填空3 )
  53. int mutil: // 是否是组合型包含(个位+十位) 打分框
  54. int * a_res; // 存放结果的数组
  55. int len; // a_res的长度 len应该等于cols
  56. * @output : int ret; // 0: succ 1: faild 2: img.empty||img.error
  57. * @return : int ret;
  58. * @author : Duste 2020/03/27 10:07
  59. * @History :
  60. *********************************************************/
  61. __declspec(dllimport) int api_red_check_analysis(cv::Mat&img, int cols, int type, int mutil, int * arr_result, int arr_len);
  62. /********************************************************
  63. * @function :
  64. * @brief : DTK的定位点查找
  65. * @input : @见函数说明 给定图像+模板定位点位置信息,
  66. * @output :
  67. * @return : int,(0:succ) (-1:input error) (-2:img.empty||img type.error) (-3:not found)
  68. * @author : Duste 2020/07/02 15:33
  69. * @History :
  70. *********************************************************/
  71. int api_location_anchor_points(const cv::Mat&img_gray, // 输入的灰度图像
  72. const std::vector<cv::Rect>&vec_lcpts, // json内的定位点信息
  73. const cv::Size&mb_size, // json内模板图像的宽高
  74. std::vector<cv::Rect>&vec_tops, // 接受找到的上面三个定位点 下标访问
  75. std::vector<cv::Rect>&vec_bottoms, // 接受找到的下面的两个定位点 下标访问
  76. std::vector<int>&vec_len, // 解决不同版本内存权限问题
  77. int&dir, // 接受识别到的本图像的方向(0123-上下左右)
  78. int top_number, // 知心慧学 top_number=3或4 在线模板 top_number=3
  79. int need_bottom // 是否需要查找bottom定位点
  80. );// Vec_lcpts: json内的两页内所有的定位点的矩形信息合集
  81. #if _DEBUG
  82. #pragma comment(lib,"../lib/myqr/debug/GetQR.lib")
  83. #pragma comment(lib,"../Win32/Debug/client_interactive_api_ttpdd.lib")
  84. #pragma comment(lib,"../Win32/Debug/client_interactive_api_d.lib")
  85. #pragma comment(lib,"../Win32/Debug/client_interactive_api_code_d.lib")
  86. #else
  87. #pragma comment(lib,"../lib/myqr/Release/GetQR.lib")
  88. #pragma comment(lib,"../Win32/Release/client_interactive_api_ttpd.lib")
  89. #pragma comment(lib,"../Win32/Release/client_interactive_api.lib")
  90. #pragma comment(lib,"../Win32/Release/client_interactive_api_code.lib")
  91. #endif
  92. using namespace cv;
  93. using namespace std;
  94. using namespace zxing;
  95. namespace OnLineCard{
  96. enum SelectType{
  97. //未选中
  98. UNSELECT,
  99. //不确定
  100. UNCERTAIN,
  101. //选中
  102. SELECTED
  103. };
  104. #define MAX_ITEM 64
  105. enum
  106. {
  107. JIEDATITYPE = 0,
  108. TIANKONGTITYPE = 1,
  109. };
  110. void SetTBLR(SchemaQuestionScore& score)
  111. {
  112. score.lt.x = score.lb.x = (int)(0.5 + score.centerx - score.width / 2.0);
  113. score.rt.x = score.rb.x = (int)(0.5 + score.centerx + score.width / 2.0);
  114. score.lt.y = score.rt.y = (int)(0.5 + score.centery - score.height / 2.0);
  115. score.lb.y = score.rb.y = (int)(0.5 + score.centery + score.height / 2.0);
  116. }
  117. COnLineCardPageIdentifier::COnLineCardPageIdentifier()
  118. {
  119. HMODULE module = GetModuleHandle(0);
  120. char pFileName[MAX_PATH + 2] = { 0 };
  121. GetModuleFileNameA(module, pFileName, MAX_PATH);
  122. std::string strPath = pFileName;
  123. int pos = strPath.find_last_of('\\', strPath.length());
  124. strPath = strPath.substr(0, pos) + "\\..\\config.ini";
  125. int nType = GetPrivateProfileIntA("USER", "algorithm_boption", 1, strPath.c_str());
  126. m_nBxuanxiang = (nType == 1);
  127. m_pTemplate = nullptr;
  128. src = NULL;
  129. feedDirection = ROTATE_0;
  130. m_strQrClass = "";
  131. m_bUseQr = false;
  132. }
  133. COnLineCardPageIdentifier::~COnLineCardPageIdentifier()
  134. {
  135. }
  136. // 识别
  137. int COnLineCardPageIdentifier::Identify()
  138. {
  139. try{
  140. int r = Identify_impl();
  141. if (r == identify::result::IDF_SUCCESS){
  142. omr_result->identified = TRUE;
  143. omr_result->identify_msg = "识别成功";
  144. return r;
  145. }
  146. }catch (std::exception &e){
  147. //LOGI("COnLineCardPageIdentifier::Identify 异常");
  148. return identify::result::IDF_FAILURE;
  149. }
  150. return identify::result::IDF_CANNOT_MATCH_TEMPLATE;
  151. }
  152. void COnLineCardPageIdentifier::SetScanMode(int type)
  153. {
  154. m_scantype = type;
  155. }
  156. void COnLineCardPageIdentifier::SetUseQr(std::string strQr, bool bUse)
  157. {
  158. m_bUseQr = bUse;
  159. m_strQrClass = strQr;
  160. }
  161. void COnLineCardPageIdentifier::SetOnlineScanType(int nOnlineScanType)
  162. {
  163. m_nOnlineScanType = nOnlineScanType;
  164. }
  165. void COnLineCardPageIdentifier::SetPaperMode(int mode)
  166. {
  167. m_paper_mode = mode;
  168. }
  169. void COnLineCardPageIdentifier::SetIdentiforMode(int subject, int mode)
  170. {
  171. m_subjectMode = subject;
  172. m_identiforMode = mode;
  173. }
  174. #define ROUND_SCALE(x) ((int)(x*scale+0.5))
  175. #define ROUND_SCALE_ARRAY(_dst,_src,count) \
  176. for (int i=0;i<count;i++)\
  177. {\
  178. _dst[i]=((int)(_src[i]*scale+0.5));\
  179. }
  180. void COnLineCardPageIdentifier::myscale_shema_param0(const schema_const_param& default_schema_const_param, schema_const_param& schema_param, double scale){
  181. schema_param.main_locate_point = default_schema_const_param.main_locate_point;
  182. ROUND_SCALE_ARRAY(schema_param.minw, default_schema_const_param.minw, 4);
  183. ROUND_SCALE_ARRAY(schema_param.maxw, default_schema_const_param.maxw, 4);
  184. ROUND_SCALE_ARRAY(schema_param.minh, default_schema_const_param.minh, 4);
  185. ROUND_SCALE_ARRAY(schema_param.maxh, default_schema_const_param.maxh, 4);
  186. }
  187. //旋转指定区域
  188. int COnLineCardPageIdentifier::xuanzhuan(IplImage * src0, IplImage * &dst_img, CvRect myRect)
  189. {
  190. cvSetImageROI(src0, myRect);
  191. IplImage* result = cvCreateImage(cvSize(myRect.width, myRect.height), src0->depth, src0->nChannels);
  192. cvCopy(src0, result, NULL);
  193. cvResetImageROI(src0);
  194. dst_img = cvCreateImage(cvSize(myRect.height, myRect.width), src0->depth, src0->nChannels);
  195. cvTranspose(result, dst_img);
  196. cvFlip(dst_img, NULL, 1);
  197. cvReleaseImage(&result);
  198. return identify::result::IDF_SUCCESS;
  199. }
  200. inline int get_distance(CvPoint2D32f p1, CvPoint2D32f p2){
  201. double dy = p2.y - p1.y;
  202. double dx = p2.x - p1.x;
  203. return static_cast<int>(sqrt(dx*dx + dy*dy));
  204. }
  205. // 纠偏后的新坐标点
  206. CvPoint2D32f get_new_point(CvPoint2D32f ptOld, int iWidth, int iHight, float thir)
  207. {
  208. // 转图像中心坐标系
  209. CvPoint2D32f ptNew, ptNew_, ptOld_;
  210. CvPoint2D32f ptMid = cvPoint2D32f(iWidth / 2.0, iHight / 2.0);
  211. ptOld_.x = ptOld.x - ptMid.x;
  212. ptOld_.y = -(ptOld.y - ptMid.y);
  213. float thetia = atan2(float(ptOld_.y), float(ptOld_.x));
  214. float angleOld = static_cast<int>( thetia * 180.0 / CV_PI);
  215. float angleNew = angleOld - thir;
  216. double dx = cos(angleNew*CV_PI / 180.0)*ptOld_.y / sin(angleOld*CV_PI / 180.0);
  217. double dy = sin(angleNew*CV_PI / 180.0)*ptOld_.y / sin(angleOld*CV_PI / 180.0);
  218. ptNew_.x = dx > 0 ? (dx + 0.5) : (dx - 0.5);
  219. ptNew_.y = dy > 0 ? (dy + 0.5) : (dy - 0.5);
  220. // 转回原坐标系坐标
  221. // 这里需要重新计算 中心点坐标(计算四个顶点后计算外接矩形再确定中心点)
  222. // 这里简化处理 因为只需要处理 90,180,270,等固定角度旋转
  223. if (90 == abs(thir) || 270 == abs(thir))
  224. ptMid = cvPoint2D32f(iHight / 2.0, iWidth / 2.0);
  225. ptNew.x = ptNew_.x + ptMid.x;
  226. ptNew.y = ptMid.y - ptNew_.y;
  227. return ptNew;
  228. }
  229. bool sort_locate_point_by_x_pt2f(const CvPoint2D32f c1, const CvPoint2D32f c2)
  230. {
  231. return c1.x < c2.x;
  232. }
  233. inline void sort_point_list(CvPoint2D32f * points_src){
  234. int max_y = std::max(std::max(points_src[0].y, points_src[1].y), points_src[2].y);
  235. vector<CvPoint2D32f> vec_one, vec_two;
  236. for (int i = 0; i < 3; i++)
  237. {
  238. CvPoint2D32f tmp = points_src[i];
  239. if (tmp.y > max_y / 2)
  240. vec_two.push_back(tmp);
  241. else
  242. vec_one.push_back(tmp);
  243. }
  244. std::sort(vec_one.begin(), vec_one.end(), sort_locate_point_by_x_pt2f);
  245. for (std::size_t i = 0; i < vec_one.size(); i++)
  246. points_src[i] = vec_one[i];
  247. points_src[vec_one.size()] = vec_two[0];
  248. return;
  249. }
  250. inline cv::Mat IplImageToMat(const IplImage* image, bool copyData = false)
  251. {
  252. if (!image)
  253. return cv::Mat();
  254. int imageDepth = IPL2CV_DEPTH(image->depth);
  255. int type = CV_MAKETYPE(imageDepth, image->nChannels);
  256. cv::Mat ret(image->height, image->width, type, image->imageData, image->widthStep);
  257. return copyData ? ret.clone() : ret;
  258. }
  259. int COnLineCardPageIdentifier::JiaoZheng_20200418_first(IplImage * src0_, IplImage * &dst_img,
  260. const vector<CvRect> & top_locate_point_list, const vector<CvRect> & bottom_locate_point_list, const vector<CvRect> & word_location_point_list,
  261. int dir)
  262. {
  263. // printf("线程:%s JiaoZheng_20200418_first1 \n", std::to_string(GetCurrentThreadId()).c_str());
  264. int bottom_id = 0;
  265. if (3 == dir)
  266. bottom_id = 1;
  267. CvPoint2D32f points_src[3] = {
  268. cvPoint2D32f(top_locate_point_list[0].x + top_locate_point_list[0].width / 2.0, top_locate_point_list[0].y + top_locate_point_list[0].height / 2.0),
  269. cvPoint2D32f(top_locate_point_list[top_locate_point_list.size() - 1].x + top_locate_point_list[top_locate_point_list.size() - 1].width / 2.0, top_locate_point_list[top_locate_point_list.size() - 1].y + top_locate_point_list[top_locate_point_list.size() - 1].height / 2.0),
  270. cvPoint2D32f(bottom_locate_point_list[bottom_id].x + bottom_locate_point_list[bottom_id].width / 2.0, bottom_locate_point_list[bottom_id].y + bottom_locate_point_list[bottom_id].height / 2.0)
  271. };
  272. IplImage * src0 = nullptr;
  273. // printf("线程:%s JiaoZheng_20200418_first2 \n", std::to_string(GetCurrentThreadId()).c_str());
  274. try{
  275. src0 = cvCloneImage(src0_);
  276. }
  277. catch (cv::Exception&e){
  278. // printf("线程:%s cvCloneImage %s\n", std::to_string(GetCurrentThreadId()).c_str(), e.what());
  279. }
  280. // printf("线程:%s JiaoZheng_20200418_first3 \n", std::to_string(GetCurrentThreadId()).c_str());
  281. // 0:上 旋转0度 1:下 旋转180度 2:左 旋转90度 3:右 旋转270度 如果不对请自行调整
  282. CvPoint2D32f points_src_rotate[3] = { points_src[0], points_src[1], points_src[2] };
  283. // printf("线程:%s JiaoZheng_20200418_first cvCreateImage dir=%d %d %d %d %d \n", std::to_string(GetCurrentThreadId()).c_str(), dir, src0->width, src0->height, src0->depth, src0->nChannels);
  284. float angle = 0.0;
  285. if (0 == dir) {
  286. angle = 0.0;
  287. dst_img = cvCreateImage(cvSize(src0->width, src0->height), src0->depth, src0->nChannels);
  288. }
  289. else if (1 == dir){
  290. angle = 180;
  291. //cvFlip(src0,src0,1);
  292. dst_img = cvCreateImage(cvSize(src0->width, src0->height), src0->depth, src0->nChannels);
  293. }
  294. else if (2 == dir) {
  295. angle = 90;
  296. dst_img = cvCreateImage(cvSize(src0->height, src0->width), src0->depth, src0->nChannels);
  297. }
  298. else if (3 == dir) {
  299. angle = 270;
  300. dst_img = cvCreateImage(cvSize(src0->height, src0->width), src0->depth, src0->nChannels);
  301. }
  302. else
  303. return identify::result::IDF_FAILURE;
  304. // printf("线程:%s JiaoZheng_20200418_first cvCreateImage end \n", std::to_string(GetCurrentThreadId()).c_str());
  305. if (angle > 1.0){
  306. for (int i = 0; i < 3; i++){
  307. points_src_rotate[i] = get_new_point(points_src_rotate[i], src0->width, src0->height, angle);
  308. }
  309. }
  310. // printf("线程:%s JiaoZheng_20200418_first4 \n", std::to_string(GetCurrentThreadId()).c_str());
  311. CvPoint2D32f points_dst[3];
  312. for (int i = 0; i < 3; i++){
  313. CvPoint2D32f pt_src_tmp = points_src_rotate[i];
  314. int nearst_id = -1;
  315. int nearst_distance = 100000000;
  316. for (std::size_t j = 0; j < word_location_point_list.size(); j++){
  317. CvPoint2D32f pt_dst_tmp = cvPoint2D32f(word_location_point_list[j].x + word_location_point_list[j].width / 2.0, word_location_point_list[j].y + word_location_point_list[j].height / 2.0);
  318. int distance = get_distance(pt_src_tmp, pt_dst_tmp);
  319. if (distance < nearst_distance){
  320. nearst_distance = distance;
  321. nearst_id = j;
  322. }
  323. }
  324. if (-1 == nearst_id)
  325. return identify::result::IDF_FAILURE;
  326. points_dst[i] = cvPoint2D32f(word_location_point_list[nearst_id].x + word_location_point_list[nearst_id].width / 2.0, word_location_point_list[nearst_id].y + word_location_point_list[nearst_id].height / 2.0);;
  327. }
  328. // printf("线程:%s JiaoZheng_20200418_first5 \n", std::to_string(GetCurrentThreadId()).c_str());
  329. CvMat * warp_mat = cvCreateMat(2, 3, CV_32FC1);
  330. cvGetAffineTransform(points_src, points_dst, warp_mat);
  331. cvWarpAffine(src0, dst_img, warp_mat, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(255));
  332. cvReleaseMat(&warp_mat);
  333. #if _DEBUG
  334. cvSaveImage("D:\\show\\1.png", src0);
  335. cvSaveImage("D:\\show\\2.png", dst_img);
  336. #endif
  337. cvReleaseImage(&src0);
  338. // printf("线程:%s JiaoZheng_20200418_first6 \n", std::to_string(GetCurrentThreadId()).c_str());
  339. return identify::result::IDF_SUCCESS;
  340. }
  341. int COnLineCardPageIdentifier::JiaoZheng_20200418_second_four(IplImage * src0_, IplImage * &dst_img,
  342. const vector<CvRect> & top_locate_point_list, const vector<CvRect> & bottom_locate_point_list, const vector<CvRect> & word_location_point_list,
  343. int dir)
  344. {
  345. //CV_Assert(top_locate_point_list.size()>2);
  346. //CV_Assert(bottom_locate_point_list.size());
  347. CvPoint2D32f points_src[4] = {
  348. cvPoint2D32f(top_locate_point_list[0].x + top_locate_point_list[0].width / 2.0, top_locate_point_list[0].y + top_locate_point_list[0].height / 2.0),
  349. cvPoint2D32f(top_locate_point_list[top_locate_point_list.size() - 1].x + top_locate_point_list[top_locate_point_list.size() - 1].width / 2.0, top_locate_point_list[top_locate_point_list.size() - 1].y + top_locate_point_list[top_locate_point_list.size() - 1].height / 2.0),
  350. cvPoint2D32f(bottom_locate_point_list[0].x + bottom_locate_point_list[0].width / 2.0, bottom_locate_point_list[0].y + bottom_locate_point_list[0].height / 2.0),
  351. cvPoint2D32f(bottom_locate_point_list[1].x + bottom_locate_point_list[1].width / 2.0, bottom_locate_point_list[1].y + bottom_locate_point_list[1].height / 2.0)
  352. };
  353. IplImage * src0 = cvCloneImage(src0_);
  354. // 0:上 旋转0度 1:下 旋转180度 2:左 旋转90度 3:右 旋转270度 如果不对请自行调整
  355. CvPoint2D32f points_src_rotate[4] = { points_src[0], points_src[1], points_src[2], points_src[3] };
  356. float angle = 0.0;
  357. if (0 == dir) {
  358. angle = 0.0;
  359. dst_img = cvCreateImage(cvSize(src0->width, src0->height), src0->depth, src0->nChannels);
  360. }
  361. else if (1 == dir){
  362. angle = 180;
  363. //cvFlip(src0,src0,1);
  364. dst_img = cvCreateImage(cvSize(src0->width, src0->height), src0->depth, src0->nChannels);
  365. }
  366. else if (2 == dir) {
  367. angle = 90;
  368. dst_img = cvCreateImage(cvSize(src0->height, src0->width), src0->depth, src0->nChannels);
  369. }
  370. else if (3 == dir) {
  371. angle = 270;
  372. dst_img = cvCreateImage(cvSize(src0->height, src0->width), src0->depth, src0->nChannels);
  373. }
  374. else
  375. return identify::result::IDF_FAILURE;
  376. if (angle > 1.0){
  377. for (int i = 0; i < 4; i++){
  378. points_src_rotate[i] = get_new_point(points_src_rotate[i], src0->width, src0->height, angle);
  379. }
  380. }
  381. CvPoint2D32f points_dst[4];
  382. for (std::size_t i = 0; i < 4; i++){
  383. CvPoint2D32f pt_src_tmp = points_src_rotate[i];
  384. int nearst_id = -1;
  385. int nearst_distance = 100000000;
  386. for (std::size_t j = 0; j < word_location_point_list.size(); j++){
  387. CvPoint2D32f pt_dst_tmp = cvPoint2D32f(word_location_point_list[j].x + word_location_point_list[j].width / 2.0, word_location_point_list[j].y + word_location_point_list[j].height / 2.0);
  388. int distance = get_distance(pt_src_tmp, pt_dst_tmp);
  389. if (distance < nearst_distance){
  390. nearst_distance = distance;
  391. nearst_id = j;
  392. }
  393. }
  394. if (-1 == nearst_id)
  395. return identify::result::IDF_FAILURE;
  396. points_dst[i] = cvPoint2D32f(word_location_point_list[nearst_id].x + word_location_point_list[nearst_id].width / 2.0, word_location_point_list[nearst_id].y + word_location_point_list[nearst_id].height / 2.0);;
  397. }
  398. Point2f pts[4];
  399. for (int i = 0; i < 4; i++)
  400. pts[i] = Point2f(points_src[i].x, points_src[i].y);
  401. Point2f ptd[4];
  402. for (int i = 0; i < 4; i++)
  403. ptd[i] = Point2f(points_dst[i].x, points_dst[i].y);
  404. cv::Mat warp_mat(cv::Size(3, 3), CV_32FC1);
  405. warp_mat = getPerspectiveTransform(pts, ptd);
  406. CvMat b = warp_mat;
  407. cvWarpPerspective(src0_, dst_img, &b, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(255));
  408. #if _DEBUG
  409. cvSaveImage("D:\\show\\1.png", src0);
  410. cvSaveImage("D:\\show\\2.png", dst_img);
  411. #endif
  412. cvReleaseImage(&src0);
  413. return identify::result::IDF_SUCCESS;
  414. }
  415. int COnLineCardPageIdentifier::JiaoZheng_20200418_second_five(IplImage * src0_, IplImage * &dst_img,
  416. const vector<CvRect> & top_locate_point_list, const vector<CvRect> & bottom_locate_point_list, const vector<CvRect> & word_location_point_list,
  417. int dir)
  418. {
  419. //CV_Assert(top_locate_point_list.size()>2);
  420. //CV_Assert(bottom_locate_point_list.size());
  421. CvPoint2D32f points_src[5] = {
  422. cvPoint2D32f(top_locate_point_list[0].x + top_locate_point_list[0].width / 2.0, top_locate_point_list[0].y + top_locate_point_list[0].height / 2.0),
  423. cvPoint2D32f(top_locate_point_list[1].x + top_locate_point_list[1].width / 2.0, top_locate_point_list[1].y + top_locate_point_list[1].height / 2.0),
  424. cvPoint2D32f(top_locate_point_list[top_locate_point_list.size() - 1].x + top_locate_point_list[top_locate_point_list.size() - 1].width / 2.0, top_locate_point_list[top_locate_point_list.size() - 1].y + top_locate_point_list[top_locate_point_list.size() - 1].height / 2.0),
  425. cvPoint2D32f(bottom_locate_point_list[0].x + bottom_locate_point_list[0].width / 2.0, bottom_locate_point_list[0].y + bottom_locate_point_list[0].height / 2.0),
  426. cvPoint2D32f(bottom_locate_point_list[1].x + bottom_locate_point_list[1].width / 2.0, bottom_locate_point_list[1].y + bottom_locate_point_list[1].height / 2.0)
  427. };
  428. IplImage * src0 = cvCloneImage(src0_);
  429. // 0:上 旋转0度 1:下 旋转180度 2:左 旋转90度 3:右 旋转270度 如果不对请自行调整
  430. CvPoint2D32f points_src_rotate[5] = { points_src[0], points_src[1], points_src[2], points_src[3], points_src[4] };
  431. float angle = 0.0;
  432. if (0 == dir) {
  433. angle = 0.0;
  434. dst_img = cvCreateImage(cvSize(src0->width, src0->height), src0->depth, src0->nChannels);
  435. }
  436. else if (1 == dir){
  437. angle = 180;
  438. //cvFlip(src0,src0,1);
  439. dst_img = cvCreateImage(cvSize(src0->width, src0->height), src0->depth, src0->nChannels);
  440. }
  441. else if (2 == dir) {
  442. angle = 90;
  443. dst_img = cvCreateImage(cvSize(src0->height, src0->width), src0->depth, src0->nChannels);
  444. }
  445. else if (3 == dir) {
  446. angle = 270;
  447. dst_img = cvCreateImage(cvSize(src0->height, src0->width), src0->depth, src0->nChannels);
  448. }
  449. else
  450. return identify::result::IDF_FAILURE;
  451. if (angle > 1.0){
  452. for (int i = 0; i < 5; i++){
  453. points_src_rotate[i] = get_new_point(points_src_rotate[i], src0->width, src0->height, angle);
  454. }
  455. }
  456. CvPoint2D32f points_dst[5];
  457. for (std::size_t i = 0; i < 5; i++){
  458. CvPoint2D32f pt_src_tmp = points_src_rotate[i];
  459. int nearst_id = -1;
  460. int nearst_distance = 100000000;
  461. for (std::size_t j = 0; j < word_location_point_list.size(); j++){
  462. CvPoint2D32f pt_dst_tmp = cvPoint2D32f(word_location_point_list[j].x + word_location_point_list[j].width / 2.0, word_location_point_list[j].y + word_location_point_list[j].height / 2.0);
  463. int distance = get_distance(pt_src_tmp, pt_dst_tmp);
  464. if (distance < nearst_distance){
  465. nearst_distance = distance;
  466. nearst_id = j;
  467. }
  468. }
  469. if (-1 == nearst_id)
  470. return identify::result::IDF_FAILURE;
  471. points_dst[i] = cvPoint2D32f(word_location_point_list[nearst_id].x + word_location_point_list[nearst_id].width / 2.0, word_location_point_list[nearst_id].y + word_location_point_list[nearst_id].height / 2.0);;
  472. }
  473. Point2f pts[5];
  474. for (int i = 0; i < 5; i++)
  475. pts[i] = Point2f(points_src[i].x, points_src[i].y);
  476. Point2f ptd[5];
  477. for (int i = 0; i < 5; i++)
  478. ptd[i] = Point2f(points_dst[i].x, points_dst[i].y);
  479. cv::Mat warp_mat(cv::Size(3, 3), CV_32FC1);
  480. warp_mat = getPerspectiveTransform(pts, ptd);
  481. CvMat b = warp_mat;
  482. cvWarpPerspective(src0_, dst_img, &b, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(255));
  483. #if _DEBUG
  484. cvSaveImage("D:\\show\\1.png", src0);
  485. cvSaveImage("D:\\show\\2.png", dst_img);
  486. #endif
  487. cvReleaseImage(&src0);
  488. return identify::result::IDF_SUCCESS;
  489. }
  490. int COnLineCardPageIdentifier::JiaoZheng_20200418(IplImage * src0, IplImage * &dst_img,
  491. const vector<CvRect> & top_locate_point_list, const vector<CvRect> & bottom_locate_point_list, const vector<CvRect> & word_location_point_list,
  492. int dir, double scale_x, double scale_y, int word_w, int word_h)
  493. {
  494. int len_l = top_locate_point_list.size();
  495. if (len_l != 3 || bottom_locate_point_list.size() != 2)
  496. return identify::result::IDF_FAILURE;
  497. // 中心点
  498. CvPoint2D32f points_src_[3] = {
  499. cvPoint2D32f(top_locate_point_list[0].x + top_locate_point_list[0].width / 2.0, top_locate_point_list[0].y + top_locate_point_list[0].height / 2.0),
  500. cvPoint2D32f(top_locate_point_list[len_l - 1].x + top_locate_point_list[len_l - 1].width / 2.0, top_locate_point_list[len_l - 1].y + top_locate_point_list[len_l - 1].height / 2.0),
  501. cvPoint2D32f(bottom_locate_point_list[0].x + bottom_locate_point_list[0].width / 2.0, bottom_locate_point_list[0].y + bottom_locate_point_list[0].height / 2.0)
  502. };
  503. cv::Size location_size[3] = {
  504. cvSize(top_locate_point_list[0].width, top_locate_point_list[0].height),
  505. cvSize(top_locate_point_list[len_l - 1].width, top_locate_point_list[len_l - 1].height),
  506. cvSize(bottom_locate_point_list[0].width, bottom_locate_point_list[0].height)
  507. };
  508. // 0:上 旋转0度 1:下 旋转180度 2:左 旋转90度 3:右 旋转270度 如果不对请自行调整
  509. CvPoint2D32f points_src_rotate[3] = { points_src_[0], points_src_[1], points_src_[2] };
  510. float angle = 0.0;
  511. int use_w = src0->width;
  512. int use_h = src0->height;
  513. if (0 == dir) {
  514. angle = 0.0;
  515. }
  516. else if (1 == dir){
  517. angle = 180;
  518. }
  519. else if (2 == dir) {
  520. angle = 90;
  521. swap(use_w, use_h);
  522. for (int i = 0; i < 3; i++)
  523. swap(location_size[i].width, location_size[i].height);
  524. }
  525. else if (3 == dir) {
  526. angle = 270;
  527. swap(use_w, use_h);
  528. for (int i = 0; i < 3; i++)
  529. swap(location_size[i].width, location_size[i].height);
  530. }
  531. else
  532. return identify::result::IDF_FAILURE;
  533. if (angle > 1.0){
  534. for (int i = 0; i < 3; i++){
  535. points_src_rotate[i] = get_new_point(points_src_rotate[i], src0->width, src0->height, angle);
  536. }
  537. }
  538. // 按照从左到右 从上到下 排序
  539. sort_point_list(points_src_rotate);
  540. // 推算边缘点
  541. CvPoint2D32f points_src[3] = {
  542. cvPoint2D32f(points_src_rotate[0].x - (location_size[0].width / 2.0 + 0.55), top_locate_point_list[0].y - (location_size[0].height / 2.0 + 0.55)),
  543. cvPoint2D32f(points_src_rotate[1].x + (location_size[1].width / 2.0 + 0.55), top_locate_point_list[1].y - (location_size[1].height / 2.0 + 0.55)),
  544. cvPoint2D32f(points_src_rotate[2].x - (location_size[2].width / 2.0 + 0.55), top_locate_point_list[2].y + (location_size[2].height / 2.0 + 0.55))
  545. };
  546. // 寻找json数据的对应定位点坐标
  547. CvPoint2D32f points_dst_[3]; // 中心点
  548. CvPoint2D32f points_dst[3]; // 边缘点
  549. for (std::size_t i = 0; i < 3; i++){
  550. CvPoint2D32f pt_src_tmp = points_src_rotate[i];
  551. int nearst_id = -1;
  552. int nearst_distance = 100000000;
  553. for (std::size_t j = 0; j < word_location_point_list.size(); j++){
  554. CvRect rc_tmp;
  555. rc_tmp.x = word_location_point_list[j].x*scale_x + 0.55;
  556. rc_tmp.y = word_location_point_list[j].y*scale_y + 0.55;
  557. rc_tmp.width = word_location_point_list[j].width*scale_x + 0.55;
  558. rc_tmp.height = word_location_point_list[j].height*scale_y + 0.55;
  559. CvPoint2D32f pt_dst_tmp = cvPoint2D32f(rc_tmp.x + rc_tmp.width / 2.0, rc_tmp.y + rc_tmp.height / 2.0);
  560. int distance = get_distance(pt_src_tmp, pt_dst_tmp);
  561. if (distance < nearst_distance){
  562. nearst_distance = distance;
  563. nearst_id = j;
  564. }
  565. }
  566. if (-1 == nearst_id)
  567. return identify::result::IDF_FAILURE;
  568. points_dst_[i] = cvPoint2D32f(word_location_point_list[nearst_id].x + word_location_point_list[nearst_id].width / 2.0, word_location_point_list[nearst_id].y + word_location_point_list[nearst_id].height / 2.0);;
  569. if (0 == i)
  570. points_dst[i] = cvPoint2D32f(word_location_point_list[nearst_id].x, word_location_point_list[nearst_id].y);
  571. else if (1 == i)
  572. points_dst[i] = cvPoint2D32f(word_location_point_list[nearst_id].x + word_location_point_list[nearst_id].width, word_location_point_list[nearst_id].y);
  573. else
  574. points_dst[i] = cvPoint2D32f(word_location_point_list[nearst_id].x, word_location_point_list[nearst_id].y + word_location_point_list[nearst_id].height);
  575. }
  576. #define GET_SCALE 1
  577. #if GET_SCALE
  578. int len = 3;
  579. int dis_w = get_distance(points_src_rotate[1], points_src_rotate[0]);
  580. int dis_h = get_distance(points_src_rotate[0], points_src_rotate[2]);
  581. int dis_ww = get_distance(points_dst[1], points_dst[0]);
  582. int dis_hw = get_distance(points_dst[0], points_dst[2]);
  583. double s_x = dis_w*1.0 / dis_ww*1.0;
  584. double s_y = dis_h*1.0 / dis_hw*1.0;
  585. int add_left = points_src_rotate[0].x - points_dst[0].x*s_x;
  586. int dis_w_right = (word_w - points_dst[1].x)*s_x;
  587. int dis_s_right = use_w - points_src_rotate[1].x;
  588. int add_right = dis_s_right - dis_w_right;
  589. int add_top = points_src_rotate[0].y - points_dst[0].y*s_y;
  590. int dis_w_bottom = (word_h - points_dst[1].y)*s_y;
  591. int dis_s_bottom = use_h - points_src_rotate[1].y;
  592. int add_bottom = dis_s_bottom - dis_w_bottom;
  593. IplImage* lp_after_warp = NULL;
  594. int xn_w = use_w - add_left - add_right;
  595. int xn_h = use_h - add_bottom - add_top;
  596. CvRect rc_box;//= cvRect(add_left,add_top,src0->width-add_left-add_right,src0->height-add_top-add_bottom);
  597. // 旋转矩形
  598. // 新的水平坐标点更新回 切割后的原图
  599. points_src_[0] = cvPoint2D32f(points_src_rotate[0].x - add_left, points_src_rotate[0].y - add_top);
  600. points_src_[1] = cvPoint2D32f(points_src_rotate[1].x - add_left, points_src_rotate[1].y - add_top);
  601. points_src_[2] = cvPoint2D32f(points_src_rotate[2].x - add_left, points_src_rotate[2].y - add_top);
  602. angle = 0.0;
  603. if (0 == dir) {
  604. rc_box = cvRect(add_left, add_top, src0->width - add_left - add_right, src0->height - add_top - add_bottom);
  605. lp_after_warp = cvCreateImage(cvSize(rc_box.width, rc_box.height), src0->depth, src0->nChannels);
  606. dst_img = cvCreateImage(cvSize(lp_after_warp->width, lp_after_warp->height), lp_after_warp->depth, lp_after_warp->nChannels);
  607. }
  608. else if (1 == dir){
  609. angle = -180;
  610. rc_box = cvRect(add_right, add_bottom, src0->width - add_left - add_right, src0->height - add_top - add_bottom);
  611. lp_after_warp = cvCreateImage(cvSize(rc_box.width, rc_box.height), src0->depth, src0->nChannels);
  612. dst_img = cvCreateImage(cvSize(lp_after_warp->width, lp_after_warp->height), lp_after_warp->depth, lp_after_warp->nChannels);
  613. }
  614. else if (2 == dir) {
  615. angle = -90;
  616. rc_box = cvRect(add_top, add_right, src0->width - add_top - add_bottom, src0->height - add_right - add_left);
  617. lp_after_warp = cvCreateImage(cvSize(rc_box.width, rc_box.height), src0->depth, src0->nChannels);
  618. dst_img = cvCreateImage(cvSize(lp_after_warp->height, lp_after_warp->width), lp_after_warp->depth, lp_after_warp->nChannels);
  619. }
  620. else if (3 == dir) {
  621. angle = -270;
  622. rc_box = cvRect(add_bottom, add_left, src0->width - add_bottom - add_top, src0->height - add_left - add_right);
  623. lp_after_warp = cvCreateImage(cvSize(rc_box.width, rc_box.height), src0->depth, src0->nChannels);
  624. dst_img = cvCreateImage(cvSize(lp_after_warp->height, lp_after_warp->width), lp_after_warp->depth, lp_after_warp->nChannels);
  625. }
  626. else
  627. return identify::result::IDF_FAILURE;
  628. if (angle < -1.0){
  629. for (int i = 0; i < 3; i++){
  630. points_src_[i] = get_new_point(points_src_[i], xn_w, xn_h, angle);
  631. }
  632. }
  633. cvSetImageROI(src0, rc_box);
  634. cvCopy(src0, lp_after_warp, NULL);
  635. #if _DEBUG
  636. cvSaveImage("D:\\show\\0.png", lp_after_warp);
  637. #endif
  638. cvResetImageROI(src0);
  639. //}
  640. use_w = lp_after_warp->width;
  641. use_h = lp_after_warp->height;
  642. if (dir > 1)
  643. swap(use_h, use_w);
  644. double d_x = use_w*1.0 / word_w;
  645. double d_y = use_h*1.0 / word_h;
  646. points_dst_[0] = cvPoint2D32f(points_dst_[0].x*d_x + 0.55, points_dst_[0].y*d_y + 0.55);
  647. points_dst_[1] = cvPoint2D32f(points_dst_[1].x*d_x + 0.55, points_dst_[1].y*d_y + 0.55);
  648. points_dst_[2] = cvPoint2D32f(points_dst_[2].x*d_x + 0.55, points_dst_[2].y*d_y + 0.55);
  649. #endif
  650. CvMat * warp_mat = cvCreateMat(2, 3, CV_32FC1);
  651. cvGetAffineTransform(points_src_, points_dst_, warp_mat);
  652. cvWarpAffine(lp_after_warp, dst_img, warp_mat, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(255));
  653. cvReleaseMat(&warp_mat);
  654. cvReleaseImage(&lp_after_warp);
  655. #if _DEBUG
  656. cvSaveImage("D:\\show\\1.png", src0);
  657. cvSaveImage("D:\\show\\2.png", dst_img);
  658. #endif
  659. return identify::result::IDF_SUCCESS;
  660. }
  661. int COnLineCardPageIdentifier::MyFindDingWeiDian(IplImage * src_gray_img, CvMemStorage* storage, schema_const_param &schema_param, vector<CvRect>& locate_point_list, int & _dir)
  662. {
  663. int range = m_pTemplate->dingweidian_range_top;
  664. //LOGFMTI("MyFindDingWeiDian 1 range=%d", range);
  665. CvRect roi[4] = {//上下左右 roi
  666. cvRect(0, 0, src_gray_img->width, min(range, src_gray_img->height)),
  667. cvRect(0, src_gray_img->height - min(range, src_gray_img->height) - 1, src_gray_img->width, min(range, src_gray_img->height)),
  668. cvRect(0, 0, min(range, src_gray_img->width), src_gray_img->height),
  669. cvRect(src_gray_img->width - min(range, src_gray_img->width) - 1, 0, min(range, src_gray_img->width), src_gray_img->height)
  670. };
  671. //LOGFMTI("MyFindDingWeiDian 2 range=%d", range);
  672. CvSeq * contour = NULL;
  673. for (int dir = 0; dir < 4; dir++)
  674. {
  675. if (dir < 2)myscale_shema_param0(m_default_schema_param, schema_param, src_gray_img->width / (double)m_default_schema_param.shijuanwidth);
  676. else myscale_shema_param0(m_default_schema_param, schema_param, src_gray_img->height / (double)m_default_schema_param.shijuanwidth);
  677. IplImage * binary_img = cvCreateImage(cvSize(roi[dir].width + 10, roi[dir].height + 10), IPL_DEPTH_8U, 1);
  678. IplImage * binary_img2 = cvCreateImage(cvSize(roi[dir].width + 10, roi[dir].height + 10), IPL_DEPTH_8U, 1);
  679. cvSet(binary_img, cvScalarAll(0));
  680. //LOGFMTI("MyFindDingWeiDian 3 x=%d y= %d,w = %d h = %d", roi[dir].x, roi[dir].y, roi[dir].width, roi[dir].height);
  681. cvSetImageROI(src_gray_img, roi[dir]);
  682. cvSetImageROI(binary_img, cvRect(5, 5, roi[dir].width, roi[dir].height));
  683. cvThreshold(src_gray_img, binary_img, 180, 255, CV_THRESH_BINARY_INV);
  684. cvResetImageROI(binary_img);
  685. int an = 2;
  686. IplConvKernel * element = cvCreateStructuringElementEx(an * 2 + 1, an * 2 + 1, an, an, CV_SHAPE_RECT, 0);//创建结构元素
  687. cvErode(binary_img, binary_img2, element, 1);//腐蚀图像
  688. cvDilate(binary_img2, binary_img2, element, 1);//膨胀图像
  689. cvSub(binary_img, binary_img2, binary_img2);
  690. cvDilate(binary_img, binary_img, element, 1);//膨胀图像
  691. cvErode(binary_img, binary_img, element, 1);//腐蚀图像
  692. cvSub(binary_img, binary_img2, binary_img);
  693. if (element)
  694. cvReleaseStructuringElement(&element);
  695. if (m_pTemplate->open_save_debug_img){
  696. char szImageName[1024] = { 0 };
  697. sprintf_s(szImageName, "%s\\image\\Debug\\find_dingweidian_top%d.jpg", g_appFilePathName, dir);
  698. cvSaveImage(szImageName, binary_img);
  699. }
  700. int contours = cvFindContours(binary_img, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(roi[dir].x - 5, roi[dir].y - 5));
  701. cvReleaseImage(&binary_img);
  702. cvReleaseImage(&binary_img2);
  703. int validCount = 0;
  704. vector<CvContour *> contourList;
  705. for (CvContour * c = (CvContour *)contour; c != 0; c = (CvContour *)c->h_next)
  706. {
  707. locate_point_list.clear();
  708. if (c->rect.width > schema_param.maxw[dir] * m_pTemplate->dingweidian_w_max_rate)continue;
  709. if (c->rect.height > schema_param.maxh[dir] * m_pTemplate->dingweidian_h_max_rate)continue;
  710. if (c->rect.width < schema_param.minw[dir] * m_pTemplate->dingweidian_w_min_rate)continue;
  711. if (c->rect.height < schema_param.minh[dir] * m_pTemplate->dingweidian_h_min_rate)continue;
  712. cvSetImageROI(src_gray_img, c->rect);
  713. int area = GetBlackArea(src_gray_img);
  714. if (area < c->rect.width*c->rect.height*0.75)continue;
  715. validCount++;
  716. contourList.push_back(c);
  717. }
  718. cvResetImageROI(src_gray_img);
  719. std::vector<CvRect> list;
  720. for (int i = 0; i < contourList.size(); i++)
  721. {
  722. CvRect rect = contourList[i]->rect;
  723. list.push_back(rect);
  724. }
  725. CvRect rect;
  726. float k = 0, b = 0;
  727. if (dir == 0 || dir == 1){
  728. std::sort(list.begin(), list.end(), sort_locate_point_by_x);
  729. }
  730. else {
  731. std::sort(list.begin(), list.end(), sort_locate_point_by_y);
  732. }
  733. if (list.size() > 1)
  734. {
  735. bool bV = false;
  736. locate_point_list.push_back(list[0]);
  737. if (list[list.size() - 1].x - list[0].x>-0.00001 && list[list.size() - 1].x - list[0].x<0.00001)
  738. {
  739. bV = true;
  740. }
  741. else{
  742. k = (list[list.size() - 1].y - list[0].y + 0.0) / (list[list.size() - 1].x - list[0].x);
  743. }
  744. b = list[0].y - k*list[0].x;
  745. for (int i = 1; i < list.size() - 1; i++)
  746. {
  747. if (dir == 0 || dir == 1){
  748. int y = k*list[i].x + b;
  749. if (fabs(y - list[i].y + 0.0) < list[i].height / 2)
  750. locate_point_list.push_back(list[i]);
  751. }
  752. else
  753. {
  754. int x = 0;
  755. if (bV){
  756. x = list[0].x;
  757. }
  758. else{
  759. x =(list[i].y - b) / k;
  760. }
  761. if (fabs(x - list[i].x + 0.0) < list[i].width / 2)
  762. locate_point_list.push_back(list[i]);
  763. }
  764. }
  765. locate_point_list.push_back(list[list.size() - 1]);
  766. }
  767. if (locate_point_list.size() == 3)
  768. {
  769. //LOGFMTI("MyFindDingWeiDian 4 range=%d", range);
  770. _dir = dir;
  771. if (MyFindBottomDingWeiDian(src_gray_img, storage, schema_param, locate_point_list, dir) == identify::result::IDF_FAILURE)
  772. return identify::result::IDF_FAILURE;
  773. //LOGFMTI("MyFindDingWeiDian 5 range=%d", range);
  774. return identify::result::IDF_SUCCESS;
  775. break;
  776. }
  777. }
  778. return identify::result::IDF_FAILURE;
  779. }
  780. //查找定位点
  781. int COnLineCardPageIdentifier::MyFindBottomDingWeiDian(IplImage * src_gray_img, CvMemStorage* storage, schema_const_param &schema_param, vector<CvRect>& locate_point_list, int & _dir)
  782. {
  783. int nRange = m_pTemplate->dingweidian_rang_buttom;
  784. CvRect roi[4] = {//上下左右 roi
  785. cvRect(0, 0, src_gray_img->width, min(nRange, src_gray_img->height)),
  786. cvRect(0, src_gray_img->height - min(nRange, src_gray_img->height) - 1, src_gray_img->width, min(nRange, src_gray_img->height)),
  787. cvRect(0, 0, min(nRange, src_gray_img->width), src_gray_img->height),
  788. cvRect(src_gray_img->width - min(nRange, src_gray_img->width) - 1, 0, min(nRange, src_gray_img->width), src_gray_img->height)
  789. };
  790. CvSeq * contour = NULL;
  791. int dir;
  792. _dir > 1 ? dir = 3 - _dir + 2 : dir = 1 - _dir;
  793. m_vecBottom.clear();
  794. //for (int dir=0;dir<4;dir++)
  795. {
  796. IplImage * binary_img = cvCreateImage(cvSize(roi[dir].width + 10, roi[dir].height + 10), IPL_DEPTH_8U, 1);
  797. IplImage * binary_img2 = cvCreateImage(cvSize(roi[dir].width + 10, roi[dir].height + 10), IPL_DEPTH_8U, 1);
  798. cvSet(binary_img, cvScalarAll(0));
  799. cvSetImageROI(src_gray_img, roi[dir]);
  800. cvSetImageROI(binary_img, cvRect(5, 5, roi[dir].width, roi[dir].height));
  801. cvThreshold(src_gray_img, binary_img, 180, 255, CV_THRESH_BINARY_INV);
  802. cvResetImageROI(binary_img);
  803. int an = 2;
  804. IplConvKernel * element = cvCreateStructuringElementEx(an * 2 + 1, an * 2 + 1, an, an, CV_SHAPE_RECT, 0);//创建结构元素
  805. cvErode(binary_img, binary_img2, element, 1);//腐蚀图像
  806. cvDilate(binary_img2, binary_img2, element, 1);//膨胀图像
  807. cvSub(binary_img, binary_img2, binary_img2);
  808. cvDilate(binary_img, binary_img, element, 1);//膨胀图像
  809. cvErode(binary_img, binary_img, element, 1);//腐蚀图像
  810. cvSub(binary_img, binary_img2, binary_img);
  811. if (m_pTemplate->open_save_debug_img){
  812. char szImageName[1024] = { 0 };
  813. sprintf_s(szImageName, "%s\\image\\Debug\\find_dingweidian_buttom%d.jpg", g_appFilePathName, dir);
  814. cvSaveImage(szImageName, binary_img);
  815. }
  816. int contours = cvFindContours(binary_img, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(roi[dir].x - 5, roi[dir].y - 5));
  817. if (element)
  818. cvReleaseStructuringElement(&element);
  819. cvReleaseImage(&binary_img);
  820. cvReleaseImage(&binary_img2);
  821. int validCount = 0;
  822. vector<CvContour *> contourList;
  823. for (CvContour * c = (CvContour *)contour; c != 0; c = (CvContour *)c->h_next)
  824. {
  825. if (c->rect.width > schema_param.maxw[dir] * m_pTemplate->dingweidian_w_max_rate)continue;
  826. if (c->rect.height > schema_param.maxh[dir] * m_pTemplate->dingweidian_h_max_rate)continue;
  827. if (c->rect.width < schema_param.minw[dir] * m_pTemplate->dingweidian_w_min_rate)continue;
  828. if (c->rect.height < schema_param.minh[dir] * m_pTemplate->dingweidian_h_min_rate)continue;
  829. cvSetImageROI(src_gray_img, c->rect);
  830. //cvSaveImage( "d://debug1.jpg", binary_img );
  831. int area = GetBlackArea(src_gray_img);
  832. if (area < c->rect.width*c->rect.height*0.75)continue;
  833. validCount++;
  834. contourList.push_back(c);
  835. }
  836. cvResetImageROI(src_gray_img);
  837. if (contourList.size() == 2){
  838. for (std::size_t i = 0; i < contourList.size(); i++){
  839. CvRect rect = contourList[i]->rect;
  840. m_vecBottom.push_back(rect);
  841. }
  842. if (m_vecBottom.size() > 1){
  843. std::sort(m_vecBottom.begin(), m_vecBottom.end(), sort_locate_point_by_x);
  844. if (_dir == 1)
  845. std::sort(m_vecBottom.begin(), m_vecBottom.end(), sort_locate_point_by_x);
  846. }
  847. return identify::result::IDF_SUCCESS;
  848. }
  849. else
  850. {
  851. // 大于要求数量
  852. if (contourList.size()>2)
  853. {
  854. //查找顶部定位点,宽高最大最小值
  855. int minw = locate_point_list[0].width;
  856. int maxw = locate_point_list[0].width;
  857. int minh = locate_point_list[0].height;
  858. int maxh = locate_point_list[0].height;
  859. for (int i = 1; i < locate_point_list.size(); i++)
  860. {
  861. //宽度计算
  862. if (locate_point_list[i].width > maxw)
  863. {
  864. maxw = locate_point_list[i].width;
  865. }
  866. else if (locate_point_list[i].width < minw)
  867. {
  868. minw = locate_point_list[i].width;
  869. }
  870. //高度计算
  871. if (locate_point_list[i].height > maxh)
  872. {
  873. maxh = locate_point_list[i].height;
  874. }
  875. else if (locate_point_list[i].height < minh)
  876. {
  877. minh = locate_point_list[i].height;
  878. }
  879. }
  880. minw = (schema_param.minw[dir] + minw) / 2;
  881. minh = (schema_param.minh[dir] + minh) / 2;
  882. maxw = (schema_param.maxw[dir] + maxw) / 2;
  883. maxh = (schema_param.maxh[dir] + maxh) / 2;
  884. // locate_point_list
  885. std::vector<CvRect> tempvecBottom;
  886. for (std::size_t i = 0; i < contourList.size(); i++){
  887. CvRect rect = contourList[i]->rect;
  888. if (rect.width >= minw && rect.width <= maxw && rect.height >= minh && rect.height <= maxh)
  889. {
  890. tempvecBottom.push_back(rect);
  891. }
  892. }
  893. if (tempvecBottom.size() == 2)
  894. {
  895. //成功找到数据
  896. for (std::size_t i = 0; i < tempvecBottom.size(); i++){
  897. m_vecBottom.push_back(tempvecBottom[i]);
  898. }
  899. if (m_vecBottom.size() > 1){
  900. std::sort(m_vecBottom.begin(), m_vecBottom.end(), sort_locate_point_by_x);
  901. if (_dir == 1)
  902. std::sort(m_vecBottom.begin(), m_vecBottom.end(), sort_locate_point_by_x);
  903. }
  904. return identify::result::IDF_SUCCESS;
  905. }
  906. else if (tempvecBottom.size() > 2)
  907. {
  908. //水平方向
  909. if (dir == 0 || dir == 1){
  910. std::sort(tempvecBottom.begin(), tempvecBottom.end(), sort_locate_point_by_x);
  911. //计算在识别区范围内
  912. float k = (float)(locate_point_list[locate_point_list.size() - 1].y - locate_point_list[0].y) / (locate_point_list[locate_point_list.size() - 1].x - locate_point_list[0].x);
  913. for (int i = 1; i < tempvecBottom.size() - 1; i++)
  914. {
  915. float y = tempvecBottom[0].y + k*(tempvecBottom[i].x - tempvecBottom[0].x);
  916. if ((tempvecBottom[i].y >(y - maxh)) && (tempvecBottom[i].y < (y + maxh)))
  917. return identify::result::IDF_FAILURE;
  918. }
  919. m_vecBottom.push_back(tempvecBottom[0]);
  920. m_vecBottom.push_back(tempvecBottom[tempvecBottom.size() - 1]);
  921. if (m_vecBottom.size() > 1){
  922. std::sort(m_vecBottom.begin(), m_vecBottom.end(), sort_locate_point_by_x);
  923. if (_dir == 1)
  924. std::sort(m_vecBottom.begin(), m_vecBottom.end(), sort_locate_point_by_x);
  925. }
  926. return identify::result::IDF_SUCCESS;
  927. }
  928. else {
  929. //水平方向垂直方向
  930. std::sort(tempvecBottom.begin(), tempvecBottom.end(), sort_locate_point_by_y);
  931. float k = (locate_point_list[locate_point_list.size() - 1].x - locate_point_list[0].x)/(float)(locate_point_list[locate_point_list.size() - 1].y - locate_point_list[0].y);
  932. for (int i = 1; i < tempvecBottom.size() - 1; i++)
  933. {
  934. float x = tempvecBottom[0].x + k*(tempvecBottom[i].y - tempvecBottom[0].y);
  935. if ((tempvecBottom[i].x >(x - maxw)) && (tempvecBottom[i].x < (x + maxw)))
  936. return identify::result::IDF_FAILURE;
  937. }
  938. m_vecBottom.push_back(tempvecBottom[0]);
  939. m_vecBottom.push_back(tempvecBottom[tempvecBottom.size() - 1]);
  940. if (m_vecBottom.size() > 1){
  941. std::sort(m_vecBottom.begin(), m_vecBottom.end(), sort_locate_point_by_x);
  942. if (_dir == 1)
  943. std::sort(m_vecBottom.begin(), m_vecBottom.end(), sort_locate_point_by_x);
  944. }
  945. return identify::result::IDF_SUCCESS;
  946. }
  947. }
  948. }
  949. return identify::result::IDF_FAILURE;
  950. }
  951. }
  952. }
  953. double COnLineCardPageIdentifier::CalcRectArea(IplImage* src, const std::vector<CvRect> & top_locate_point_list, const std::vector<CvRect> & bottom_locate_point_list)
  954. {
  955. int areaS = 0;
  956. int h = 0;
  957. int w = 0;
  958. std::vector<int> xVec;
  959. std::vector<int> yVec;
  960. for (int i = 0; i < top_locate_point_list.size(); i++)
  961. {
  962. xVec.push_back(top_locate_point_list[i].x);
  963. yVec.push_back(top_locate_point_list[i].y);
  964. }
  965. for (int i = 0; i < bottom_locate_point_list.size(); i++)
  966. {
  967. xVec.push_back(bottom_locate_point_list[i].x);
  968. yVec.push_back(bottom_locate_point_list[i].y);
  969. }
  970. std::sort(xVec.begin(), xVec.end());
  971. std::sort(yVec.begin(), yVec.end());
  972. areaS = (xVec[yVec.size() - 1] - xVec[0]) *(yVec[yVec.size() - 1] - yVec[0]);
  973. return (double)areaS / (double)(src->width*src->height);
  974. }
  975. int COnLineCardPageIdentifier::OnLineCardJZ(IplImage* src, IplImage* &dst_img, int nPageIndex, int dir, std::vector<CvRect>&main_locate_point, int &wordloctlt)
  976. {
  977. int ret = identify::result::IDF_FAILURE;
  978. std::vector<CvRect> vec_locations_word; vec_locations_word.clear();
  979. double tt[4] = { 0.0, 0.0, 0.0, 0.0 };
  980. if (!m_pTemplate->pages.empty()){
  981. PageTemplate&page = m_pTemplate->pages[nPageIndex];
  982. std::vector<Location>& lcs = page.location;
  983. for (std::size_t j = 0; j < lcs.size(); j++){
  984. CvRect rc_tmp;
  985. rc_tmp.x = lcs[j].pos.x;
  986. rc_tmp.y = lcs[j].pos.y;
  987. rc_tmp.width = lcs[j].pos.w;
  988. rc_tmp.height = lcs[j].pos.h;
  989. vec_locations_word.push_back(rc_tmp);
  990. }
  991. wordloctlt = vec_locations_word[3].y - vec_locations_word[0].y;
  992. double word_w = page.w;
  993. double word_h = page.h;
  994. tt[2] = word_w;
  995. tt[3] = word_h;
  996. // resize point
  997. int mb_w = src->width;
  998. int mb_h = src->height;
  999. if (2 == dir || 3 == dir) //
  1000. swap(mb_h, mb_w);
  1001. float rio_w = mb_w*1.0 / word_w*1.0;
  1002. float rio_h = mb_h*1.0 / word_h*1.0;
  1003. tt[0] = rio_w;
  1004. tt[1] = rio_h;
  1005. for (std::size_t i = 0; i < vec_locations_word.size(); i++){
  1006. vec_locations_word[i].x = vec_locations_word[i].x*rio_w + 0.55;
  1007. vec_locations_word[i].y = vec_locations_word[i].y*rio_h + 0.55;
  1008. vec_locations_word[i].width = vec_locations_word[i].width*rio_w + 0.55;
  1009. vec_locations_word[i].height = vec_locations_word[i].height*rio_h + 0.55;
  1010. }
  1011. }
  1012. if (2 == m_vecBottom.size() && vec_locations_word.size()>3){
  1013. try{
  1014. // 三点校正
  1015. ret = JiaoZheng_20200418_first(src, dst_img, main_locate_point, m_vecBottom, vec_locations_word, dir);
  1016. }
  1017. catch (cv::Exception &e)
  1018. {
  1019. return identify::result::IDF_FAILURE;
  1020. }
  1021. }
  1022. return ret;
  1023. }
  1024. void COnLineCardPageIdentifier::InitScoreArea(SchemaQuestionScore&sqs, const ScoreBox&box, CvPoint offset, double w_scale, double h_scale)
  1025. {
  1026. // 打分框类型
  1027. sqs.scoreBox.type = box.type;
  1028. // 填空题打分类型
  1029. sqs.scoreBox.vctScore = box.vctScore;
  1030. // 上限
  1031. sqs.scoreBox.limit = box.limit;
  1032. // 最后一个格子是否为小数(0.5分)
  1033. sqs.scoreBox.bPoint = box.bPoint;
  1034. // 位置
  1035. sqs.scoreBox.pos.x = offset.x + box.pos.x * w_scale;
  1036. sqs.scoreBox.pos.y = offset.y + box.pos.y * h_scale;
  1037. sqs.scoreBox.pos.w = box.pos.w *w_scale;
  1038. sqs.scoreBox.pos.h = box.pos.h *h_scale;
  1039. }
  1040. int COnLineCardPageIdentifier::GetQrCode(IplImage* dst_gray_img, schema_const_param &schema_param, vector<CvRect> &main_locate_point, vector<SchemaCode> &codev, string& paper_id, string& student_code, int code_type)
  1041. {
  1042. std::string result;
  1043. CvRect myRect = cvRect( main_locate_point[0].x + schema_param.paper_bar_offsetx1,
  1044. main_locate_point[0].y + schema_param.paper_bar_offsety1,
  1045. schema_param.paper_bar_width1,
  1046. schema_param.paper_bar_height1);
  1047. IplImage* mydst_gray_img = NULL;
  1048. xuanzhuan(dst_gray_img, mydst_gray_img, myRect);
  1049. m_pTemplate->open_save_debug_img = 1;
  1050. if (m_pTemplate->open_save_debug_img)
  1051. {
  1052. string tempfile = g_appFilePathName + "\\image\\Debug\\GetQrCode.JPG";
  1053. cv::imwrite(tempfile.c_str(), cvarrToMat(mydst_gray_img));
  1054. }
  1055. cv::Mat matDstGray = cvarrToMat(mydst_gray_img);
  1056. int ret = 0;
  1057. try{
  1058. // ret = GetQR(matDstGray, result);
  1059. char szRes[1024];
  1060. ret = api_parse_barcode_qrcode(matDstGray, code_type, szRes, 1024);
  1061. result = szRes;
  1062. }
  1063. catch (cv::Exception&e){
  1064. const char* what = e.what();
  1065. int n = 0;
  1066. ++n;
  1067. }
  1068. //CParseQRInfo test;
  1069. //char result[512] = { 0 };
  1070. //bool bRet = test.parseQRInfo(matDstGray, result, sizeof(result)/sizeof(char));
  1071. //if (/*ret == identify::result::IDF_SUCCESS*/bRet){
  1072. if (ret == identify::result::IDF_SUCCESS){
  1073. SchemaCode qrcode1;
  1074. qrcode1.centerx = (main_locate_point[0].x + schema_param.paper_bar_offsetx1) + schema_param.paper_bar_width1 / 2.0;
  1075. qrcode1.centery = (main_locate_point[0].y + schema_param.paper_bar_offsety1) + schema_param.paper_bar_height1 / 2.0;
  1076. qrcode1.width = schema_param.paper_bar_width1;
  1077. qrcode1.height = schema_param.paper_bar_height1;
  1078. qrcode1.index = obj_index++;
  1079. qrcode1.nQrOrBar = 1;
  1080. qrcode1.isXuanZhuan = false;
  1081. student_code = result;
  1082. qrcode1.strContent = result;
  1083. codev.push_back(qrcode1);
  1084. }
  1085. if (mydst_gray_img != NULL)cvReleaseImage(&mydst_gray_img);
  1086. return identify::result::IDF_SUCCESS;
  1087. }
  1088. void COnLineCardPageIdentifier::CalcPos(const Pos&pos, const CvPoint& offset, double w_scale, double h_scale, int &center_x, int &center_y, int &w, int &h)
  1089. {
  1090. w = static_cast<int>(pos.w*w_scale);
  1091. h = static_cast<int>(pos.h*h_scale);
  1092. center_x = static_cast<int>(offset.x + pos.x*w_scale + pos.w*w_scale / 2);
  1093. center_y = static_cast<int>(offset.y + pos.y*h_scale + pos.h*h_scale / 2);
  1094. }
  1095. void COnLineCardPageIdentifier::SetPageDefault(SchemaPage& page)
  1096. {
  1097. page.codeCount = 0;
  1098. page.cutAreaCount = 0;
  1099. page.group_spacer = ';';
  1100. page.groupCount = 0;
  1101. page.stuItemCount = 0;
  1102. page.stuGroupsCount = 0;
  1103. page.hei_du_ling_min_du = 4;
  1104. page.height = 0;
  1105. page.index = 0;
  1106. page.itemCount = 0;
  1107. page.locateAreaCount = 0;
  1108. page.locateCrossCount = 0;
  1109. page.locatePointCount = 0;
  1110. page.option_spacer = ',';
  1111. page.question_score_count = 0;
  1112. page.unselect_char = '*';
  1113. page.user_float_heidu = 4;
  1114. page.width = 0;
  1115. page.worthwhileBlk = 4;
  1116. }
  1117. void COnLineCardPageIdentifier::SetTemplate(OnLineCard::PaperTemplate*pT)
  1118. {
  1119. m_pTemplate = pT;
  1120. int dingweidianH;
  1121. int dingweidianW;
  1122. if (m_pTemplate->pages[1].location.size() >= 5){
  1123. int m = 0;
  1124. dingweidianH = m_pTemplate->pages[1].location[m].pos.h;
  1125. dingweidianW = m_pTemplate->pages[1].location[m].pos.w;
  1126. m_default_schema_param.main_locate_point_distance = m_pTemplate->pages[1].location[m + 2].pos.x - m_pTemplate->pages[1].location[m].pos.x;
  1127. m_default_schema_param.shijuanwidth = m_pTemplate->pages[1].w;
  1128. for (int i = 0; i < 4; i++)
  1129. {
  1130. if (i < 2)
  1131. {
  1132. m_default_schema_param.minw[i] = dingweidianW*0.75;
  1133. m_default_schema_param.maxw[i] = dingweidianW*1.25;
  1134. m_default_schema_param.minh[i] = dingweidianH*0.75;
  1135. m_default_schema_param.maxh[i] = dingweidianH*1.25;
  1136. }
  1137. else
  1138. {
  1139. m_default_schema_param.minw[i] = dingweidianH*0.75;
  1140. m_default_schema_param.maxw[i] = dingweidianH*1.25;
  1141. m_default_schema_param.minh[i] = dingweidianW*0.75;
  1142. m_default_schema_param.maxh[i] = dingweidianW*1.25;
  1143. }
  1144. }
  1145. }
  1146. }
  1147. #ifdef _draw_debug
  1148. void drawDebug(IplImage* dst_img, PageTemplate*page)
  1149. {
  1150. if (!dst_img)return;
  1151. double w_rate = dst_img->width*1.0 / page->w;
  1152. double h_rate = dst_img->height*1.0 / page->h;
  1153. auto pDrawFunc = [&](const OnLineCard::Pos&pos, CvScalar clr,bool flag){
  1154. cvDrawRect(dst_img,
  1155. cvPoint(((flag ? 0 : page->location[0].pos.x) + pos.x)*w_rate, ((flag ? 0 : page->location[0].pos.y) + pos.y)*h_rate),
  1156. cvPoint(((flag ? 0 : page->location[0].pos.x) + pos.x + pos.w)*w_rate, ((flag ? 0 : page->location[0].pos.y) + pos.y + pos.h)*h_rate),
  1157. clr);
  1158. };
  1159. // 绘制定位点
  1160. for (auto it : page->location){
  1161. pDrawFunc(it.pos, CvScalar(0, 255, 0),true);
  1162. }
  1163. if (page->pageNo==1){ // 首页
  1164. for (auto it : page->studentcode_fill){
  1165. for (auto it1:it){
  1166. pDrawFunc(it1.pos, CvScalar(0, 255, 0), false);
  1167. }
  1168. }
  1169. pDrawFunc(page->absent, CvScalar(0, 255, 0), false);
  1170. pDrawFunc(page->QrCode, CvScalar(0, 255, 0), false);
  1171. }
  1172. for (auto it: page->vctQuestions)
  1173. {
  1174. for (auto opt: it.opt){
  1175. pDrawFunc(opt.pos, CvScalar(0, 255, 0), false);
  1176. }
  1177. pDrawFunc(it.scoreBox.pos, CvScalar(0, 255, 0), false);
  1178. pDrawFunc(it.cut.pos, CvScalar(255, 255, 0), false);
  1179. }
  1180. std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
  1181. auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
  1182. std::time_t timestamp = tmp.count();
  1183. std::string filetemppath = g_appFilePathName + "\\image1\\";
  1184. CreateDirectoryA(filetemppath.c_str(), NULL);
  1185. std::string path = g_appFilePathName + std::string("\\image1\\") + std::to_string(timestamp) + ".jpg";
  1186. cvSaveImage(path.c_str(), dst_img);
  1187. }
  1188. #endif
  1189. //构建试卷的识别区域
  1190. int COnLineCardPageIdentifier::createSchema(const IplImage* src, IplImage* *dst, SchemaPage* *pageSchema, const char * result_path, bool flag, std::string qr, bool bUseQr)
  1191. {
  1192. ////LOGI("createSchema 1");
  1193. if (!m_pTemplate /*|| m_pTemplate->pages.size() < 2 */|| m_pTemplate->pages[1].location.size() < 5){
  1194. return identify::result::IDF_FAILURE;
  1195. }
  1196. int wordloctlt = 0;
  1197. int schoolCardStatus = m_pTemplate->schoolCardStatus;
  1198. obj_index = 0;
  1199. //#ifdef _draw_debug
  1200. // IplImage*src_debug = cvCloneImage(src);
  1201. //#endif
  1202. ////LOGI("createSchema 1.3");
  1203. CvRect roi_old = cvGetImageROI(src);
  1204. IplImage * src0 = (IplImage *)src;
  1205. int ret;
  1206. int mode_type = 1115;
  1207. vector<SchemaGroup> groupv;
  1208. vector<SchemaItem> itemv;
  1209. vector<SchemaQuestionScore> scorev;
  1210. vector<SchemaCode> codev;
  1211. vector<SchemaCutArea> cutareav;
  1212. vector<SchemaLocatePoint> questionLocatePoints;
  1213. vector<KEGUANTI_QUESTION_LOCATEPOINT_INDEX> keguanti_question_locatepoint_indexs;
  1214. vector<SchemaGroup> stuGroupv;
  1215. vector<SchemaItem> stuItemv;
  1216. vector<KEGUANTI_QUESTION_LOCATEPOINT_INDEX> stuIndex;
  1217. vector<KEGUANTI_QUESTION_LOCATEPOINT_INDEX> quekaoIndex;
  1218. int qrFlag = 0;
  1219. ////LOGI("createSchema 1.31");
  1220. CvMemStorage* storage = cvCreateMemStorage(0);
  1221. IplImage * src_gray_img;
  1222. int question_count = 0;
  1223. int itemcount = 0;
  1224. ////LOGI("createSchema 1.4");
  1225. cvResetImageROI((IplImage*)src0);
  1226. ////LOGI("createSchema 1.41");
  1227. src_gray_img = cvCreateImage(cvGetSize(src0), IPL_DEPTH_8U, 1);
  1228. ////LOGI("createSchema 1.42");
  1229. #ifdef _DEBUG
  1230. m_pTemplate->open_save_debug_img = true;
  1231. cvSaveImage("d:\\image\\src0.png", src0);
  1232. cvSaveImage("d:\\image\\src_gray_img.png", src_gray_img);
  1233. #endif
  1234. cvCvtColor(src0, src_gray_img, CV_BGR2GRAY);
  1235. ////LOGI("createSchema 1.43");
  1236. ////LOGI("createSchema 1.5");
  1237. schema_const_param schema_param;
  1238. std::vector<CvRect> main_locate_point;
  1239. CvPoint offset_pt;
  1240. int dir;//上下左右
  1241. //1.查找定位点
  1242. // printf("线程:%s 第一次查找定位点\n", std::to_string(GetCurrentThreadId()).c_str());
  1243. ret = MyFindDingWeiDian(src_gray_img, storage, schema_param, main_locate_point, dir);
  1244. if (ret != identify::result::IDF_SUCCESS){
  1245. cv::Size sz;
  1246. std::vector<cv::Rect> vec_locations_word; vec_locations_word.clear();
  1247. if (!m_pTemplate->pages.empty()){
  1248. PageTemplate&page = m_pTemplate->pages[1];
  1249. sz = cv::Size(page.w, page.h);
  1250. std::vector<Location>& lcs = page.location;
  1251. for (std::size_t j = 0; j < lcs.size(); j++){
  1252. cv::Rect rc_tmp;
  1253. rc_tmp.x = lcs[j].pos.x;
  1254. rc_tmp.y = lcs[j].pos.y;
  1255. rc_tmp.width = lcs[j].pos.w;
  1256. rc_tmp.height = lcs[j].pos.h;
  1257. vec_locations_word.push_back(rc_tmp);
  1258. }
  1259. }
  1260. std::vector<cv::Rect> vec_tops; // 接受找到的上面三个定位点 下标访问
  1261. std::vector<cv::Rect> vec_bottoms; // 接受找到的下面的两个定位点 下标访问
  1262. std::vector<int> vec_len;
  1263. vec_tops.resize(100);
  1264. vec_bottoms.resize(100);
  1265. vec_len.push_back(-1);
  1266. vec_len.push_back(-1);
  1267. #ifdef _DEBUG
  1268. cv::imwrite("D:\\src_gray_img.png", cv::cvarrToMat(src_gray_img));
  1269. #endif
  1270. int nRet = api_location_anchor_points(cv::cvarrToMat(src_gray_img), vec_locations_word, sz, vec_tops, vec_bottoms, vec_len, dir, 3, 1);
  1271. if (nRet == 0 && vec_len.size() == 2 && vec_len[0] == 3 && vec_len[1] == 2){
  1272. main_locate_point.clear();
  1273. m_vecBottom.clear();
  1274. for (std::size_t i = 0; i < vec_tops.size() && i < vec_len[0]; ++i){
  1275. main_locate_point.push_back(vec_tops[i]);
  1276. }
  1277. for (std::size_t i = 0; i < vec_bottoms.size() && i < vec_len[1]; ++i){
  1278. m_vecBottom.push_back(vec_bottoms[i]);
  1279. }
  1280. }
  1281. else{
  1282. cvReleaseMemStorage(&storage);
  1283. cvReleaseImage(&src_gray_img);
  1284. cvSetImageROI(src0, roi_old);
  1285. return identify::result::IDF_TEMPLATE_NOTFOUND_ENOUGH_LOCATEPOINT;
  1286. }
  1287. }
  1288. if (m_nOnlineScanType == 0)
  1289. {
  1290. // 魔法词汇宝
  1291. if (CalcRectArea(src0, main_locate_point, m_vecBottom) < POSITION_JUGDMENT_THRESHOLD)
  1292. {
  1293. cvReleaseMemStorage(&storage);
  1294. cvReleaseImage(&src_gray_img);
  1295. cvSetImageROI(src0, roi_old);
  1296. return identify::result::IDF_TEMPLATE_NOTFOUND_ENOUGH_LOCATEPOINT;
  1297. }
  1298. }
  1299. IplImage * dst_img = NULL;
  1300. //2.矫正图像
  1301. ret = OnLineCardJZ(src0, dst_img, 1, dir, main_locate_point, wordloctlt);
  1302. if (ret != identify::result::IDF_SUCCESS){
  1303. cvReleaseMemStorage(&storage);
  1304. cvReleaseImage(&src_gray_img);
  1305. cvSetImageROI(src0, roi_old);
  1306. if (dst_img)cvReleaseImage(&dst_img);
  1307. return identify::result::IDF_TEMPLATE_NOTFOUND_ENOUGH_LOCATEPOINT;
  1308. }
  1309. IplImage* dst_gray_img = cvCreateImage(cvGetSize(dst_img), IPL_DEPTH_8U, 1);
  1310. cvCvtColor(dst_img, dst_gray_img, CV_BGR2GRAY);
  1311. int locate_point_count = main_locate_point.size();
  1312. main_locate_point.clear();
  1313. ret = MyFindDingWeiDian(dst_gray_img, storage, schema_param, main_locate_point, dir);
  1314. std::sort(main_locate_point.begin(), main_locate_point.end(), sort_locate_point_by_x);
  1315. if (ret != identify::result::IDF_SUCCESS || main_locate_point.size() != locate_point_count){
  1316. cv::Size sz;
  1317. std::vector<cv::Rect> vec_locations_word; vec_locations_word.clear();
  1318. if (!m_pTemplate->pages.empty()){
  1319. PageTemplate&page = m_pTemplate->pages[1];
  1320. sz = cv::Size(page.w, page.h);
  1321. std::vector<Location>& lcs = page.location;
  1322. for (std::size_t j = 0; j < lcs.size(); j++){
  1323. cv::Rect rc_tmp;
  1324. rc_tmp.x = lcs[j].pos.x;
  1325. rc_tmp.y = lcs[j].pos.y;
  1326. rc_tmp.width = lcs[j].pos.w;
  1327. rc_tmp.height = lcs[j].pos.h;
  1328. vec_locations_word.push_back(rc_tmp);
  1329. }
  1330. }
  1331. std::vector<cv::Rect> vec_tops; // 接受找到的上面三个定位点 下标访问
  1332. std::vector<cv::Rect> vec_bottoms; // 接受找到的下面的两个定位点 下标访问
  1333. std::vector<int> vec_len;
  1334. vec_tops.resize(100);
  1335. vec_bottoms.resize(100);
  1336. vec_len.push_back(-1);
  1337. vec_len.push_back(-1);
  1338. int nRet = api_location_anchor_points(cv::cvarrToMat(dst_gray_img), vec_locations_word, sz, vec_tops, vec_bottoms, vec_len, dir, 3, 1);
  1339. if (nRet == 0 && vec_len.size() == 2 && vec_len[0] == 3 && vec_len[1] == 2){
  1340. main_locate_point.clear();
  1341. m_vecBottom.clear();
  1342. for (std::size_t i = 0; i < vec_tops.size() && i < vec_len[0]; ++i){
  1343. main_locate_point.push_back(vec_tops[i]);
  1344. }
  1345. for (std::size_t i = 0; i < vec_bottoms.size() && i < vec_len[1]; ++i){
  1346. m_vecBottom.push_back(vec_bottoms[i]);
  1347. }
  1348. }
  1349. else{
  1350. cvReleaseMemStorage(&storage);
  1351. cvReleaseImage(&src_gray_img);
  1352. cvReleaseImage(&dst_gray_img);
  1353. cvSetImageROI(src0, roi_old);
  1354. if (dst_img)cvReleaseImage(&dst_img);
  1355. return identify::result::IDF_TEMPLATE_NOTFOUND_ENOUGH_LOCATEPOINT;
  1356. }
  1357. }
  1358. //3.确定模板和识别图片的比例关系
  1359. double fffx = (main_locate_point[main_locate_point.size() - 1].x - main_locate_point[0].x)*(main_locate_point[main_locate_point.size() - 1].x - main_locate_point[0].x);
  1360. double fffy = (main_locate_point[main_locate_point.size() - 1].y - main_locate_point[0].y)*(main_locate_point[main_locate_point.size() - 1].y - main_locate_point[0].y);
  1361. double w_scale = (sqrt(fffx + fffy)) / m_default_schema_param.main_locate_point_distance;
  1362. double Hfffx = (m_vecBottom[0].x - main_locate_point[0].x)*(m_vecBottom[0].x - main_locate_point[0].x);
  1363. double Hfffy = (m_vecBottom[0].y - main_locate_point[0].y)*(m_vecBottom[0].y - main_locate_point[0].y);
  1364. double h_scale = (sqrt(Hfffx + Hfffy)) / wordloctlt;
  1365. {
  1366. //保存
  1367. SchemaLocatePoint leftp;
  1368. leftp.centerx = main_locate_point[0].x + main_locate_point[0].width / 2.0;
  1369. leftp.centery = main_locate_point[0].y + main_locate_point[0].height / 2.0;
  1370. leftp.width = main_locate_point[0].width;
  1371. leftp.height = main_locate_point[0].height;
  1372. questionLocatePoints.push_back(leftp);
  1373. SchemaLocatePoint rightp;
  1374. rightp.centerx = main_locate_point[2].x + main_locate_point[2].width / 2.0;
  1375. rightp.centery = main_locate_point[2].y + main_locate_point[2].height / 2.0;
  1376. rightp.width = main_locate_point[2].width;
  1377. rightp.height = main_locate_point[2].height;
  1378. questionLocatePoints.push_back(rightp);
  1379. leftp.centerx = m_vecBottom[0].x + m_vecBottom[0].width / 2.0;
  1380. leftp.centery = m_vecBottom[0].y + m_vecBottom[0].height / 2.0;
  1381. leftp.width = m_vecBottom[0].width;
  1382. leftp.height = m_vecBottom[0].height;
  1383. questionLocatePoints.push_back(leftp);
  1384. rightp.centerx = m_vecBottom[1].x + m_vecBottom[1].width / 2.0;
  1385. rightp.centery = m_vecBottom[1].y + m_vecBottom[1].height / 2.0;
  1386. rightp.width = m_vecBottom[1].width;
  1387. rightp.height = m_vecBottom[1].height;
  1388. questionLocatePoints.push_back(rightp);
  1389. }
  1390. std::string exam_id_by_qr_code;
  1391. bool is_same_exam_id = true;
  1392. int page_index = 1;
  1393. string paper_id = "";
  1394. string student_code = "";
  1395. bool is_stu_id = false;
  1396. offset_pt.x = main_locate_point[0].x;
  1397. offset_pt.y = main_locate_point[0].y;
  1398. bool front_page = false;
  1399. //判断前后页
  1400. if ((main_locate_point[1].x - main_locate_point[0].x) < (main_locate_point[2].x - main_locate_point[1].x)){//正面
  1401. front_page = true;
  1402. }
  1403. else{
  1404. front_page = false;
  1405. }
  1406. PageTemplate&page = m_pTemplate->pages[front_page ? 1 : 2];
  1407. //#ifdef _draw_debug
  1408. // drawDebug(src_debug, &page);
  1409. // cvReleaseImage(&src_debug);
  1410. //#endif
  1411. #ifdef _draw_debug
  1412. IplImage*src_debug = cvCloneImage(dst_img);
  1413. #endif
  1414. ////LOGI("createSchema 5");
  1415. // 正面
  1416. if (front_page){
  1417. SchemaCode code_tmp;
  1418. std::vector<std::string> split_qr;
  1419. split(qr, (std::string)",", &split_qr);
  1420. if (split_qr.size() > 0){
  1421. if (split_qr.size() > 0)
  1422. paper_id = split_qr[0];
  1423. if (bUseQr){
  1424. if (split_qr.size() > 2){
  1425. std::vector<std::string> _vct;
  1426. split(split_qr[2], std::string("@"), &_vct);
  1427. if (_vct.size() > 1){
  1428. student_code = _vct[2];
  1429. is_stu_id = !student_code.empty();
  1430. }
  1431. }
  1432. }
  1433. }
  1434. if (bUseQr&&!student_code.empty())
  1435. {
  1436. SchemaCode code;
  1437. code.index = obj_index++;
  1438. code.nQrOrBar = 1;
  1439. code.strContent = paper_id + "_" + student_code + "_3_1";
  1440. codev.push_back(code);
  1441. }
  1442. else{
  1443. // 二维码
  1444. double x = page.QrCode.x*w_scale - 20;
  1445. double y = page.QrCode.y*h_scale - 20;
  1446. schema_param.paper_bar_width1 = page.QrCode.w*w_scale + 40;
  1447. if (schema_param.paper_bar_width1 + x >= dst_gray_img->width)
  1448. schema_param.paper_bar_width1 = dst_gray_img->width - x - 2;
  1449. schema_param.paper_bar_height1 = page.QrCode.h*h_scale + 40;
  1450. schema_param.paper_bar_offsetx1 = x;
  1451. schema_param.paper_bar_offsety1 = y;
  1452. if (m_pTemplate->useQrCode){
  1453. std::size_t nOld = codev.size();
  1454. ret = GetQrCode(dst_gray_img, schema_param, main_locate_point, codev, paper_id, student_code, CODE_QR);
  1455. if (student_code != "")
  1456. qrFlag = 1;
  1457. else
  1458. qrFlag = -1;
  1459. if (ret != identify::result::IDF_SUCCESS){
  1460. cvReleaseMemStorage(&storage);
  1461. cvReleaseImage(&src_gray_img);
  1462. cvReleaseImage(&dst_img);
  1463. cvReleaseImage(&dst_gray_img);
  1464. return identify::result::IDF_TEMPLATE_QRCODE_ILLEGAL;
  1465. }
  1466. if (nOld < codev.size()){
  1467. vector<std::string> splits;
  1468. split(student_code, (std::string)"@", &splits);
  1469. if (splits.size() == 1){
  1470. paper_id = splits[0];
  1471. codev[codev.size() - 1].strContent = paper_id + "_6582234412574771625_3_1_12_1_1_1";
  1472. student_code = "";
  1473. }
  1474. else if (splits.size() >= 2){
  1475. student_code = splits[0];
  1476. paper_id = splits[1];
  1477. codev[codev.size() - 1].strContent = paper_id + "_" + student_code + "_3_1";
  1478. }
  1479. if (splits.size()> 0)
  1480. {
  1481. std::lock_guard<std::mutex> _lck(g_mtx_examid);
  1482. if (g_examid_by_qr_code.load() == 0){
  1483. g_examid_by_qr_code.store(1);
  1484. exam_id_by_qr_code = paper_id;
  1485. std::string str_cfg_path = GetExePath() + "\\paperinfo.ini";
  1486. char cfg_exam_id[128] = { 0 };
  1487. //paperinfo/exam_id
  1488. GetPrivateProfileStringA("paperinfo", "exam_id", "", cfg_exam_id, 128, str_cfg_path.c_str());
  1489. if (exam_id_by_qr_code != cfg_exam_id && m_nOnlineScanType != 0){
  1490. is_same_exam_id = false;
  1491. }
  1492. }
  1493. }
  1494. if (student_code.empty()){
  1495. if (!codev.empty())
  1496. code_tmp = codev[0];
  1497. codev.clear();
  1498. }
  1499. }
  1500. is_stu_id = !student_code.empty();
  1501. }
  1502. else{
  1503. std::lock_guard<std::mutex> _lck(g_mtx_examid);
  1504. if (g_examid_by_qr_code.load() == 0){
  1505. std::string paper_id, student_code;
  1506. std::vector<SchemaCode> vct_tmp_codev;
  1507. ret = GetQrCode(dst_gray_img, schema_param, main_locate_point, vct_tmp_codev, student_code, student_code, CODE_QR);
  1508. if (!vct_tmp_codev.empty()){
  1509. vector<std::string> splits;
  1510. split(student_code, (std::string)"@", &splits);
  1511. if (splits.size() == 1){
  1512. paper_id = splits[0];
  1513. student_code = "";
  1514. }
  1515. else if (splits.size() >= 2){
  1516. student_code = splits[0];
  1517. paper_id = splits[1];
  1518. }
  1519. g_examid_by_qr_code.store(1);
  1520. exam_id_by_qr_code = paper_id;
  1521. std::string str_cfg_path = GetExePath() + "\\paperinfo.ini";
  1522. char cfg_exam_id[128] = { 0 };
  1523. //paperinfo/exam_id
  1524. GetPrivateProfileStringA("paperinfo", "exam_id", "", cfg_exam_id, 128, str_cfg_path.c_str());
  1525. if (exam_id_by_qr_code != cfg_exam_id && m_nOnlineScanType != 0){
  1526. is_same_exam_id = false;
  1527. }
  1528. }
  1529. }
  1530. }
  1531. // 二维码中含有学生信息 不需要识别条形码和填涂考号
  1532. if (!m_pTemplate->useQrCode)
  1533. {
  1534. //条形码
  1535. schema_param.paper_bar_width1 = page.studentcode_bar.w*w_scale;
  1536. schema_param.paper_bar_height1 = page.studentcode_bar.h*h_scale;
  1537. schema_param.paper_bar_offsetx1 = page.studentcode_bar.x*w_scale;
  1538. schema_param.paper_bar_offsety1 = page.studentcode_bar.y*h_scale;
  1539. if (schema_param.paper_bar_width1 != 0 && schema_param.paper_bar_height1 != 0){
  1540. auto oldSize = codev.size();
  1541. ret = GetQrCode(dst_gray_img, schema_param, main_locate_point, codev, paper_id, student_code, CODE_BAR);
  1542. if (ret != identify::result::IDF_SUCCESS){
  1543. cvReleaseMemStorage(&storage);
  1544. cvReleaseImage(&src_gray_img);
  1545. cvReleaseImage(&dst_img);
  1546. cvReleaseImage(&dst_gray_img);
  1547. return identify::result::IDF_TEMPLATE_QRCODE_ILLEGAL;;
  1548. }
  1549. if (oldSize < codev.size()){
  1550. codev[codev.size() - 1].strContent = paper_id + "_" + student_code + "_3_1_12_1_1_1";
  1551. }
  1552. }
  1553. //填涂考号
  1554. if (student_code.empty())
  1555. {
  1556. int q_item_count = 10;
  1557. int itemcount = 0;
  1558. int question_count = 0;
  1559. int x, y, w, h;
  1560. for (std::size_t j = 0; j < page.studentcode_fill.size(); j++)
  1561. {
  1562. SchemaGroup group;
  1563. group.question_code = "";
  1564. group.index = question_count++;
  1565. group.question_index = question_count;
  1566. group.itemCount = q_item_count;
  1567. group.itemIndex.clear();
  1568. group.omr_out_type = OMR_OUT_TYPE::OMR_OUT_TYPE_SINGLE_NONSTRICT;
  1569. for (std::size_t m = 0; m < page.studentcode_fill[j].size(); m++)
  1570. {
  1571. group.itemIndex.push_back(itemcount + m);
  1572. SchemaItem item;
  1573. CalcPos(page.studentcode_fill[j][m].pos, offset_pt, w_scale, h_scale, item.centerx, item.centery, item.width, item.height);
  1574. std::string&optName = page.studentcode_fill[j][m].optName;
  1575. if (!optName.empty())
  1576. item.OutChar = std::stoi(optName) + '0';
  1577. item.keguanti_question_locate_point_index = stuIndex.size();
  1578. stuItemv.push_back(item);
  1579. }
  1580. if (j == 0){
  1581. x = stuItemv[0].centerx - stuItemv[0].width / 2 - 35;
  1582. y = stuItemv[0].centery - stuItemv[0].height - stuItemv[0].height / 2 - 105;
  1583. }
  1584. else if (j == page.studentcode_fill.size() - 1){
  1585. SchemaItem &item = stuItemv[stuItemv.size() - 1];
  1586. int tx = item.centerx + item.width / 2 + 35;
  1587. int ty = item.centery + item.height / 2 + 5;
  1588. w = tx - x;
  1589. h = ty - y;
  1590. }
  1591. itemcount += q_item_count;
  1592. stuGroupv.push_back(group);
  1593. }
  1594. KEGUANTI_QUESTION_LOCATEPOINT_INDEX kqli;
  1595. kqli[0] = 0;
  1596. kqli[1] = 1;
  1597. kqli[2] = 2;
  1598. kqli[3] = 3;
  1599. stuIndex.push_back(kqli);
  1600. code_tmp.centerx = x + w / 2;
  1601. code_tmp.centery = y + h / 2;
  1602. code_tmp.width = w;
  1603. code_tmp.height = h;
  1604. code_tmp.index = obj_index++;
  1605. code_tmp.nQrOrBar = 1;
  1606. code_tmp.strContent = paper_id + "_6582234412574771625_3_1_12_1_1_1";
  1607. codev.clear();
  1608. codev.push_back(code_tmp);
  1609. }
  1610. }
  1611. }
  1612. // 缺考标记
  1613. {
  1614. schema_param.quekao_item_offset_x = main_locate_point[0].x + page.absent.x*w_scale;
  1615. schema_param.quekao_item_offset_y = main_locate_point[0].y + page.absent.y*h_scale;
  1616. schema_param.quekao_item_width = page.absent.w*w_scale;
  1617. schema_param.quekao_item_height = page.absent.h*h_scale;
  1618. KEGUANTI_QUESTION_LOCATEPOINT_INDEX QKIndex;
  1619. QKIndex[0] = 0;
  1620. QKIndex[1] = 1;
  1621. QKIndex[2] = 2;
  1622. QKIndex[3] = 3;
  1623. quekaoIndex.push_back(QKIndex);
  1624. }
  1625. }
  1626. for (std::size_t i = 0; i < page.vctQuestions.size(); ++i){
  1627. question&qt = page.vctQuestions[i];
  1628. if (qt.type == 0 || qt.type == 8 || qt.type == 5){//选
  1629. SchemaGroup group;
  1630. question_count++;
  1631. group.question_code = qt.id;
  1632. static int id = 0; id = std::stoi(qt.id);
  1633. group.index = obj_index++;
  1634. group.question_index = question_count;
  1635. group.itemCount = qt.opt.size();
  1636. group.itemIndex.clear();
  1637. group.omr_out_type = ((qt.type == 0) ? OMR_OUT_TYPE::OMR_OUT_TYPE_SINGLE_NONSTRICT : ((qt.type == 8) ? OMR_OUT_TYPE::OMR_OUT_TYPE_MUTIL_STRICT : OMR_OUT_TYPE::OMR_OUT_TYPE_MUTIL_STRICT_EX));
  1638. for (int q = 0; q < group.itemCount; q++)
  1639. {
  1640. group.itemIndex.push_back(itemcount + q);
  1641. SchemaItem item;
  1642. CalcPos(qt.opt[q].pos, offset_pt, w_scale, h_scale, item.centerx, item.centery, item.width, item.height);
  1643. if (!qt.opt[q].optName.empty())
  1644. item.OutChar = qt.opt[q].optName.at(0);
  1645. item.keguanti_question_locate_point_index = keguanti_question_locatepoint_indexs.size();
  1646. itemv.push_back(item);
  1647. }
  1648. itemcount += group.itemCount;
  1649. groupv.push_back(group);
  1650. KEGUANTI_QUESTION_LOCATEPOINT_INDEX kqli;
  1651. kqli[0] = 0;
  1652. kqli[1] = 1;
  1653. kqli[2] = 2;
  1654. kqli[3] = 3;
  1655. keguanti_question_locatepoint_indexs.push_back(kqli);
  1656. }
  1657. else if (qt.type == 1 || qt.type == 2){
  1658. //通过linkparm判断第一次还是第二次出现(第一次一定小于2)
  1659. if (qt.cut.linkparm < (m_pTemplate->_version=="old" ?2:1)){
  1660. SchemaQuestionScore sqs;
  1661. sqs.type = QUESTION_SCORE_TYPE::MUTIL_SCORE;
  1662. InitScoreArea(sqs, qt.scoreBox, offset_pt, w_scale, h_scale);
  1663. if (sqs.scoreBox.type == 1){ // 1-16
  1664. sqs.count = sqs.scoreBox.limit + (m_pTemplate->_version == "old" ? 0 : 1); // 加入0 老版没有这
  1665. if (sqs.scoreBox.bPoint)
  1666. sqs.count++;
  1667. }
  1668. else if (sqs.scoreBox.type == 2){ // 29/49
  1669. sqs.count = ((int)sqs.scoreBox.limit / 10) + 12;// 12 = 各位数字+ 十/个位标记 10+2
  1670. if (sqs.scoreBox.bPoint)
  1671. sqs.count++;
  1672. }
  1673. sqs.scoreBox.bXuanZuoTi = (qt.type == 2);
  1674. sqs.index = obj_index++;
  1675. if (!qt.id.empty())
  1676. sqs.question_index = std::stoi(qt.id);
  1677. sqs.question_code = qt.id;
  1678. if (qt.type ==2){
  1679. sqs.question_code_all = qt.all_id;
  1680. sqs.question_code_temp = qt.all_id;
  1681. }
  1682. else
  1683. {
  1684. sqs.question_code_all = "";
  1685. sqs.question_code_temp = "";
  1686. }
  1687. sqs.scoreBox.maxsorce = qt.scoreBox.maxsorce;
  1688. sqs.valid_option_count = 0;
  1689. sqs.questionLocatePointIndex[0] = 0;
  1690. sqs.questionLocatePointIndex[1] = 1;
  1691. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, sqs.centerx, sqs.centery, sqs.width, sqs.height);
  1692. if (qt.type == 2){
  1693. sqs.questionLocatePointIndex[2] = 2;
  1694. sqs.questionLocatePointIndex[3] = 3;
  1695. for (int v = 0; v < qt.selTotal && qt.opt.size() == qt.selTotal; v++)
  1696. {
  1697. CalcPos(qt.opt[v].pos, offset_pt, w_scale, h_scale, sqs.item[v].centerx, sqs.item[v].centery, sqs.item[v].width, sqs.item[v].height);
  1698. }
  1699. sqs.countItemSelete = qt.selItem;
  1700. sqs.countNum = qt.selTotal;
  1701. SetTBLR(sqs);
  1702. }
  1703. if (qt.smallQtNo == -1 || qt.smallQtNo == 0)
  1704. {
  1705. SchemaCutArea cutarea;
  1706. cutarea.index = obj_index++;
  1707. cutarea.area_name = sqs.question_code;
  1708. cutarea.isfirst = TRUE;
  1709. cutarea.question_index = sqs.question_index;
  1710. cutarea.iscut = 1;
  1711. cutarea.marktype = qt.marktype;
  1712. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, cutarea.centerx, cutarea.centery, cutarea.width, cutarea.height);
  1713. SetTBLR(sqs);
  1714. scorev.push_back(sqs);
  1715. cutareav.push_back(cutarea);
  1716. }
  1717. else
  1718. {
  1719. SchemaCutArea cutarea;
  1720. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, cutarea.centerx, cutarea.centery, cutarea.width, cutarea.height);
  1721. cutarea.index = obj_index++;
  1722. cutarea.isfirst = FALSE;
  1723. cutarea.question_index = -1;
  1724. cutarea.area_name = qt.id;
  1725. cutarea.marktype = qt.marktype;
  1726. SetTBLR(sqs);
  1727. scorev.push_back(sqs);
  1728. cutareav.push_back(cutarea);
  1729. }
  1730. }else{
  1731. SchemaCutArea cutarea;
  1732. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, cutarea.centerx, cutarea.centery, cutarea.width, cutarea.height);
  1733. cutarea.index = obj_index++;
  1734. cutarea.isfirst = FALSE;
  1735. cutarea.question_index = -1;
  1736. cutarea.area_name = qt.id;
  1737. cutarea.marktype = qt.marktype;
  1738. cutareav.push_back(cutarea);
  1739. }
  1740. }
  1741. else if (qt.type == 3){
  1742. //填空(不会跨页,所以可以直接处理)
  1743. SchemaQuestionScore sqs;
  1744. sqs.type = QUESTION_SCORE_TYPE::SINGLE_SCORE;
  1745. InitScoreArea(sqs, qt.scoreBox, offset_pt, w_scale, h_scale);
  1746. sqs.count = sqs.scoreBox.vctScore.size();
  1747. if (sqs.count <= 0)
  1748. sqs.count = 1;
  1749. sqs.index = obj_index++;
  1750. if (!qt.id.empty())
  1751. sqs.question_index = std::stoi(qt.id);
  1752. sqs.scoreBox.maxsorce = qt.scoreBox.maxsorce;
  1753. sqs.question_code = qt.id;
  1754. sqs.question_code_all = "";
  1755. sqs.question_code_temp = "";
  1756. sqs.valid_option_count = 0;
  1757. sqs.questionLocatePointIndex[0] = 0;
  1758. sqs.questionLocatePointIndex[1] = 1;
  1759. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, sqs.centerx, sqs.centery, sqs.width, sqs.height);
  1760. SchemaCutArea cutarea;
  1761. cutarea.index = obj_index++;
  1762. cutarea.area_name = sqs.question_code;
  1763. if (qt.smallQtNo == -1)
  1764. {
  1765. cutarea.isfirst = TRUE;
  1766. cutarea.iscut = 0;
  1767. }
  1768. else if (qt.smallQtNo == 0)
  1769. {
  1770. cutarea.isfirst = TRUE;
  1771. cutarea.iscut = 2;
  1772. }
  1773. else
  1774. {
  1775. cutarea.isfirst = false;
  1776. cutarea.iscut = 0;
  1777. }
  1778. cutarea.question_index = question_count;
  1779. cutarea.marktype = qt.marktype;
  1780. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, cutarea.centerx, cutarea.centery, cutarea.width, cutarea.height);
  1781. SetTBLR(sqs);
  1782. scorev.push_back(sqs);
  1783. cutareav.push_back(cutarea);
  1784. }
  1785. else if (qt.type == 4)
  1786. {
  1787. //题组概念
  1788. //通过linkparm判断第一次还是第二次出现(第一次一定小于2)
  1789. if (qt.cut.linkparm < 1){
  1790. SchemaQuestionScore sqs;
  1791. sqs.type = QUESTION_SCORE_TYPE::MUTIL_SCORE;
  1792. InitScoreArea(sqs, qt.scoreBox, offset_pt, w_scale, h_scale);
  1793. if (sqs.scoreBox.type == 1){ // 1-16
  1794. sqs.count = sqs.scoreBox.limit + (m_pTemplate->_version == "old" ? 0 : 1); // 加入0 老版没有这
  1795. if (sqs.scoreBox.bPoint)
  1796. sqs.count++;
  1797. }
  1798. else if (sqs.scoreBox.type == 2){ // 29/49
  1799. sqs.count = ((int)sqs.scoreBox.limit / 10) + 12;// 12 = 各位数字+ 十/个位标记 10+2
  1800. if (sqs.scoreBox.bPoint)
  1801. sqs.count++;
  1802. }
  1803. sqs.index = obj_index++;
  1804. if (!qt.id.empty())
  1805. sqs.question_index = std::stoi(qt.id);
  1806. sqs.question_code = qt.id;
  1807. bool flagcreateselect = false;
  1808. sqs.question_code_temp = qt.all_id;
  1809. //设置all_id
  1810. {
  1811. std::vector<std::string> _vct_all_questions;
  1812. split(qt.all_id, (std::string)",", &_vct_all_questions);
  1813. int idfisrtindex = -1;
  1814. for (int i = 0; i < _vct_all_questions.size();i++)
  1815. {
  1816. if (_vct_all_questions[i] == qt.id)
  1817. {
  1818. idfisrtindex = i;
  1819. flagcreateselect = (i == 0) ? true : false;
  1820. }
  1821. }
  1822. sqs.question_code_all = qt.id;
  1823. for (int i = idfisrtindex + _vct_all_questions.size() / qt.selTotal; i < _vct_all_questions.size(); i += _vct_all_questions.size() / qt.selTotal)
  1824. {
  1825. sqs.question_code_all += ",";
  1826. sqs.question_code_all += _vct_all_questions[i];
  1827. }
  1828. }
  1829. sqs.scoreBox.bXuanZuoTi = flagcreateselect;
  1830. sqs.scoreBox.maxsorce = qt.scoreBox.maxsorce;
  1831. sqs.valid_option_count = 0;
  1832. sqs.questionLocatePointIndex[0] = 0;
  1833. sqs.questionLocatePointIndex[1] = 1;
  1834. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, sqs.centerx, sqs.centery, sqs.width, sqs.height);
  1835. sqs.countItemSelete = qt.selItem;
  1836. sqs.countNum = qt.selTotal;
  1837. if (flagcreateselect){
  1838. sqs.questionLocatePointIndex[2] = 2;
  1839. sqs.questionLocatePointIndex[3] = 3;
  1840. for (int v = 0; v < qt.selTotal && qt.opt.size() == qt.selTotal; v++)
  1841. {
  1842. CalcPos(qt.opt[v].pos, offset_pt, w_scale, h_scale, sqs.item[v].centerx, sqs.item[v].centery, sqs.item[v].width, sqs.item[v].height);
  1843. }
  1844. SetTBLR(sqs);
  1845. }
  1846. if (qt.smallQtNo == -1 || qt.smallQtNo == 0)
  1847. {
  1848. SchemaCutArea cutarea;
  1849. cutarea.index = obj_index++;
  1850. cutarea.area_name = sqs.question_code;
  1851. cutarea.isfirst = TRUE;
  1852. cutarea.question_index = sqs.question_index;
  1853. cutarea.iscut = 1;
  1854. cutarea.marktype = qt.marktype;
  1855. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, cutarea.centerx, cutarea.centery, cutarea.width, cutarea.height);
  1856. SetTBLR(sqs);
  1857. scorev.push_back(sqs);
  1858. cutareav.push_back(cutarea);
  1859. }
  1860. else
  1861. {
  1862. SchemaCutArea cutarea;
  1863. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, cutarea.centerx, cutarea.centery, cutarea.width, cutarea.height);
  1864. cutarea.index = obj_index++;
  1865. cutarea.isfirst = FALSE;
  1866. cutarea.question_index = -1;
  1867. cutarea.area_name = qt.id;
  1868. cutarea.marktype = qt.marktype;
  1869. SetTBLR(sqs);
  1870. scorev.push_back(sqs);
  1871. cutareav.push_back(cutarea);
  1872. }
  1873. }
  1874. else{
  1875. SchemaCutArea cutarea;
  1876. CalcPos(qt.cut.pos, offset_pt, w_scale, h_scale, cutarea.centerx, cutarea.centery, cutarea.width, cutarea.height);
  1877. cutarea.index = obj_index++;
  1878. cutarea.isfirst = FALSE;
  1879. cutarea.question_index = -1;
  1880. cutarea.area_name = qt.id;
  1881. cutarea.marktype = qt.marktype;
  1882. cutareav.push_back(cutarea);
  1883. }
  1884. }
  1885. }
  1886. ////LOGI("createSchema 6");
  1887. SchemaPage* schemapage = (SchemaPage*) new SchemaPage;
  1888. SetPageDefault(*schemapage);
  1889. schemapage->schoolCardStatus = m_pTemplate->schoolCardStatus;
  1890. schemapage->isZaiXianDaTiKa = true;
  1891. schemapage->qrFlag = qrFlag;
  1892. schemapage->index = front_page ? page_index : 2;
  1893. strcpy(schemapage->paper_id, paper_id.c_str());
  1894. strcpy(schemapage->student_code, student_code.c_str());
  1895. schemapage->is_stu_id = is_stu_id;
  1896. schemapage->width = dst_img->width;
  1897. schemapage->height = dst_img->height;
  1898. schemapage->locatePointCount = main_locate_point.size();
  1899. schemapage->questionLocatePoints = questionLocatePoints;
  1900. schemapage->keguantiQuestionLocatePointIndexs = keguanti_question_locatepoint_indexs;
  1901. schemapage->stuQuestionLocatePointIndex = stuIndex;
  1902. schemapage->codeCount = codev.size();
  1903. schemapage->groupCount = groupv.size();
  1904. schemapage->itemCount = itemv.size();
  1905. schemapage->stuGroupsCount = stuGroupv.size();
  1906. schemapage->stuItemCount = stuItemv.size();
  1907. schemapage->question_score_count = scorev.size();
  1908. schemapage->cutAreaCount = cutareav.size();
  1909. int offset = sizeof(SchemaPage);
  1910. //确定上面三个定位点区域
  1911. for (std::size_t i = 0; i < main_locate_point.size(); i++)
  1912. {
  1913. SchemaLocatePoint slp;
  1914. slp.centerx = main_locate_point[i].x + main_locate_point[i].width / 2.;
  1915. slp.centery = main_locate_point[i].y + main_locate_point[i].height / 2.;
  1916. slp.width = main_locate_point[i].width;
  1917. slp.height = main_locate_point[i].height;
  1918. schemapage->locatePoints.push_back(slp);
  1919. }
  1920. schemapage->codes = codev;
  1921. schemapage->groups = groupv;
  1922. schemapage->items = itemv;
  1923. schemapage->question_score = scorev;
  1924. schemapage->cutAreas = cutareav;
  1925. schemapage->mode_type = mode_type;
  1926. schemapage->stuGroups = stuGroupv;
  1927. schemapage->stuItems = stuItemv;
  1928. schemapage->schoolCardStatus = schoolCardStatus;
  1929. //if (!m_pTemplate->useQrCode){
  1930. schemapage->is_same_exam_id = is_same_exam_id;
  1931. //}
  1932. {
  1933. std::lock_guard<std::mutex> _lck(g_mtx_examid);
  1934. schemapage->examid_by_qr_code = (g_examid_by_qr_code==1);
  1935. }
  1936. schemapage->is_front_page = front_page;
  1937. schemapage->is_use_qr_code = m_pTemplate->useQrCode;
  1938. //缺考标记相关参数
  1939. schemapage->quekao.centerx = schema_param.quekao_item_offset_x + schema_param.quekao_item_width / 2;
  1940. schemapage->quekao.centery = schema_param.quekao_item_offset_y + schema_param.quekao_item_height / 2;
  1941. schemapage->quekao.width = schema_param.quekao_item_width;
  1942. schemapage->quekao.height = schema_param.quekao_item_height;
  1943. schemapage->quekaoLocatePointIndex = quekaoIndex;
  1944. *pageSchema = schemapage;
  1945. //if (flag){
  1946. // DrawSchema(dst_img, schemapage);
  1947. // cvSaveImage(result_path, dst_img);
  1948. //}
  1949. #ifdef _draw_debug
  1950. //drawDebug(src_debug, &page);
  1951. DrawSchema(src_debug, schemapage);
  1952. {
  1953. std::chrono::time_point<std::chrono::system_clock, std::chrono::microseconds> tp = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::system_clock::now());
  1954. auto tmp = std::chrono::duration_cast<std::chrono::microseconds>(tp.time_since_epoch());
  1955. std::time_t timestamp = tmp.count();
  1956. std::string imagePath = g_appFilePathName + "\\image1\\";
  1957. CreateDirectoryA(imagePath.c_str(), NULL);
  1958. std::string path = imagePath + std::to_string(timestamp) + ".jpg";
  1959. cvSaveImage(path.c_str(), src_debug);
  1960. }
  1961. cvReleaseImage(&src_debug);
  1962. #endif
  1963. cvReleaseMemStorage(&storage);
  1964. cvReleaseImage(&src_gray_img);
  1965. *dst = dst_img;
  1966. ////LOGI("createSchema 7");
  1967. cvReleaseImage(&dst_gray_img);
  1968. {
  1969. cvSetImageROI(src0, roi_old);
  1970. return identify::result::IDF_SUCCESS;
  1971. }
  1972. //printf("线程:%s 单页识别结束\n", std::to_string(GetCurrentThreadId()).c_str());
  1973. }
  1974. std::string getTimeStamp()
  1975. {
  1976. std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> tp = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
  1977. auto tmp = std::chrono::duration_cast<std::chrono::milliseconds>(tp.time_since_epoch());
  1978. std::time_t timestamp = tmp.count();
  1979. return std::move(std::to_string(timestamp));
  1980. }
  1981. //按照指定模板进行识别
  1982. int COnLineCardPageIdentifier::Identify_impl()
  1983. {
  1984. int ret = BaseCheck();
  1985. if (ret != identify::result::IDF_SUCCESS)return ret;
  1986. InitalizeTempVar();
  1987. CvMemStorage* storage = cvCreateMemStorage(0);
  1988. IplImage * dst = NULL;
  1989. SchemaPage* pschemaPage = NULL;
  1990. std::string path = g_appFilePathName + std::string("\\image\\") + getTimeStamp() + ".jpg";
  1991. //LOGI("Identify_impl 1");
  1992. ret = createSchema(src, &dst, &pschemaPage, path.c_str(), true, m_strQrClass, m_bUseQr);
  1993. //LOGI("Identify_impl 2");
  1994. if (ret != identify::result::IDF_SUCCESS){
  1995. if (dst != NULL)cvReleaseImage(&dst);
  1996. cvReleaseMemStorage(&storage);
  1997. return ret;
  1998. }
  1999. SchemaPage& schemaPage = *pschemaPage;
  2000. omr_result->card_qrFlag = schemaPage.qrFlag;
  2001. omr_result->is_same_exam_id = schemaPage.is_same_exam_id;
  2002. omr_result->is_front_page = schemaPage.is_front_page;
  2003. omr_result->is_use_qr_code = schemaPage.is_use_qr_code;
  2004. omr_result->examid_by_qr_code = schemaPage.examid_by_qr_code;
  2005. omr_result->strMagicClassId = schemaPage.paper_id;
  2006. omr_result->strMagicStudentId = schemaPage.student_code;
  2007. /************************读取学生学号*********************************************/
  2008. ret = ReadStudentID(schemaPage, dst);
  2009. if (ret != identify::result::IDF_SUCCESS) {
  2010. if (dst != NULL)cvReleaseImage(&dst);
  2011. cvReleaseMemStorage(&storage);
  2012. if (pschemaPage != NULL)delete pschemaPage;
  2013. return ret;
  2014. }
  2015. //LOGI("Identify_impl 3");
  2016. /************************查找题目定位点,准备定位数据*********************************************/
  2017. ret = FindQuestionLocatePoints(schemaPage, dst);
  2018. if (ret != identify::result::IDF_SUCCESS) {
  2019. if (dst != NULL)cvReleaseImage(&dst);
  2020. cvReleaseMemStorage(&storage);
  2021. if (pschemaPage != NULL)delete pschemaPage;
  2022. return ret;
  2023. }
  2024. /************************客观题()*********************************************/
  2025. ret = ReadKeGuanTi1(schemaPage, dst);
  2026. if (ret != identify::result::IDF_SUCCESS) {
  2027. if (dst != NULL)cvReleaseImage(&dst);
  2028. cvReleaseMemStorage(&storage);
  2029. if (pschemaPage != NULL)delete pschemaPage;
  2030. return ret;
  2031. }
  2032. //LOGI("Identify_impl 4");
  2033. /************************阅卷读取*************************************************/
  2034. ret = ReadQuestionScore(schemaPage, dst, storage);
  2035. if (ret != identify::result::IDF_SUCCESS) {
  2036. if (dst != NULL)cvReleaseImage(&dst);
  2037. cvReleaseMemStorage(&storage);
  2038. if (pschemaPage != NULL)delete pschemaPage;
  2039. return ret;
  2040. }
  2041. //LOGI("Identify_impl 6");
  2042. /************************根据给定区域切割图片*********************************************/
  2043. ret = ClipImg(schemaPage, dst);
  2044. if (ret != identify::result::IDF_SUCCESS) {
  2045. if (dst != NULL)cvReleaseImage(&dst);
  2046. cvReleaseMemStorage(&storage);
  2047. if (pschemaPage != NULL)delete pschemaPage;
  2048. return ret;
  2049. }
  2050. //LOGI("Identify_impl 7");
  2051. /************************读取缺考标记*********************************************/
  2052. omr_result->quekaoFlag = ReadQuekaoFlag(schemaPage, dst);
  2053. omr_result->card_index = schemaPage.index;
  2054. /*
  2055. if (1){
  2056. DrawSchema(dst, pschemaPage);
  2057. cvSaveImage(path.c_str(), dst);
  2058. }
  2059. */
  2060. if (dst != NULL)cvReleaseImage(&dst);
  2061. cvReleaseMemStorage(&storage);
  2062. if (pschemaPage != NULL)delete pschemaPage;
  2063. return identify::result::IDF_SUCCESS;
  2064. }
  2065. //获取全局背景灰度,通过统计所有涂点周围像素(灰度不小于160)的灰度平均值获得
  2066. int COnLineCardPageIdentifier::caculate_global_background(SchemaPage& schemaPage, IplImage * dst, bool flag)
  2067. {
  2068. int global_background_color_count = 0;
  2069. int global_background_color_avg = 0;
  2070. int totalCount = 0;
  2071. if (!flag)
  2072. {
  2073. totalCount = schemaPage.itemCount;
  2074. }
  2075. else
  2076. {
  2077. totalCount = schemaPage.stuItemCount;
  2078. }
  2079. for (int i = 0; i<totalCount; i++)
  2080. {
  2081. SchemaItem & item = m_items[i];
  2082. RESULT_RECT result_rect;
  2083. GetRect(item.centerx, item.centery, item.width, item.height, result_rect);
  2084. int left = int(result_rect.x);
  2085. int top = int(result_rect.y);
  2086. int right = int(result_rect.x + result_rect.width);
  2087. int bottom = int(result_rect.y + result_rect.height);
  2088. int vtop = (int)(result_rect.y - (result_rect.width + 2) / 4);
  2089. int vbottom = (int)(result_rect.y + result_rect.height + (result_rect.height + 2) / 4);
  2090. int background_color_count = 0, background_color_avg = 0;
  2091. int gray;
  2092. int(*GetGray)(const IplImage * dst, int x, int y);
  2093. if (dst->nChannels == 1)GetGray = GetGrayValue;
  2094. else GetGray = GetBGRGray;
  2095. for (int x = left; x <= right; x++)
  2096. {
  2097. gray = GetGray(dst, x, vtop);
  2098. if (gray>160){ background_color_avg += gray; background_color_count++; }
  2099. gray = GetGray(dst, x, vbottom);
  2100. if (gray>160){ background_color_avg += gray; background_color_count++; }
  2101. }
  2102. background_color_avg = background_color_count>0 ? background_color_avg / background_color_count : 0;
  2103. if (background_color_avg>100){
  2104. global_background_color_avg += background_color_avg;
  2105. global_background_color_count++;
  2106. }
  2107. }
  2108. global_background_color_avg = global_background_color_count>0 ? global_background_color_avg / global_background_color_count : 0;
  2109. return global_background_color_avg;
  2110. }
  2111. //计算涂点属性
  2112. inline int COnLineCardPageIdentifier::caculate_cell_property(SchemaPage& schemaPage, SchemaItem &item, IplImage * dst, int &cellSize, double &refArea, double &avg_gray, int &grayNum, double &mo_hu_du, double &gao_heidu,/*字符区域大小*/int &char_area_size,/*字符区域黑色点数(统计选项的字符区域的灰度判定为黑色的点数,用于统计试卷该选项全局的平均字符黑点数)*/int& char_area_black_count,/*字符区域黑色点的平均灰度*/double& char_area_avg_gray, int flag)
  2113. {
  2114. int gray;//临时变量,灰度值
  2115. //int tembackcolor=max((anlyseResult.globalBackGround+200)/2,200);
  2116. int tembackcolor = (flag == 0) ? max(anlyseResult.backGroundGray - 20, 200) : max(anlyseResult.backGroundGray - 20, 0);
  2117. //获取灰度的函数指针
  2118. int(*ggray)(const IplImage * dst, int x, int y);
  2119. if (dst->nChannels == 1)ggray = GetGrayValue;
  2120. else ggray = GetBGRGray;
  2121. int left = int(item.centerx - item.width / 2. + 0.5);
  2122. int top = int(item.centery - item.height / 2. + 0.5);
  2123. int right = int(item.centerx + item.width / 2. + 0.5);
  2124. int bottom = int(item.centery + item.height / 2. + 0.5);
  2125. int vtop = (int)(item.centery - item.height*0.75 + 0.5);
  2126. /*******************统计字符区域属性************************************/
  2127. int cl = left + ((right - left + 1) + 2) / 4;
  2128. int cr = left + ((right - left + 1) * 3 + 2) / 4;
  2129. int ct = top;
  2130. int cb = bottom;
  2131. // auto rcRoi = cvRect(cl, ct, cr - cl, cb - ct);
  2132. // //auto rcRoi = cvRect(left, top, right - left, bottom - top);
  2133. // cvSetImageROI(dst, rcRoi);
  2134. // cvSaveImage("d:\\test_roi.png", dst);
  2135. //初始化字符区域参数
  2136. char_area_avg_gray = 0;
  2137. char_area_black_count = 0;
  2138. char_area_size = 0;
  2139. for (int y = ct; y <= cb; y++)
  2140. {
  2141. for (int x = cl; x <= cr; x++)
  2142. {
  2143. gray = ggray(dst, x, y);
  2144. char_area_size++;
  2145. if (gray<tembackcolor){
  2146. char_area_avg_gray += gray;
  2147. char_area_black_count++;
  2148. }
  2149. }
  2150. }
  2151. if (char_area_black_count>0)char_area_avg_gray /= char_area_black_count;
  2152. //灰度直方图
  2153. int gray256[260];
  2154. cellSize = 0;
  2155. memset(gray256, 0, sizeof(gray256));
  2156. double yRatio, xRatio, maxRatio;
  2157. double dist;
  2158. if (item.width >= item.height){
  2159. xRatio = 1.0;
  2160. yRatio = (item.width / (double)item.height)*(1 - 0.3);
  2161. if (yRatio<1)yRatio = 1;
  2162. }
  2163. else{
  2164. yRatio = 1.0;
  2165. xRatio = (item.height / (double)item.width)*(1 - 0.3);
  2166. if (xRatio<1)xRatio = 1;
  2167. }
  2168. double yd = item.centery;
  2169. double xd = item.centerx;
  2170. //理想点数
  2171. int li_xiang_dian_shu = item.width*item.height;
  2172. double dx;
  2173. double dy;
  2174. maxRatio = sqrt(xRatio*xRatio + yRatio*yRatio);
  2175. xRatio = xRatio / (item.width / 2.0);
  2176. yRatio = yRatio / (item.height / 2.0);
  2177. xRatio = xRatio / maxRatio;
  2178. yRatio = yRatio / maxRatio;
  2179. refArea = 0;
  2180. double distSum = 0;
  2181. avg_gray = 0;
  2182. grayNum = 0;
  2183. for (int y = top; y <= bottom; y++)
  2184. {
  2185. for (int x = left; x <= right; x++)
  2186. {
  2187. gray = ggray(dst, x, y);
  2188. gray256[gray]++;
  2189. cellSize++;
  2190. if (gray<tembackcolor){
  2191. avg_gray += gray;
  2192. grayNum++;
  2193. }
  2194. dx = (x - xd)*xRatio;
  2195. dy = (y - yd)*xRatio;
  2196. dist = sqrt(dx*dx + dy*dy);
  2197. gray = max(0, 255 - gray - 32);
  2198. refArea += gray*(1 - dist);
  2199. distSum += (1 - dist);
  2200. }
  2201. }
  2202. avg_gray = grayNum>0 ? avg_gray / grayNum : 256;
  2203. refArea /= 16;
  2204. refArea /= distSum;
  2205. //灰度上限
  2206. int grayUp = 256 - schemaPage.hei_du_ling_min_du * 16;
  2207. mo_hu_du = 0;
  2208. //合格黑点数
  2209. int he_ge_hei_dian_shu = 0;
  2210. for (int i = 0; i<grayUp; i++)
  2211. {
  2212. if (gray256[i]>0){
  2213. he_ge_hei_dian_shu += gray256[i];
  2214. mo_hu_du += (i*gray256[i]);
  2215. }
  2216. }
  2217. mo_hu_du = he_ge_hei_dian_shu>0 ? mo_hu_du / he_ge_hei_dian_shu : 0;
  2218. double ping_jun_hei_du = he_ge_hei_dian_shu>0 ? mo_hu_du / he_ge_hei_dian_shu : 0;
  2219. int i, endpos, flag = 0;
  2220. int hui_du_he = 0;
  2221. int hei_dian_shu = 0;
  2222. for (i = 0, endpos = grayUp; i<endpos; i++)
  2223. {
  2224. if (gray256[i]>0){
  2225. if (flag == 0){
  2226. flag = 1;
  2227. endpos = min(grayUp, i + 40);
  2228. }
  2229. hui_du_he += (i*gray256[i]);
  2230. hei_dian_shu += gray256[i];
  2231. }
  2232. if (hei_dian_shu>li_xiang_dian_shu*0.1 || hei_dian_shu >= he_ge_hei_dian_shu*0.3 + 1){
  2233. endpos = i;
  2234. i++;
  2235. break;
  2236. }
  2237. }
  2238. for (; i<grayUp&&hei_dian_shu<dianshu_meihaomi*dianshu_meihaomi / 4 + 1 && hei_dian_shu<hei_dian_shu >= he_ge_hei_dian_shu*0.3 + 1; i++)
  2239. {
  2240. hui_du_he += (i*gray256[i]);
  2241. hei_dian_shu += gray256[i];
  2242. }
  2243. endpos = i;
  2244. gao_heidu = hei_dian_shu>0 ? (255 - hui_du_he / hei_dian_shu) / 16 : 0;
  2245. refArea /= max(1.0, gao_heidu - 2);
  2246. /************确定清晰度**************************/
  2247. int pnt_high = 0, pnt_low = 0, pnt_mid = 0;
  2248. int pos1, pos2, pos3;
  2249. double minRatio = 0.3; maxRatio = 0.8;
  2250. pos1 = 255 - (int)(gao_heidu*maxRatio * 16);
  2251. for (i = 0; i <= pos1; i++)
  2252. {
  2253. pnt_high += gray256[i];
  2254. }
  2255. for (i = pos1 + 1; i<grayUp&&pnt_high<dianshu_meihaomi*dianshu_meihaomi / 4 + 1; i++)
  2256. {
  2257. pnt_high += gray256[i];
  2258. }
  2259. pos1 = i;
  2260. pos2 = hei_dian_shu*minRatio >= 3 ? 255 - (int)(gao_heidu*minRatio * 16) : 255 - 48;
  2261. pos3 = min(pos2, pos1 + 16);
  2262. for (i = pos1 + 1; i <= pos3; i++)
  2263. {
  2264. pnt_mid += gray256[i];
  2265. }
  2266. for (i = pos3 + 1; i <= pos2; i++)
  2267. {
  2268. pnt_low += gray256[i];
  2269. }
  2270. if (pnt_high == 0){ return identify::result::IDF_DIVIDE_ZERO; }
  2271. mo_hu_du = ((pnt_low + pnt_mid) / (double)pnt_high + pnt_low / (double)(pnt_high + pnt_mid)) / 2.0;
  2272. mo_hu_du = min(mo_hu_du, gao_heidu - ping_jun_hei_du);
  2273. mo_hu_du = min(min(gao_heidu, mo_hu_du), 5.0);
  2274. return identify::result::IDF_SUCCESS;
  2275. }
  2276. //分析全局属性
  2277. int COnLineCardPageIdentifier::analyseOmrPanoramic(SchemaPage& schemaPage)
  2278. {
  2279. int i;
  2280. int lev;
  2281. int count; // 所有涂点个数
  2282. int numAblePnt; // 参与分析的点的个数
  2283. double maxblk[16];
  2284. int numBlk[16];
  2285. double maxBlur[16];
  2286. int numBlur[16];
  2287. double stdBlur;
  2288. memset(numBlk, 0, sizeof(int) * 16);
  2289. memset(maxblk, 0, sizeof(double) * 16);
  2290. memset(maxBlur, 0, sizeof(double) * 16);
  2291. memset(numBlur, 0, sizeof(int) * 16);
  2292. count = schemaPage.itemCount;
  2293. numAblePnt = 0;
  2294. double& m_avaMaxBlk = anlyseResult.avgMax_heidu;
  2295. double& m_benchmarkBlk = anlyseResult.benchmarkBlk;
  2296. double& m_benchmarkArea = anlyseResult.benchmarkAreaBlk;
  2297. m_avaMaxBlk = 0;
  2298. for (i = 0; i<count; i++)
  2299. {
  2300. OnlineAnalyseResult::itemResult& itemr = anlyseResult.itemAnalyseResult[i];
  2301. if (itemr.heidu < m_benchmarkBlk || itemr.xiangdui_value < m_benchmarkArea)
  2302. continue;
  2303. if (itemr.heidu< 4.0) continue;
  2304. m_avaMaxBlk += itemr.heidu;
  2305. numAblePnt++;
  2306. lev = int(itemr.heidu);
  2307. maxblk[lev] += itemr.heidu;
  2308. numBlk[lev] ++;
  2309. lev = int(itemr.mohudu);
  2310. maxBlur[lev] += itemr.mohudu;
  2311. numBlur[lev] ++;
  2312. //pPnt++;
  2313. }
  2314. if (numAblePnt > 0)
  2315. m_avaMaxBlk /= numAblePnt;
  2316. else
  2317. m_avaMaxBlk = 12;
  2318. int stdId = 2;
  2319. int numStd = numBlk[0] + numBlk[1] + numBlk[2];
  2320. int numCur = numStd;
  2321. for (i = 3; i<16; i++)
  2322. {
  2323. numCur += (numBlk[i] - numBlk[i - 3]);
  2324. if (numCur > numStd)
  2325. {
  2326. numStd = numCur;
  2327. stdId = i;
  2328. }
  2329. }
  2330. double stdBlk = maxblk[stdId - 2] + maxblk[stdId - 1] + maxblk[stdId];
  2331. if (numStd > 0)
  2332. stdBlk /= numStd;
  2333. else
  2334. stdBlk = 12;
  2335. // 计算模糊度代表值
  2336. stdId = 2;
  2337. numStd = numBlur[0] + numBlur[1] + numBlur[2];
  2338. numCur = numStd;
  2339. for (i = 3; i<16; i++)
  2340. {
  2341. numCur += (numBlur[i] - numBlur[i - 3]);
  2342. if (numCur > numStd)
  2343. {
  2344. numStd = numCur;
  2345. stdId = i;
  2346. }
  2347. }
  2348. stdBlur = maxBlur[stdId - 2] + maxBlur[stdId - 1] + maxBlur[stdId];
  2349. if (numStd > 0)
  2350. stdBlur /= numStd;
  2351. else
  2352. stdBlur = 0;
  2353. if (stdBlur < 2.0)
  2354. stdBlk -= stdBlur;
  2355. else
  2356. stdBlk -= 2.0;
  2357. if (stdBlk < m_avaMaxBlk)
  2358. m_avaMaxBlk = stdBlk;
  2359. //统计字符区域的黑色点数的平均值
  2360. if (schemaPage.groupCount>0){
  2361. int max_item = 1;
  2362. for (int gi = 0; gi<schemaPage.groupCount; gi++)
  2363. {
  2364. if (schemaPage.groups[gi].itemCount>max_item) max_item = schemaPage.groups[gi].itemCount;
  2365. }
  2366. int * char_area_black_counts = new int[schemaPage.groupCount];
  2367. int cur_item_count;//当前选项的数量
  2368. for (int ii = 0; ii<max_item; ii++)
  2369. {
  2370. cur_item_count = 0;
  2371. long total_char_area_size = 0;
  2372. for (int gi = 0; gi<schemaPage.groupCount; gi++)
  2373. {
  2374. if (schemaPage.groups[gi].itemCount>ii){
  2375. OnlineAnalyseResult::itemResult& ir = anlyseResult.itemAnalyseResult[schemaPage.groups[gi].itemIndex[ii]];
  2376. char_area_black_counts[cur_item_count] = ir.char_area_black_count;
  2377. total_char_area_size += ir.char_area_size;
  2378. cur_item_count++;
  2379. }
  2380. }
  2381. double char_area_size_avg = total_char_area_size / (double)cur_item_count;
  2382. int temp;
  2383. for (int m = 0; m<cur_item_count; m++)
  2384. {
  2385. for (int n = m + 1; n<cur_item_count; n++)
  2386. {
  2387. if (char_area_black_counts[n]<char_area_black_counts[m]){
  2388. temp = char_area_black_counts[n];
  2389. char_area_black_counts[n] = char_area_black_counts[m];
  2390. char_area_black_counts[m] = temp;
  2391. }
  2392. }
  2393. }
  2394. anlyseResult.char_area_black_avg[ii] = -1;
  2395. if (char_area_black_counts[0] * 1.4<char_area_black_counts[cur_item_count - 1] || char_area_black_counts[cur_item_count - 1] * 1.4<char_area_size_avg){
  2396. double t = char_area_black_counts[0] * 1.15;
  2397. double f = 0;
  2398. int count__ = 0;
  2399. for (int l = 0; l<cur_item_count; l++)
  2400. {
  2401. if (t<char_area_black_counts[l]){
  2402. f += char_area_black_counts[l];
  2403. count__++;
  2404. }
  2405. }
  2406. anlyseResult.char_area_black_avg[ii] = f / count__;
  2407. }
  2408. }
  2409. delete[]char_area_black_counts;
  2410. }
  2411. return(0);
  2412. }
  2413. /************************************************************************/
  2414. /* 生成OMR涂点字符串(客观题填涂判断) 2014年11月28日10:54:19 */
  2415. /************************************************************************/
  2416. int COnLineCardPageIdentifier::GenerateOmrStr(SchemaPage& schemaPage)
  2417. {
  2418. char omr_str[512] = { 0 };
  2419. int omr_str_len = 0;
  2420. omr_result->group_result.resize(schemaPage.groups.size());
  2421. identify::result::KEGUANTI_RESULT* ketuanti_result_value = omr_result->group_result.data();
  2422. //标记是否被选中
  2423. SelectType sel[MAX_ITEM];
  2424. for (int groupIndex = 0; groupIndex<schemaPage.groupCount; groupIndex++)
  2425. {
  2426. omr_str_len = 0;
  2427. memset(sel, UNSELECT, sizeof(sel));
  2428. SchemaGroup& group = schemaPage.groups[groupIndex];
  2429. int pn = min(MAX_ITEM, group.itemCount);
  2430. OnlineAnalyseResult::itemResult* results[MAX_ITEM];
  2431. SchemaItem* items[MAX_ITEM] = { nullptr };
  2432. int maxgraynum = -1;
  2433. int mingraynum = 0xffffff;
  2434. for (int i = 0; i<pn; i++){
  2435. results[i] = &anlyseResult.itemAnalyseResult[group.itemIndex[i]];
  2436. items[i] = &m_items[group.itemIndex[i]];
  2437. if (results[i]->gray_num > maxgraynum)maxgraynum = results[i]->gray_num;
  2438. if (results[i]->gray_num < mingraynum)mingraynum = results[i]->gray_num;
  2439. }
  2440. int selected_num = 0;
  2441. int unselect_num = 0;
  2442. int uncertain_num = 0;
  2443. for (int i = 0; i<pn; i++)
  2444. {
  2445. //被涂面积小于10%
  2446. if (results[i]->gray_num<results[i]->cell_size*0.1){
  2447. sel[i] = UNSELECT;
  2448. unselect_num++;
  2449. continue;
  2450. }
  2451. //面积超过65%
  2452. if (results[i]->gray_num>results[i]->cell_size*0.65){
  2453. sel[i] = SELECTED;
  2454. selected_num++;
  2455. continue;
  2456. }
  2457. //填涂面积相差达%15,将面积不小于最大点填涂面积的总面积%10的涂点视为填涂
  2458. if (maxgraynum - mingraynum>results[i]->cell_size*0.2&&results[i]->gray_num>maxgraynum - results[i]->cell_size*0.1){
  2459. sel[i] = SELECTED;
  2460. selected_num++;
  2461. continue;
  2462. }
  2463. //填涂面积相差达%50,将面积不小于最大点填涂面积的总面积%30的涂点视为填涂
  2464. if (maxgraynum - mingraynum>results[i]->cell_size*0.5&&results[i]->gray_num>maxgraynum - results[i]->cell_size*0.3){
  2465. sel[i] = SELECTED;
  2466. selected_num++;
  2467. continue;
  2468. }
  2469. if (maxgraynum - results[i]->gray_num>results[i]->cell_size*0.2){
  2470. sel[i] = UNSELECT;
  2471. unselect_num++;
  2472. continue;
  2473. }
  2474. sel[i] = UNCERTAIN;
  2475. uncertain_num++;
  2476. }
  2477. int out_count;
  2478. identify::result::OMR_QUESTION_STATE question_state = identify::result::OMR_QUESTION_STATE::OMR_QUESTION_STATE_NORMAL;
  2479. if (group.omr_out_type == OMR_OUT_TYPE::OMR_OUT_TYPE_SINGLE_NONSTRICT){
  2480. if (selected_num>1){
  2481. int maxgrayindex = -1;
  2482. int maxgraynum = -101;
  2483. for (int i = 0; i<pn; i++)
  2484. {
  2485. if (sel[i] == SELECTED)
  2486. {
  2487. if ((results[i]->gray_num - maxgraynum>100) || (abs(results[i]->gray_num - maxgraynum) <= 100 && results[i]->heidu > results[maxgrayindex]->heidu))
  2488. {
  2489. maxgraynum = results[i]->gray_num;
  2490. maxgrayindex = i;
  2491. }
  2492. }
  2493. }
  2494. for (int i = 0; i<pn; i++)
  2495. {
  2496. if (sel[i] == SELECTED&&i != maxgrayindex){
  2497. sel[i] = UNSELECT;
  2498. }
  2499. }
  2500. }
  2501. out_count = 0;
  2502. for (int i = 0; i<pn; i++){
  2503. if (sel[i] == SELECTED){
  2504. if (out_count>0)omr_str[omr_str_len++] = schemaPage.option_spacer;
  2505. omr_str[omr_str_len++] = items[i]->OutChar;
  2506. out_count++;
  2507. }
  2508. }
  2509. if (out_count == 0){
  2510. omr_str[omr_str_len++] = schemaPage.unselect_char;
  2511. question_state = identify::result::OMR_QUESTION_STATE::OMR_QUESTION_STATE_LOUTU;
  2512. }
  2513. }
  2514. else if (group.omr_out_type == OMR_OUT_TYPE::OMR_OUT_TYPE_MUTIL_STRICT||
  2515. group.omr_out_type == OMR_OUT_TYPE::OMR_OUT_TYPE_MUTIL_STRICT_EX)
  2516. {
  2517. out_count = 0;
  2518. for (int i = 0; i < pn; i++)
  2519. {
  2520. if (sel[i] == SELECTED){
  2521. if (out_count > 0)omr_str[omr_str_len++] = schemaPage.option_spacer;
  2522. omr_str[omr_str_len++] = items[i]->OutChar;
  2523. out_count++;
  2524. }
  2525. }
  2526. if (out_count == 0){
  2527. omr_str[omr_str_len++] = schemaPage.unselect_char;
  2528. question_state = identify::result::OMR_QUESTION_STATE::OMR_QUESTION_STATE_LOUTU;
  2529. }
  2530. }
  2531. omr_str[omr_str_len] = '\0';
  2532. ketuanti_result_value[groupIndex].question_code = group.question_code;
  2533. ketuanti_result_value[groupIndex].obj_id = static_cast<unsigned int>(group.question_index);
  2534. ketuanti_result_value[groupIndex].answer = omr_str;
  2535. ketuanti_result_value[groupIndex].question_state = identify::result::OMR_QUESTION_STATE::OMR_QUESTION_STATE_NORMAL;
  2536. ketuanti_result_value[groupIndex].omr_question_type = (group.omr_out_type == OMR_OUT_TYPE::OMR_OUT_TYPE_SINGLE_NONSTRICT ?
  2537. identify::DANXUANTI : (group.omr_out_type == OMR_OUT_TYPE::OMR_OUT_TYPE_MUTIL_STRICT_EX ? identify::DUOXUANTIEX : identify::DUOXUANTI));
  2538. }
  2539. return 0;
  2540. }
  2541. // //识别指定图像
  2542. int COnLineCardPageIdentifier::Identify(const IplImage* img, void* out_result, int out_size)
  2543. {
  2544. if (out_result){
  2545. this->out_result = out_result;
  2546. this->omr_result = (identify::result::OMR_RESULT*)this->out_result;
  2547. // printf("线程:%s 缓冲区:缺考标记:%d %p\n", std::to_string(GetCurrentThreadId()).c_str(), omr_result->quekaoFlag, this->out_result);
  2548. src = (IplImage*)img;
  2549. if (src == NULL)
  2550. return identify::result::IDF_FAILURE;
  2551. }else{
  2552. return identify::result::IDF_FAILURE;
  2553. }
  2554. return Identify();
  2555. }
  2556. /************************************************************************/
  2557. /* 获取区域黑色点数量 */
  2558. /************************************************************************/
  2559. int COnLineCardPageIdentifier::GetBlackArea(const IplImage * dst)
  2560. {
  2561. int blackArea = 0;
  2562. CvRect rect = cvGetImageROI(dst);
  2563. int(*GetGray)(const IplImage * dst, int x, int y);
  2564. if (dst->nChannels == 1)GetGray = GetGrayValue;
  2565. else GetGray = GetBGRGray;
  2566. for (int y = rect.y, endy = rect.y + rect.height; y<endy; y++)
  2567. {
  2568. for (int x = rect.x, endx = rect.x + rect.width; x<endx; x++)
  2569. {
  2570. if (GetGray(dst, x, y)<180)blackArea++;
  2571. }
  2572. }
  2573. return blackArea;
  2574. }
  2575. /************************************************************************/
  2576. /*
  2577. 图像基本信息检查
  2578. 最小宽高限制
  2579. 长宽比例限制 */
  2580. /************************************************************************/
  2581. int COnLineCardPageIdentifier::BaseCheck()
  2582. {
  2583. if (src->width<200 || src->height<200){
  2584. return identify::result::IDF_SIZE2SMALL;
  2585. }
  2586. if (abs(max(src->width, src->height) / (double)min(src->width, src->height) - max(21.0, 27.9) / (double)min(21.0, 27.9))>0.2){
  2587. //图像长宽比不符
  2588. return identify::result::IDF_SIDERATIO;
  2589. }
  2590. return identify::result::IDF_SUCCESS;
  2591. }
  2592. /*
  2593. 临时变量初始化
  2594. */
  2595. void COnLineCardPageIdentifier::InitalizeTempVar()
  2596. {
  2597. m_relationKey.clear();
  2598. m_relationValue.clear();
  2599. }
  2600. // 读取学生学号
  2601. int COnLineCardPageIdentifier::ReadStudentID(SchemaPage &schemaPage, IplImage * dst)
  2602. {
  2603. omr_result->qr_result.clear();
  2604. identify::result::CODE_RESULT code_rst; code_rst.qr_str = "";
  2605. code_rst.is_stu_id = false;
  2606. if (strlen(schemaPage.student_code)>0){
  2607. code_rst.qr_str = schemaPage.student_code;
  2608. code_rst.is_stu_id = schemaPage.is_stu_id;
  2609. }else{
  2610. m_items = schemaPage.stuItems;
  2611. if (m_nBxuanxiang == 1 && schemaPage.stuGroupsCount > 0){
  2612. try{
  2613. IdentifyArea *ptr_area = new IdentifyArea(schemaPage.stuGroupsCount);
  2614. ptr_area->btiantu = true;
  2615. ptr_area->type = GROUP_TYPE_IDENTIFY::TIANTUKAOHAO;
  2616. for (int groupIndex = 0; groupIndex < schemaPage.stuGroupsCount; groupIndex++)
  2617. {
  2618. SchemaGroup& group = schemaPage.stuGroups[groupIndex];
  2619. IdentifyGroup*ptr_group = ptr_area->get_group_by_index(groupIndex);
  2620. if (ptr_group){
  2621. ptr_group->init(group.itemCount, group.question_code.c_str(), false);
  2622. for (int itemindex = 0; itemindex < group.itemCount; itemindex++){
  2623. IdentifyItem*ptr_item = ptr_group->get_item_by_index(itemindex);
  2624. if (ptr_item){
  2625. SchemaItem*ptr_sc_item = &m_items[group.itemIndex[itemindex]];
  2626. auto rc = GetCVRect(*ptr_sc_item);
  2627. ptr_item->x = rc.x;
  2628. ptr_item->y = rc.y;
  2629. ptr_item->w = rc.width;
  2630. ptr_item->h = rc.height;
  2631. ptr_item->name[0] = ptr_sc_item->OutChar;
  2632. }
  2633. }
  2634. }
  2635. }
  2636. IplImage*dst_gray_img = cvCreateImage(cvGetSize(dst), IPL_DEPTH_8U, 1);
  2637. cvCvtColor(dst, dst_gray_img, CV_BGR2GRAY);
  2638. //cvSaveImage("D:\\src_src.png", dst_gray_img);
  2639. int bret = api_ttpd_analysis_part(cv::cvarrToMat(dst_gray_img), ptr_area);
  2640. code_rst.qr_str = std::string();
  2641. for (int i = 0; i < ptr_area->group_count; ++i){
  2642. code_rst.qr_str += get_str_by_identify_group(0, ptr_area->get_group_by_index(i), schemaPage.option_spacer);
  2643. }
  2644. delete ptr_area; ptr_area = nullptr;
  2645. cvReleaseImage(&dst_gray_img); dst_gray_img = nullptr;
  2646. }
  2647. catch (...){
  2648. }
  2649. }
  2650. else{
  2651. // 计算每个填图区域图像信息
  2652. int global_background_color_avg = caculate_global_background(schemaPage, dst, true);
  2653. //模糊度,面积,高黑度,平均黑度
  2654. double mo_hu_du, refArea, gao_heidu, avg_gray;
  2655. //黑色像素数,涂点大小
  2656. int grayNum, cellSize;
  2657. //高黑度和
  2658. double topGraySum = 0;
  2659. //高黑度面积
  2660. double topGrayAreaSum = 0;
  2661. anlyseResult.backGroundGray = global_background_color_avg;
  2662. anlyseResult.cellAvgGray = 0;
  2663. anlyseResult.cellNum = 0;
  2664. int numTopGray = 0;
  2665. int ret;
  2666. item_result.resize(schemaPage.stuItemCount);
  2667. for (int i = 0; i < schemaPage.stuItemCount; i++)
  2668. {
  2669. SchemaItem & item = m_items[i];
  2670. item_result[i] = GetCVRect(item);
  2671. ret = caculate_cell_property(schemaPage, item, dst, cellSize, refArea, avg_gray, grayNum, mo_hu_du, gao_heidu, anlyseResult.itemAnalyseResult[i].char_area_size, anlyseResult.itemAnalyseResult[i].char_area_black_count, anlyseResult.itemAnalyseResult[i].char_area_avg_gray);
  2672. if (ret != identify::result::IDF_SUCCESS)return ret;
  2673. topGraySum += gao_heidu;
  2674. topGrayAreaSum += refArea;
  2675. numTopGray++;
  2676. anlyseResult.cellAvgGray += avg_gray;
  2677. anlyseResult.cellNum++;
  2678. anlyseResult.itemAnalyseResult[i].avg_gray = avg_gray;
  2679. anlyseResult.itemAnalyseResult[i].cell_size = cellSize;
  2680. anlyseResult.itemAnalyseResult[i].gray_num = grayNum;
  2681. anlyseResult.itemAnalyseResult[i].heidu = gao_heidu;
  2682. anlyseResult.itemAnalyseResult[i].mohudu = mo_hu_du;
  2683. anlyseResult.itemAnalyseResult[i].xiangdui_mianji = refArea;
  2684. anlyseResult.itemAnalyseResult[i].xiangdui_value = refArea;
  2685. }
  2686. anlyseResult.cellAvgGray = anlyseResult.cellNum > 0 ? anlyseResult.cellAvgGray / anlyseResult.cellNum : 256;
  2687. anlyseResult.avg_heidu = topGrayAreaSum > 0 ? topGraySum / topGrayAreaSum : 6;
  2688. if (numTopGray >= int(schemaPage.itemCount / 5)){
  2689. anlyseResult.float_heidu = anlyseResult.avg_heidu / schemaPage.user_float_heidu;
  2690. anlyseResult.benchmarkBlk = schemaPage.worthwhileBlk + anlyseResult.float_heidu;
  2691. }
  2692. else{
  2693. anlyseResult.benchmarkBlk = numTopGray > 0 ? min(6.0, schemaPage.worthwhileBlk + anlyseResult.avg_heidu / schemaPage.user_float_heidu) : 6;
  2694. anlyseResult.float_heidu = anlyseResult.benchmarkBlk - schemaPage.worthwhileBlk;
  2695. }
  2696. if (numTopGray > 0){
  2697. anlyseResult.avg_heidu = topGrayAreaSum / numTopGray;
  2698. anlyseResult.float_heidu = anlyseResult.avg_heidu / schemaPage.user_float_heidu;
  2699. anlyseResult.benchmarkAreaBlk = anlyseResult.float_heidu;
  2700. }
  2701. else{
  2702. anlyseResult.avg_heidu = 0;
  2703. anlyseResult.float_heidu = 0;
  2704. anlyseResult.benchmarkAreaBlk = 0.1;
  2705. }
  2706. // 获取
  2707. std::string strStudent;
  2708. if (GenerateOmrStr(schemaPage, strStudent) == 0){
  2709. code_rst.qr_str = strStudent;
  2710. }
  2711. }
  2712. }
  2713. if (!code_rst.qr_str.empty())
  2714. omr_result->qr_result.push_back(code_rst);
  2715. omr_result->online_card_paperid = schemaPage.paper_id;
  2716. omr_result->schoolCardStatus = schemaPage.schoolCardStatus;
  2717. if (m_bUseQr)
  2718. {
  2719. std::vector<std::string> split_qr;
  2720. split(m_strQrClass, (std::string)",", &split_qr);
  2721. if (split_qr.size()>1&& !split_qr[1].empty()){
  2722. omr_result->paper_id = std::stoi(split_qr[1]);
  2723. }
  2724. }
  2725. else{
  2726. omr_result->paper_id = -1;
  2727. }
  2728. return identify::result::IDF_SUCCESS;
  2729. }
  2730. //读取客观题分数
  2731. int COnLineCardPageIdentifier::ReadKeGuanTi1(SchemaPage &schemaPage, IplImage * dst)
  2732. {
  2733. m_items = schemaPage.items;
  2734. if (m_nBxuanxiang == 1 && schemaPage.groups.size() > 0){
  2735. try{
  2736. omr_result->group_result.resize(schemaPage.groups.size());
  2737. identify::result::KEGUANTI_RESULT* ketuanti_result_value = omr_result->group_result.data();
  2738. IdentifyArea *ptr_area = new IdentifyArea(schemaPage.groupCount);
  2739. ptr_area->type = GROUP_TYPE_IDENTIFY::KEGUANTI;
  2740. for (int groupIndex = 0; groupIndex < schemaPage.groupCount; groupIndex++)
  2741. {
  2742. SchemaGroup& group = schemaPage.groups[groupIndex];
  2743. IdentifyGroup*ptr_group = ptr_area->get_group_by_index(groupIndex);
  2744. if (ptr_group){
  2745. bool mul = (group.omr_out_type != OMR_OUT_TYPE::OMR_OUT_TYPE_SINGLE_NONSTRICT);
  2746. ptr_group->init(group.itemCount, group.question_code.c_str(), mul);
  2747. for (int itemindex = 0; itemindex < group.itemCount; itemindex++){
  2748. IdentifyItem*ptr_item = ptr_group->get_item_by_index(itemindex);
  2749. if (ptr_item){
  2750. SchemaItem*ptr_sc_item = &m_items[group.itemIndex[itemindex]];
  2751. auto rc = GetCVRect(*ptr_sc_item);
  2752. ptr_item->x = rc.x;
  2753. ptr_item->y = rc.y;
  2754. ptr_item->w = rc.width;
  2755. ptr_item->h = rc.height;
  2756. ptr_item->name[0] = ptr_sc_item->OutChar;
  2757. }
  2758. }
  2759. }
  2760. }
  2761. cvResetImageROI(dst);
  2762. IplImage*dst_gray_img = cvCreateImage(cvGetSize(dst), IPL_DEPTH_8U, 1);
  2763. cvCvtColor(dst, dst_gray_img, CV_BGR2GRAY);
  2764. //cvSaveImage("D:\\src_src.png", dst_gray_img);
  2765. if (0 == api_ttpd_analysis_part(cv::cvarrToMat(dst_gray_img), ptr_area)){
  2766. omr_result->group_result.resize(schemaPage.groups.size());
  2767. identify::result::KEGUANTI_RESULT* ketuanti_result_value = omr_result->group_result.data();
  2768. for (int groupIndex = 0; groupIndex < schemaPage.groupCount &&groupIndex < ptr_area->group_count; groupIndex++)
  2769. {
  2770. SchemaGroup& group = schemaPage.groups[groupIndex];
  2771. bool mul = (group.omr_out_type != OMR_OUT_TYPE::OMR_OUT_TYPE_SINGLE_NONSTRICT);
  2772. IdentifyGroup* ptr_group = ptr_area->get_group_by_index(groupIndex);
  2773. if (ptr_group->select_count == ptr_group->item_count) // 单选题如果全选,结果值会保留全部值
  2774. {
  2775. mul = true;
  2776. }
  2777. std::string omr_str = get_str_by_identify_group(mul ? 1 : 0, ptr_area->get_group_by_index(groupIndex), schemaPage.option_spacer);
  2778. ketuanti_result_value[groupIndex].question_code = group.question_code;
  2779. ketuanti_result_value[groupIndex].obj_id = static_cast<unsigned int>(group.question_index);
  2780. ketuanti_result_value[groupIndex].answer = omr_str;
  2781. ketuanti_result_value[groupIndex].question_state = identify::result::OMR_QUESTION_STATE::OMR_QUESTION_STATE_NORMAL;
  2782. ketuanti_result_value[groupIndex].omr_question_type = (group.omr_out_type == OMR_OUT_TYPE::OMR_OUT_TYPE_SINGLE_NONSTRICT ?
  2783. identify::DANXUANTI : (group.omr_out_type == OMR_OUT_TYPE::OMR_OUT_TYPE_MUTIL_STRICT_EX ? identify::DUOXUANTIEX : identify::DUOXUANTI));
  2784. }
  2785. }
  2786. cvReleaseImage(&dst_gray_img);
  2787. }
  2788. catch (cv::Exception e){
  2789. const char * ss = e.what();
  2790. return identify::result::IDF_FAILURE;
  2791. }
  2792. int global_background_color_avg = caculate_global_background(schemaPage, dst);
  2793. anlyseResult.backGroundGray = global_background_color_avg;
  2794. anlyseResult.cellAvgGray = 0;
  2795. anlyseResult.cellNum = 0;
  2796. return identify::result::IDF_SUCCESS;
  2797. }
  2798. int global_background_color_avg = caculate_global_background(schemaPage, dst);
  2799. //模糊度,面积,高黑度,平均黑度
  2800. double mo_hu_du, refArea, gao_heidu, avg_gray;
  2801. //黑色像素数,涂点大小
  2802. int grayNum, cellSize;
  2803. //高黑度和
  2804. double topGraySum = 0;
  2805. //高黑度面积
  2806. double topGrayAreaSum = 0;
  2807. anlyseResult.backGroundGray = global_background_color_avg;
  2808. anlyseResult.cellAvgGray = 0;
  2809. anlyseResult.cellNum = 0;
  2810. int numTopGray = 0;
  2811. int ret;
  2812. item_result.resize(schemaPage.itemCount);
  2813. for (int i = 0; i<schemaPage.itemCount; i++)
  2814. {
  2815. SchemaItem & item = m_items[i];
  2816. item_result[i] = GetCVRect(item);
  2817. ret = caculate_cell_property(schemaPage, item, dst, cellSize, refArea, avg_gray, grayNum, mo_hu_du, gao_heidu, anlyseResult.itemAnalyseResult[i].char_area_size, anlyseResult.itemAnalyseResult[i].char_area_black_count, anlyseResult.itemAnalyseResult[i].char_area_avg_gray);
  2818. if (ret != identify::result::IDF_SUCCESS)return ret;
  2819. topGraySum += gao_heidu;
  2820. topGrayAreaSum += refArea;
  2821. numTopGray++;
  2822. anlyseResult.cellAvgGray += avg_gray;
  2823. anlyseResult.cellNum++;
  2824. anlyseResult.itemAnalyseResult[i].avg_gray = avg_gray;
  2825. anlyseResult.itemAnalyseResult[i].cell_size = cellSize;
  2826. anlyseResult.itemAnalyseResult[i].gray_num = grayNum;
  2827. anlyseResult.itemAnalyseResult[i].heidu = gao_heidu;
  2828. anlyseResult.itemAnalyseResult[i].mohudu = mo_hu_du;
  2829. anlyseResult.itemAnalyseResult[i].xiangdui_mianji = refArea;
  2830. anlyseResult.itemAnalyseResult[i].xiangdui_value = refArea;
  2831. }
  2832. anlyseResult.cellAvgGray = anlyseResult.cellNum>0 ? anlyseResult.cellAvgGray / anlyseResult.cellNum : 256;
  2833. anlyseResult.avg_heidu = topGrayAreaSum>0 ? topGraySum / topGrayAreaSum : 6;
  2834. if (numTopGray >= int(schemaPage.itemCount / 5)){
  2835. anlyseResult.float_heidu = anlyseResult.avg_heidu / schemaPage.user_float_heidu;
  2836. anlyseResult.benchmarkBlk = schemaPage.worthwhileBlk + anlyseResult.float_heidu;
  2837. }
  2838. else{
  2839. anlyseResult.benchmarkBlk = numTopGray>0 ? min(6.0, schemaPage.worthwhileBlk + anlyseResult.avg_heidu / schemaPage.user_float_heidu) : 6;
  2840. anlyseResult.float_heidu = anlyseResult.benchmarkBlk - schemaPage.worthwhileBlk;
  2841. }
  2842. if (numTopGray>0){
  2843. anlyseResult.avg_heidu = topGrayAreaSum / numTopGray;
  2844. anlyseResult.float_heidu = anlyseResult.avg_heidu / schemaPage.user_float_heidu;
  2845. anlyseResult.benchmarkAreaBlk = anlyseResult.float_heidu;
  2846. }
  2847. else{
  2848. anlyseResult.avg_heidu = 0;
  2849. anlyseResult.float_heidu = 0;
  2850. anlyseResult.benchmarkAreaBlk = 0.1;
  2851. }
  2852. analyseOmrPanoramic(schemaPage);
  2853. GenerateOmrStr(schemaPage);
  2854. return identify::result::IDF_SUCCESS;
  2855. }
  2856. //读取填空题,和主观题分数
  2857. int COnLineCardPageIdentifier::ReadQuestionScore(SchemaPage &schemaPage, IplImage * dst, CvMemStorage* storage)
  2858. {
  2859. int tiankong_total_count = 0;
  2860. int zhuguan_total_count = 0;
  2861. for (int i = 0; i<schemaPage.question_score_count; i++){
  2862. if (schemaPage.question_score[i].type == QUESTION_SCORE_TYPE::MUTIL_SCORE)
  2863. zhuguan_total_count++;
  2864. else if (schemaPage.question_score[i].type == QUESTION_SCORE_TYPE::SINGLE_SCORE)
  2865. tiankong_total_count++;
  2866. }
  2867. omr_result->tiankongti_result.resize(tiankong_total_count);
  2868. omr_result->zhutuanti_result.resize(zhuguan_total_count);
  2869. identify::result::TIANKONGTI_RESULT*tiankongti_result_value = omr_result->tiankongti_result.data();
  2870. identify::result::ZHUGUANTI_RESULT*zhutuanti_result_value = omr_result->zhutuanti_result.data();
  2871. int score_count = 0;
  2872. int tiankong_count = 0;
  2873. for (int i = 0; i<schemaPage.question_score_count; i++){
  2874. SchemaQuestionScore& qs = schemaPage.question_score[i];
  2875. //查找题目定位点,校正题目位置
  2876. SchemaLocatePoint& lp1 = schemaPage.questionLocatePoints[qs.questionLocatePointIndex[0]];
  2877. SchemaLocatePoint& lp2 = schemaPage.questionLocatePoints[qs.questionLocatePointIndex[1]];
  2878. CvPoint2D32f point1, point2;
  2879. BOOL s1 = FindQuestionLocatePoint(dst, lp1, point1);
  2880. BOOL s2 = FindQuestionLocatePoint(dst, lp2, point2);
  2881. if (!s1&&!s2){
  2882. point1.x = lp1.centerx;
  2883. point1.y = lp1.centery;
  2884. point2.x = lp2.centerx;
  2885. point2.y = lp2.centery;
  2886. }
  2887. else if (!s1){
  2888. point1.x = lp1.centerx + (point2.x - lp2.centerx);
  2889. point1.y = lp1.centery + (point2.y - lp2.centery);
  2890. }
  2891. else if (!s2){
  2892. point2.x = lp2.centerx + (point1.x - lp1.centerx);
  2893. point2.y = lp2.centery + (point1.y - lp1.centery);
  2894. }
  2895. float data[6];
  2896. CvMat* m = caculate_scm(point1, point2, cvPoint2D32f(lp1.centerx, lp1.centery), cvPoint2D32f(lp2.centerx, lp2.centery));
  2897. memcpy(data, m->data.fl, sizeof(float) * 6);
  2898. cvReleaseMat(&m);
  2899. SchemaQuestionScore::Point * point[4] = { &qs.lt, &qs.rt, &qs.lb, &qs.rb };
  2900. float lx = -1, ly = -1;
  2901. float rx = -1, ry = -1;
  2902. double pscale = 1.0;
  2903. int redcount[30] = { 0 };
  2904. int red_in_count[30] = { 0 };
  2905. lx = data[0] * qs.lt.x + data[1] * qs.lt.y + data[2];
  2906. rx = data[0] * qs.rt.x + data[1] * qs.rt.y + data[2];
  2907. ly = data[3] * qs.lt.x + data[4] * qs.lt.y + data[5];
  2908. ry = data[3] * qs.rt.x + data[4] * qs.rt.y + data[5];
  2909. if (qs.type == QUESTION_SCORE_TYPE::MUTIL_SCORE){
  2910. if (lx >= 0 && rx >= 0){
  2911. float h = (point[2]->y - point[0]->y)*pscale;
  2912. CvRect detect_rect = cvRect(qs.scoreBox.pos.x, qs.scoreBox.pos.y, qs.scoreBox.pos.w, qs.scoreBox.pos.h);
  2913. //LOGI("GetZhuGuanTiRedCount-BEIGIN");
  2914. GetZhuGuanTiRedCount(dst, detect_rect, detect_rect, redcount, red_in_count, qs.count, qs.scoreBox.type != 1 || qs.scoreBox.bPoint, false);
  2915. //LOGI("GetZhuGuanTiRedCount-END");
  2916. float score = 0;
  2917. if (qs.scoreBox.type == 1)
  2918. {
  2919. // 小数位
  2920. if (qs.scoreBox.bPoint){
  2921. if (red_in_count[0] > 25){
  2922. score += 0.5;
  2923. }
  2924. }
  2925. // 整数位
  2926. {
  2927. int red_area = 0;
  2928. int red_area_index = -1;
  2929. for (int i = (qs.scoreBox.bPoint ? 1 : 0);
  2930. i < qs.count; ++i)
  2931. {
  2932. if (red_in_count[i]>red_area){
  2933. red_area = red_in_count[i]; red_area_index = i;
  2934. }
  2935. }
  2936. if (red_area_index >= (qs.scoreBox.bPoint ? 1 : 0)){
  2937. score += (qs.scoreBox.limit - red_area_index + (qs.scoreBox.bPoint ? 1 : 0));
  2938. }
  2939. }
  2940. }
  2941. else if (qs.scoreBox.type == 2)
  2942. {
  2943. // 小数位
  2944. if (qs.scoreBox.bPoint){
  2945. if (red_in_count[0] > 25){
  2946. score += 0.5;
  2947. }
  2948. }
  2949. {
  2950. //个位数
  2951. int red_area = 0;
  2952. int red_area_index = -1;
  2953. for (int i = (qs.scoreBox.bPoint ? 1 : 0);
  2954. i < (qs.scoreBox.bPoint ? 11 : 10); ++i)
  2955. {
  2956. if (red_in_count[i]>red_area){
  2957. red_area = red_in_count[i]; red_area_index = i;
  2958. }
  2959. }
  2960. if (red_area_index >= (qs.scoreBox.bPoint ? 1 : 0)){
  2961. score += (9 - red_area_index + (qs.scoreBox.bPoint ? 1 : 0));
  2962. }
  2963. }
  2964. {
  2965. //十位数
  2966. int red_area = 0;
  2967. int red_area_index = -1;
  2968. int begin_index = (qs.scoreBox.bPoint ? 11 : 10);
  2969. for (int i = begin_index; i < begin_index + (int)(qs.scoreBox.limit / 10) + 1; ++i)
  2970. {
  2971. if (red_in_count[i] > red_area){ red_area = red_in_count[i]; red_area_index = i; }
  2972. }
  2973. if (red_area_index >= begin_index){
  2974. score = score + 10 * ((int)(qs.scoreBox.limit / 10) - (red_area_index - begin_index) + 1);
  2975. }
  2976. }
  2977. }
  2978. if (score > qs.scoreBox.maxsorce && qs.scoreBox.maxsorce > 0)
  2979. zhutuanti_result_value[score_count].question_score = qs.scoreBox.maxsorce;
  2980. else
  2981. zhutuanti_result_value[score_count].question_score = score;
  2982. zhutuanti_result_value[score_count].question_state = identify::result::OMR_QUESTION_STATE::OMR_QUESTION_STATE_NORMAL;
  2983. }
  2984. else{
  2985. zhutuanti_result_value[score_count].question_score = 0;
  2986. zhutuanti_result_value[score_count].question_state = identify::result::OMR_QUESTION_STATE::OMR_QUESTION_STATE_WUFASHIBIE;
  2987. }
  2988. zhutuanti_result_value[score_count].obj_id = static_cast<unsigned int>(qs.question_index);
  2989. zhutuanti_result_value[score_count].maxscore = qs.scoreBox.maxsorce;
  2990. zhutuanti_result_value[score_count].question_code = qs.question_code;
  2991. zhutuanti_result_value[score_count].omr_question_type = identify::OMR_QUESTION_TYPE::ZHUTUANTI;
  2992. zhutuanti_result_value[score_count].total = 0;
  2993. zhutuanti_result_value[score_count].required = 0;
  2994. zhutuanti_result_value[score_count].xuanze = -1;
  2995. if (qs.scoreBox.bXuanZuoTi){
  2996. zhutuanti_result_value[score_count].omr_question_type = identify::OMR_QUESTION_TYPE::XUANZUOTI;
  2997. zhutuanti_result_value[score_count].total = qs.countNum;
  2998. zhutuanti_result_value[score_count].required = qs.countItemSelete;
  2999. zhutuanti_result_value[score_count].xuanze = ReadXuanZuoTi(schemaPage, qs, dst);
  3000. zhutuanti_result_value[score_count].question_code_all = qs.question_code_all;
  3001. zhutuanti_result_value[score_count].question_code_temp = qs.question_code_temp;
  3002. }
  3003. else if (qs.question_code_temp != "")
  3004. {
  3005. zhutuanti_result_value[score_count].omr_question_type = identify::OMR_QUESTION_TYPE::XUANZUOTI;
  3006. zhutuanti_result_value[score_count].total = qs.countNum;
  3007. zhutuanti_result_value[score_count].required = qs.countItemSelete;
  3008. zhutuanti_result_value[score_count].xuanze = -2;
  3009. zhutuanti_result_value[score_count].question_code_all = qs.question_code_all;
  3010. zhutuanti_result_value[score_count].question_code_temp = qs.question_code_temp;
  3011. }
  3012. score_count++;
  3013. }
  3014. else
  3015. {
  3016. if (lx >= 0 && rx >= 0){
  3017. if (qs.count > 1){
  3018. float score = 0;
  3019. int red_area_index = -1;
  3020. // 在线答题卡-填空题
  3021. if (qs.scoreBox.type == 3){
  3022. float h = (point[2]->y - point[0]->y)*pscale;
  3023. CvRect detect_rect = cvRect(qs.scoreBox.pos.x, qs.scoreBox.pos.y, qs.scoreBox.pos.w, qs.scoreBox.pos.h);
  3024. GetZhuGuanTiRedCount(dst, detect_rect, detect_rect, redcount, red_in_count, qs.count, 0, true);
  3025. int red_area = 0;
  3026. for (int m = 0; m<qs.scoreBox.vctScore.size(); m++){
  3027. if (red_in_count[m]>red_area&&red_in_count[m]>25){ red_area = red_in_count[m]; red_area_index = m; }
  3028. }
  3029. if (red_area_index >= 0 && static_cast<int>(qs.scoreBox.vctScore.size())>red_area_index){
  3030. score = qs.scoreBox.vctScore.at(qs.count - red_area_index - 1);
  3031. }
  3032. }
  3033. if (qs.scoreBox.maxsorce > 0)
  3034. {
  3035. tiankongti_result_value[tiankong_count].question_score = qs.scoreBox.maxsorce > score ? score : qs.scoreBox.maxsorce;
  3036. }
  3037. else
  3038. {
  3039. tiankongti_result_value[tiankong_count].question_score = score;
  3040. }
  3041. tiankongti_result_value[tiankong_count].maxscore = qs.scoreBox.maxsorce;
  3042. tiankongti_result_value[tiankong_count].omr_question_type = identify::OMR_QUESTION_TYPE::TIANKONGTI2;
  3043. }else{
  3044. float w = (rx - lx) / qs.count;
  3045. float h = (point[2]->y - point[0]->y)*pscale;
  3046. for (int j = 0; j<1; j++){
  3047. CvRect rect_detect = cvRect(qs.scoreBox.pos.x, qs.scoreBox.pos.y, qs.scoreBox.pos.w, qs.scoreBox.pos.h);
  3048. int red_area = GetTianKongTiRedCount(dst, rect_detect, rect_detect, 1, 0);
  3049. if (red_area>25){
  3050. tiankongti_result_value[tiankong_count].isright = TRUE;
  3051. break;
  3052. }
  3053. else{
  3054. tiankongti_result_value[tiankong_count].isright = FALSE;
  3055. }
  3056. }
  3057. if (qs.scoreBox.maxsorce > 0)
  3058. {
  3059. tiankongti_result_value[tiankong_count].question_score = tiankongti_result_value[tiankong_count].isright ? qs.scoreBox.maxsorce : 0;
  3060. }
  3061. tiankongti_result_value[tiankong_count].maxscore = qs.scoreBox.maxsorce;
  3062. tiankongti_result_value[tiankong_count].omr_question_type = identify::OMR_QUESTION_TYPE::TIANKONGTI;
  3063. }
  3064. }else{
  3065. tiankongti_result_value[tiankong_count].maxscore = qs.scoreBox.maxsorce;
  3066. tiankongti_result_value[tiankong_count].isright = FALSE;
  3067. if (qs.scoreBox.maxsorce > 0)
  3068. {
  3069. tiankongti_result_value[tiankong_count].question_score = 0;
  3070. }
  3071. if (qs.count > 1)
  3072. {
  3073. tiankongti_result_value[tiankong_count].omr_question_type = identify::OMR_QUESTION_TYPE::TIANKONGTI2;
  3074. }
  3075. else
  3076. {
  3077. tiankongti_result_value[tiankong_count].omr_question_type = identify::OMR_QUESTION_TYPE::TIANKONGTI;
  3078. }
  3079. }
  3080. tiankongti_result_value[tiankong_count].obj_id = static_cast<unsigned int>(qs.question_index);
  3081. tiankongti_result_value[tiankong_count].question_code = qs.question_code;
  3082. tiankong_count++;
  3083. }
  3084. }
  3085. return identify::result::IDF_SUCCESS;
  3086. }
  3087. //截取图像
  3088. int COnLineCardPageIdentifier::ClipImg(SchemaPage& schemaPage, IplImage * dst)
  3089. {
  3090. cvResetImageROI(dst);
  3091. vector<int> compression_params;
  3092. compression_params.push_back(CV_IMWRITE_JPEG_QUALITY);
  3093. compression_params.push_back(60);
  3094. omr_result->cut_area_result.resize(schemaPage.cutAreaCount);
  3095. identify::result::CUT_AREA_RESULT* cut_area_result_value = omr_result->cut_area_result.data();
  3096. for (int i = 0; i<schemaPage.cutAreaCount; i++)
  3097. {
  3098. SchemaCutArea& cutArea = schemaPage.cutAreas[i];
  3099. CvRect rect = cvRect(cutArea.centerx - cutArea.width / 2, cutArea.centery - cutArea.height / 2, cutArea.width, cutArea.height);
  3100. SaveRect(rect, cut_area_result_value[i]);
  3101. IplImage* img = CutImage(dst, rect);
  3102. vector<uchar> dst;
  3103. cv::Mat _img = cvarrToMat(img);
  3104. imencode(".jpg", _img, dst, compression_params);
  3105. cut_area_result_value[i].obj_id = cutArea.index;
  3106. cut_area_result_value[i].area_index = cutArea.index;
  3107. cut_area_result_value[i].area_sub_index = cutArea.isfirst?1:0;
  3108. cut_area_result_value[i].centerx = cutArea.centerx;
  3109. cut_area_result_value[i].centery = cutArea.centery;
  3110. cut_area_result_value[i].width = cutArea.width;
  3111. cut_area_result_value[i].height = cutArea.height;
  3112. cut_area_result_value[i].area_name = cutArea.area_name;
  3113. cut_area_result_value[i].is_cut = cutArea.iscut;
  3114. cut_area_result_value[i].img_data.resize(dst.size());
  3115. memcpy(cut_area_result_value[i].img_data.data(), &dst[0], dst.size());
  3116. cvReleaseImage(&img);
  3117. }
  3118. return identify::result::IDF_SUCCESS;
  3119. }
  3120. //获取图像指定区域红色点数(主观题) 去除长直线和周边像素 减少直线边缘偏色影响
  3121. int COnLineCardPageIdentifier::GetZhuGuanTiRedCount(IplImage * dst, const CvRect& rect, const CvRect& normal_rect, int * red_counts, int * red_in_counts, int count, int mutil, bool bTianKongTi /*= false*/)
  3122. {
  3123. if (dst->nChannels != 3){ return 0; }
  3124. if (m_nXianXiaDaFenType == 1)
  3125. {
  3126. memset(red_counts, 0, sizeof(int)*count);
  3127. memset(red_in_counts, 0, sizeof(int)*count);
  3128. cvSetImageROI(dst, rect);
  3129. Mat img= cv::cvarrToMat(dst);
  3130. m_pTemplate->open_save_debug_img = 1;
  3131. if (m_pTemplate->open_save_debug_img)
  3132. cv::imwrite("D:\\image\\Debug\\GetQrCode.PNG", img);
  3133. cvResetImageROI(dst);
  3134. int tempi[50] = { 0 };
  3135. try{
  3136. EnterCriticalSection(m_althom_rcs);
  3137. // printf("线程:%s 新算法判分-开始\n", std::to_string(GetCurrentThreadId()).c_str());
  3138. if (api_red_check_analysis(img, count, bTianKongTi ? 3 : 2, mutil, tempi, count) != 0)
  3139. {
  3140. // printf("线程:%s 新算法判分-错误\n", std::to_string(GetCurrentThreadId()).c_str());
  3141. LeaveCriticalSection(m_althom_rcs);
  3142. return 0;
  3143. }
  3144. // printf("线程:%s 新算法判分-结束\n", std::to_string(GetCurrentThreadId()).c_str());
  3145. LeaveCriticalSection(m_althom_rcs);
  3146. }
  3147. catch (cv::Exception&e){
  3148. // printf("线程:%s 新算法判分-异常\n", std::to_string(GetCurrentThreadId()).c_str());
  3149. const char *error = e.what();
  3150. LeaveCriticalSection(m_althom_rcs);
  3151. }
  3152. for (int i = 0; i < count; i++)
  3153. {
  3154. red_in_counts[i] = tempi[count - i - 1];
  3155. if (red_in_counts[i] > 0)
  3156. {
  3157. red_counts[i] = 30; //目的大于25
  3158. }
  3159. }
  3160. return identify::result::IDF_SUCCESS;
  3161. }
  3162. IplImage* red = NULL;
  3163. BOOL s = GetRedBinary(dst, rect, &red);
  3164. if (!s)return 0;
  3165. IplImage * temp = cvCloneImage(red);
  3166. CvMemStorage* storage = cvCreateMemStorage();
  3167. CvSeq* contours = NULL;
  3168. cvFindContours(temp, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));
  3169. int top = normal_rect.y - rect.y;
  3170. int bottom = normal_rect.y + normal_rect.height - 1 - rect.y;
  3171. for (CvContour * c = (CvContour *)contours; c != NULL; c = (CvContour *)c->h_next) {
  3172. CvRect b_rect = cvBoundingRect(c);
  3173. if (b_rect.y>bottom - 1 || b_rect.y + b_rect.height - 1<top + 1){
  3174. cvDrawContours(red, (CvSeq *)c, cvScalar(0), cvScalar(0), 0, CV_FILLED, 8);
  3175. }
  3176. }
  3177. cvReleaseImage(&temp);
  3178. cvReleaseMemStorage(&storage);
  3179. memset(red_counts, 0, sizeof(int)*count);
  3180. memset(red_in_counts, 0, sizeof(int)*count);
  3181. int w = red->width;
  3182. int h = red->height;
  3183. for (int x = 0; x<w; x++){
  3184. int option_index = (count - 1) - x*count / w;
  3185. for (int y = 0; y<h; y++){
  3186. if (CV_IMAGE_ELEM(red, unsigned char, y, x)){
  3187. red_counts[option_index]++;
  3188. if (y <= bottom - 1 && y >= top + 1){
  3189. red_in_counts[option_index]++;
  3190. }
  3191. }
  3192. }
  3193. }
  3194. cvReleaseImage(&red);
  3195. return identify::result::IDF_SUCCESS;
  3196. }
  3197. //获取图像指定区域红色点数(填空题)
  3198. int COnLineCardPageIdentifier::GetTianKongTiRedCount(IplImage * dst, const CvRect& rect_normal, const CvRect& rect_detect,int count,int multi)
  3199. {
  3200. CvRect rect = cvGetImageROI(dst);
  3201. if (dst->nChannels != 3){ return 0; }
  3202. if (m_nXianXiaDaFenType == 1){
  3203. cvSetImageROI(dst, rect_detect);
  3204. Mat img = cv::cvarrToMat(dst);
  3205. cvResetImageROI(dst);
  3206. int tempi[50] = { 0 };
  3207. try{
  3208. EnterCriticalSection(m_althom_rcs);
  3209. // printf("线程:%s 新算法判分-开始\n", std::to_string(GetCurrentThreadId()).c_str());
  3210. int fff = api_red_check_analysis(img, count, 3, multi, tempi, count);
  3211. if (fff != 0){
  3212. // printf("线程:%s 新算法判分-错误\n", std::to_string(GetCurrentThreadId()).c_str());
  3213. LeaveCriticalSection(m_althom_rcs);
  3214. return 0;
  3215. }
  3216. // printf("线程:%s 新算法判分-结束\n", std::to_string(GetCurrentThreadId()).c_str());
  3217. LeaveCriticalSection(m_althom_rcs);
  3218. }
  3219. catch (cv::Exception&e){
  3220. // printf("线程:%s 新算法判分-异常\n", std::to_string(GetCurrentThreadId()).c_str());
  3221. LeaveCriticalSection(m_althom_rcs);
  3222. #if 0
  3223. static const char* str;
  3224. str = e.what();
  3225. #endif
  3226. }
  3227. if (tempi[0] > 0)
  3228. return 300;
  3229. return 0;
  3230. }
  3231. int w = rect_detect.width;
  3232. int h = rect_detect.height;
  3233. IplImage * red = NULL;
  3234. BOOL r = GetRedBinary(dst, rect_detect, &red);
  3235. if (!r)return 0;
  3236. vector<CvPoint2D32f> redPoints;
  3237. for (int y = 0; y<red->height; y++)
  3238. {
  3239. unsigned char * red_row_first = (unsigned char *)(red->imageData + (y*red->widthStep));
  3240. for (int x = 0; x<red->width; x++)
  3241. {
  3242. if (red_row_first[x]){
  3243. redPoints.push_back(cvPoint2D32f(x, y));
  3244. }
  3245. }
  3246. }
  3247. if (redPoints.size()==0)
  3248. {
  3249. return 0;
  3250. }
  3251. CvMemStorage * storage = cvCreateMemStorage();
  3252. CvSeq* contour = NULL;
  3253. int contours = cvFindContours(red, storage, &contour, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
  3254. int red_count = 0;
  3255. int normal_left = rect_normal.x - rect_detect.x;
  3256. int normal_top = rect_normal.y - rect_detect.y;
  3257. int normal_right = normal_left + rect_normal.width;
  3258. int normal_bottom = normal_top + rect_normal.height;
  3259. for (CvContour * c = (CvContour *)contour; c != NULL; c = (CvContour *)c->h_next){
  3260. float sum_x = 0;
  3261. float sum_y = 0;
  3262. int in_count = 0;
  3263. int area_c = 0;//轮廓在评分框内的点数
  3264. for (std::size_t i = 0; i<redPoints.size(); i++)
  3265. {
  3266. double distance = cvPointPolygonTest(c, redPoints[i], FALSE);
  3267. if (distance >= 0){
  3268. sum_x += redPoints[i].x;
  3269. sum_y += redPoints[i].y;
  3270. in_count++;
  3271. if (normal_left <= redPoints[i].x&&redPoints[i].x <= normal_right&&normal_top <= redPoints[i].y&&redPoints[i].y <= normal_bottom){
  3272. area_c++;
  3273. }
  3274. }
  3275. }
  3276. if (in_count == 0)continue;
  3277. if (area_c>60){
  3278. red_count += in_count;
  3279. continue;
  3280. }
  3281. //重心位置
  3282. float avg_x = sum_x / in_count;
  3283. float avg_y = sum_y / in_count;
  3284. if (normal_left <= avg_x&&avg_x <= normal_right + 30 && normal_top <= avg_y&&avg_y <= normal_bottom){
  3285. red_count += in_count;
  3286. }
  3287. }
  3288. cvReleaseImage(&red);
  3289. cvReleaseMemStorage(&storage);
  3290. return red_count;
  3291. }
  3292. int COnLineCardPageIdentifier::SaveLocateInfo(SchemaPage& schemaPage, vector<CvPoint2D32f> &relationKey, vector<CvPoint2D32f> &relationValue)
  3293. {
  3294. if (relationKey.size() >= 2)
  3295. {
  3296. m_relationKey = relationKey;
  3297. m_relationValue = relationValue;
  3298. double distance = 0;
  3299. int index1;
  3300. int index2;
  3301. for (std::size_t i = 0; i<m_relationKey.size(); i++)
  3302. {
  3303. for (std::size_t j = i + 1; j<m_relationKey.size(); j++)
  3304. {
  3305. double d = sqrt((m_relationKey[i].x - m_relationKey[j].x)*(m_relationKey[i].x - m_relationKey[j].x) + (m_relationKey[j].y - m_relationKey[i].y)*(m_relationKey[j].y - m_relationKey[i].y));
  3306. if (d>distance){ distance = d; index1 = i; index2 = j; }
  3307. }
  3308. }
  3309. //模板上的定位点
  3310. CvPoint2D32f *p11 = &m_relationKey[index1];
  3311. CvPoint2D32f *p12 = &m_relationKey[index2];
  3312. //试卷上的定位点
  3313. CvPoint2D32f*p21 = &m_relationValue[index1];
  3314. CvPoint2D32f*p22 = &m_relationValue[index2];
  3315. CvMat* m_m = caculate_scm(*p21, *p22, *p11, *p12);
  3316. memcpy(data, m_m->data.fl, sizeof(float) * 6);
  3317. cvReleaseMat(&m_m);
  3318. return identify::result::IDF_SUCCESS;
  3319. }
  3320. return identify::result::IDF_NOTFOUND_ENOUGH_LOCATEPOINT;
  3321. }
  3322. int COnLineCardPageIdentifier::GetRect(const int centerx, const int centery, const int width, const int height, RESULT_RECT& result_rect)
  3323. {
  3324. result_rect.x = centerx - width / 2;
  3325. result_rect.y = centery - height / 2;
  3326. result_rect.width = width;
  3327. result_rect.height = height;
  3328. return identify::result::IDF_FAILURE;
  3329. }
  3330. int COnLineCardPageIdentifier::GetRect2(const int centerx, const int centery, const int width, const int height, RESULT_RECT& result_rect)
  3331. {
  3332. double x = data[0] * centerx + data[1] * centery + data[2];
  3333. double y = data[3] * centerx + data[4] * centery + data[5];
  3334. result_rect.x = (int)(x - width / 2 + 0.5);
  3335. result_rect.y = (int)(y - height / 2 + 0.5);
  3336. result_rect.width = width;
  3337. result_rect.height = height;
  3338. return identify::result::IDF_SUCCESS;
  3339. }
  3340. template<class T>
  3341. void COnLineCardPageIdentifier::SaveRect(CvRect& rect, T& result)
  3342. {
  3343. result.centerx = static_cast<decltype(result.centerx)>(rect.x + rect.width / 2.0);
  3344. result.centery = static_cast<decltype(result.centery)>(rect.y + rect.height / 2.0);
  3345. result.width = static_cast<decltype(result.width)>(rect.width);
  3346. result.height = static_cast<decltype(result.height)>(rect.height);
  3347. result.angle = 0;
  3348. }
  3349. template<class T >
  3350. void COnLineCardPageIdentifier::DrawObjRect(IplImage * dst, T& obj_result, CvScalar& scalar)
  3351. {
  3352. cvDrawRect(dst,
  3353. cvPoint(obj_result.centerx - obj_result.width / 2, obj_result.centery - obj_result.height / 2),
  3354. cvPoint(obj_result.centerx + obj_result.width / 2, obj_result.centery + obj_result.height / 2),
  3355. scalar);
  3356. }
  3357. //查找题目定位点
  3358. BOOL COnLineCardPageIdentifier::FindQuestionLocatePoint(IplImage * dst, SchemaLocatePoint& lp1, CvPoint2D32f& point1)
  3359. {
  3360. int top = max(0, (int)(lp1.centery - lp1.height*1.5 + 0.5));
  3361. int bottom = min(dst->height - 1, (int)(lp1.centery + lp1.height*1.5 + 0.5));
  3362. int left = max(0, (int)(lp1.centerx - lp1.width*1.5 + 0.5));
  3363. int right = min(dst->width - 1, (int)(lp1.centerx + lp1.width*1.5 + 0.5));
  3364. int x = left;
  3365. int y = top;
  3366. int width = right - left + 1;
  3367. int height = bottom - top + 1;
  3368. if (width<1 || height<1)return FALSE;
  3369. IplImage* dst_gray_img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
  3370. IplImage* dst_binary_img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
  3371. CvMemStorage* storage = cvCreateMemStorage();
  3372. cvSetImageROI(dst, cvRect(x, y, width, height));
  3373. if (dst->nChannels == 3)cvCvtColor(dst, dst_gray_img, CV_BGR2GRAY);
  3374. else cvCopy(dst, dst_gray_img);
  3375. cvThreshold(dst_gray_img, dst_binary_img, 180, 255, CV_THRESH_BINARY);
  3376. int an = 2;
  3377. IplConvKernel * element = cvCreateStructuringElementEx(an * 2 + 1, an * 2 + 1, an, an, CV_SHAPE_RECT, 0);//创建结构元素
  3378. cvDilate(dst_binary_img, dst_binary_img, element, 1);//膨胀图像
  3379. cvErode(dst_binary_img, dst_binary_img, element, 1);//腐蚀图像
  3380. cvReleaseStructuringElement(&element);
  3381. CvSeq * pcontours = NULL;
  3382. int ncontours = cvFindContours(dst_binary_img, storage, &pcontours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
  3383. vector<CvContour*> quesiontLocatePoints;
  3384. for (CvContour * c = (CvContour *)pcontours; c != NULL; c = (CvContour *)c->h_next)
  3385. {
  3386. if (c->rect.width<lp1.width - 3)continue;
  3387. if (c->rect.height<lp1.height - 3)continue;
  3388. if (c->rect.width>lp1.width + 3)continue;
  3389. if (c->rect.height>lp1.height + 3)continue;
  3390. cvSetImageROI(dst_gray_img, c->rect);
  3391. int area = GetBlackArea(dst_gray_img);
  3392. if (area<c->rect.width*c->rect.height*0.75)continue;
  3393. quesiontLocatePoints.push_back(c);
  3394. }
  3395. double distance = 99999;
  3396. int min_index = -1;
  3397. for (std::size_t i = 0; i<quesiontLocatePoints.size(); i++)
  3398. {
  3399. double d = GetDistance(cvPoint2D32f(lp1.centerx - x, lp1.centery - y), cvPoint2D32f(quesiontLocatePoints[i]->rect.x + quesiontLocatePoints[i]->rect.width / 2.0, quesiontLocatePoints[i]->rect.y + quesiontLocatePoints[i]->rect.height / 2.0));
  3400. if (d<distance){ distance = d; min_index = i; }
  3401. }
  3402. if (min_index >= 0){
  3403. point1 = cvPoint2D32f(quesiontLocatePoints[min_index]->rect.x + quesiontLocatePoints[min_index]->rect.width / 2.0 + x, quesiontLocatePoints[min_index]->rect.y + quesiontLocatePoints[min_index]->rect.height / 2.0 + y);
  3404. cvReleaseImage(&dst_gray_img);
  3405. cvReleaseImage(&dst_binary_img);
  3406. cvReleaseMemStorage(&storage);
  3407. return TRUE;
  3408. }
  3409. cvReleaseImage(&dst_gray_img);
  3410. cvReleaseImage(&dst_binary_img);
  3411. cvReleaseMemStorage(&storage);
  3412. return FALSE;
  3413. }
  3414. double COnLineCardPageIdentifier::GetDistance(CvPoint2D32f point1, CvPoint2D32f point2)
  3415. {
  3416. double dx = point1.x - point2.x;
  3417. double dy = point1.y - point2.y;
  3418. return sqrt(dx*dx + dy*dy);
  3419. }
  3420. int COnLineCardPageIdentifier::FindQuestionLocatePoints(SchemaPage& schemaPage, IplImage * dst)
  3421. {
  3422. std::vector<SchemaLocatePoint>& questionLocatePoints = schemaPage.questionLocatePoints;
  3423. m_question_locate_points.resize(questionLocatePoints.size());
  3424. m_question_locate_point_found.resize(questionLocatePoints.size());
  3425. for (std::size_t i = 0; i<questionLocatePoints.size(); i++)
  3426. {
  3427. m_question_locate_point_found[i] = FindQuestionLocatePoint(dst, questionLocatePoints[i], m_question_locate_points[i]);
  3428. }
  3429. return identify::result::IDF_SUCCESS;
  3430. }
  3431. BOOL COnLineCardPageIdentifier::GetRedBinary(IplImage * dst, const CvRect& rect_detect, IplImage ** red_binary)
  3432. {
  3433. #define GETREDBINARY_DEBUG 0
  3434. #define rename_image(img,new_name) IplImage * new_name = img; img = NULL;
  3435. if (dst == NULL || dst->nChannels != 3)return FALSE;
  3436. int w = rect_detect.width;
  3437. int h = rect_detect.height;
  3438. IplImage * hsv = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 3);
  3439. //存储灰度图像和二值化图像
  3440. IplImage * gray = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  3441. IplImage * black = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  3442. IplImage * black_dilate = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  3443. //记录较浅一些的红色
  3444. IplImage * red_low = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  3445. //记录较深一些的红色
  3446. IplImage * red_high = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  3447. IplImage * red = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  3448. cvSetImageROI(dst, rect_detect);
  3449. #if GETREDBINARY_DEBUG
  3450. cvSaveImage("D:\\dst.png", dst);
  3451. #endif
  3452. cvCvtColor(dst, hsv, CV_BGR2HSV);
  3453. cvCvtColor(dst, gray, CV_BGR2GRAY);
  3454. cvAdaptiveThreshold(gray, gray, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, 9, 10.0);
  3455. #ifdef _DEBUG
  3456. cvSaveImage("D:\\red_high.png", gray);
  3457. #endif
  3458. rename_image(gray, binary);
  3459. #define HSV_VALUE_H hsv_row_first[hsv_offset]
  3460. #define HSV_VALUE_S hsv_row_first[hsv_offset+1]
  3461. #define HSV_VALUE_V hsv_row_first[hsv_offset+2]
  3462. const int hsv_v_valve = 255 * 50 / 100;
  3463. const int hsv_h_max_valve = 180 * 20 / 360;
  3464. const int hsv_h_min_valve = 180 * 315 / 360;
  3465. const int hsv_s_valve_1 = 255 * 23 / 100;
  3466. const int hsv_s_valve_2 = 255 * 33 / 100;
  3467. const int hsv_valve_high = 255 * (23 + 70) / 100;
  3468. const int hsv_valve_low = 255 * (13 + 60) / 100;
  3469. for (int y = 0; y<h; y++)
  3470. {
  3471. unsigned char * hsv_row_first = (unsigned char *)(hsv->imageData + y*hsv->widthStep);
  3472. unsigned char * red1_row_first = (unsigned char *)(red_low->imageData + y*red_low->widthStep);
  3473. unsigned char * red2_row_first = (unsigned char *)(red_high->imageData + y*red_high->widthStep);
  3474. unsigned char * binary_row_first = (unsigned char *)(binary->imageData + y*binary->widthStep);
  3475. unsigned char * black_row_first = (unsigned char *)(black->imageData + y*black->widthStep);
  3476. for (int x = 0, hsv_offset = 0; x<w; x++, hsv_offset += 3)
  3477. {
  3478. if (HSV_VALUE_V >= hsv_v_valve && (HSV_VALUE_H >= hsv_h_min_valve || HSV_VALUE_H <= hsv_h_max_valve)){
  3479. red1_row_first[x] = (HSV_VALUE_S >= hsv_s_valve_1 && (HSV_VALUE_S + HSV_VALUE_V) >= hsv_valve_low) ? 255 : 0;
  3480. red2_row_first[x] = (HSV_VALUE_S >= hsv_s_valve_2 && (HSV_VALUE_S + HSV_VALUE_V) >= hsv_valve_high) ? HSV_VALUE_S : 0;
  3481. }
  3482. else{
  3483. red1_row_first[x] = red2_row_first[x] = 0;
  3484. }
  3485. black_row_first[x] = (binary_row_first[x] && !red1_row_first[x]) ? 255 : 0;
  3486. }
  3487. }
  3488. #if GETREDBINARY_DEBUG
  3489. cvSaveImage("D:\\hsv.png", hsv);
  3490. cvSaveImage("D:\\red_low.png", red_low);
  3491. cvSaveImage("D:\\red_high.png", red_high);
  3492. cvSaveImage("D:\\binary.png", binary);
  3493. cvSaveImage("D:\\black.png", black);
  3494. #endif
  3495. cvThreshold(red_high, red_high, 0, 255, CV_THRESH_OTSU);
  3496. #ifdef _DEBUG
  3497. cvSaveImage("D:\\red_high.png", red_high);
  3498. #endif
  3499. int an = 1;
  3500. IplConvKernel * element = cvCreateStructuringElementEx(an * 2 + 1, an * 2 + 1, an, an, CV_SHAPE_RECT, 0);//创建结构元素
  3501. cvDilate(binary, binary, element, 2);//膨胀图像
  3502. cvDilate(red_high, red_high, element, 2);//膨胀图像
  3503. cvDilate(black, black_dilate, element, 2);//膨胀图像
  3504. for (int y = 0; y<h; y++)
  3505. {
  3506. unsigned char * binary_row_first = (unsigned char *)(binary->imageData + y*binary->widthStep);
  3507. unsigned char * red1_row_first = (unsigned char *)(red_low->imageData + y*red_low->widthStep);
  3508. unsigned char * red2_row_first = (unsigned char *)(red_high->imageData + y*red_high->widthStep);
  3509. unsigned char * red_row_first = (unsigned char *)(red->imageData + y*red->widthStep);
  3510. unsigned char * black_dilate_row_first = (unsigned char *)(black_dilate->imageData + y*black_dilate->widthStep);
  3511. for (int x = 0; x<w; x++)
  3512. {
  3513. red_row_first[x] = (red1_row_first[x] && !black_dilate_row_first[x] && ((!binary_row_first[x]) || red2_row_first[x])) ? 255 : 0;
  3514. }
  3515. }
  3516. rename_image(red_high, red_dilate);
  3517. cvDilate(red, red_dilate, element, 2);//膨胀图像
  3518. for (int y = 0; y<h; y++)
  3519. {
  3520. unsigned char * red1_row_first = (unsigned char *)(red_low->imageData + y*red_low->widthStep);
  3521. unsigned char * red_row_first = (unsigned char *)(red->imageData + y*red->widthStep);
  3522. unsigned char * black_row_first = (unsigned char *)(black->imageData + y*black->widthStep);
  3523. unsigned char * red_dilate_row_first = (unsigned char *)(red_dilate->imageData + y*red_dilate->widthStep);
  3524. for (int x = 0; x<w; x++)
  3525. {
  3526. red_row_first[x] = (red_row_first[x] || (red_dilate_row_first[x] && (red1_row_first[x] && !black_row_first[x]))) ? 255 : 0;
  3527. }
  3528. }
  3529. cvDilate(red, red_dilate, element, 1);//膨胀图像
  3530. for (int y = 0; y<h; y++)
  3531. {
  3532. unsigned char * red_row_first = (unsigned char *)(red->imageData + y*red->widthStep);
  3533. unsigned char * black_row_first = (unsigned char *)(black->imageData + y*black->widthStep);
  3534. unsigned char * red_dilate_row_first = (unsigned char *)(red_dilate->imageData + y*red_dilate->widthStep);
  3535. for (int x = 0; x<w; x++)
  3536. {
  3537. if (!red_row_first[x] && black_row_first[x] && red_dilate_row_first[x]){
  3538. red_row_first[x] = 255;
  3539. }
  3540. }
  3541. }
  3542. cvReleaseStructuringElement(&element);
  3543. *red_binary = red;
  3544. cvReleaseImage(&hsv);
  3545. cvReleaseImage(&binary);
  3546. cvReleaseImage(&black);
  3547. cvReleaseImage(&black_dilate);
  3548. cvReleaseImage(&red_low);
  3549. cvReleaseImage(&red_dilate);
  3550. return TRUE;
  3551. }
  3552. int COnLineCardPageIdentifier::mycaculate_global_background(SchemaItem items[], int itemCount, IplImage * dst)
  3553. {
  3554. int global_background_color_count = 0;
  3555. int global_background_color_avg = 0;
  3556. for (int i = 0; i<itemCount; i++)
  3557. {
  3558. //SchemaItem & item = schemaPage.items[i];
  3559. SchemaItem & item = items[i];
  3560. RESULT_RECT result_rect;
  3561. GetRect(item.centerx, item.centery, item.width, item.height, result_rect);
  3562. int left = int(result_rect.x);
  3563. int top = int(result_rect.y);
  3564. int right = int(result_rect.x + result_rect.width);
  3565. int bottom = int(result_rect.y + result_rect.height);
  3566. int vtop = (int)(result_rect.y - (result_rect.width + 2) / 4);
  3567. int vbottom = (int)(result_rect.y + result_rect.height + (result_rect.height + 2) / 4);
  3568. int background_color_count = 0, background_color_avg = 0;
  3569. int gray;
  3570. int(*GetGray)(const IplImage * dst, int x, int y);
  3571. if (dst->nChannels == 1)GetGray = GetGrayValue;
  3572. else GetGray = GetBGRGray;
  3573. for (int x = left; x <= right; x++)
  3574. {
  3575. gray = GetGray(dst, x, vtop);
  3576. if (gray>160){ background_color_avg += gray; background_color_count++; }
  3577. gray = GetGray(dst, x, vbottom);
  3578. if (gray>160){ background_color_avg += gray; background_color_count++; }
  3579. }
  3580. background_color_avg = background_color_count>0 ? background_color_avg / background_color_count : 0;
  3581. if (background_color_avg>100){
  3582. global_background_color_avg += background_color_avg;
  3583. global_background_color_count++;
  3584. }
  3585. }
  3586. global_background_color_avg = global_background_color_count>0 ? global_background_color_avg / global_background_color_count : 0;
  3587. return global_background_color_avg;
  3588. }
  3589. //选做题分析全局属性
  3590. int COnLineCardPageIdentifier::myanalyseOmrPanoramic(OnlineAnalyseResult& anlyseResultTemp, int count)
  3591. {
  3592. int i;
  3593. int lev;
  3594. //int count; // 所有涂点个数
  3595. int numAblePnt; // 参与分析的点的个数
  3596. double maxblk[16];
  3597. int numBlk[16];
  3598. double maxBlur[16];
  3599. int numBlur[16];
  3600. double stdBlur;
  3601. memset(numBlk, 0, sizeof(int) * 16);
  3602. memset(maxblk, 0, sizeof(double) * 16);
  3603. memset(maxBlur, 0, sizeof(double) * 16);
  3604. memset(numBlur, 0, sizeof(int) * 16);
  3605. //count = schemaPage.itemCount;
  3606. numAblePnt = 0;
  3607. double& m_avaMaxBlk = anlyseResultTemp.avgMax_heidu;
  3608. double& m_benchmarkBlk = anlyseResultTemp.benchmarkBlk;
  3609. double& m_benchmarkArea = anlyseResultTemp.benchmarkAreaBlk;
  3610. m_avaMaxBlk = 0;
  3611. for (i = 0; i<count; i++)
  3612. {
  3613. OnlineAnalyseResult::itemResult& itemr = anlyseResultTemp.itemAnalyseResult[i];
  3614. if (itemr.heidu < m_benchmarkBlk || itemr.xiangdui_value < m_benchmarkArea)
  3615. continue;
  3616. if (itemr.heidu< 4.0) continue;
  3617. m_avaMaxBlk += itemr.heidu;
  3618. numAblePnt++;
  3619. lev = int(itemr.heidu);
  3620. maxblk[lev] += itemr.heidu;
  3621. numBlk[lev] ++;
  3622. lev = int(itemr.mohudu);
  3623. maxBlur[lev] += itemr.mohudu;
  3624. numBlur[lev] ++;
  3625. //pPnt++;
  3626. }
  3627. if (numAblePnt > 0)
  3628. m_avaMaxBlk /= numAblePnt;
  3629. else
  3630. m_avaMaxBlk = 12;
  3631. int stdId = 2;
  3632. int numStd = numBlk[0] + numBlk[1] + numBlk[2];
  3633. int numCur = numStd;
  3634. for (i = 3; i<16; i++)
  3635. {
  3636. numCur += (numBlk[i] - numBlk[i - 3]);
  3637. if (numCur > numStd)
  3638. {
  3639. numStd = numCur;
  3640. stdId = i;
  3641. }
  3642. }
  3643. double stdBlk = maxblk[stdId - 2] + maxblk[stdId - 1] + maxblk[stdId];
  3644. if (numStd > 0)
  3645. stdBlk /= numStd;
  3646. else
  3647. stdBlk = 12;
  3648. // 计算模糊度代表值
  3649. stdId = 2;
  3650. numStd = numBlur[0] + numBlur[1] + numBlur[2];
  3651. numCur = numStd;
  3652. for (i = 3; i<16; i++)
  3653. {
  3654. numCur += (numBlur[i] - numBlur[i - 3]);
  3655. if (numCur > numStd)
  3656. {
  3657. numStd = numCur;
  3658. stdId = i;
  3659. }
  3660. }
  3661. stdBlur = maxBlur[stdId - 2] + maxBlur[stdId - 1] + maxBlur[stdId];
  3662. if (numStd > 0)
  3663. stdBlur /= numStd;
  3664. else
  3665. stdBlur = 0;
  3666. if (stdBlur < 2.0)
  3667. stdBlk -= stdBlur;
  3668. else
  3669. stdBlk -= 2.0;
  3670. if (stdBlk < m_avaMaxBlk)
  3671. m_avaMaxBlk = stdBlk;
  3672. //统计字符区域的黑色点数的平均值
  3673. int max_item = count;
  3674. int * char_area_black_counts = new int[1];
  3675. int cur_item_count;//当前选项的数量
  3676. for (int ii = 0; ii<max_item; ii++)
  3677. {
  3678. cur_item_count = 0;
  3679. long total_char_area_size = 0;
  3680. for (int gi = 0; gi<1; gi++)
  3681. {
  3682. if (max_item > ii){
  3683. OnlineAnalyseResult::itemResult & ir = anlyseResultTemp.itemAnalyseResult[ii];
  3684. char_area_black_counts[cur_item_count] = ir.char_area_black_count;
  3685. total_char_area_size += ir.char_area_size;
  3686. cur_item_count++;
  3687. }
  3688. }
  3689. double char_area_size_avg = total_char_area_size / (double)cur_item_count;
  3690. int temp;
  3691. for (int m = 0; m<cur_item_count; m++)
  3692. {
  3693. for (int n = m + 1; n<cur_item_count; n++)
  3694. {
  3695. if (char_area_black_counts[n]<char_area_black_counts[m]){
  3696. temp = char_area_black_counts[n];
  3697. char_area_black_counts[n] = char_area_black_counts[m];
  3698. char_area_black_counts[m] = temp;
  3699. }
  3700. }
  3701. }
  3702. anlyseResultTemp.char_area_black_avg[ii] = -1;
  3703. if (char_area_black_counts[0] * 1.4<char_area_black_counts[cur_item_count - 1] || char_area_black_counts[cur_item_count - 1] * 1.4<char_area_size_avg){
  3704. double t = char_area_black_counts[0] * 1.15;
  3705. double f = 0;
  3706. int count__ = 0;
  3707. for (int l = 0; l<cur_item_count; l++)
  3708. {
  3709. if (t<char_area_black_counts[l]){
  3710. f += char_area_black_counts[l];
  3711. count__++;
  3712. }
  3713. }
  3714. anlyseResultTemp.char_area_black_avg[ii] = f / count__;
  3715. }
  3716. }
  3717. delete[]char_area_black_counts;
  3718. return(0);
  3719. }
  3720. /************************************************************************/
  3721. /* 获取选做题选择项 */
  3722. /************************************************************************/
  3723. int COnLineCardPageIdentifier::myGenerateOmrStr(OnlineAnalyseResult& anlyseResultTemp, int count)
  3724. {
  3725. int omr_str_len = 0;
  3726. //标记是否被选中
  3727. SelectType sel[MAX_ITEM] = { SelectType::UNSELECT };
  3728. int pn = min(MAX_ITEM, count);
  3729. OnlineAnalyseResult::itemResult* results[MAX_ITEM];
  3730. for (int i = 0; i<pn; i++){
  3731. results[i] = &anlyseResultTemp.itemAnalyseResult[i];
  3732. }
  3733. int maxgraynum = -1;
  3734. int mingraynum = 0xffffff;
  3735. for (int i = 0; i<pn; i++)
  3736. {
  3737. if (results[i]->gray_num>maxgraynum)maxgraynum = results[i]->gray_num;
  3738. if (results[i]->gray_num<mingraynum)mingraynum = results[i]->gray_num;
  3739. }
  3740. int selected_num = 0;
  3741. int unselect_num = 0;
  3742. int uncertain_num = 0;
  3743. for (int i = 0; i<pn; i++)
  3744. {
  3745. //被涂面积小于10%
  3746. if (results[i]->gray_num<results[i]->cell_size*0.1){
  3747. sel[i] = UNSELECT;
  3748. unselect_num++;
  3749. continue;
  3750. }
  3751. //面积超过65%
  3752. if (results[i]->gray_num>results[i]->cell_size*0.65){
  3753. sel[i] = SELECTED;
  3754. selected_num++;
  3755. continue;
  3756. }
  3757. //填涂面积相差达%15,将面积不小于最大点填涂面积的总面积%10的涂点视为填涂
  3758. if (maxgraynum - mingraynum>results[i]->cell_size*0.15&&results[i]->gray_num>maxgraynum - results[i]->cell_size*0.1){
  3759. sel[i] = SELECTED;
  3760. selected_num++;
  3761. continue;
  3762. }
  3763. //填涂面积相差达%50,将面积不小于最大点填涂面积的总面积%30的涂点视为填涂
  3764. if (maxgraynum - mingraynum>results[i]->cell_size*0.5&&results[i]->gray_num>maxgraynum - results[i]->cell_size*0.3){
  3765. sel[i] = SELECTED;
  3766. selected_num++;
  3767. continue;
  3768. }
  3769. if (maxgraynum - results[i]->gray_num>results[i]->cell_size*0.2){
  3770. sel[i] = UNSELECT;
  3771. unselect_num++;
  3772. continue;
  3773. }
  3774. sel[i] = UNCERTAIN;
  3775. uncertain_num++;
  3776. }
  3777. identify::result::OMR_QUESTION_STATE question_state = identify::result::OMR_QUESTION_STATE::OMR_QUESTION_STATE_NORMAL;
  3778. if (selected_num>1){
  3779. int maxgrayindex = -1;
  3780. int maxgraynum = -101;
  3781. for (int i = 0; i<pn; i++){
  3782. if (sel[i] == SELECTED){
  3783. if ((results[i]->gray_num - maxgraynum > 100) || (abs(results[i]->gray_num - maxgraynum) <= 100 && results[i]->heidu > results[maxgrayindex]->heidu))
  3784. {
  3785. maxgraynum = results[i]->gray_num;
  3786. maxgrayindex = i;
  3787. }
  3788. }
  3789. }
  3790. for (int i = 0; i<pn; i++){
  3791. if (sel[i] == SELECTED&&i != maxgrayindex){
  3792. sel[i] = UNSELECT;
  3793. }
  3794. }
  3795. }
  3796. for (int i = 0; i<pn; i++){
  3797. if (sel[i] == SELECTED){
  3798. return i;
  3799. }
  3800. }
  3801. return -1;
  3802. }
  3803. //读取选做题选项数据
  3804. int COnLineCardPageIdentifier::ReadXuanZuoTi(SchemaPage& schemaPage, SchemaQuestionScore& qs, IplImage * dst)
  3805. {
  3806. if (m_nBxuanxiang == 1){
  3807. auto m_items = qs.item;
  3808. try{
  3809. IdentifyArea *ptr_area = new IdentifyArea(1);
  3810. ptr_area->type = GROUP_TYPE_IDENTIFY::XUANZUOTI;
  3811. IdentifyGroup*ptr_group = ptr_area->get_group_by_index(0);
  3812. if (ptr_group){
  3813. ptr_group->init(qs.countNum, "", false);
  3814. for (int itemindex = 0; itemindex < qs.countNum; itemindex++){
  3815. SchemaItem & item = qs.item[itemindex];
  3816. IdentifyItem*ptr_item = ptr_group->get_item_by_index(itemindex);
  3817. if (ptr_item){
  3818. SchemaItem*ptr_sc_item = &item;
  3819. auto rc = GetCVRect(*ptr_sc_item);
  3820. ptr_item->x = rc.x;
  3821. ptr_item->y = rc.y;
  3822. ptr_item->w = rc.width;
  3823. ptr_item->h = rc.height;
  3824. ptr_item->name[0] = ptr_sc_item->OutChar;
  3825. }
  3826. }
  3827. }
  3828. auto roi = cvGetImageROI(dst);
  3829. cvResetImageROI(dst);
  3830. IplImage*dst_gray_img = cvCreateImage(cvGetSize(dst), IPL_DEPTH_8U, 1);
  3831. cvCvtColor(dst, dst_gray_img, CV_BGR2GRAY);
  3832. //cv::imwrite("d:\\exception.png", cv::cvarrToMat(dst_gray_img));
  3833. int bret = api_ttpd_analysis_part(cv::cvarrToMat(dst_gray_img), ptr_area);
  3834. int n_ret = -1;
  3835. if (ptr_group&&ptr_group->select){
  3836. for (int i = 0; i < ptr_group->item_count; ++i){
  3837. int *ptr_int = ptr_group->select + i;
  3838. auto ptr_tmp = ptr_group->get_item_by_index(i);
  3839. if (*ptr_int == 1 && ptr_tmp){
  3840. n_ret = i;
  3841. break;
  3842. }
  3843. }
  3844. }
  3845. cvSetImageROI(dst, roi);
  3846. delete ptr_area; ptr_area = nullptr;
  3847. cvReleaseImage(&dst_gray_img); dst_gray_img = nullptr;
  3848. return n_ret;
  3849. }
  3850. catch (cv::Exception e){
  3851. const char * er = e.what();
  3852. return -1;
  3853. }
  3854. return -1;
  3855. }
  3856. else{
  3857. int global_background_color_avg = mycaculate_global_background(qs.item, qs.countNum, dst);
  3858. //模糊度,面积,高黑度,平均黑度
  3859. double mo_hu_du, refArea, gao_heidu, avg_gray;
  3860. //黑色像素数,涂点大小
  3861. int grayNum, cellSize;
  3862. //高黑度和
  3863. double topGraySum = 0;
  3864. //高黑度面积
  3865. double topGrayAreaSum = 0;
  3866. OnlineAnalyseResult anlyseResultTemp;
  3867. anlyseResultTemp.backGroundGray = global_background_color_avg;
  3868. anlyseResultTemp.cellAvgGray = 0;
  3869. anlyseResultTemp.cellNum = 0;
  3870. int numTopGray = 0;
  3871. int ret;
  3872. for (int i = 0; i < qs.countNum; i++)
  3873. {
  3874. SchemaItem & item = qs.item[i];
  3875. ret = caculate_cell_property(schemaPage, item, dst, cellSize, refArea, avg_gray, grayNum, mo_hu_du, gao_heidu, anlyseResultTemp.itemAnalyseResult[i].char_area_size, anlyseResultTemp.itemAnalyseResult[i].char_area_black_count, anlyseResultTemp.itemAnalyseResult[i].char_area_avg_gray);
  3876. if (ret != identify::result::IDF_SUCCESS)return ret;
  3877. topGraySum += gao_heidu;
  3878. topGrayAreaSum += refArea;
  3879. numTopGray++;
  3880. anlyseResultTemp.cellAvgGray += static_cast<int>(avg_gray);
  3881. anlyseResultTemp.cellNum++;
  3882. anlyseResultTemp.itemAnalyseResult[i].avg_gray = static_cast<int>(avg_gray);
  3883. anlyseResultTemp.itemAnalyseResult[i].cell_size = cellSize;
  3884. anlyseResultTemp.itemAnalyseResult[i].gray_num = grayNum;
  3885. anlyseResultTemp.itemAnalyseResult[i].heidu = gao_heidu;
  3886. anlyseResultTemp.itemAnalyseResult[i].mohudu = mo_hu_du;
  3887. anlyseResultTemp.itemAnalyseResult[i].xiangdui_mianji = refArea;
  3888. anlyseResultTemp.itemAnalyseResult[i].xiangdui_value = refArea;
  3889. }
  3890. anlyseResultTemp.cellAvgGray = anlyseResultTemp.cellNum > 0 ? anlyseResultTemp.cellAvgGray / anlyseResultTemp.cellNum : 256;
  3891. anlyseResultTemp.avg_heidu = topGrayAreaSum > 0 ? topGraySum / topGrayAreaSum : 6;
  3892. if (numTopGray >= int(schemaPage.itemCount / 5)){
  3893. anlyseResultTemp.float_heidu = anlyseResultTemp.avg_heidu / schemaPage.user_float_heidu;
  3894. anlyseResultTemp.benchmarkBlk = schemaPage.worthwhileBlk + anlyseResultTemp.float_heidu;
  3895. }
  3896. else{
  3897. anlyseResultTemp.benchmarkBlk = numTopGray > 0 ? min(6.0, schemaPage.worthwhileBlk + anlyseResultTemp.avg_heidu / schemaPage.user_float_heidu) : 6;
  3898. anlyseResultTemp.float_heidu = anlyseResultTemp.benchmarkBlk - schemaPage.worthwhileBlk;
  3899. }
  3900. if (numTopGray > 0){
  3901. anlyseResultTemp.avg_heidu = topGrayAreaSum / numTopGray;
  3902. anlyseResultTemp.float_heidu = anlyseResultTemp.avg_heidu / schemaPage.user_float_heidu;
  3903. anlyseResultTemp.benchmarkAreaBlk = anlyseResultTemp.float_heidu;
  3904. }
  3905. else{
  3906. anlyseResultTemp.avg_heidu = 0;
  3907. anlyseResultTemp.float_heidu = 0;
  3908. anlyseResultTemp.benchmarkAreaBlk = 0.1;
  3909. }
  3910. int r = myanalyseOmrPanoramic(anlyseResultTemp, qs.countNum);
  3911. r = myGenerateOmrStr(anlyseResultTemp, qs.countNum);
  3912. return r;
  3913. }
  3914. }
  3915. int COnLineCardPageIdentifier::GenerateOmrStr(SchemaPage& schemaPage, string &ret)
  3916. {
  3917. char omr_str[512] = { 0 };
  3918. //标记是否被选中
  3919. SelectType sel[200] = { SelectType::UNSELECT };
  3920. for (int groupIndex = 0; groupIndex<schemaPage.stuGroupsCount; groupIndex++)
  3921. {
  3922. int omr_str_len = 0;
  3923. memset(sel, UNSELECT, sizeof(sel));
  3924. SchemaGroup& group = schemaPage.stuGroups[groupIndex];
  3925. int pn = min(200, group.itemCount);
  3926. OnlineAnalyseResult::itemResult* results[200];
  3927. SchemaItem* items[200];
  3928. int maxgraynum = -1;
  3929. int mingraynum = 0xffffff;
  3930. for (int i = 0; i<pn; i++){
  3931. results[i] = &anlyseResult.itemAnalyseResult[group.itemIndex[i]];
  3932. items[i] = &m_items[group.itemIndex[i]];
  3933. if (results[i]->gray_num > maxgraynum)maxgraynum = results[i]->gray_num;
  3934. if (results[i]->gray_num < mingraynum)mingraynum = results[i]->gray_num;
  3935. }
  3936. int selected_num = 0;
  3937. int unselect_num = 0;
  3938. int uncertain_num = 0;
  3939. for (int i = 0; i<pn; i++)
  3940. {
  3941. //面积超过65%
  3942. if (results[i]->gray_num>results[i]->cell_size*0.65){
  3943. sel[i] = SELECTED;
  3944. selected_num++;
  3945. continue;
  3946. }
  3947. //填涂面积相差达%20,将面积不小于最大点填涂面积的总面积%10的涂点视为填涂
  3948. if (maxgraynum - mingraynum>results[i]->cell_size*0.2&&results[i]->gray_num>maxgraynum - results[i]->cell_size*0.1){
  3949. sel[i] = SELECTED;
  3950. selected_num++;
  3951. continue;
  3952. }
  3953. //填涂面积相差达%50,将面积不小于最大点填涂面积的总面积%30的涂点视为填涂
  3954. if (maxgraynum - mingraynum>results[i]->cell_size*0.5&&results[i]->gray_num>maxgraynum - results[i]->cell_size*0.3){
  3955. sel[i] = SELECTED;
  3956. selected_num++;
  3957. continue;
  3958. }
  3959. sel[i] = UNCERTAIN;
  3960. uncertain_num++;
  3961. }
  3962. if (group.omr_out_type == OMR_OUT_TYPE::OMR_OUT_TYPE_SINGLE_NONSTRICT){
  3963. if (selected_num > 1){
  3964. int maxgrayindex = -1;
  3965. int maxgraynum = -101;
  3966. int countSel = 0;
  3967. for (int i = 0; i < pn; i++)
  3968. {
  3969. if (sel[i] == SELECTED)
  3970. {
  3971. countSel++;
  3972. if ((results[i]->gray_num - maxgraynum>100) || (abs(results[i]->gray_num - maxgraynum) <= 100 && results[i]->heidu > results[maxgrayindex]->heidu))
  3973. {
  3974. maxgraynum = results[i]->gray_num;
  3975. maxgrayindex = i;
  3976. }
  3977. }
  3978. }
  3979. for (int i = 0; i < pn; i++)
  3980. {
  3981. if (sel[i] == SELECTED&&i != maxgrayindex){
  3982. sel[i] = UNSELECT;
  3983. }
  3984. }
  3985. }
  3986. int out_count = 0;
  3987. for (int i = 0; i < pn; i++)
  3988. {
  3989. if (sel[i] == SELECTED){
  3990. if (out_count > 0)omr_str[omr_str_len++] = schemaPage.option_spacer;
  3991. omr_str[omr_str_len++] = items[i]->OutChar;
  3992. out_count++;
  3993. }
  3994. }
  3995. if (omr_str_len != 1){
  3996. return 1;
  3997. }
  3998. omr_str[omr_str_len] = '\0';
  3999. ret += omr_str;
  4000. }
  4001. }
  4002. return 0;
  4003. }
  4004. //读取缺考标记
  4005. bool COnLineCardPageIdentifier::ReadQuekaoFlag(SchemaPage &schemaPage, IplImage * dst)
  4006. {
  4007. if (schemaPage.quekaoLocatePointIndex.size() < 1)
  4008. return false;
  4009. SchemaItem item;
  4010. item.centerx = schemaPage.quekao.centerx;
  4011. item.centery = schemaPage.quekao.centery;
  4012. item.width = schemaPage.quekao.width;
  4013. item.height = schemaPage.quekao.height;
  4014. int cellSize, grayNum, char_area_size, char_area_black_count;
  4015. double refArea, avg_gray, mo_hu_du, gao_heidu, char_area_avg_gray;
  4016. if (schemaPage.itemCount == 0 && schemaPage.stuQuestionLocatePointIndex.size() < 1)
  4017. {
  4018. anlyseResult.backGroundGray = 225;
  4019. }
  4020. int ret = caculate_cell_property(schemaPage, item, dst, cellSize, refArea, avg_gray, grayNum, mo_hu_du, gao_heidu, char_area_size, char_area_black_count, char_area_avg_gray);
  4021. OnlineAnalyseResult anlyseResult;
  4022. anlyseResult.cellAvgGray += static_cast<int>(avg_gray);
  4023. anlyseResult.cellNum++;
  4024. anlyseResult.itemAnalyseResult[0].avg_gray = static_cast<int>(avg_gray);
  4025. anlyseResult.itemAnalyseResult[0].cell_size = cellSize;
  4026. anlyseResult.itemAnalyseResult[0].gray_num = grayNum;
  4027. anlyseResult.itemAnalyseResult[0].heidu = gao_heidu;
  4028. anlyseResult.itemAnalyseResult[0].mohudu = mo_hu_du;
  4029. anlyseResult.itemAnalyseResult[0].xiangdui_mianji = refArea;
  4030. anlyseResult.itemAnalyseResult[0].xiangdui_value = refArea;
  4031. //标记是否被选中
  4032. SelectType sel = UNSELECT;
  4033. OnlineAnalyseResult::itemResult* results;
  4034. results = &anlyseResult.itemAnalyseResult[0];
  4035. int maxgraynum = -1;
  4036. int mingraynum = 0xffffff;
  4037. if (results->gray_num>maxgraynum)maxgraynum = results->gray_num;
  4038. if (results->gray_num<mingraynum)mingraynum = results->gray_num;
  4039. //面积超过65%
  4040. if (results->gray_num>results->cell_size*0.65){
  4041. sel = SELECTED;
  4042. return true;
  4043. }
  4044. //填涂面积相差达%20,将面积不小于最大点填涂面积的总面积%10的涂点视为填涂
  4045. if (maxgraynum - mingraynum>results->cell_size*0.2&&results->gray_num>maxgraynum - results->cell_size*0.1){
  4046. sel = SELECTED;
  4047. return true;
  4048. }
  4049. //填涂面积相差达%50,将面积不小于最大点填涂面积的总面积%30的涂点视为填涂
  4050. if (maxgraynum - mingraynum>results->cell_size*0.5&&results->gray_num>maxgraynum - results->cell_size*0.3){
  4051. sel = SELECTED;
  4052. return true;
  4053. }
  4054. return false;
  4055. }
  4056. void COnLineCardPageIdentifier::DrawSchema(IplImage*dst_img, SchemaPage* & pageSchema)
  4057. {
  4058. // 绘制定位点
  4059. for (std::size_t i = 0; i < pageSchema->questionLocatePoints.size(); i++){
  4060. cvDrawRect(dst_img,
  4061. cvPoint(pageSchema->questionLocatePoints[i].centerx - pageSchema->questionLocatePoints[i].width / 2.0, pageSchema->questionLocatePoints[i].centery - pageSchema->questionLocatePoints[i].height / 2.0),
  4062. cvPoint(pageSchema->questionLocatePoints[i].centerx + pageSchema->questionLocatePoints[i].width / 2.0, pageSchema->questionLocatePoints[i].centery + pageSchema->questionLocatePoints[i].height / 2.0),
  4063. cvScalar(0, 255, 0));
  4064. }
  4065. // 绘制二维码 条形码
  4066. for (std::size_t i = 0; i < pageSchema->codes.size(); i++){
  4067. cvDrawRect(dst_img,
  4068. cvPoint(pageSchema->codes[i].centerx - pageSchema->codes[i].width / 2.0, pageSchema->codes[i].centery - pageSchema->codes[i].height / 2.0),
  4069. cvPoint(pageSchema->codes[i].centerx + pageSchema->codes[i].width / 2.0, pageSchema->codes[i].centery + pageSchema->codes[i].height / 2.0),
  4070. cvScalar(0, 255, 0));
  4071. }
  4072. // 绘制
  4073. for (std::size_t i = 0; i < pageSchema->items.size(); i++){
  4074. cvDrawRect(dst_img,
  4075. cvPoint(pageSchema->items[i].centerx - pageSchema->items[i].width / 2.0, pageSchema->items[i].centery - pageSchema->items[i].height / 2.0),
  4076. cvPoint(pageSchema->items[i].centerx + pageSchema->items[i].width / 2.0, pageSchema->items[i].centery + pageSchema->items[i].height / 2.0),
  4077. cvScalar(0, 255, 0));
  4078. }
  4079. // 主观题
  4080. for (std::size_t i = 0; i < pageSchema->question_score.size(); i++){
  4081. if (pageSchema->question_score[i].scoreBox.bXuanZuoTi){
  4082. for (int j = 0; j < pageSchema->question_score[i].countNum; j++){
  4083. cvDrawRect(dst_img,
  4084. cvPoint(pageSchema->question_score[i].item[j].centerx - pageSchema->question_score[i].item[j].width / 2.0, pageSchema->question_score[i].item[j].centery - pageSchema->question_score[i].item[j].height / 2.0),
  4085. cvPoint(pageSchema->question_score[i].item[j].centerx + pageSchema->question_score[i].item[j].width / 2.0, pageSchema->question_score[i].item[j].centery + pageSchema->question_score[i].item[j].height / 2.0),
  4086. cvScalar(0, 255, 0));
  4087. }
  4088. }
  4089. // 绘制打分区域
  4090. int x = pageSchema->question_score[i].scoreBox.pos.x,
  4091. y = pageSchema->question_score[i].scoreBox.pos.y,
  4092. w = pageSchema->question_score[i].scoreBox.pos.w,
  4093. h = pageSchema->question_score[i].scoreBox.pos.h;
  4094. cvDrawRect(dst_img,
  4095. cvPoint(x, y),
  4096. cvPoint(x + w, y + h),
  4097. cvScalar(0, 0,255));
  4098. }
  4099. // 剪裁区域
  4100. for (std::size_t i = 0; i < pageSchema->cutAreas.size(); ++i)
  4101. {
  4102. cvDrawRect(dst_img,
  4103. cvPoint(pageSchema->cutAreas[i].centerx - pageSchema->cutAreas[i].width / 2.0, pageSchema->cutAreas[i].centery - pageSchema->cutAreas[i].height / 2.0),
  4104. cvPoint(pageSchema->cutAreas[i].centerx + pageSchema->cutAreas[i].width / 2.0, pageSchema->cutAreas[i].centery + pageSchema->cutAreas[i].height / 2.0),
  4105. cvScalar(0, 255, 0));
  4106. }
  4107. // 学号
  4108. for (std::size_t i = 0; i < pageSchema->stuItems.size(); i++){
  4109. cvDrawRect(dst_img,
  4110. cvPoint(pageSchema->stuItems[i].centerx - pageSchema->stuItems[i].width / 2.0, pageSchema->stuItems[i].centery - pageSchema->stuItems[i].height / 2.0),
  4111. cvPoint(pageSchema->stuItems[i].centerx + pageSchema->stuItems[i].width / 2.0, pageSchema->stuItems[i].centery + pageSchema->stuItems[i].height / 2.0),
  4112. cvScalar(0, 255, 0));
  4113. }
  4114. //缺考标识
  4115. if (pageSchema->quekaoLocatePointIndex.size()>0){
  4116. cvDrawRect(dst_img,
  4117. cvPoint(pageSchema->quekao.centerx - pageSchema->quekao.width / 2.0, pageSchema->quekao.centery - pageSchema->quekao.height / 2.0),
  4118. cvPoint(pageSchema->quekao.centerx + pageSchema->quekao.width / 2.0, pageSchema->quekao.centery + pageSchema->quekao.height / 2.0),
  4119. cvScalar(0, 0, 255));
  4120. }
  4121. }
  4122. }