tea_sorter.cpp 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810
  1. #include <opencv.hpp>
  2. #include <math.h>
  3. #include <io.h>
  4. #include "tea_sorter.h"
  5. #include "utils.h"
  6. using namespace cv;
  7. namespace graft_cv{
  8. CTeaSort::CTeaSort(
  9. ConfigParam& cp,
  10. img_type dtpye,
  11. CGcvLogger*pLog)
  12. :
  13. m_cp(cp),
  14. m_dtype(dtpye),
  15. m_pLogger(pLog),
  16. m_ppImgSaver(0),
  17. m_pImginfoRaw(0),
  18. m_pImginfoDetected(0)
  19. {
  20. m_drop_detector = RetinaDrop(m_pLogger, 0.5, 0.5);
  21. }
  22. CTeaSort::~CTeaSort()
  23. {
  24. clear_imginfo();
  25. }
  26. int CTeaSort::detect(
  27. ImgInfo*imginfo,
  28. PositionInfo& posinfo,
  29. const char* fn
  30. )
  31. {
  32. //1 model status
  33. if (!m_drop_detector.IsModelLoaded()) {
  34. m_pLogger->ERRORINFO(
  35. string("tea detect model NOT loaded"));
  36. return 1;
  37. }
  38. //2 update recognize threshold
  39. if (m_dtype == img_type::tea_grab) {
  40. m_drop_detector.SetThreshold(m_cp.object_threshold_grab, m_cp.nms_threshold_grab);
  41. }
  42. else {
  43. m_drop_detector.SetThreshold(m_cp.object_threshold_cut, m_cp.nms_threshold_cut);
  44. }
  45. //3 load data
  46. load_data(imginfo, fn);
  47. if (m_cp.image_show) {
  48. cv::destroyAllWindows();
  49. imshow_wait("input_img", m_raw_img);
  50. }
  51. //4 generate_detect_windows(vector<Rect>&boxes)
  52. vector<Rect> drop_regions;
  53. int region_cnt = generate_detect_windows(drop_regions);
  54. if (region_cnt == 0) {
  55. stringstream buff_;
  56. buff_ << m_imgId << m_dtype_str << "tea detect image regions' size == 0";
  57. m_pLogger->ERRORINFO(buff_.str());
  58. return 1;
  59. }
  60. else {
  61. stringstream bufftmp;
  62. bufftmp << m_imgId << m_dtype_str << "tea detect image regions' size = "<<region_cnt;
  63. m_pLogger->INFO(bufftmp.str());
  64. }
  65. if (m_cp.image_show) {
  66. cv::Mat rects_img = m_raw_img.clone();
  67. int step_c = int(255 / (float)region_cnt);
  68. int step_cc = step_c / 2;
  69. int step_ccc = step_cc / 2;
  70. int cnt = 0;
  71. for (auto&r : drop_regions) {
  72. cv::rectangle(rects_img, r, cv::Scalar(step_cc*cnt, step_c*cnt, step_ccc*cnt), 3);
  73. cnt += 1;
  74. }
  75. imshow_wait("regions_img", rects_img);
  76. }
  77. //5 empty feeder dection
  78. if (m_dtype == img_type::tea_grab) {
  79. bool is_empty = is_empty_feeder(m_raw_gray_img);
  80. if (is_empty) {
  81. stringstream bufftmp;
  82. bufftmp << m_imgId << m_dtype_str << "empty feeder" ;
  83. m_pLogger->INFO(bufftmp.str());
  84. //拍照无苗, 返回识别结果-1
  85. return -1;
  86. }
  87. }
  88. //6 detect
  89. vector<Bbox> droplets_raw;
  90. int dn = detect_impl(m_raw_img, drop_regions, droplets_raw);
  91. if (m_dtype == img_type::tea_grab) {
  92. //up-down flip
  93. cv::Mat flip_img;
  94. cv::flip(m_raw_img, flip_img, 0);
  95. if (m_cp.image_show) {
  96. imshow_wait("flip_img", flip_img);
  97. }
  98. vector<Bbox> droplets_flip;
  99. int dn_flip = detect_impl(flip_img, drop_regions, droplets_flip);
  100. for (auto&b: droplets_flip) {
  101. int y2 = flip_img.rows - b.y1;
  102. int y1 = flip_img.rows - b.y2;
  103. b.y1 = y1;
  104. b.y2 = y2;
  105. for (int i = 0; i < 5; ++i) {
  106. b.ppoint[2 * i + 1] = flip_img.rows - b.ppoint[2 * i + 1];
  107. }
  108. }
  109. if (dn_flip > 0) {
  110. droplets_raw.insert(
  111. droplets_raw.end(),
  112. droplets_flip.begin(),
  113. droplets_flip.end());
  114. }
  115. }
  116. if (m_pLogger) {
  117. stringstream buff_;
  118. buff_ << m_imgId<<m_dtype_str << "image detect over. tea number is " << droplets_raw.size();
  119. m_pLogger->INFO(buff_.str());
  120. }
  121. //7 nms, width(height) filt and area calculation
  122. vector<Bbox> droplets;
  123. vector<int> keep;
  124. nms_bbox(droplets_raw, m_drop_detector.GetNmsThreshold(), keep);
  125. if (m_pLogger) {
  126. stringstream buff_;
  127. buff_ << m_imgId << m_dtype_str << "after nms_bbox, keep size is " << keep.size();
  128. m_pLogger->INFO(buff_.str());
  129. }
  130. //nms keep and area filter
  131. double min_area_th = m_cp.min_area_ratio_grab;
  132. double max_area_th = m_cp.max_area_ratio_grab;
  133. if (m_dtype == img_type::tea_cut) {
  134. min_area_th = m_cp.min_area_ratio_cut;
  135. max_area_th = m_cp.max_area_ratio_cut;
  136. }
  137. for (int i : keep) {
  138. Bbox&tbox = droplets_raw[i];
  139. double area_ratio = static_cast<double>(tbox.y2 - tbox.y1) * static_cast<double>(tbox.x2 - tbox.x1);
  140. area_ratio = fabs(area_ratio);
  141. area_ratio /= static_cast<double>(m_raw_img.rows);
  142. area_ratio /= static_cast<double>(m_raw_img.cols);
  143. tbox.area = area_ratio;
  144. if (m_pLogger) {
  145. stringstream buff_;
  146. buff_ << m_imgId << m_dtype_str << "object's area ratio is " << area_ratio<<", range is ["<< min_area_th<<", "<< max_area_th <<"]";
  147. m_pLogger->INFO(buff_.str());
  148. }
  149. if (area_ratio < min_area_th || area_ratio > max_area_th) {
  150. continue;
  151. }
  152. //检查box边界是否在图像内,如果没有,修改之
  153. if (tbox.x1 < 0) { tbox.x1 = 0; }
  154. if (tbox.y1 < 0) { tbox.y1 = 0; }
  155. if (tbox.x2 >= m_raw_img.cols) { tbox.x2 = m_raw_img.cols - 1; }
  156. if (tbox.y2 >= m_raw_img.rows) { tbox.y2 = m_raw_img.rows - 1; }
  157. droplets.push_back(tbox);
  158. }
  159. if (m_pLogger) {
  160. stringstream buff_;
  161. buff_ << m_imgId << m_dtype_str << "after nms, keep tea number is " << droplets.size();
  162. for (auto&tbox : droplets) {
  163. buff_ << "\nscore:" << tbox.score << ", area_ratio:" << tbox.area << ", left_top:(" << tbox.x1 << "," << tbox.y1 << "), bottom_rigt:(" << tbox.x2 << "," << tbox.y2 << ")";
  164. }
  165. m_pLogger->INFO(buff_.str());
  166. }
  167. int valid_cnt = 0;
  168. if (m_dtype == img_type::tea_grab) {
  169. //grab
  170. calculate_overall_score_grab(droplets);//通过综合得分排序
  171. double pre_cx, pre_cy;
  172. double min_dist_grab = m_cp.min_distance_grab;
  173. pre_cx = -min_dist_grab;
  174. pre_cy = -min_dist_grab;
  175. for (int i = 0; i < droplets.size(); ++i) {
  176. if (valid_cnt > 1) { break; }
  177. Bbox&b = droplets.at(i);
  178. if (b.score_overall < m_cp.object_threshold_grab) { continue; }
  179. double cx = 0.5*(b.x1 + b.x2);
  180. double cy = 0.5*(b.y1 + b.y2);
  181. double dist = sqrt((cx - pre_cx)*(cx - pre_cx) + (cy - pre_cy)*(cy - pre_cy));
  182. if (dist < min_dist_grab) {
  183. continue;
  184. }
  185. double grab_x, grab_y;
  186. double angle = calculate_angle(b,/* true, */grab_x, grab_y);
  187. //grab point
  188. if (valid_cnt == 0) {
  189. posinfo.tea_grab_x1 = grab_x;
  190. posinfo.tea_grab_y1 = grab_y;
  191. posinfo.tea_grab_angle1 = angle;
  192. }
  193. else {
  194. posinfo.tea_grab_x2 = grab_x;
  195. posinfo.tea_grab_y2 = grab_y;
  196. posinfo.tea_grab_angle2 = angle;
  197. }
  198. b.operate_point[0] = grab_x;
  199. b.operate_point[1] = grab_y;
  200. b.operate_angle = angle;
  201. b.status = 1;
  202. pre_cx = cx;
  203. pre_cy = cy;
  204. valid_cnt += 1;
  205. }
  206. }
  207. else {
  208. //cut
  209. for (int i = 0; i < droplets.size();++i) {
  210. if (i > 1) { break; }
  211. Bbox&b = droplets.at(i);
  212. double grab_x, grab_y;
  213. double angle = calculate_angle(b,/* true,*/ grab_x, grab_y);
  214. valid_cnt += 1;
  215. if (i == 0) {
  216. // 切割点是3、4的中间的点
  217. posinfo.tea_cut_x1 = grab_x;
  218. posinfo.tea_cut_y1 = grab_y;
  219. posinfo.tea_cut_angle1 = angle;
  220. }
  221. else {
  222. // 切割点是3、4的中间的点
  223. posinfo.tea_cut_x2 = grab_x;
  224. posinfo.tea_cut_y2 = grab_y;
  225. posinfo.tea_cut_angle2 = angle;
  226. }
  227. b.operate_point[0] = grab_x;
  228. b.operate_point[1] = grab_y;
  229. b.operate_angle = angle;
  230. b.status = 1; // selected
  231. }
  232. }
  233. //8 draw
  234. if (m_cp.image_return) {
  235. this->clear_imginfo();
  236. cv::Mat img_rst = m_raw_img.clone();
  237. for (auto& b : droplets) {
  238. //rectangle
  239. cv::Rect r = cv::Rect(cv::Point2i(b.x1, b.y1), cv::Point2i(b.x2, b.y2));
  240. if (b.status > 0) {
  241. cv::rectangle(img_rst, r, cv::Scalar(0, 0, 255),2);
  242. }
  243. else {
  244. cv::rectangle(img_rst, r, cv::Scalar(0, 255, 0),2);
  245. }
  246. //score
  247. char name[256];
  248. cv::Scalar color(120, 120, 0);//bgr
  249. sprintf_s(name, "%.2f - %.2f", b.score, b.score_overall);
  250. cv::putText(img_rst, name,
  251. cv::Point(b.x1, b.y1),
  252. cv::FONT_HERSHEY_COMPLEX, 0.7, color, 2);
  253. //points
  254. cv::circle(img_rst, cv::Point(int(b.ppoint[0]), int(b.ppoint[1])), 4, cv::Scalar(255, 0, 255), -1, 3, 0);
  255. cv::circle(img_rst, cv::Point(int(b.ppoint[2]), int(b.ppoint[3])), 4, cv::Scalar(0, 255, 255), -1, 3, 0);
  256. cv::circle(img_rst, cv::Point(int(b.ppoint[4]), int(b.ppoint[5])), 4, cv::Scalar(255, 0, 0), -1, 3, 0);
  257. cv::circle(img_rst, cv::Point(int(b.ppoint[6]), int(b.ppoint[7])), 4, cv::Scalar(0, 255, 0), -1, 3, 0);
  258. cv::circle(img_rst, cv::Point(int(b.ppoint[8]), int(b.ppoint[9])), 4, cv::Scalar(0, 0, 255), -1, 3, 0);
  259. //grab points
  260. if (m_dtype == img_type::tea_grab) {
  261. if (b.status == 1) {
  262. double grab_x, grab_y, grab_angle;
  263. grab_x = b.operate_point[0];
  264. grab_y = b.operate_point[1];
  265. grab_angle = b.operate_angle;
  266. //bool need_precise = b.status == 1;
  267. //double grab_angle = calculate_angle(b, /*need_precise,*/ grab_x, grab_y);
  268. //cv::circle(img_rst, cv::Point(int(grab_x), int(grab_y)), 4, cv::Scalar(0, 215, 255), -1, 3, 0);
  269. //lines, p4-p5, p5-grab
  270. cv::line(img_rst,
  271. cv::Point(int(b.ppoint[6]), int(b.ppoint[7])),
  272. cv::Point(int(b.ppoint[8]), int(b.ppoint[9])),
  273. cv::Scalar(0, 215, 255), 2);
  274. cv::line(img_rst,
  275. cv::Point(int(b.ppoint[8]), int(b.ppoint[9])),
  276. cv::Point(int(grab_x), int(grab_y)),
  277. cv::Scalar(0, 215, 255), 2);
  278. //line x
  279. int radius = 20;
  280. int cx = int(grab_x);
  281. int cy = int(grab_y);
  282. cv::line(img_rst, cv::Point(cx - radius, cy - radius), cv::Point(cx + radius, cy + radius), cv::Scalar(0, 215, 255), 2);
  283. cv::line(img_rst, cv::Point(cx - radius, cy + radius), cv::Point(cx + radius, cy - radius), cv::Scalar(0, 215, 255), 2);
  284. //grab point angle
  285. int radius_dir = m_cp.offset_grab / 2;
  286. grab_angle *= (CV_PI / 180.0);
  287. double dx = radius_dir * sin(grab_angle);
  288. double dy = radius_dir * cos(grab_angle);
  289. int dir_x = int(grab_x + dx);
  290. int dir_y = int(grab_y + dy);
  291. cv::line(img_rst, cv::Point(cx, cy), cv::Point(dir_x, dir_y), cv::Scalar(20, 255, 20), 2);
  292. }
  293. }
  294. //cut points
  295. if (m_dtype == img_type::tea_cut) {
  296. //lines, p2-p3
  297. cv::line(img_rst,
  298. cv::Point(int(b.ppoint[2]), int(b.ppoint[3])),
  299. cv::Point(int(b.ppoint[4]), int(b.ppoint[5])),
  300. cv::Scalar(0, 215, 255), 2);
  301. //line x
  302. int cx = int(b.operate_point[0]);
  303. int cy = int(b.operate_point[1]);
  304. int radius = 20;
  305. cv::line(img_rst, cv::Point(cx - radius, cy - radius), cv::Point(cx + radius, cy + radius), cv::Scalar(0, 215, 255),2);
  306. cv::line(img_rst, cv::Point(cx - radius, cy + radius), cv::Point(cx + radius, cy - radius), cv::Scalar(0, 215, 255),2);
  307. }
  308. }
  309. if (m_cp.image_show) {
  310. imshow_wait("result_img", img_rst);
  311. }
  312. m_pImginfoRaw = mat2imginfo(m_raw_img);
  313. m_pImginfoDetected = mat2imginfo(img_rst);
  314. posinfo.pp_images[0] = m_pImginfoRaw;
  315. posinfo.pp_images[1] = m_pImginfoDetected;
  316. if (m_ppImgSaver && *m_ppImgSaver) {
  317. (*m_ppImgSaver)->saveImage(img_rst, m_imgId + "_rst_0");
  318. }
  319. }
  320. //结果为1无: 算法结果(相机范围内有苗,但是算法没能识别到可以抓取的苗,告诉嵌入式需要抖动)
  321. if (valid_cnt == 0) { return 1; }
  322. return 0;
  323. }
  324. int CTeaSort::detect_impl(
  325. cv::Mat& raw_img, //input, image
  326. vector<Rect>&drop_regions, //input, detect regions
  327. vector<Bbox> &droplets_raw //output, detect result
  328. )
  329. {
  330. //return number of detect result
  331. droplets_raw.clear();
  332. for (auto rect : drop_regions) {
  333. Mat roi = raw_img(rect);
  334. vector<Bbox> head_droplets = m_drop_detector.RunModel(roi, m_pLogger);
  335. if (m_pLogger) {
  336. stringstream buff_;
  337. buff_ << m_imgId << m_dtype_str << "-------crop_rect[" << rect.x << "," << rect.y << "," << rect.width
  338. << "," << rect.height << "],"
  339. << " roi image detect over. tea number is " << head_droplets.size();
  340. m_pLogger->INFO(buff_.str());
  341. }
  342. for (Bbox& b : head_droplets) {
  343. b.x1 += rect.x;
  344. b.x2 += rect.x;
  345. b.y1 += rect.y;
  346. b.y2 += rect.y;
  347. for (int i = 0; i < 5; ++i) {
  348. b.ppoint[2 * i] += rect.x;
  349. b.ppoint[2 * i + 1] += rect.y;
  350. }
  351. }
  352. if (head_droplets.size()) {
  353. droplets_raw.insert(
  354. droplets_raw.end(),
  355. head_droplets.begin(),
  356. head_droplets.end());
  357. }
  358. }
  359. return droplets_raw.size();
  360. }
  361. double CTeaSort::calculate_angle(
  362. Bbox&b, //input
  363. //bool need_precise_angle,//input
  364. double& grab_x, //output
  365. double& grab_y //output
  366. )
  367. {
  368. grab_x = grab_y = 0.0;
  369. double angle = 0.0;
  370. float x2, y2, x3,y3,x4,y4,x5,y5;
  371. x2 = b.ppoint[2];
  372. y2 = b.ppoint[3];
  373. x3 = b.ppoint[4];
  374. y3 = b.ppoint[5];
  375. x4 = b.ppoint[6];
  376. y4 = b.ppoint[7];
  377. x5 = b.ppoint[8];
  378. y5 = b.ppoint[9];
  379. if (m_dtype == img_type::tea_grab) {
  380. angle = atan2(x5 - x3, y5 - y3);
  381. calculate_stem_grab_position_opt(b, grab_x, grab_y, angle);
  382. //计算抓取点
  383. if (grab_x < 0 && grab_y < 0) {
  384. double pr = (double)m_cp.offset_grab;
  385. double dx = pr * sin(angle);
  386. double dy = pr * cos(angle);
  387. grab_x = x5 + dx;
  388. grab_y = y5 + dy;
  389. }
  390. }
  391. else {
  392. //tea cut, calculate line of p3 ans p4
  393. angle = atan2(x2 - x3, y2 - y3);
  394. calculate_stem_cut_position_opt(b, grab_x, grab_y, angle);
  395. }
  396. angle *= (180.0 / 3.1415926);
  397. return angle;
  398. }
  399. int CTeaSort::load_data(
  400. ImgInfo*imginfo,
  401. const char* fn/* = 0*/)
  402. {
  403. //数据加载功能实现,并生成imageid,保存原始数据到文件
  404. int rst = 0;
  405. //generate image id
  406. if (m_dtype == img_type::tea_grab) {
  407. m_imgId = getImgId(img_type::tea_grab);
  408. m_dtype_str = string(" tea_grab ");
  409. }
  410. else {
  411. m_imgId = getImgId(img_type::tea_cut);
  412. m_dtype_str = string(" tea_cut ");
  413. }
  414. if (imginfo) {
  415. if (m_pLogger) {
  416. stringstream buff;
  417. buff << "raw image stream: " << m_imgId << m_dtype_str << "image, width=" << imginfo->width
  418. << "\theight=" << imginfo->height << "\tchannels=" << imginfo->channel;
  419. m_pLogger->INFO(buff.str());
  420. }
  421. if (!isvalid(imginfo) || (imginfo->channel!=1 && imginfo->channel!=3)) {
  422. if (m_pLogger) {
  423. m_pLogger->ERRORINFO(m_imgId + m_dtype_str + "input image invalid.");
  424. }
  425. throw_msg(m_imgId + " invalid input image");
  426. }
  427. if (imginfo->channel == 1) {
  428. cv::Mat tmp_img = imginfo2mat(imginfo);
  429. vector<Mat> channels;
  430. for (size_t i = 0; i < 3; ++i) { channels.push_back(tmp_img); }
  431. cv::merge(channels, m_raw_img);
  432. }
  433. else {
  434. m_raw_img = imginfo2mat(imginfo);
  435. }
  436. if (m_pLogger) {
  437. stringstream buff;
  438. buff << "load image stream: " << m_imgId << m_dtype_str << "image, width=" << m_raw_img.cols
  439. << "\theight=" << m_raw_img.rows << "\tchannels=" << m_raw_img.channels();
  440. m_pLogger->INFO(buff.str());
  441. }
  442. }
  443. else {
  444. cv::Mat img = imread(fn, cv::IMREAD_COLOR);
  445. if (img.empty()) {
  446. if (m_pLogger) {
  447. m_pLogger->ERRORINFO(m_imgId + m_dtype_str + "input image invalid:" + string(fn));
  448. }
  449. throw_msg(m_imgId + m_dtype_str + "invalid input image: " + string(fn));
  450. }
  451. if (m_pLogger) {
  452. stringstream buff;
  453. buff <<"read image file: "<< m_imgId << m_dtype_str << "image, width=" << img.cols
  454. << "\theight=" << img.rows << "\tchannels=" << img.channels();
  455. m_pLogger->INFO(buff.str());
  456. }
  457. m_raw_img = img.clone();
  458. }
  459. if(m_dtype == img_type::tea_grab){
  460. double rot = m_cp.rot_degree_grab;
  461. if(fabs(rot)>1.0e-3){
  462. //rotate image
  463. cv::rotate(m_raw_img, m_raw_img,ROTATE_180);
  464. }
  465. }
  466. if (m_raw_img.channels() == 3 && m_dtype == img_type::tea_cut) {
  467. img_rgb2bgr(m_raw_img);
  468. }
  469. //image saver
  470. if (m_ppImgSaver && *m_ppImgSaver) {
  471. (*m_ppImgSaver)->saveImage(m_raw_img, m_imgId);
  472. if (m_pLogger) {
  473. stringstream buff;
  474. buff <<"saved: "<< m_imgId << m_dtype_str << "image, width=" << m_raw_img.cols
  475. << "\theight=" << m_raw_img.rows<<"\tchannels="<< m_raw_img.channels();
  476. m_pLogger->INFO(buff.str());
  477. }
  478. }
  479. //to gray
  480. if (m_raw_img.channels() == 1) { m_raw_gray_img = m_raw_img; }
  481. else {
  482. cv::cvtColor(m_raw_img, m_raw_gray_img, cv::COLOR_BGR2GRAY);
  483. }
  484. return rst;
  485. }
  486. void CTeaSort::img_rgb2bgr(cv::Mat&img) {
  487. assert(img.channels() == 3);
  488. unsigned char pixel = 0;
  489. for (int r = 0; r < img.rows; ++r) {
  490. unsigned char* pRow = img.ptr(r);
  491. for (int c = 0; c < img.cols; ++c) {
  492. pixel = pRow[c*img.channels()];
  493. pRow[c*img.channels()] = pRow[c*img.channels() + 2];
  494. pRow[c*img.channels() + 2] = pixel;
  495. }
  496. }
  497. }
  498. int CTeaSort::load_model()
  499. {
  500. bool b = false;
  501. if (!m_drop_detector.IsModelLoaded()) {
  502. if (m_dtype == img_type::tea_grab) {
  503. b = m_drop_detector.LoadModel(m_cp.model_path_grab);
  504. }
  505. else {
  506. b = m_drop_detector.LoadModel(m_cp.model_path_cut);
  507. }
  508. }
  509. else {
  510. b = true;
  511. }
  512. return b ? 0 : 1;
  513. }
  514. void CTeaSort::clear_imginfo() {
  515. if (m_pImginfoDetected) {
  516. imginfo_release(&m_pImginfoDetected);
  517. m_pImginfoDetected = 0;
  518. }
  519. if (m_pImginfoRaw) {
  520. imginfo_release(&m_pImginfoRaw);
  521. m_pImginfoRaw = 0;
  522. }
  523. }
  524. int CTeaSort::generate_detect_windows(vector<Rect>&boxes)
  525. {
  526. boxes.clear();
  527. int grid_row = m_cp.grid_row_cut;
  528. int grid_col = m_cp.grid_col_cut;
  529. int grid_padding = m_cp.grid_padding_cut;
  530. if (m_dtype == img_type::tea_grab) {
  531. grid_row = m_cp.grid_row_grab;
  532. grid_col = m_cp.grid_col_grab;
  533. grid_padding = m_cp.grid_padding_grab;
  534. }
  535. if (grid_row < 1) { grid_row = 1; }
  536. if (grid_col < 1) { grid_col = 1; }
  537. if (grid_padding < 0) { grid_padding = 0; }
  538. int block_height = int(m_raw_img.rows / (float)grid_row + 0.5);
  539. int block_width = int(m_raw_img.cols / (float)grid_col + 0.5);
  540. for (int r = 0; r < grid_row; ++r) {
  541. for (int c = 0; c < grid_col; ++c) {
  542. int x0 = c*block_width - grid_padding;
  543. int y0 = r*block_height - grid_padding;
  544. int x1 = (c+1)*block_width + grid_padding;
  545. int y1 = (r+1)*block_height + grid_padding;
  546. if (x0 < 0) { x0 = 0; }
  547. if (y0 < 0) { y0 = 0; }
  548. if (x1 > m_raw_img.cols) { x1 = m_raw_img.cols; }
  549. if (y1 > m_raw_img.rows) { y1 = m_raw_img.rows; }
  550. Rect r(x0, y0, x1-x0, y1-y0);
  551. boxes.push_back(r);
  552. }
  553. }
  554. return boxes.size();
  555. }
  556. //void CTeaSort::calculate_stem_grab_position(
  557. // Bbox&b,
  558. // double& grab_x, //output
  559. // double& grab_y, //output
  560. // double& grab_angle //output
  561. //)
  562. //{
  563. //
  564. // grab_x = grab_y = -1.0;
  565. // //crop image
  566. // int padding = 2 * m_cp.offset_grab;
  567. // int y3 = int(b.ppoint[5]);
  568. // int y5 = int(b.ppoint[9]);
  569. // cv::Point p3(int(b.ppoint[4] - b.x1), int(b.ppoint[5] - b.y1));
  570. // cv::Point p4(int(b.ppoint[6] - b.x1), int(b.ppoint[7] - b.y1));
  571. // cv::Point p5(int(b.ppoint[8] - b.x1), int(b.ppoint[9] - b.y1));
  572. // cv::Mat crop_img;
  573. // if (y5 > y3) {
  574. // // Y position
  575. // int ymax = b.y2 + padding;
  576. // if (ymax > m_raw_img.rows) {
  577. // ymax = m_raw_img.rows;
  578. // }
  579. // crop_img = m_raw_img(cv::Range(b.y1, ymax), cv::Range(b.x1, b.x2)).clone();
  580. // }
  581. // else {
  582. // // ^ position
  583. // if (b.y1 - padding < 0) {
  584. // padding = b.y1;
  585. // }
  586. // p5.y = int(b.ppoint[9] - b.y1 + padding);
  587. // p4.y = int(b.ppoint[7] - b.y1 + padding);
  588. // p3.y = int(b.ppoint[5] - b.y1 + padding);
  589. // crop_img = m_raw_img(cv::Range(b.y1 - padding, b.y2), cv::Range(b.x1, b.x2)).clone();
  590. //
  591. // }
  592. // if (m_cp.image_show) {
  593. // cv::Mat crop_img_tmp = crop_img.clone();
  594. // cv::circle(crop_img_tmp, p3, 4, cv::Scalar(255, 0, 0), -1, 3, 0);
  595. // cv::circle(crop_img_tmp, p4, 4, cv::Scalar(0, 255, 0), -1, 3, 0);
  596. // cv::circle(crop_img_tmp, p5, 4, cv::Scalar(0, 0, 255), -1, 3, 0);
  597. //
  598. // imshow_wait("cropped box", crop_img_tmp);
  599. // }
  600. //
  601. // //to gray
  602. // cv::Mat gray_img;
  603. // if (crop_img.channels() == 1) { gray_img = crop_img; }
  604. // else {
  605. // cv::cvtColor(crop_img, gray_img, cv::COLOR_BGR2GRAY);
  606. // }
  607. // //binary
  608. // cv::Mat bin_img;
  609. // double th = cv::threshold(gray_img, bin_img, 255, 255, cv::THRESH_OTSU);
  610. // cv::bitwise_not(bin_img, bin_img);
  611. // if (m_cp.image_show) {
  612. // imshow_wait("cropped binary img", bin_img);
  613. // }
  614. //
  615. // // skeletonize() or medial_axis()
  616. // cv::Mat ske_img;
  617. // thinning(bin_img, ske_img);
  618. // /*if (m_cp.image_show) {
  619. // imshow_wait("skeleton img", ske_img);
  620. // }*/
  621. //
  622. // //遍历所有点,找到距离等于指定距离的点的位置, 以及距离p5最近的骨架上的点
  623. // std::vector<cv::Point> candidate_pts;
  624. // cv::Point p5_nearst;
  625. // double dist_th = 5;
  626. // double dist_min = 1.0e6;
  627. // for (int r = 0; r < ske_img.rows; ++r) {
  628. // for (int c = 0; c < ske_img.cols; ++c) {
  629. // if (ske_img.at<unsigned char>(r, c) == 0) { continue; }
  630. // double dist = std::powf((p5.x - c), 2) + std::powf((p5.y - r),2);
  631. // dist = std::sqrtf(dist);
  632. // if (dist < dist_min) {
  633. // dist_min = dist;
  634. // p5_nearst.x = c;
  635. // p5_nearst.y = r;
  636. // }
  637. // if (std::fabs(dist - m_cp.offset_grab) < dist_th) {
  638. // candidate_pts.push_back(cv::Point(c, r));
  639. // }
  640. // }
  641. // }
  642. //
  643. // //按与参考角度的差,找到有效的候选点集合
  644. // std::vector<cv::Point> valid_candidate_pts;
  645. // double ref_angle = atan2(p5.x - p3.x, p5.y - p3.y);
  646. // cv::Point p_min_angle(-1,-1);
  647. // double min_angle = CV_PI;
  648. // for (auto&p : candidate_pts) {
  649. // double angle_to_p3 = atan2(p.x - p3.x, p.y - p3.y);
  650. // //计算夹角
  651. // double fabs_angle = intersection_angle(ref_angle, angle_to_p3);
  652. // /*if (ref_angle > 0.5 * CV_PI) {
  653. // if (angle_to_p3 < 0) {
  654. // angle_to_p3 += 2 * CV_PI;
  655. // }
  656. // fabs_angle = std::fabs(angle_to_p3 - ref_angle);
  657. // }
  658. // else {
  659. // if (ref_angle < -0.5 * CV_PI) {
  660. // if (angle_to_p3 > 0) {
  661. // angle_to_p3 -= 2 * CV_PI;
  662. // }
  663. // fabs_angle = std::fabs(angle_to_p3 - ref_angle);
  664. // }
  665. // else {
  666. // fabs_angle = std::fabs(angle_to_p3 - ref_angle);
  667. // }
  668. // }*/
  669. // if (fabs_angle > CV_PI / 4.0) { continue; }
  670. // if (fabs_angle < min_angle) {
  671. // min_angle = fabs_angle;
  672. // p_min_angle.x = p.x;
  673. // p_min_angle.y = p.y;
  674. // }
  675. // valid_candidate_pts.push_back(p);
  676. // }
  677. // if (p_min_angle.x>0 && p_min_angle.y>0) {
  678. // grab_x = p_min_angle.x;
  679. // grab_y = p_min_angle.y;
  680. // }
  681. //
  682. // if (m_cp.image_show) {
  683. // cv::Mat ske_img_tmp = ske_img.clone();
  684. // for (auto&p : valid_candidate_pts) {
  685. // ske_img_tmp.at<unsigned char>(p) = 100;
  686. // }
  687. // cv::circle(ske_img_tmp, p5, 4, cv::Scalar(255, 0, 255), 1, 3, 0);
  688. // if (grab_x > 0 && grab_y > 0) {
  689. // cv::circle(ske_img_tmp, cv::Point(int(grab_x), int(grab_y)), 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  690. // }
  691. // imshow_wait("skeleton img label", ske_img_tmp);
  692. // }
  693. //
  694. // //计算grab点的抓取角度
  695. // if (p_min_angle.x > 0 && p_min_angle.y > 0) {
  696. // grab_angle = get_grab_position(ske_img, p_min_angle, ref_angle);
  697. // }
  698. //
  699. // //重新得到grab_x,grab_y的坐标
  700. // if (grab_x > 0 && grab_y > 0) {
  701. // int real_padding_y = p5.y - int(b.ppoint[9] - b.y1);
  702. // grab_y -= real_padding_y;
  703. // grab_y += b.y1;
  704. // grab_x += b.x1;
  705. // }
  706. //
  707. //}
  708. /**
  709. * Code for thinning a binary image using Zhang-Suen algorithm.
  710. *
  711. * Author: Nash (nash [at] opencv-code [dot] com)
  712. * Website: http://opencv-code.com
  713. */
  714. /**
  715. * Perform one thinning iteration.
  716. * Normally you wouldn't call this function directly from your code.
  717. *
  718. * Parameters:
  719. * im Binary image with range = [0,1]
  720. * iter 0=even, 1=odd
  721. */
  722. void CTeaSort::thinningIteration(cv::Mat& img, int iter)
  723. {
  724. CV_Assert(img.channels() == 1);
  725. CV_Assert(img.depth() != sizeof(uchar));
  726. CV_Assert(img.rows > 3 && img.cols > 3);
  727. cv::Mat marker = cv::Mat::zeros(img.size(), CV_8UC1);
  728. int nRows = img.rows;
  729. int nCols = img.cols;
  730. if (img.isContinuous()) {
  731. nCols *= nRows;
  732. nRows = 1;
  733. }
  734. int x, y;
  735. uchar *pAbove;
  736. uchar *pCurr;
  737. uchar *pBelow;
  738. uchar *nw, *no, *ne; // north (pAbove)
  739. uchar *we, *me, *ea;
  740. uchar *sw, *so, *se; // south (pBelow)
  741. uchar *pDst;
  742. // initialize row pointers
  743. pAbove = NULL;
  744. pCurr = img.ptr<uchar>(0);
  745. pBelow = img.ptr<uchar>(1);
  746. for (y = 1; y < img.rows - 1; ++y) {
  747. // shift the rows up by one
  748. pAbove = pCurr;
  749. pCurr = pBelow;
  750. pBelow = img.ptr<uchar>(y + 1);
  751. pDst = marker.ptr<uchar>(y);
  752. // initialize col pointers
  753. no = &(pAbove[0]);
  754. ne = &(pAbove[1]);
  755. me = &(pCurr[0]);
  756. ea = &(pCurr[1]);
  757. so = &(pBelow[0]);
  758. se = &(pBelow[1]);
  759. for (x = 1; x < img.cols - 1; ++x) {
  760. // shift col pointers left by one (scan left to right)
  761. nw = no;
  762. no = ne;
  763. ne = &(pAbove[x + 1]);
  764. we = me;
  765. me = ea;
  766. ea = &(pCurr[x + 1]);
  767. sw = so;
  768. so = se;
  769. se = &(pBelow[x + 1]);
  770. int A = (*no == 0 && *ne == 1) + (*ne == 0 && *ea == 1) +
  771. (*ea == 0 && *se == 1) + (*se == 0 && *so == 1) +
  772. (*so == 0 && *sw == 1) + (*sw == 0 && *we == 1) +
  773. (*we == 0 && *nw == 1) + (*nw == 0 && *no == 1);
  774. int B = *no + *ne + *ea + *se + *so + *sw + *we + *nw;
  775. int m1 = iter == 0 ? (*no * *ea * *so) : (*no * *ea * *we);
  776. int m2 = iter == 0 ? (*ea * *so * *we) : (*no * *so * *we);
  777. if (A == 1 && (B >= 2 && B <= 6) && m1 == 0 && m2 == 0)
  778. pDst[x] = 1;
  779. }
  780. }
  781. img &= ~marker;
  782. }
  783. /**
  784. * Function for thinning the given binary image
  785. *
  786. * Parameters:
  787. * src The source image, binary with range = [0,255]
  788. * dst The destination image
  789. */
  790. void CTeaSort::thinning(const cv::Mat& src, cv::Mat& dst)
  791. {
  792. dst = src.clone();
  793. dst /= 255; // convert to binary image
  794. cv::Mat prev = cv::Mat::zeros(dst.size(), CV_8UC1);
  795. cv::Mat diff;
  796. do {
  797. thinningIteration(dst, 0);
  798. thinningIteration(dst, 1);
  799. cv::absdiff(dst, prev, diff);
  800. dst.copyTo(prev);
  801. } while (cv::countNonZero(diff) > 0);
  802. dst *= 255;
  803. }
  804. /**
  805. distance_thinning()
  806. distance transform based thinning
  807. -----disused
  808. */
  809. //void CTeaSort::distance_thinning(const cv::Mat& src, cv::Mat& dst)
  810. //{
  811. //
  812. // cv::Mat dist_mat(src.size(), CV_32FC1);
  813. // cv::distanceTransform(src, dist_mat, DIST_L2, 3);
  814. //
  815. // float max_dist = *max_element(dist_mat.begin<float>(), dist_mat.end<float>());
  816. // double r = 1.0;
  817. // if (max_dist > 1.0e-3) {
  818. // r = 255.0 / max_dist;
  819. // }
  820. // cv::Mat dist_img;
  821. // dist_mat.convertTo(dist_img, CV_8UC1, r, 0.0);
  822. //
  823. // cv::Canny(dist_img, dst, 50, 100, 7);
  824. //
  825. // unsigned char udist = *max_element(dst.begin<unsigned char>(), dst.end<unsigned char>());
  826. // if (m_cp.image_show) {
  827. // imshow_wait("dist_img", dist_img);
  828. // imshow_wait("canny", dst);
  829. // }
  830. //
  831. //
  832. //}
  833. /**
  834. part_thinning()
  835. 将图片缩小,thinning, 然后放大得到,用以提高效率
  836. */
  837. void CTeaSort::part_thinning(const cv::Mat& src, cv::Mat& dst)
  838. {
  839. cv::Mat part_img;
  840. cv::resize(src, part_img, cv::Size(src.cols / 2, src.rows / 2));
  841. cv::Mat part_ske_img;
  842. thinning(part_img, part_ske_img);
  843. cv::Mat gray_img;
  844. cv::resize(part_ske_img, gray_img, src.size());
  845. double th = cv::threshold(gray_img, dst, 255, 255, cv::THRESH_OTSU);
  846. /*if (m_cp.image_show) {
  847. imshow_wait("part_img", part_img);
  848. imshow_wait("part_ske_img", part_ske_img);
  849. imshow_wait("dst", dst);
  850. }*/
  851. }
  852. /**
  853. 计算 [-pi,pi]间的两个角间的夹角
  854. */
  855. double CTeaSort::intersection_angle(
  856. double ref_angle,
  857. double angle_to_p3
  858. )
  859. {
  860. //计算夹角
  861. double fabs_angle = 0;
  862. if (ref_angle > 0.5 * CV_PI) {
  863. if (angle_to_p3 < 0) {
  864. angle_to_p3 += 2 * CV_PI;
  865. }
  866. fabs_angle = std::fabs(angle_to_p3 - ref_angle);
  867. }
  868. else {
  869. if (ref_angle < -0.5 * CV_PI) {
  870. if (angle_to_p3 > 0) {
  871. angle_to_p3 -= 2 * CV_PI;
  872. }
  873. fabs_angle = std::fabs(angle_to_p3 - ref_angle);
  874. }
  875. else {
  876. fabs_angle = std::fabs(angle_to_p3 - ref_angle);
  877. }
  878. }
  879. return fabs_angle;
  880. }
  881. /**
  882. *
  883. */
  884. double CTeaSort::get_grab_position(
  885. const std::vector<cv::Point2f>& inner_pixels,
  886. const cv::Mat& skele_img,
  887. cv::Point&vertex,
  888. double ref_angle
  889. )
  890. {
  891. double grab_point_angle = CV_2PI;
  892. cv::Point pt0, pt1, pt2, pt3;
  893. double radius = static_cast<double>(m_cp.offset_grab) * 0.5;
  894. calc_bottom_vertex(vertex, ref_angle, CV_PI / 8.0, radius, pt0, pt1);
  895. calc_bottom_vertex(vertex, ref_angle+CV_PI, CV_PI / 8.0, radius, pt2, pt3);
  896. std::vector<cv::Point> triangle_region;
  897. triangle_region.push_back(pt0);
  898. triangle_region.push_back(pt1);
  899. triangle_region.push_back(pt2);
  900. triangle_region.push_back(pt3);
  901. //构建多边形,然后判别骨架图中在多边形内的骨架像素
  902. std::vector<cv::Point2f> curve_pts;
  903. for (auto&pt : inner_pixels) {
  904. double d = cv::pointPolygonTest(triangle_region, pt, false);
  905. // d 1-内部点, 0-边缘点 -1-外部点
  906. if (d > 0) {
  907. curve_pts.push_back(pt);
  908. }
  909. }
  910. //根据curve_pts进行曲线拟合,得到茎的曲线
  911. cv::Vec4f line_model;//[vx,vy, x0,y0], vx,vy---方向的归一化向量,x0,y0---直线上任意一点
  912. line_fit(curve_pts, line_model);
  913. double y_angle = atan2(line_model[0], line_model[1]);// y_angle in range [-pi, pi]
  914. double fabs_angle = intersection_angle(ref_angle, y_angle);
  915. double y_angle_inv = atan2(-line_model[0], -line_model[1]);; //y_angle_inv in range [-pi, pi]
  916. double fabs_angle_inv = intersection_angle(ref_angle, y_angle_inv);
  917. grab_point_angle = y_angle;
  918. if (fabs_angle_inv < fabs_angle) {
  919. grab_point_angle = y_angle_inv;
  920. }
  921. //可视化
  922. if (m_cp.image_show) {
  923. cv::Mat ske_img_tmp = skele_img.clone();
  924. for (auto&p : curve_pts) {
  925. ske_img_tmp.at<unsigned char>(p) = 100;
  926. }
  927. cv::circle(ske_img_tmp, vertex, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  928. cv::circle(ske_img_tmp, pt0, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  929. cv::circle(ske_img_tmp, pt1, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  930. cv::circle(ske_img_tmp, pt2, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  931. cv::circle(ske_img_tmp, pt3, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  932. cv::line(ske_img_tmp, pt0, pt1, cv::Scalar(255, 215, 255), 2);
  933. cv::line(ske_img_tmp, pt0, pt3, cv::Scalar(255, 215, 255), 2);
  934. cv::line(ske_img_tmp, pt1, pt2, cv::Scalar(255, 215, 255), 2);
  935. cv::line(ske_img_tmp, pt2, pt3, cv::Scalar(255, 215, 255), 2);
  936. double dcx = radius * sin(grab_point_angle);
  937. double dcy = radius * cos(grab_point_angle);
  938. cv::Point dir_o;
  939. cv::Point dir_p;
  940. dir_o.x = vertex.x + 10;
  941. dir_o.y = vertex.y;
  942. dir_p.x = int(vertex.x + 10 + dcx);
  943. dir_p.y = int(vertex.y + dcy);
  944. cv::line(ske_img_tmp, dir_o, dir_p, cv::Scalar(255, 215, 255), 2);
  945. imshow_wait("grab angle", ske_img_tmp);
  946. }
  947. return grab_point_angle;
  948. }
  949. /**
  950. * calc_bottom_vertex
  951. * 找到等腰三角形两个底角点
  952. *
  953. *
  954. */
  955. void CTeaSort::calc_bottom_vertex(
  956. cv::Point&vertex, //input
  957. double ref_angle, //input, rad, 等腰三角形高的方向
  958. double delta_angle, //input, rad, 等腰三角形1/2分角
  959. double radius, //input, 等腰三角形腰长
  960. cv::Point&bpt0, //output
  961. cv::Point&bpt1 //output
  962. )
  963. {
  964. //double delta_angle = CV_PI / 8.0; // 22.5 degree
  965. //double radius = static_cast<double>(m_cp.offset_grab) * 1.5;
  966. double angle = ref_angle - delta_angle;
  967. int x = static_cast<int>(radius * sin(angle) + 0.5) + vertex.x;
  968. int y = static_cast<int>(radius * cos(angle) + 0.5) + vertex.y;
  969. bpt0.x = x;
  970. bpt0.y = y;
  971. angle = ref_angle + delta_angle;
  972. x = static_cast<int>(radius * sin(angle) + 0.5) + vertex.x;
  973. y = static_cast<int>(radius * cos(angle) + 0.5) + vertex.y;
  974. bpt1.x = x;
  975. bpt1.y = y;
  976. }
  977. //cv::Mat CTeaSort::poly_fit(
  978. // std::vector<cv::Point2f>& chain,
  979. // int n
  980. //)
  981. //{
  982. // //https://blog.csdn.net/jpc20144055069/article/details/103232641
  983. // cv::Mat y(chain.size(), 1, CV_32F, cv::Scalar::all(0));
  984. // cv::Mat phy(chain.size(), n, CV_32F, cv::Scalar::all(0));
  985. // for(int i=0;i<phy.rows;++i){
  986. // float* pr = phy.ptr<float>(i);
  987. // for(int j=0; j<phy.cols;++j){
  988. // pr[j] = pow(chain[i].x,j);
  989. // }
  990. // y.at<float>(i) = chain[i].y;
  991. // }
  992. //
  993. // cv::Mat phy_t = phy.t();
  994. // cv::Mat phyMULphy_t = phy.t() * phy;
  995. // cv::Mat phyMphyInv = phyMULphy_t.inv();
  996. // cv::Mat a = phyMphyInv * phy_t;
  997. // a = a*y;
  998. // return a;
  999. //}
  1000. void CTeaSort::line_fit(std::vector<cv::Point2f>& key_point, cv::Vec4f& lines)
  1001. {
  1002. /*std::vector<cv::Point2f> pts;
  1003. for (auto&p : key_point) {
  1004. pts.push_back(cv::Point2f(p.x, p.y));
  1005. }*/
  1006. double param = 0;
  1007. double reps = 0.01;
  1008. double aeps = 0.01;
  1009. //cv::Vec4f lines;//[vx,vy, x0,y0], vx,vy---方向的归一化向量,x0,y0---直线上任意一点
  1010. cv::fitLine(key_point, lines, DIST_L1, param, reps, aeps);
  1011. }
  1012. //bool CTeaSort::poly_fit_cv(
  1013. //std::vector<cv::Point>& key_point,
  1014. //int n,
  1015. //cv::Mat& A
  1016. //)
  1017. //{
  1018. // //https://blog.csdn.net/KYJL888/article/details/103073956
  1019. // int N = key_point.size();
  1020. //
  1021. // //构造矩阵X
  1022. // cv::Mat X = cv::Mat::zeros(n+1, n+1, CV_64FC1);
  1023. // for(int i=0;i<n+1; ++i){
  1024. // for(int j=0;j<n+1;++j){
  1025. // for(int k=0;k<N;++k){
  1026. // X.at<double>(i,j) = X.at<double>(i,j) +
  1027. // std::pow(key_point[k].x, i+j);
  1028. // }
  1029. // }
  1030. // }
  1031. //
  1032. // //构造矩阵Y
  1033. // cv::Mat Y = cv::Mat::zeros(n+1, 1, CV_64FC1);
  1034. // for(int i=0;i<n+1;++i){
  1035. // for(int k=0;k<N;++k){
  1036. // Y.at<double>(i,0) = Y.at<double>(i,0) +
  1037. // std::pow(key_point[k].x, i) + key_point[k].y;
  1038. // }
  1039. // }
  1040. //
  1041. // A = cv::Mat::zeros(n+1, 1, CV_64FC1);
  1042. // cv::solve(X,Y,A,cv::DECOMP_LU);
  1043. // return true;
  1044. //}
  1045. //double CTeaSort::calc_fit_y(
  1046. //double x, //input
  1047. //cv::Mat& A //input
  1048. //)
  1049. //{
  1050. // //double y = A.at<double>(0,0) + A.at<double>(1,0) * x +
  1051. // // A.at<double>(2,0) * std::pow(x,2) + A.at<double>(3,0) * std::pow(x,3);
  1052. // //return y;
  1053. //
  1054. // double y = 0.0;
  1055. // for(int i=0; i<A.rows;++i){
  1056. // y += A.at<double>(i,0) * std::pow(x,i);
  1057. // }
  1058. // return y;
  1059. //}
  1060. //}
  1061. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1062. // calculate_stem_grab_position_opt()替代calculate_stem_grab_position函数
  1063. // 1)采用局部thinning方法提高效率
  1064. // 2) 重新用局部线性拟合的方向替代ref_angle(原始是p5和p3点连线与y正方向的夹角)
  1065. void CTeaSort::calculate_stem_grab_position_opt(
  1066. Bbox&b_original,
  1067. double& grab_x, //output
  1068. double& grab_y, //output
  1069. double& grab_angle //input-output
  1070. )
  1071. {
  1072. //扩展box的范围,4个方向全部扩展
  1073. Bbox b(b_original);
  1074. int padding_border = m_cp.offset_grab;
  1075. b.x1 -= padding_border;
  1076. b.x1 = b.x1 < 0 ? 0 : b.x1;
  1077. b.y1 -= padding_border;
  1078. b.y1 = b.y1 < 0 ? 0 : b.y1;
  1079. b.x2 += padding_border;
  1080. b.x2 = b.x2 < m_raw_img.cols ? b.x2 : m_raw_img.cols - 1;
  1081. b.y2 += padding_border;
  1082. b.y2 = b.y2 < m_raw_img.rows ? b.y2 : m_raw_img.rows - 1;
  1083. grab_x = grab_y = -1.0;
  1084. //crop image
  1085. int padding = 0;
  1086. int y3 = int(b.ppoint[5]);
  1087. int y5 = int(b.ppoint[9]);
  1088. cv::Point p3(int(b.ppoint[4] - b.x1), int(b.ppoint[5] - b.y1));
  1089. cv::Point p4(int(b.ppoint[6] - b.x1), int(b.ppoint[7] - b.y1));
  1090. cv::Point p5(int(b.ppoint[8] - b.x1), int(b.ppoint[9] - b.y1));
  1091. cv::Mat crop_img;
  1092. if (y5 > y3) {
  1093. // Y position
  1094. int ymax = b.y2 + padding;
  1095. if (ymax > m_raw_img.rows) {
  1096. ymax = m_raw_img.rows;
  1097. }
  1098. crop_img = m_raw_img(cv::Range(b.y1, ymax), cv::Range(b.x1, b.x2)).clone();
  1099. }
  1100. else {
  1101. // ^ position
  1102. if (b.y1 - padding < 0) {
  1103. padding = b.y1;
  1104. }
  1105. p5.y = int(b.ppoint[9] - b.y1 + padding);
  1106. p4.y = int(b.ppoint[7] - b.y1 + padding);
  1107. p3.y = int(b.ppoint[5] - b.y1 + padding);
  1108. crop_img = m_raw_img(cv::Range(b.y1 - padding, b.y2), cv::Range(b.x1, b.x2)).clone();
  1109. }
  1110. if (m_cp.image_show) {
  1111. cv::Mat crop_img_tmp = crop_img.clone();
  1112. cv::circle(crop_img_tmp, p3, 4, cv::Scalar(255, 0, 0), -1, 3, 0);
  1113. cv::circle(crop_img_tmp, p4, 4, cv::Scalar(0, 255, 0), -1, 3, 0);
  1114. cv::circle(crop_img_tmp, p5, 4, cv::Scalar(0, 0, 255), -1, 3, 0);
  1115. imshow_wait("cropped box", crop_img_tmp);
  1116. }
  1117. //to gray
  1118. cv::Mat gray_img;
  1119. if (crop_img.channels() == 1) { gray_img = crop_img; }
  1120. else {
  1121. cv::cvtColor(crop_img, gray_img, cv::COLOR_BGR2GRAY);
  1122. }
  1123. //binary
  1124. cv::Mat bin_img;
  1125. double th = cv::threshold(gray_img, bin_img, 255, 255, cv::THRESH_OTSU);
  1126. cv::bitwise_not(bin_img, bin_img);
  1127. if (m_cp.image_show) {
  1128. imshow_wait("cropped binary img", bin_img);
  1129. }
  1130. // skeletonize() or medial_axis()
  1131. cv::Mat ske_img;
  1132. //thinning(bin_img, ske_img);
  1133. part_thinning(bin_img, ske_img);
  1134. /*if (m_cp.image_show) {
  1135. imshow_wait("skeleton img", ske_img);
  1136. }*/
  1137. //获取ske_img中骨架上的点坐标
  1138. std::vector<cv::Point2f> ske_pixels;
  1139. for (int r = 1; r < ske_img.rows-1; ++r) {
  1140. for (int c = 1; c < ske_img.cols-1; ++c) {
  1141. if (ske_img.at<unsigned char>(r, c) == 0) { continue; }
  1142. ske_pixels.push_back(cv::Point2f(c, r));
  1143. }
  1144. }
  1145. //在grab_angle的指导下找到最优方向,截图,进行局部thinning
  1146. double ref_angle_init = grab_angle;
  1147. double delta_angle = CV_PI / 24.0;
  1148. double radius = static_cast<double>(m_cp.offset_grab);
  1149. cv::Point pt0, pt1, pt2, pt3;
  1150. double step_angle = CV_PI / 36.0; // 5 degree
  1151. int max_pixels = 0;
  1152. cv::Point pt0_opt, pt1_opt, pt2_opt, pt3_opt, center_opt;
  1153. //int minx_opt, maxx_opt, miny_opt, maxy_opt;
  1154. std::vector<cv::Point2f> ske_pixels_opt;
  1155. double target_angle_opt;
  1156. for (int i = -10; i <= 10; ++i) { //-30 degree ---- 30 degree
  1157. //在指定方向的矩形框内,找到内部点最多的方向,作为主方向
  1158. double target_angle = ref_angle_init + i*step_angle;
  1159. cv::Point center_pt;
  1160. center_pt.x = p4.x + static_cast<int>(radius * sin(target_angle));
  1161. center_pt.y = p4.y + static_cast<int>(radius * cos(target_angle));
  1162. calc_bottom_vertex(center_pt, target_angle, delta_angle, radius, pt0, pt1);
  1163. calc_bottom_vertex(center_pt, target_angle + CV_PI, delta_angle, radius, pt2, pt3);
  1164. std::vector<cv::Point> triangle_region;
  1165. triangle_region.push_back(pt0);
  1166. triangle_region.push_back(pt1);
  1167. triangle_region.push_back(pt2);
  1168. triangle_region.push_back(pt3);
  1169. //counting
  1170. int pixel_num = 0;
  1171. std::vector<cv::Point2f> inner_pixels;
  1172. for (auto&pt : ske_pixels) {
  1173. double d = cv::pointPolygonTest(triangle_region, pt, false);
  1174. // d 1-内部点, 0-边缘点 -1-外部点
  1175. if (d >= 0) {
  1176. pixel_num++;
  1177. inner_pixels.push_back(pt);
  1178. }
  1179. }
  1180. if (pixel_num > max_pixels) {
  1181. max_pixels = pixel_num;
  1182. pt0_opt = pt0;
  1183. pt1_opt = pt1;
  1184. pt2_opt = pt2;
  1185. pt3_opt = pt3;
  1186. center_opt = center_pt;
  1187. ske_pixels_opt.clear();
  1188. ske_pixels_opt.insert(ske_pixels_opt.begin(), inner_pixels.begin(), inner_pixels.end());
  1189. target_angle_opt = target_angle;
  1190. }
  1191. /*if (m_cp.image_show) {
  1192. cv::Mat bin_tmp = bin_img.clone();
  1193. cv::circle(bin_tmp, p5, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1194. cv::circle(bin_tmp, pt0, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1195. cv::circle(bin_tmp, pt1, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1196. cv::circle(bin_tmp, pt2, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1197. cv::circle(bin_tmp, pt3, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1198. cv::line(bin_tmp, pt0, pt1, cv::Scalar(180, 215, 255), 2);
  1199. cv::line(bin_tmp, pt0, pt3, cv::Scalar(180, 215, 255), 2);
  1200. cv::line(bin_tmp, pt1, pt2, cv::Scalar(180, 215, 255), 2);
  1201. cv::line(bin_tmp, pt2, pt3, cv::Scalar(180, 215, 255), 2);
  1202. imshow_wait("binary img box", bin_tmp);
  1203. }*/
  1204. }
  1205. //opt box process
  1206. if (m_cp.image_show) {
  1207. cv::Mat bin_tmp = ske_img.clone();
  1208. cv::circle(bin_tmp, p4, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1209. cv::circle(bin_tmp, pt0_opt, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1210. cv::circle(bin_tmp, pt1_opt, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1211. cv::circle(bin_tmp, pt2_opt, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1212. cv::circle(bin_tmp, pt3_opt, 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1213. cv::line(bin_tmp, pt0_opt, pt1_opt, cv::Scalar(180, 215, 255), 2);
  1214. cv::line(bin_tmp, pt0_opt, pt3_opt, cv::Scalar(180, 215, 255), 2);
  1215. cv::line(bin_tmp, pt1_opt, pt2_opt, cv::Scalar(180, 215, 255), 2);
  1216. cv::line(bin_tmp, pt2_opt, pt3_opt, cv::Scalar(180, 215, 255), 2);
  1217. imshow_wait("binary img box opt", bin_tmp);
  1218. }
  1219. //计算ref_angle
  1220. cv::Vec4f line_model;//[vx,vy, x0,y0], vx,vy---方向的归一化向量,x0,y0---直线上任意一点
  1221. line_fit(ske_pixels_opt, line_model);
  1222. double y_angle = atan2(line_model[0], line_model[1]);// y_angle in range [-pi, pi]
  1223. double fabs_angle = intersection_angle(target_angle_opt, y_angle);
  1224. double y_angle_inv = atan2(-line_model[0], -line_model[1]);; //y_angle_inv in range [-pi, pi]
  1225. double fabs_angle_inv = intersection_angle(target_angle_opt, y_angle_inv);
  1226. double ref_angle = y_angle;
  1227. if (fabs_angle_inv < fabs_angle) {
  1228. ref_angle = y_angle_inv;
  1229. }
  1230. //可视化
  1231. /*if (m_cp.image_show) {
  1232. cv::Mat ske_img_tmp = ske_img.clone();
  1233. for (auto&p : in_region_pts) {
  1234. ske_img_tmp.at<unsigned char>(p) = 100;
  1235. }
  1236. double dcx = radius * sin(ref_angle);
  1237. double dcy = radius * cos(ref_angle);
  1238. cv::Point dir_o;
  1239. cv::Point dir_p;
  1240. dir_o.x = center_opt.x + 10;
  1241. dir_o.y = center_opt.y;
  1242. dir_p.x = int(center_opt.x + 10 + dcx);
  1243. dir_p.y = int(center_opt.y + dcy);
  1244. cv::line(ske_img_tmp, dir_o, dir_p, cv::Scalar(255, 215, 255), 2);
  1245. imshow_wait("ref angle", ske_img_tmp);
  1246. }*/
  1247. //遍历所有点,找到距离等于指定距离的点的位置, 以及距离p4最近的骨架上的点
  1248. std::vector<cv::Point> candidate_pts;
  1249. cv::Point p4_nearst;
  1250. double dist_th = 5;
  1251. double dist_min = 1.0e6;
  1252. for (auto& pt : ske_pixels_opt) {
  1253. int c = int(pt.x);
  1254. int r = int(pt.y);
  1255. double dist = std::powf((p4.x - c), 2) + std::powf((p4.y - r), 2);
  1256. dist = std::sqrtf(dist);
  1257. if (dist < dist_min) {
  1258. dist_min = dist;
  1259. p4_nearst.x = c;
  1260. p4_nearst.y = r;
  1261. }
  1262. if (std::fabs(dist - m_cp.offset_grab) < dist_th) {
  1263. candidate_pts.push_back(cv::Point(c, r));
  1264. }
  1265. }
  1266. //按与参考角度的差,找到有效的候选点集合
  1267. std::vector<cv::Point> valid_candidate_pts;
  1268. cv::Point p_min_angle(-1, -1);
  1269. double min_angle = CV_PI;
  1270. for (auto&p : candidate_pts) {
  1271. double angle_to_p3 = atan2(p.x - p3.x, p.y - p3.y);
  1272. //计算夹角
  1273. double fabs_angle = intersection_angle(ref_angle, angle_to_p3);
  1274. if (fabs_angle > CV_PI / 4.0) { continue; }
  1275. if (fabs_angle < min_angle) {
  1276. min_angle = fabs_angle;
  1277. p_min_angle.x = p.x;
  1278. p_min_angle.y = p.y;
  1279. }
  1280. valid_candidate_pts.push_back(p);
  1281. }
  1282. if (p_min_angle.x>0 && p_min_angle.y>0) {
  1283. grab_x = p_min_angle.x;
  1284. grab_y = p_min_angle.y;
  1285. }
  1286. if (m_cp.image_show) {
  1287. cv::Mat ske_img_tmp = ske_img.clone();
  1288. for (auto&p : valid_candidate_pts) {
  1289. ske_img_tmp.at<unsigned char>(p) = 100;
  1290. }
  1291. cv::circle(ske_img_tmp, p4, 4, cv::Scalar(255, 0, 255), 1, 3, 0);
  1292. if (grab_x > 0 && grab_y > 0) {
  1293. cv::circle(ske_img_tmp, cv::Point(int(grab_x), int(grab_y)), 4, cv::Scalar(156, 0, 255), 1, 3, 0);
  1294. }
  1295. imshow_wait("skeleton img label", ske_img_tmp);
  1296. }
  1297. //计算grab点的抓取角度
  1298. if (p_min_angle.x > 0 && p_min_angle.y > 0) {
  1299. grab_angle = get_grab_position(ske_pixels_opt, ske_img, p_min_angle, ref_angle);
  1300. }
  1301. //重新得到grab_x,grab_y的坐标
  1302. if (grab_x > 0 && grab_y > 0) {
  1303. int real_padding_y = p4.y - int(b.ppoint[7] - b.y1);
  1304. grab_y -= real_padding_y;
  1305. grab_y += b.y1;
  1306. grab_x += b.x1;
  1307. }
  1308. }
  1309. void CTeaSort::calculate_stem_cut_position_opt(
  1310. Bbox&b,
  1311. double& grab_x, //output
  1312. double& grab_y, //output
  1313. double& grab_angle //input-output
  1314. )
  1315. {
  1316. int padding = 40;
  1317. grab_x = grab_y = -1.0;
  1318. //crop image
  1319. cv::Point p3o(int(b.ppoint[4]), int(b.ppoint[5]));
  1320. cv::Point p2o(int(b.ppoint[2]), int(b.ppoint[3]));
  1321. int x1, y1, x2, y2;
  1322. x1 = min(p3o.x, p2o.x);
  1323. y1 = min(p3o.y, p2o.y);
  1324. x2 = max(p3o.x, p2o.x);
  1325. y2 = max(p3o.y, p2o.y);
  1326. x1 -= padding;
  1327. x1 = x1 < 0 ? 0 : x1;
  1328. y1 -= padding;
  1329. y1 = y1 < 0 ? 0 : y1;
  1330. x2 += padding;
  1331. x2 = x2 < m_raw_img.cols ?x2 : m_raw_img.cols - 1;
  1332. y2 += padding;
  1333. y2 = y2 < m_raw_img.rows ? y2 : m_raw_img.rows - 1;
  1334. cv::Point p3(int(b.ppoint[4] - x1), int(b.ppoint[5] - y1));
  1335. cv::Point p2(int(b.ppoint[2] - x1), int(b.ppoint[3] - y1));
  1336. cv::Mat crop_img;
  1337. crop_img = m_raw_img(cv::Range(y1, y2), cv::Range(x1, x2)).clone();
  1338. if (m_cp.image_show) {
  1339. cv::Mat crop_img_tmp = crop_img.clone();
  1340. cv::circle(crop_img_tmp, p2, 4, cv::Scalar(255, 0, 0), -1, 3, 0);
  1341. cv::circle(crop_img_tmp, p3, 4, cv::Scalar(0, 255, 0), -1, 3, 0);
  1342. imshow_wait("cropped box", crop_img_tmp);
  1343. }
  1344. //to gray
  1345. cv::Mat gray_img;
  1346. if (crop_img.channels() == 1) { gray_img = crop_img; }
  1347. else {
  1348. cv::cvtColor(crop_img, gray_img, cv::COLOR_BGR2GRAY);
  1349. }
  1350. //binary
  1351. cv::Mat bin_img;
  1352. double th = cv::threshold(gray_img, bin_img, 255, 255, cv::THRESH_OTSU);
  1353. cv::bitwise_not(bin_img, bin_img);
  1354. if (m_cp.image_show) {
  1355. imshow_wait("cropped binary img", bin_img);
  1356. }
  1357. // skeletonize() or medial_axis()
  1358. cv::Mat ske_img;
  1359. thinning(bin_img, ske_img);
  1360. if (m_cp.image_show) {
  1361. imshow_wait("skeleton img", ske_img);
  1362. }
  1363. cv::Point2f center_pt;
  1364. double p3_ratio = m_cp.kp3_weight_cut;
  1365. if(p3_ratio > 1.0) { p3_ratio = 1.0; }
  1366. if(p3_ratio < 0.0) { p3_ratio = 0.0; }
  1367. center_pt.x = p3_ratio*p3.x + (1.0 - p3_ratio)*p2.x;
  1368. center_pt.y = p3_ratio*p3.y + (1.0 - p3_ratio)*p2.y;
  1369. //检查center_pt附近,是否有目标,如果有就用center_pt点作为切割点
  1370. int nnr = 3;
  1371. int cx, cy, knn, x, y;
  1372. cx = int(center_pt.x);
  1373. cy = int(center_pt.y);
  1374. knn = 0;
  1375. for (int r = -nnr; r <= nnr; ++r) {
  1376. y = r + cy;
  1377. if (y < 0 || y >= bin_img.rows) { continue; }
  1378. for (int c = -nnr; c <= nnr; ++c) {
  1379. x = cx + c;
  1380. if (x < 0 || x >= bin_img.cols) { continue; }
  1381. if (bin_img.at<unsigned char>(y, x) > 0) { knn++; }
  1382. }
  1383. }
  1384. if (knn > 0) {
  1385. grab_x = cx;
  1386. grab_y = cy;
  1387. grab_x += x1;
  1388. grab_y += y1;
  1389. return;
  1390. }
  1391. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  1392. // 否则通过骨架化图,找到旁边的点(适用于茎弯曲的情况)
  1393. int min_x, min_y;
  1394. min_x = cx;
  1395. min_y = cy;
  1396. double min_loss = 1.0e6;
  1397. double ref_angle = grab_angle + CV_PI / 2.0;
  1398. if (ref_angle > CV_PI) {
  1399. ref_angle = ref_angle - 2 * CV_PI;
  1400. }
  1401. for (int r = 0; r < ske_img.rows; ++r) {
  1402. for (int c = 0; c < ske_img.cols; ++c) {
  1403. if (ske_img.at<unsigned char>(r, c) == 0) { continue; }
  1404. double target_angle = atan2(double(c- center_pt.x), double(r - center_pt.y));
  1405. double dangle = intersection_angle(ref_angle, target_angle);
  1406. if (dangle > CV_PI / 36.0) { continue; }
  1407. double dist = std::powf((center_pt.x - c), 2) + std::powf((center_pt.y - r), 2);
  1408. dist = std::sqrtf(dist);
  1409. double loss = dist;
  1410. // d 1-内部点, 0-边缘点 -1-外部点
  1411. if (loss < min_loss) {
  1412. min_loss = loss;
  1413. min_x = c;
  1414. min_y = r;
  1415. }
  1416. }
  1417. }
  1418. //另一个方向
  1419. ref_angle = grab_angle - CV_PI / 2.0;
  1420. if (ref_angle < -CV_PI) {
  1421. ref_angle = ref_angle + 2 * CV_PI;
  1422. }
  1423. for (int r = 0; r < ske_img.rows; ++r) {
  1424. for (int c = 0; c < ske_img.cols; ++c) {
  1425. if (ske_img.at<unsigned char>(r, c) == 0) { continue; }
  1426. double target_angle = atan2(double(c - center_pt.x), double(r - center_pt.y));
  1427. double dangle = intersection_angle(ref_angle, target_angle);
  1428. if (dangle > CV_PI / 36.0) { continue; }
  1429. double dist = std::powf((center_pt.x - c), 2) + std::powf((center_pt.y - r), 2);
  1430. dist = std::sqrtf(dist);
  1431. double loss = dist;
  1432. // d 1-内部点, 0-边缘点 -1-外部点
  1433. if (loss < min_loss) {
  1434. min_loss = loss;
  1435. min_x = c;
  1436. min_y = r;
  1437. }
  1438. }
  1439. }
  1440. grab_x = min_x;
  1441. grab_y = min_y;
  1442. grab_x += x1;
  1443. grab_y += y1;
  1444. }
  1445. bool CTeaSort::is_empty_feeder(
  1446. cv::Mat& raw_img,
  1447. double th/*=50.0*/
  1448. )
  1449. {
  1450. vector<Rect> drop_regions;
  1451. //生成grid
  1452. int grid_row = 16;
  1453. int grid_col = 16;
  1454. int block_height = int(raw_img.rows / (float)grid_row + 0.5);
  1455. int block_width = int(raw_img.cols / (float)grid_col + 0.5);
  1456. for (int r = 0; r < grid_row; ++r) {
  1457. for (int c = 0; c < grid_col; ++c) {
  1458. int x0 = c*block_width;
  1459. int y0 = r*block_height;
  1460. int x1 = (c + 1)*block_width;
  1461. int y1 = (r + 1)*block_height;
  1462. if (x0 < 0) { x0 = 0; }
  1463. if (y0 < 0) { y0 = 0; }
  1464. if (x1 > raw_img.cols) { x1 = raw_img.cols; }
  1465. if (y1 > raw_img.rows) { y1 = raw_img.rows; }
  1466. Rect r(x0, y0, x1 - x0, y1 - y0);
  1467. drop_regions.push_back(r);
  1468. }
  1469. }
  1470. //对原始灰度图进行分析
  1471. std::vector<double> gray_values;
  1472. for (auto rect : drop_regions) {
  1473. Mat roi = raw_img(rect);
  1474. cv::Scalar mu = cv::mean(roi);
  1475. gray_values.push_back(mu[0]);
  1476. }
  1477. bool is_empty = true;
  1478. double maxv = *max_element(gray_values.begin(), gray_values.end());
  1479. double minv = *min_element(gray_values.begin(), gray_values.end());
  1480. if((maxv-minv)>th){
  1481. is_empty = false;
  1482. }
  1483. if (is_empty) {
  1484. return is_empty;
  1485. }
  1486. //计算前景的百分比
  1487. cv::Mat bin_img;
  1488. double th_bin = cv::threshold(raw_img, bin_img, 255, 255, cv::THRESH_OTSU);
  1489. //统计bin_img中0个数
  1490. double fg_area = 0;
  1491. cv::Mat_<uchar>::iterator it = bin_img.begin<uchar>();
  1492. cv::Mat_<uchar>::iterator it_end = bin_img.end<uchar>();
  1493. for (; it != it_end; ++it) {
  1494. if ((*it)==0) {
  1495. fg_area += 1;
  1496. }
  1497. }
  1498. if (m_cp.image_show) {
  1499. imshow_wait("overall bin", bin_img);
  1500. }
  1501. double objects_ratio = fg_area / static_cast<double>(bin_img.cols * bin_img.rows);
  1502. if (objects_ratio <= 0.005) {
  1503. is_empty = true;
  1504. }
  1505. return is_empty;
  1506. }
  1507. double CTeaSort::singleten_ratio(
  1508. Bbox& box
  1509. )
  1510. { //计算苗的方向,找到抓取的位置
  1511. float x3 = box.ppoint[4];
  1512. float y3 = box.ppoint[5];
  1513. float x5 = box.ppoint[8];
  1514. float y5 = box.ppoint[9];
  1515. double angle = atan2(x5 - x3, y5 - y3);
  1516. int padding_border = m_cp.offset_grab;
  1517. float grab_x = x3 + (float)padding_border * sin(angle);
  1518. float grab_y = y3 + (float)padding_border * cos(angle);
  1519. double singleten_ratio = 0.0;
  1520. if (grab_x < 0 || grab_y <0 || grab_x> m_raw_img.cols - 1 || grab_y >m_raw_img.rows - 1) {
  1521. return singleten_ratio;
  1522. }
  1523. int x1 = int(grab_x) - padding_border/2;
  1524. int y1 = int(grab_y) - padding_border/2;
  1525. int x2 = x1 + padding_border;
  1526. int y2 = y1 + padding_border;
  1527. x1 = x1 < 0 ? 0 : x1;
  1528. y1 = y1 < 0 ? 0 : y1;
  1529. x2 = x2 < m_raw_img.cols ? x2 : m_raw_img.cols - 1;
  1530. y2 = y2 < m_raw_img.rows ? y2 : m_raw_img.rows - 1;
  1531. cv::Rect r(x1, y1, x2 - x1, y2 - y1);
  1532. //debug
  1533. if (m_cp.image_show) {
  1534. cv::Mat tmp = m_raw_img.clone();
  1535. cv::Rect br(box.x1, box.y1, box.x2 - box.x1, box.y2 - box.y1);
  1536. cv::rectangle(tmp, br, cv::Scalar(0, 0, 200), 2);
  1537. cv::rectangle(tmp, r, cv::Scalar(0, 100, 0), 2);
  1538. imshow_wait("box", tmp);
  1539. }
  1540. cv::Mat roi = m_raw_gray_img(r).clone();
  1541. cv::Mat bin_img;
  1542. double th = cv::threshold(roi, bin_img, 255, 255, cv::THRESH_OTSU);
  1543. if (m_cp.image_show) {
  1544. imshow_wait("box bin_img", bin_img);
  1545. }
  1546. //统计bin_img中非0个数
  1547. double bg_area = 0;
  1548. cv::Mat_<uchar>::iterator it = bin_img.begin<uchar>();
  1549. cv::Mat_<uchar>::iterator it_end = bin_img.end<uchar>();
  1550. for (; it != it_end; ++it) {
  1551. if ((*it)>0) {
  1552. bg_area += 1;
  1553. }
  1554. }
  1555. singleten_ratio = bg_area / static_cast<double>(roi.cols * roi.rows);
  1556. return singleten_ratio;
  1557. ////计算图片中背景的占有率
  1558. ////padding
  1559. ////扩展box的范围,4个方向全部扩展
  1560. //int x1 = box.x1;
  1561. //int y1 = box.y1;
  1562. //int x2 = box.x2;
  1563. //int y2 = box.y2;
  1564. //int padding_border = m_cp.offset_grab;
  1565. //x1 -= padding_border;
  1566. //x1 = x1 < 0 ? 0 : x1;
  1567. //y1 -= padding_border;
  1568. //y1 = y1 < 0 ? 0 : y1;
  1569. //x2 += padding_border;
  1570. //x2 = x2 < m_raw_img.cols ? x2 : m_raw_img.cols - 1;
  1571. //y2 += padding_border;
  1572. //y2 = y2 < m_raw_img.rows ? y2 : m_raw_img.rows - 1;
  1573. //
  1574. //cv::Rect r(x1,y1,x2-x1,y2-y1);
  1575. //
  1576. //cv::Mat roi = m_raw_gray_img(r).clone();
  1577. //cv::Mat bin_img;
  1578. //double th = cv::threshold(roi, bin_img, 255, 255, cv::THRESH_OTSU);
  1579. //
  1580. ////统计bin_img中非0个数
  1581. //double bg_area = 0;
  1582. //cv::Mat_<uchar>::iterator it = bin_img.begin<uchar>();
  1583. //cv::Mat_<uchar>::iterator it_end = bin_img.end<uchar>();
  1584. //for(;it!=it_end;++it){
  1585. // if((*it)>0){
  1586. // bg_area+=1;
  1587. // }
  1588. //}
  1589. //double singleten_ratio = bg_area / static_cast<double>(roi.cols * roi.rows);
  1590. //return singleten_ratio;
  1591. }
  1592. double CTeaSort::direction_ratio(
  1593. Bbox& box
  1594. )
  1595. {
  1596. float x3 = box.ppoint[4];
  1597. float y3 = box.ppoint[5];
  1598. float x5 = box.ppoint[8];
  1599. float y5 = box.ppoint[9];
  1600. double angle = atan2(x5 - x3, y5 - y3);
  1601. double ratio = cos(angle);
  1602. if(ratio < 0) {
  1603. ratio *= -0.75;
  1604. }
  1605. return ratio;
  1606. }
  1607. void CTeaSort::calculate_overall_score_grab(
  1608. std::vector<Bbox> &boxes
  1609. )
  1610. {
  1611. for (auto&b : boxes) {
  1612. double single_ratio = singleten_ratio(b);
  1613. double dir_score = direction_ratio(b);
  1614. b.score_overall = single_ratio * dir_score;
  1615. }
  1616. sort(boxes.begin(), boxes.end(),
  1617. [=](const Bbox& left, const Bbox& right) {
  1618. return left.score_overall > right.score_overall;
  1619. });
  1620. }
  1621. }