ImageLoc.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. //#include "stdafx.h"
  2. #include "ImageLoc.h"
  3. #include <assert.h>
  4. #include <time.h>
  5. #include <numeric>
  6. #include "helpfunc.h"
  7. #include "imageView.hpp"
  8. using std::to_wstring;
  9. int check_transparent(Image* img)
  10. {
  11. if (img->width < 2 || img->height < 2) return 0;
  12. uint c0 = *img->begin();
  13. bool x = c0 == img->at<uint>(0, img->width - 1) &&
  14. c0 == img->at<uint>(img->height - 1, 0) &&
  15. c0 == img->at<uint>(img->height - 1, img->width - 1);
  16. if (!x) return 0;
  17. int ct = 0;
  18. for (auto it : *img)
  19. if (it == c0) ++ct;
  20. int total = img->height * img->width;
  21. return total * 0.5 <= ct && ct < total ? ct : 0;
  22. }
  23. void get_match_points(const Image& img, vector<uint>& points)
  24. {
  25. points.clear();
  26. uint cbk = *img.begin();
  27. for (int i = 0; i < img.height; ++i) {
  28. for (int j = 0; j < img.width; ++j)
  29. if (cbk != img.at<uint>(i, j)) points.push_back((i << 16) | j);
  30. }
  31. }
  32. void gen_next(const Image& img, vector<int>& next)
  33. {
  34. next.resize(img.width * img.height);
  35. auto t = img.ptr<int>(0);
  36. auto p = next.data();
  37. p[0] = -1;
  38. int k = -1, j = 0;
  39. while (j < next.size() - 1) {
  40. if (k == -1 || t[k] == t[j]) {
  41. k++;
  42. j++;
  43. p[j] = k;
  44. }
  45. else {
  46. k = p[k];
  47. }
  48. }
  49. }
  50. void Connectivity(const ImageBin& bin, ImageBin& rec) {}
  51. void extractConnectivity(const ImageBin& src, int threshold, std::vector<ImageBin>& out)
  52. {
  53. ImageBin bin = src;
  54. for (auto& it : bin) {
  55. it = it > threshold ? 0xffu : 0;
  56. }
  57. ImageBin rec;
  58. rec.create(bin.width, bin.height);
  59. for (auto& it : rec) {
  60. it = 0;
  61. }
  62. }
  63. struct MatchContext {
  64. imageView view;
  65. Image* pic;
  66. ImageBin* gray;
  67. color_t dfcolor;
  68. int tnorm;
  69. vector<uint>& points;
  70. int use_ts_match;
  71. };
  72. enum PicMatchType { PicMatchRGB = 0, PicMatchGray = 1, PicMatchTrans = 2 };
  73. // template <int picMatchType>
  74. // point_t PicViewMatch(MatchContext context, double sim, bool *stop) {
  75. // return point_t();
  76. // }
  77. // template <>
  78. // point_t PicViewMatch<PicMatchGray>(MatchContext context, double sim,
  79. // bool *stop) {
  80. // // 计算最大误差
  81. // int max_err_ct =
  82. // (context.pic->height * context.pic->width - context.use_ts_match) *
  83. // (1.0 - sim);
  84. // rect_t &block = context.view._block;
  85. // for (int i = block.y1; i < block.y2; ++i) {
  86. // for (int j = block.x1; j < block.x2; ++j) {
  87. // if (*stop) return point_t(-1, -1);
  88. // // 开始匹配
  89. // // quick check
  90. // if ((double)abs(context.tnorm -
  91. // region_sum(x, y, x + timg->width, y + timg->height)) /
  92. // (double)context.tnorm >
  93. // 1.0 - sim)
  94. // return 0;
  95. // int err = 0;
  96. // int maxErr = (1.0 - sim) * tnorm;
  97. // for (int i = 0; i < context.gray->height; i++) {
  98. // auto ptr = context.view._src.ptr(y + i) + x;
  99. // auto ptr2 = timg->ptr(i);
  100. // for (int j = 0; j < timg->width; j++) {
  101. // err += abs(*ptr - *ptr2);
  102. // ptr++;
  103. // ptr2++;
  104. // }
  105. // if (err > maxErr) return 0;
  106. // }
  107. // return 1;
  108. // // simple_match<false>(j, i, pic, dfcolor,tnorm, sim));
  109. // if (match_ret) {
  110. // *stop = true;
  111. // return point_t(j + _x1 + _dx, i + _y1 + _dy);
  112. // }
  113. // } // end for j
  114. // } // end for i
  115. // return point_t(-1, -1);
  116. //}
  117. ImageBase::ImageBase() : m_threadPool(std::thread::hardware_concurrency()) {
  118. _x1 = _y1 = 0;
  119. _dx = _dy = 0;
  120. }
  121. ImageBase::~ImageBase() {}
  122. void ImageBase::set_offset(int x1, int y1) {
  123. _x1 = x1;
  124. _y1 = y1;
  125. }
  126. long ImageBase::GetPixel(long x, long y, color_t& cr) {
  127. auto p = _src.ptr<color_t>(0);
  128. // setlog("%d", _src.width);
  129. // static_assert(sizeof(color_t) == 4);
  130. cr = *p;
  131. return 1;
  132. }
  133. long ImageBase::CmpColor(color_t color, std::vector<color_df_t>& colors,
  134. double sim) {
  135. for (auto& it : colors) {
  136. if (IN_RANGE(color, it.color, it.df)) return 1;
  137. }
  138. return 0;
  139. }
  140. long ImageBase::FindColor(vector<color_df_t>& colors, int dir, long& x, long& y)
  141. {
  142. for (auto& it : colors) { //对每个颜色描述
  143. for (int i = 0; i < _src.height; ++i) {
  144. auto p = _src.ptr<color_t>(i);
  145. for (int j = 0; j < _src.width; ++j) {
  146. if (IN_RANGE(*(p + j), it.color, it.df)) {
  147. x = j + _x1 + _dx;
  148. y = i + _y1 + _dy;
  149. return 1;
  150. }
  151. p++;
  152. }
  153. }
  154. }
  155. x = y = -1;
  156. return 0;
  157. }
  158. long ImageBase::FindColorEx(vector<color_df_t>& colors, TString& retstr) {
  159. retstr.clear();
  160. int find_ct = 0;
  161. for (int i = 0; i < _src.height; ++i)
  162. {
  163. auto p = _src.ptr<color_t>(i);
  164. for (int j = 0; j < _src.width; ++j)
  165. {
  166. for (auto& it : colors)
  167. { //对每个颜色描述
  168. if (IN_RANGE(*p, it.color, it.df))
  169. {
  170. #ifdef UNICODE
  171. retstr += std::to_wstring(j + _x1 + _dx) + _T(",") + std::to_wstring(i + _y1 + _dy);
  172. #else
  173. retstr += std::to_string(j + _x1 + _dx) + _T(",") + std::to_string(i + _y1 + _dy);
  174. #endif
  175. retstr += _T("|");
  176. ++find_ct;
  177. // return 1;
  178. if (find_ct > _max_return_obj_ct) goto _quick_break;
  179. break;
  180. }
  181. }
  182. p++;
  183. }
  184. }
  185. _quick_break:
  186. if (!retstr.empty() && retstr.back() == _T('|')) retstr.pop_back();
  187. return find_ct;
  188. }
  189. long ImageBase::FindMultiColor(std::vector<color_df_t>& first_color, std::vector<pt_cr_df_t>& offset_color, double sim, long dir, long& x, long& y)
  190. {
  191. int max_err_ct = offset_color.size() * (1. - sim);
  192. int err_ct;
  193. for (int i = 0; i < _src.height; ++i)
  194. {
  195. auto p = _src.ptr<color_t>(i);
  196. for (int j = 0; j < _src.width; ++j)
  197. {
  198. // step 1. find first color
  199. for (auto& it : first_color)
  200. { //对每个颜色描述
  201. if (IN_RANGE(*p, it.color, it.df))
  202. {
  203. //匹配其他坐标
  204. err_ct = 0;
  205. for (auto& off_cr : offset_color)
  206. {
  207. int ptX = j + off_cr.x;
  208. int ptY = i + off_cr.y;
  209. if (ptX >= 0 && ptX < _src.width && ptY >= 0 && ptY < _src.height)
  210. {
  211. color_t currentColor = _src.at<color_t>(ptY, ptX);
  212. if (!CmpColor(currentColor, off_cr.crdfs, sim))
  213. ++err_ct;
  214. }
  215. else
  216. {
  217. ++err_ct;
  218. }
  219. if (err_ct > max_err_ct)
  220. goto _quick_break;
  221. }
  222. // ok
  223. x = j + _x1 + _dx, y = i + _y1 + _dy;
  224. return 1;
  225. }
  226. }
  227. _quick_break:
  228. p++;
  229. }
  230. }
  231. x = y = -1;
  232. return 0;
  233. }
  234. long ImageBase::FindMultiColorEx(std::vector<color_df_t>& first_color, std::vector<pt_cr_df_t>& offset_color, double sim, long dir, TString& retstr)
  235. {
  236. int max_err_ct = offset_color.size() * (1. - sim);
  237. int err_ct;
  238. int find_ct = 0;
  239. for (int i = 0; i < _src.height; ++i)
  240. {
  241. auto p = _src.ptr<color_t>(i);
  242. for (int j = 0; j < _src.width; ++j)
  243. {
  244. // step 1. find first color
  245. for (auto& it : first_color)
  246. { //对每个颜色描述
  247. if (IN_RANGE(*p, it.color, it.df))
  248. {
  249. //匹配其他坐标
  250. err_ct = 0;
  251. for (auto& off_cr : offset_color)
  252. {
  253. /* color_t currentColor = _src.at<color_t>(j + off_cr.x, i + off_cr.y);
  254. if (!CmpColor(currentColor, off_cr.crdfs, sim)) ++err_ct;
  255. if (err_ct > max_err_ct) goto _quick_break;*/
  256. //
  257. int ptX = j + off_cr.x;
  258. int ptY = i + off_cr.y;
  259. if (ptX >= 0 && ptX < _src.width && ptY >= 0 && ptY < _src.height)
  260. {
  261. color_t currentColor = _src.at<color_t>(ptY, ptX);
  262. if (!CmpColor(currentColor, off_cr.crdfs, sim))
  263. ++err_ct;
  264. }
  265. else {
  266. ++err_ct;
  267. }
  268. if (err_ct > max_err_ct) goto _quick_break;
  269. }
  270. // ok
  271. #ifdef UNICODE
  272. retstr += std::to_wstring(j + _x1 + _dx) + _T(",") + std::to_wstring(i + _y1 + _dy);
  273. #else
  274. retstr += std::to_string(j + _x1 + _dx) + _T(",") + std::to_string(i + _y1 + _dy);
  275. #endif
  276. retstr += _T("|");
  277. ++find_ct;
  278. if (find_ct > _max_return_obj_ct)
  279. goto _quick_return;
  280. else
  281. goto _quick_break;
  282. }
  283. }
  284. _quick_break:
  285. p++;
  286. }
  287. }
  288. _quick_return:
  289. if (!retstr.empty() && retstr.back() == _T('|')) retstr.pop_back();
  290. return find_ct;
  291. // x = y = -1;
  292. }
  293. long ImageBase::FindPic(std::vector<Image*>& pics, color_t dfcolor, double sim, long& x, long& y)
  294. {
  295. x = y = -1;
  296. vector<uint> points;
  297. int match_ret = 0;
  298. ImageBin gimg;
  299. _gray.fromImage4(_src);
  300. record_sum(_gray);
  301. int tnorm;
  302. //将小循环放在最外面,提高处理速度
  303. for (int pic_id = 0; pic_id < pics.size(); ++pic_id) {
  304. auto pic = pics[pic_id];
  305. int use_ts_match = check_transparent(pic);
  306. if (use_ts_match)
  307. get_match_points(*pic, points);
  308. else {
  309. gimg.fromImage4(*pic);
  310. tnorm = sum(gimg.begin(), gimg.end());
  311. }
  312. for (int i = 0; i < _src.height; ++i) {
  313. for (int j = 0; j < _src.width; ++j) {
  314. // step 1. 边界检查
  315. if (i + pic->height > _src.height || j + pic->width > _src.width)
  316. continue;
  317. // step 2. 计算最大误差
  318. int max_err_ct =
  319. (pic->height * pic->width - use_ts_match) * (1.0 - sim);
  320. // step 3. 开始匹配
  321. /*match_ret = (use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
  322. points, max_err_ct) : simple_match<false>(j, i, pic, dfcolor,
  323. max_err_ct));*/
  324. match_ret = (use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
  325. points, max_err_ct)
  326. : real_match(j, i, &gimg, tnorm, sim));
  327. // simple_match<false>(j, i, pic, dfcolor,tnorm, sim));
  328. if (match_ret) {
  329. x = j + _x1 + _dx;
  330. y = i + _y1 + _dy;
  331. return pic_id;
  332. }
  333. } // end for j
  334. } // end for i
  335. } // end for pics
  336. return -1;
  337. }
  338. long ImageBase::FindPicTh(std::vector<Image*>& pics, color_t dfcolor, double sim, long& x, long& y)
  339. {
  340. x = y = -1;
  341. vector<uint> points;
  342. int match_ret = 0;
  343. ImageBin gimg;
  344. _gray.fromImage4(_src);
  345. record_sum(_gray);
  346. int tnorm;
  347. std::vector<rect_t> blocks;
  348. //将小循环放在最外面,提高处理速度
  349. for (int pic_id = 0; pic_id < pics.size(); ++pic_id)
  350. {
  351. auto pic = pics[pic_id];
  352. int use_ts_match = check_transparent(pic);
  353. if (use_ts_match)
  354. get_match_points(*pic, points);
  355. else {
  356. gimg.fromImage4(*pic);
  357. tnorm = sum(gimg.begin(), gimg.end());
  358. }
  359. auto pgimg = &gimg;
  360. rect_t matchRect(0, 0, _src.width, _src.height);
  361. matchRect.shrinkRect(pic->width, pic->height);
  362. if (!matchRect.valid()) continue;
  363. matchRect.divideBlock(m_threadPool.getThreadNum(),
  364. matchRect.width() > matchRect.height(), blocks);
  365. std::vector<std::future<point_t>> results;
  366. bool stop = false;
  367. for (size_t i = 0; i < m_threadPool.getThreadNum(); ++i)
  368. {
  369. results.push_back(m_threadPool.enqueue(
  370. [this, dfcolor, points, pgimg, tnorm](rect_t& block, Image* pic,
  371. int use_ts_match, double sim,
  372. bool* stop) {
  373. // 计算最大误差
  374. int max_err_ct =
  375. (pic->height * pic->width - use_ts_match) * (1.0 - sim);
  376. for (int i = block.y1; i < block.y2; ++i) {
  377. for (int j = block.x1; j < block.x2; ++j) {
  378. if (*stop) return point_t(-1, -1);
  379. // 开始匹配
  380. int match_ret =
  381. (use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
  382. points, max_err_ct)
  383. : real_match(j, i, pgimg, tnorm, sim));
  384. // simple_match<false>(j, i, pic, dfcolor,tnorm, sim));
  385. if (match_ret) {
  386. *stop = true;
  387. return point_t(j + _x1 + _dx, i + _y1 + _dy);
  388. }
  389. } // end for j
  390. } // end for i
  391. return point_t(-1, -1);
  392. },
  393. blocks[i], pic, use_ts_match, sim, &stop));
  394. // results.push_back(r);
  395. }
  396. // wait all
  397. for (auto&& f : results) {
  398. point_t p = f.get();
  399. if (p.x != -1) {
  400. x = p.x;
  401. y = p.y;
  402. // return pic_id;
  403. }
  404. }
  405. if (x != -1) {
  406. return pic_id;
  407. }
  408. } // end for pics
  409. return -1;
  410. }
  411. long ImageBase::FindPicEx(std::vector<Image*>& pics, color_t dfcolor, double sim, vpoint_desc_t& vpd)
  412. {
  413. int obj_ct = 0;
  414. vpd.clear();
  415. vector<uint> points;
  416. bool nodfcolor = color2uint(dfcolor) == 0;
  417. int match_ret = 0;
  418. ImageBin gimg;
  419. _gray.fromImage4(_src);
  420. record_sum(_gray);
  421. int tnorm;
  422. for (int pic_id = 0; pic_id < pics.size(); ++pic_id)
  423. {
  424. auto pic = pics[pic_id];
  425. int use_ts_match = check_transparent(pic);
  426. if (use_ts_match)
  427. get_match_points(*pic, points);
  428. else {
  429. gimg.fromImage4(*pic);
  430. tnorm = sum(gimg.begin(), gimg.end());
  431. }
  432. for (int i = 0; i < _src.height; ++i)
  433. {
  434. for (int j = 0; j < _src.width; ++j) {
  435. // step 1. 边界检查
  436. if (i + pic->height > _src.height || j + pic->width > _src.width)
  437. continue;
  438. // step 2. 计算最大误差
  439. int max_err_ct =
  440. (pic->height * pic->width - use_ts_match) * (1.0 - sim);
  441. // step 3. 开始匹配
  442. match_ret = (use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
  443. points, max_err_ct)
  444. : real_match(j, i, &gimg, tnorm, sim));
  445. if (match_ret) {
  446. point_desc_t pd = { pic_id, point_t(j + _x1 + _dx, i + _y1 + _dy) };
  447. vpd.push_back(pd);
  448. ++obj_ct;
  449. if (obj_ct > _max_return_obj_ct) goto _quick_return;
  450. }
  451. } // end for j
  452. } // end for i
  453. } // end for pics
  454. _quick_return:
  455. return obj_ct;
  456. }
  457. long ImageBase::FindPicExTh(std::vector<Image*>& pics, color_t dfcolor, double sim, vpoint_desc_t& vpd)
  458. {
  459. vpd.clear();
  460. int obj_ct = 0;
  461. vector<uint> points;
  462. int match_ret = 0;
  463. ImageBin gimg;
  464. _gray.fromImage4(_src);
  465. record_sum(_gray);
  466. int tnorm;
  467. std::vector<rect_t> blocks;
  468. //将小循环放在最外面,提高处理速度
  469. for (int pic_id = 0; pic_id < pics.size(); ++pic_id) {
  470. auto pic = pics[pic_id];
  471. int use_ts_match = check_transparent(pic);
  472. if (use_ts_match)
  473. get_match_points(*pic, points);
  474. else {
  475. gimg.fromImage4(*pic);
  476. tnorm = sum(gimg.begin(), gimg.end());
  477. }
  478. auto pgimg = &gimg;
  479. rect_t matchRect(0, 0, _src.width, _src.height);
  480. matchRect.shrinkRect(pic->width, pic->height);
  481. if (!matchRect.valid()) continue;
  482. matchRect.divideBlock(m_threadPool.getThreadNum(),
  483. matchRect.width() > matchRect.height(), blocks);
  484. std::vector<std::future<vpoint_t>> results;
  485. for (size_t i = 0; i < m_threadPool.getThreadNum(); ++i)
  486. {
  487. results.push_back(m_threadPool.enqueue(
  488. [this, dfcolor, points, pgimg, tnorm](rect_t& block, Image* pic,
  489. int use_ts_match, double sim)->vpoint_t {
  490. vpoint_t vp;
  491. // 计算最大误差
  492. int max_err_ct =
  493. (pic->height * pic->width - use_ts_match) * (1.0 - sim);
  494. for (int i = block.y1; i < block.y2; ++i) {
  495. for (int j = block.x1; j < block.x2; ++j) {
  496. // 开始匹配
  497. int match_ret =
  498. (use_ts_match ? trans_match<false>(j, i, pic, dfcolor,
  499. points, max_err_ct)
  500. : real_match(j, i, pgimg, tnorm, sim));
  501. // simple_match<false>(j, i, pic, dfcolor,tnorm, sim));
  502. if (match_ret) {
  503. vp.push_back(point_t(j + _x1 + _dx, i + _y1 + _dy));
  504. }
  505. } // end for j
  506. } // end for i
  507. return vp;
  508. },
  509. blocks[i], pic, use_ts_match, sim));
  510. // results.push_back(r);
  511. }
  512. // wait all
  513. for (auto&& f : results) {
  514. vpoint_t vp = f.get();
  515. if (vp.size() > 0) {
  516. for (auto& p : vp) {
  517. point_desc_t pd = { pic_id,p };
  518. vpd.push_back(pd);
  519. ++obj_ct;
  520. if (obj_ct > _max_return_obj_ct) goto _quick_return;
  521. }
  522. // return pic_id;
  523. }
  524. }
  525. }
  526. _quick_return:
  527. return obj_ct;
  528. }
  529. long ImageBase::FindColorBlock(double sim, long count, long height, long width, long& x, long& y)
  530. {
  531. x = y = -1;
  532. record_sum(_binary);
  533. for (int i = 0; i <= _binary.height - height; ++i) {
  534. for (int j = 0; j < _binary.width - width; ++j) {
  535. if (region_sum(j, i, j + width, i + height) >= count) {
  536. x = j + _x1 + _dx;
  537. y = i + _y1 + _dy;
  538. return 1;
  539. }
  540. }
  541. }
  542. return -1;
  543. }
  544. long ImageBase::FindColorBlockEx(double sim, long count, long height, long width, TString& retstr)
  545. {
  546. record_sum(_binary);
  547. int cnt = 0;
  548. for (int i = 0; i <= _binary.height - height; ++i) {
  549. for (int j = 0; j < _binary.width - width; ++j) {
  550. if (region_sum(j, i, j + width, i + height) >= count) {
  551. TCHAR buff[20];
  552. wsprintf(buff, _T("%d,%d|"), j + _x1 + _dx, i + _y1 + _dy);
  553. retstr += buff;
  554. ++cnt;
  555. if (cnt > _max_return_obj_ct) goto _quick_return;
  556. }
  557. }
  558. }
  559. _quick_return:
  560. if (cnt) {
  561. retstr.pop_back();
  562. }
  563. return cnt;
  564. }
  565. long ImageBase::FindLine(double sim, TString& outStr)
  566. {
  567. outStr.clear();
  568. int h = sqrt(_binary.width * _binary.width + _binary.height * _binary.height) + 2;
  569. _sum.create(360, h);
  570. //行:距离,列:角度
  571. _sum.fill(0);
  572. for (int i = 0; i < _binary.height; i++)
  573. {
  574. for (int j = 0; j < _binary.width; j++) {
  575. if (_binary.at(i, j) == WORD_COLOR) {
  576. for (int t = 0; t < 360; t++) {
  577. int d =
  578. j * cos(t * 0.0174532925) + i * sin(t * 0.0174532925); //可以优化
  579. assert(d <= h);
  580. if (d >= 0) _sum.at<int>(d, t)++;
  581. }
  582. }
  583. }
  584. }
  585. int maxRow = 0, maxCol = 0;
  586. int maxval = -1;
  587. for (int i = 0; i < _sum.height; i++)
  588. {
  589. for (int j = 0; j < _sum.width; j++) {
  590. if (_sum.at<int>(i, j) > maxval) {
  591. maxRow = i;
  592. maxCol = j;
  593. maxval = _sum.at<int>(i, j);
  594. }
  595. }
  596. }
  597. // setlog("degree=%d,dis=%d,val=%d", maxCol, maxRow, maxval);
  598. TCHAR buffer[256];
  599. wsprintf(buffer, _T("%d,%d"), maxCol, maxRow);
  600. outStr = buffer;
  601. return maxval;
  602. }
  603. template <bool nodfcolor>
  604. long ImageBase::simple_match(long x, long y, Image* timg, color_t dfcolor, int tnorm, double sim)
  605. {
  606. int err_ct = 0;
  607. // quick check
  608. if ((double)abs(tnorm - region_sum(x, y, x + timg->width, y + timg->height)) >
  609. (double)tnorm * (1.0 - sim))
  610. return 0;
  611. int max_error = (1.0 - sim) * timg->size();
  612. uint* pscreen_top, * pscreen_bottom, * pimg_top, * pimg_bottom;
  613. pscreen_top = _src.ptr<uint>(y) + x;
  614. pscreen_bottom = _src.ptr<uint>(y + timg->height - 1) + x;
  615. pimg_top = timg->ptr<uint>(0);
  616. pimg_bottom = timg->ptr<uint>(timg->height - 1);
  617. while (pscreen_top <= pscreen_bottom)
  618. {
  619. auto ps1 = pscreen_top, ps2 = pscreen_top + timg->width - 1;
  620. auto ps3 = pscreen_bottom, ps4 = pscreen_bottom + timg->width - 1;
  621. auto pt1 = pimg_top, pt2 = pimg_top + timg->width - 1;
  622. auto pt3 = pimg_bottom, pt4 = pimg_bottom + timg->width - 1;
  623. while (ps1 <= ps2) {
  624. if (nodfcolor) {
  625. if (*ps1++ != *pt1++) ++err_ct; // top left
  626. if (*ps2-- != *pt2--) ++err_ct; // top right
  627. if (*ps3++ != *pt3++) ++err_ct; // bottom left
  628. if (*ps4-- != *pt4--) ++err_ct; // bottom right
  629. }
  630. else {
  631. if (!IN_RANGE(*(color_t*)ps1++, *(color_t*)pt1++, dfcolor)) ++err_ct;
  632. if (!IN_RANGE(*(color_t*)ps2--, *(color_t*)pt2--, dfcolor)) ++err_ct;
  633. if (!IN_RANGE(*(color_t*)ps3++, *(color_t*)pt3++, dfcolor)) ++err_ct;
  634. if (!IN_RANGE(*(color_t*)ps4--, *(color_t*)pt4--, dfcolor)) ++err_ct;
  635. }
  636. if (err_ct > max_error) return 0;
  637. }
  638. pscreen_top += _src.width;
  639. pscreen_bottom -= _src.width;
  640. }
  641. return 1;
  642. }
  643. template <bool nodfcolor>
  644. long ImageBase::trans_match(long x, long y, Image* timg, color_t dfcolor, vector<uint> pts, int max_error)
  645. {
  646. int err_ct = 0, k, dx, dy;
  647. int left, right;
  648. left = 0;
  649. right = pts.size() - 1;
  650. while (left <= right)
  651. {
  652. auto it = pts[left];
  653. if (nodfcolor) {
  654. if (_src.at<uint>(y + PTY(pts[left]), x + PTX(pts[left])) !=
  655. timg->at<uint>(PTY(pts[left]), PTX(pts[left])))
  656. ++err_ct;
  657. if (_src.at<uint>(y + PTY(pts[right]), x + PTX(pts[right])) !=
  658. timg->at<uint>(PTY(pts[right]), PTX(pts[right])))
  659. ++err_ct;
  660. }
  661. else {
  662. color_t cr1, cr2;
  663. cr1 = _src.at<color_t>(y + PTY(pts[left]), x + PTX(pts[left]));
  664. cr2 = timg->at<color_t>(PTY(pts[left]), PTX(pts[left]));
  665. if (!IN_RANGE(cr1, cr2, dfcolor)) ++err_ct;
  666. cr1 = _src.at<color_t>(y + PTY(pts[right]), x + PTX(pts[right]));
  667. cr2 = timg->at<color_t>(PTY(pts[right]), PTX(pts[right]));
  668. if (!IN_RANGE(cr1, cr2, dfcolor)) ++err_ct;
  669. }
  670. ++left;
  671. --right;
  672. if (err_ct > max_error) return 0;
  673. }
  674. return 1;
  675. }
  676. long ImageBase::real_match(long x, long y, ImageBin* timg, int tnorm, double sim)
  677. {
  678. // quick check
  679. if ((double)abs(tnorm - region_sum(x, y, x + timg->width, y + timg->height)) / (double)tnorm > 1.0 - sim)
  680. return 0;
  681. int err = 0;
  682. int maxErr = (1.0 - sim) * tnorm;
  683. for (int i = 0; i < timg->height; i++) {
  684. auto ptr = _gray.ptr(y + i) + x;
  685. auto ptr2 = timg->ptr(i);
  686. for (int j = 0; j < timg->width; j++) {
  687. err += abs(*ptr - *ptr2);
  688. ptr++;
  689. ptr2++;
  690. }
  691. if (err > maxErr) return 0;
  692. }
  693. return 1;
  694. }
  695. void ImageBase::record_sum(const ImageBin& gray)
  696. {
  697. //为了减少边界判断,这里多多加一行一列
  698. _sum.create(gray.width + 1, gray.height + 1);
  699. _sum.fill(0);
  700. int m = _sum.height;
  701. int n = _sum.width;
  702. for (int i = 1; i < m; i++) {
  703. for (int j = 1; j < n; j++) {
  704. int s = 0;
  705. s += _sum.at<int>(i - 1, j);
  706. s += _sum.at<int>(i, j - 1);
  707. s -= _sum.at<int>(i - 1, j - 1);
  708. s += (int)gray.at(i - 1, j - 1);
  709. _sum.at<int>(i, j) = s;
  710. }
  711. }
  712. }
  713. int ImageBase::region_sum(int x1, int y1, int x2, int y2) {
  714. int ans = _sum.at<int>(y2, x2) - _sum.at<int>(y2, x1) - _sum.at<int>(y1, x2) +
  715. _sum.at<int>(y1, x1);
  716. return ans;
  717. }
  718. constexpr int MIN_CUT_W = 5;
  719. constexpr int MIN_CUT_H = 2;
  720. int ImageBase::get_bk_color(inputbin bin) {
  721. int y[256] = { 0 };
  722. auto ptr = bin.pixels.data();
  723. int n = bin.pixels.size();
  724. for (int i = 0; i < n; ++i) y[ptr[i]]++;
  725. // scan max
  726. int m = 0;
  727. for (int i = 1; i < 256; ++i) {
  728. if (y[i] > y[m]) m = i;
  729. }
  730. return m;
  731. }
  732. void ImageBase::bgr2binary(vector<color_df_t>& colors) {
  733. if (_src.empty()) return;
  734. int ncols = _src.width, nrows = _src.height;
  735. _binary.fromImage4(_src);
  736. for (int i = 0; i < nrows; ++i) {
  737. auto psrc = _src.ptr<color_t>(i);
  738. auto pbin = _binary.ptr(i);
  739. for (int j = 0; j < ncols; ++j) {
  740. uchar g1 = psrc->toGray();
  741. *pbin = WORD_BKCOLOR;
  742. for (auto& it : colors) { //对每个颜色描述
  743. if (abs(g1 - it.color.toGray()) <= it.df.toGray()) {
  744. *pbin = WORD_COLOR;
  745. break;
  746. }
  747. }
  748. ++pbin;
  749. ++psrc;
  750. }
  751. }
  752. //_binary.create(ncols, nrows);
  753. // for (int i = 0; i < nrows; ++i) {
  754. // auto psrc = _src.ptr<color_t>(i);
  755. // auto pbin = _binary.ptr(i);
  756. // for (int j = 0; j < ncols; ++j) {
  757. // *pbin = WORD_BKCOLOR;
  758. // for (auto& it : colors) {//对每个颜色描述
  759. // if (IN_RANGE(*psrc, it.color, it.df)) {
  760. // *pbin = WORD_COLOR;
  761. // break;
  762. // }
  763. // }
  764. // ++pbin; ++psrc;
  765. // }
  766. //}
  767. // test
  768. // cv::imwrite("src.png", _src);
  769. // cv::imwrite("binary.png", _binary);
  770. }
  771. //二值化
  772. void ImageBase::bgr2binarybk(const vector<color_df_t>& bk_colors) {
  773. //创建二值图
  774. _binary.create(_src.width, _src.height);
  775. memset(_binary.pixels.data(), WORD_BKCOLOR, _binary.size());
  776. int n = _binary.size();
  777. auto pdst = _binary.data();
  778. if (bk_colors.size() == 0) { // auto
  779. //转为灰度图
  780. _gray.fromImage4(_src);
  781. //获取背景颜色
  782. int bkcolor = get_bk_color(_gray);
  783. auto pgray = _gray.data();
  784. for (int i = 0; i < n; ++i) {
  785. pdst[i] =
  786. (std::abs((int)pgray[i] - bkcolor) < 20 ? WORD_BKCOLOR : WORD_COLOR);
  787. }
  788. }
  789. else {
  790. for (auto bk : bk_colors) {
  791. for (int i = 0; i < n; ++i) {
  792. auto c = (color_t*)(_src.pdata + i * 4);
  793. if (!IN_RANGE(*c, bk.color, bk.df)) pdst[i] = WORD_COLOR;
  794. }
  795. }
  796. }
  797. }
  798. //垂直方向投影到x轴
  799. void ImageBase::binshadowx(const rect_t& rc, std::vector<rect_t>& out_put) {
  800. // qDebug("in x rc:%d,%d,%d,%d", rc.x1, rc.y1, rc.x2, rc.y2);
  801. out_put.clear();
  802. // ys.clear();
  803. // Mat paintx(binary.size(), CV_8UC1, cv::Scalar(255));
  804. // //创建一个全白图片,用作显示
  805. // int* blackcout = new int[binary.cols];
  806. std::vector<int> vx;
  807. vx.resize(_binary.width);
  808. memset(&vx[0], 0, _binary.width * 4);
  809. for (int j = rc.x1; j < rc.x2; j++) {
  810. for (int i = rc.y1; i < rc.y2; i++) {
  811. if (_binary.at(i, j) == WORD_COLOR) {
  812. vx[j]++; //垂直投影按列在x轴进行投影
  813. }
  814. }
  815. }
  816. int startindex = 0;
  817. int endindex = 0;
  818. bool inblock = false; //是否遍历到字符位置
  819. rect_t roi;
  820. for (int j = rc.x1; j < rc.x2; j++) {
  821. if (!inblock && vx[j] != 0) //进入有字符区域
  822. {
  823. inblock = true;
  824. startindex = j;
  825. // std::cout << "startindex:" << startindex << std::endl;
  826. }
  827. // if (inblock&&vx[j] == 0) //进入空白区
  828. else if (inblock && vx[j] == 0 &&
  829. j - startindex >= MIN_CUT_W) //进入空白区域,且宽度不小于1
  830. {
  831. endindex = j;
  832. inblock = false;
  833. // Mat roi = binary.colRange(startindex, endindex + 1);
  834. roi.x1 = startindex;
  835. roi.y1 = rc.y1;
  836. roi.x2 = endindex;
  837. roi.y2 = rc.y2;
  838. // qDebug("out xrc:%d,%d,%d,%d", roi.x1, roi.y1, roi.x2, roi.y2);
  839. out_put.push_back(roi);
  840. }
  841. }
  842. // special case
  843. if (inblock) {
  844. roi.x1 = startindex;
  845. roi.y1 = rc.y1;
  846. roi.x2 = rc.x2;
  847. roi.y2 = rc.y2;
  848. out_put.push_back(roi);
  849. }
  850. }
  851. //投影到y轴
  852. void ImageBase::binshadowy(const rect_t& rc, std::vector<rect_t>& out_put) {
  853. // qDebug("in y rc:%d,%d,%d,%d", rc.x1, rc.y1, rc.x2, rc.y2);
  854. out_put.clear();
  855. //是否为白色或者黑色根据二值图像的处理得来
  856. // Mat painty(binary.size(), CV_8UC1, cv::Scalar(255)); //初始化为全白
  857. //水平投影
  858. // int* pointcount = new int[binary.rows]; //在二值图片中记录行中特征点的个数
  859. std::vector<int> vy;
  860. vy.resize(_binary.height);
  861. memset(&vy[0], 0, _binary.height * 4); //注意这里需要进行初始化
  862. for (int i = rc.y1; i < rc.y2; i++) {
  863. for (int j = rc.x1; j < rc.x2; j++) {
  864. if (_binary.at(i, j) == WORD_COLOR) {
  865. vy[i]++; //记录每行中黑色点的个数 //水平投影按行在y轴上的投影
  866. }
  867. }
  868. }
  869. // std::vector<Mat> result;
  870. int startindex = 0;
  871. int endindex = 0;
  872. bool inblock = false; //是否遍历到字符位置
  873. rect_t roi;
  874. for (int i = rc.y1; i < rc.y2; i++) {
  875. if (!inblock && vy[i] != 0) //进入有字符区域
  876. {
  877. inblock = true;
  878. startindex = i;
  879. // std::cout << "startindex:" << startindex << std::endl;
  880. }
  881. // if (inblock&&vy[i] == 0) //进入空白区
  882. if (inblock && vy[i] == 0 &&
  883. i - startindex >= MIN_CUT_H) //进入空白区,且高度不小于1
  884. {
  885. endindex = i;
  886. inblock = false;
  887. roi.x1 = rc.x1;
  888. roi.y1 = startindex;
  889. roi.x2 = rc.x2;
  890. roi.y2 = endindex;
  891. out_put.push_back(roi);
  892. }
  893. }
  894. if (inblock) {
  895. roi.x1 = rc.x1;
  896. roi.y1 = startindex;
  897. roi.x2 = rc.x2;
  898. roi.y2 = rc.y2;
  899. out_put.push_back(roi);
  900. }
  901. }
  902. void ImageBase::bin_image_cut(int min_word_h, const rect_t& inrc, rect_t& outrc)
  903. {
  904. //水平裁剪,缩小高度
  905. std::vector<int> v;
  906. outrc = inrc;
  907. int i, j;
  908. v.resize(_binary.height);
  909. for (auto& it : v) it = 0;
  910. for (i = inrc.y1; i < inrc.y2; ++i) {
  911. for (j = inrc.x1; j < inrc.x2; ++j)
  912. v[i] += (_binary.at(i, j) == WORD_COLOR ? 1 : 0);
  913. }
  914. i = inrc.y1;
  915. while (v[i] == 0) i++;
  916. outrc.y1 = i;
  917. i = inrc.y2 - 1;
  918. while (v[i] == 0) i--;
  919. if (i + 1 - outrc.y1 > min_word_h) outrc.y2 = i + 1;
  920. //垂直裁剪.缩小宽度
  921. v.resize(_binary.width);
  922. for (auto& it : v) it = 0;
  923. for (i = inrc.y1; i < inrc.y2; ++i) {
  924. for (j = inrc.x1; j < inrc.x2; ++j)
  925. v[j] += _binary.at(i, j) == WORD_COLOR ? 1 : 0;
  926. }
  927. i = inrc.x1;
  928. while (v[i] == 0) i++;
  929. outrc.x1 = i;
  930. i = inrc.x2 - 1;
  931. while (v[i] == 0) i--;
  932. outrc.x2 = i + 1;
  933. }
  934. void ImageBase::get_rois(int min_word_h, std::vector<rect_t>& vroi)
  935. {
  936. vroi.clear();
  937. std::vector<rect_t> vrcx, vrcy;
  938. rect_t rc;
  939. rc.x1 = rc.y1 = 0;
  940. rc.x2 = _binary.width;
  941. rc.y2 = _binary.height;
  942. binshadowy(rc, vrcy);
  943. for (int i = 0; i < vrcy.size(); ++i) {
  944. binshadowx(vrcy[i], vrcx);
  945. for (int j = 0; j < vrcx.size(); j++) {
  946. if (vrcx[j].width() >= min_word_h)
  947. bin_image_cut(min_word_h, vrcx[j], vrcx[j]);
  948. vroi.push_back(vrcx[j]);
  949. }
  950. }
  951. }
  952. inline int full_match(const ImageBin& binary, rect_t& rc, const uint8_t* data) {
  953. //匹配
  954. int idx = 0;
  955. for (int x = rc.x1; x < rc.x2; ++x) {
  956. for (int y = rc.y1; y < rc.y2; ++y) {
  957. int val = GET_BIT(data[idx / 8], idx & 7);
  958. if (binary.at(y, x) != val) return 0;
  959. idx++;
  960. }
  961. }
  962. return 1;
  963. }
  964. inline int part_match(const ImageBin& binary, rect_t& rc, int max_error,
  965. const uint8_t* data) {
  966. //匹配
  967. //匹配
  968. int err_ct = 0;
  969. int idx = 0;
  970. for (int x = rc.x1; x < rc.x2; ++x) {
  971. for (int y = rc.y1; y < rc.y2; ++y) {
  972. int val = GET_BIT(data[idx / 8], idx & 7);
  973. if (binary.at(y, x) != val) {
  974. ++err_ct;
  975. if (err_ct > max_error) return 0;
  976. }
  977. idx++;
  978. }
  979. }
  980. return 1;
  981. }
  982. inline void fill_rect(ImageBin& record, const rect_t& rc) {
  983. int w = rc.width();
  984. for (int y = rc.y1; y < rc.y2; ++y) {
  985. uchar* p = record.ptr(y) + rc.x1;
  986. memset(p, 1, sizeof(uchar) * w);
  987. }
  988. }