cut_point_rs.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952
  1. /*
  2. 砧木切割点识别
  3. 1)压左侧叶
  4. 2)识别左侧叶柄分割点,
  5. 3)切割角度:-45度
  6. */
  7. #include <opencv2\imgproc\imgproc.hpp>
  8. #include <math.h>
  9. #include <algorithm>
  10. #include "cut_point_rs.h"
  11. #include "utils.h"
  12. #include "data_def.h"
  13. #include "logger.h"
  14. using namespace cv;
  15. namespace graft_cv{
  16. CRootStockCutPoint::CRootStockCutPoint(ConfigParam&cp,CGcvLogger*pLog/*=0*/)
  17. :m_cparam(cp),
  18. m_pLogger(pLog),
  19. m_pImginfoBinFork(0),
  20. m_pImgCorners(0),
  21. m_pImgCutPoint(0),
  22. m_imgId(""),
  23. m_ppImgSaver(0)
  24. {
  25. }
  26. CRootStockCutPoint::~CRootStockCutPoint()
  27. {
  28. this->clear_imginfo();
  29. }
  30. void CRootStockCutPoint::clear_imginfo(){
  31. if (m_pImginfoBinFork){
  32. imginfo_release(&m_pImginfoBinFork);
  33. m_pImginfoBinFork=0;
  34. }
  35. if (m_pImgCorners){
  36. imginfo_release(&m_pImgCorners);
  37. m_pImgCorners=0;
  38. }
  39. if (m_pImgCutPoint){
  40. imginfo_release(&m_pImgCutPoint);
  41. m_pImgCutPoint=0;
  42. }
  43. }
  44. int CRootStockCutPoint::up_point_detect(
  45. ImgInfo* imginfo,
  46. Mat&cimg,
  47. PositionInfo& posinfo
  48. )
  49. {
  50. // cimg --- color image, bgr
  51. /*
  52. # 找到竖直的径,并对径两侧兴趣区域进行分析:茎位置,分叉位置,用于协助确定上切割点位置,或下切割点位置
  53. # 1)如果找到切割位置的角点,用角点作为上切割点,确定下切割点
  54. # 2)如果没有找到上切割点,通过分叉位置确定下切割点,根据径粗推导出上切割点
  55. h_ratio = 0.7 # 垂直方向hist,最高点的70%作为阈值,判断茎的位置
  56. v_ration = 1.2 # 水平方向hist,茎粗确定后,茎粗的1.2倍断定为分叉点
  57. */
  58. m_imgId = getImgId(img_type::rs);
  59. //1 image segment
  60. if(m_pLogger){
  61. m_pLogger->INFO(m_imgId +" rootstock cut_pt detect begin");
  62. }
  63. clock_t t;
  64. clock_t t0 = clock();
  65. Mat img;
  66. if(imginfo){
  67. if(m_pLogger){
  68. stringstream buff;
  69. buff<<m_imgId<<" rootstock image, width="<<imginfo->width
  70. <<"\theight="<<imginfo->height;
  71. m_pLogger->INFO(buff.str());
  72. }
  73. if(!isvalid(imginfo)){
  74. if(m_pLogger){
  75. m_pLogger->ERRORINFO(m_imgId+" rootstock input image invalid.");
  76. }
  77. throw_msg(m_imgId+" invalid input image");
  78. }
  79. img = imginfo2mat(imginfo);
  80. }
  81. else{
  82. if(m_pLogger){
  83. stringstream buff;
  84. buff<<m_imgId<<"rootstock image, width="<<cimg.cols
  85. <<"\theight="<<cimg.rows;
  86. m_pLogger->INFO(buff.str());
  87. }
  88. if(cimg.empty()){
  89. if(m_pLogger){
  90. m_pLogger->ERRORINFO(m_imgId+" rootstock input image invalid.");
  91. }
  92. throw_msg(m_imgId +" invalid input image");
  93. }
  94. img = cimg;
  95. }
  96. if(m_cparam.self_camera){
  97. image_set_bottom(img,20,8);
  98. if(m_pLogger){
  99. m_pLogger->DEBUG(m_imgId+" image set bottom with pixel value 20.");
  100. }
  101. }
  102. if(m_cparam.rs_y_flip){
  103. flip(img,img,0);
  104. if(m_pLogger){
  105. m_pLogger->DEBUG(m_imgId+" image y fliped.");
  106. }
  107. }
  108. //image saver
  109. if(m_ppImgSaver && *m_ppImgSaver){
  110. (*m_ppImgSaver)->saveImage(img, m_imgId);
  111. }
  112. if(m_pLogger){
  113. m_pLogger->DEBUG(m_imgId+" before image segment.");
  114. }
  115. ///////////////////////////////////////////////////////
  116. // image segment
  117. this->img_segment(img);
  118. if(m_pLogger){
  119. m_pLogger->DEBUG(m_imgId+" after image segment.");
  120. }
  121. if(m_cparam.image_show){
  122. destroyAllWindows();
  123. imshow_wait("rs_bin",m_binImg);
  124. }
  125. else{
  126. t = clock();
  127. if(1000.0*((float)(t-t0))/CLOCKS_PER_SEC>(float)m_cparam.timeout_proc){
  128. if(m_pLogger){
  129. m_pLogger->ERRORINFO(m_imgId+" rootstock timeout.");
  130. }
  131. throw_msg(m_imgId+" time out");
  132. }
  133. }
  134. if(m_pLogger){
  135. m_pLogger->DEBUG(m_imgId+" after m_binImg image show.");
  136. }
  137. //2 茎位置:x方向位置范围
  138. // 2.1 计算砧木x方向位置,中心
  139. vector<int> hist_col_pre;
  140. mat_histogram(m_binImg, hist_col_pre, 0);
  141. int min_idx, max_idx;
  142. min_idx = hist_col_pre.size();
  143. max_idx = 0;
  144. vector<int>::const_iterator it0 = hist_col_pre.begin();
  145. for(vector<int>::const_iterator it = hist_col_pre.begin();it!=hist_col_pre.end();++it){
  146. if(*it>=m_cparam.rs_min_hist_value){
  147. int idx = it - it0;
  148. if(idx<min_idx){min_idx = idx;}
  149. if(idx > max_idx) {max_idx = idx;}
  150. }
  151. }
  152. if(max_idx<=min_idx){
  153. throw_msg(m_imgId+" invalid binary image, not exist valid objects");
  154. }
  155. if(m_cparam.image_show){
  156. Mat tmp = m_binImg.clone();
  157. cv::line(tmp,Point(min_idx,0), Point(min_idx,tmp.rows),Scalar(156),3);
  158. cv::line(tmp,Point(max_idx,0), Point(max_idx,tmp.rows),Scalar(156),3);
  159. imshow_wait("rs_bin_x_range", tmp);
  160. }
  161. int width = max_idx-min_idx+1;
  162. int cent_x = (int)(0.5*(double) (max_idx-min_idx));
  163. vector<int> hist_col;
  164. mat_histogram_w(m_binImg,hist_col);
  165. if(m_cparam.image_show){
  166. Mat hist_col_img;
  167. hist2image(hist_col,hist_col_img,1,m_cparam.image_col_grid,m_cparam.image_row_grid);
  168. imshow_wait("rs_hist_col", hist_col_img);
  169. }
  170. if(m_pLogger){
  171. m_pLogger->DEBUG(m_imgId+" after histogram col.");
  172. }
  173. int x0,x1,stem_x0, stem_x1;
  174. get_stem_x_range_rscut(
  175. hist_col,
  176. m_cparam.rs_col_th_ratio,
  177. m_cparam.rs_stem_x_padding,
  178. cent_x,
  179. width,
  180. x0,
  181. x1,
  182. stem_x0,
  183. stem_x1);
  184. if(m_pLogger){
  185. stringstream buff;
  186. buff<<m_imgId<<" rootstock image, x0="<<x0
  187. <<"\tx1="<<x1
  188. <<"\tstem_x0="<<stem_x0
  189. <<"\tstem_x1="<<stem_x1;
  190. m_pLogger->INFO(buff.str());
  191. }
  192. if(m_cparam.image_show){
  193. Mat tmp_img = m_binImg.clone();
  194. line(tmp_img,Point(x0,0),Point(x0,m_binImg.rows-1),Scalar(100),2);
  195. line(tmp_img,Point(x1,0),Point(x1,m_binImg.rows-1),Scalar(100),2);
  196. //fork right point
  197. imshow_wait("rs_x_field", tmp_img);
  198. }
  199. //3 茎分叉位置:y方向分叉位置识别
  200. vector<int> hist_row;
  201. mat_histogram(m_binImg,hist_row,1,-1,-1,x0,x1+1);
  202. hist_filter(hist_row,0,5);
  203. if(m_pLogger){
  204. m_pLogger->DEBUG(m_imgId+" after histogram row.");
  205. }
  206. if(m_cparam.image_show){
  207. Mat hist_row_img;
  208. hist2image(hist_row,hist_row_img, 0,m_cparam.image_col_grid,m_cparam.image_row_grid);
  209. imshow_wait("rs_hist_row", hist_row_img);
  210. }
  211. else{
  212. t = clock();
  213. if(1000.0*((float)(t-t0))/CLOCKS_PER_SEC>(float)m_cparam.timeout_proc){
  214. if(m_pLogger){
  215. m_pLogger->ERRORINFO(m_imgId+" rootstock timeout.");
  216. }
  217. throw_msg(m_imgId+" time out");
  218. }
  219. }
  220. int stem_fork_y_pre=-1,stem_fork_y=-1,stem_end_y=-1,stem_dia=-1;
  221. int roi_max_y=-1;
  222. get_stem_y_fork_rs(
  223. hist_row,
  224. m_cparam.rs_row_th_ratio,
  225. m_cparam.rs_stem_dia_min,
  226. m_cparam.rs_stem_fork_y_min,
  227. m_cparam.rs_stem_dia_mp,
  228. stem_fork_y_pre,
  229. stem_end_y,
  230. stem_dia,
  231. roi_max_y);
  232. Point fork_cent;
  233. double max_radius;
  234. double stem_angle;
  235. get_stem_y_fork_rs_update(
  236. m_binImg,
  237. m_cparam.rs_stem_x_padding,
  238. x0,
  239. x1,
  240. roi_max_y,
  241. stem_end_y,
  242. stem_dia,
  243. m_cparam.image_show,
  244. m_cparam.rs_cut_point_offset_ratio,
  245. fork_cent,
  246. max_radius,
  247. stem_angle
  248. );
  249. stem_fork_y = fork_cent.y;
  250. if(m_pLogger){
  251. stringstream buff;
  252. buff<<m_imgId<<" rootstock image, stem_fork_y="<<stem_fork_y
  253. <<"\tstem_end_y="<<stem_end_y
  254. <<"\tstem_dia="<<stem_dia;
  255. m_pLogger->INFO(buff.str());
  256. }
  257. //4 茎分叉位置,茎左侧边缘点识别
  258. int stem_fork_left_x, stem_fork_right_x;
  259. get_stem_fork_xs(
  260. m_binImg,
  261. stem_fork_y,
  262. stem_x0,
  263. stem_x1,
  264. x0,
  265. x1,
  266. stem_fork_left_x,
  267. stem_fork_right_x);
  268. int stem_fork_dia = stem_fork_right_x - stem_fork_left_x;
  269. gcv_point<int> cut_pt = gcv_point<int>((int)(stem_fork_left_x + 0.5*(stem_fork_right_x - stem_fork_left_x)),stem_fork_y);
  270. if(m_pLogger){
  271. stringstream buff;
  272. buff<<m_imgId<<" rootstock image, stem_fork_x0="<<stem_fork_left_x
  273. <<"\tstem_fork_x1="<<stem_fork_right_x
  274. <<"\tstem_fork_diameter="<<stem_fork_dia;
  275. m_pLogger->INFO(buff.str());
  276. }
  277. //lower cut point
  278. gcv_point<int> lower_cut_pt = gcv_point<int>(-1,-1);
  279. find_lower_cut_point(m_binImg,cut_pt, lower_cut_pt, m_cparam.rs_cut_angle, stem_dia);
  280. if(m_cparam.image_show){
  281. Mat stem_img = m_binImg.clone();
  282. line(stem_img,Point(stem_x0,0),Point(stem_x0,stem_img.rows-1),Scalar(100),2);
  283. line(stem_img,Point(stem_x1,0),Point(stem_x1,stem_img.rows-1),Scalar(100),2);
  284. //fork left point
  285. circle(stem_img, Point(stem_fork_left_x,stem_fork_y),5, Scalar(128,0,128), -1, 8,0);
  286. circle(stem_img, Point(stem_fork_right_x,stem_fork_y),5, Scalar(128,0,128), -1, 8,0);
  287. Mat gray_img = m_grayImg.clone();
  288. circle(gray_img, Point(stem_fork_left_x,stem_fork_y),5, Scalar(128,0,128), -1, 8,0);
  289. circle(gray_img, Point(stem_fork_right_x,stem_fork_y),5, Scalar(128,0,128), -1, 8,0);
  290. circle(gray_img, Point((int)cut_pt.x,cut_pt.y),5, Scalar(200,0,200), -1, 8,0);
  291. circle(gray_img, Point(lower_cut_pt.x,lower_cut_pt.y),5, Scalar(200,0,200), -1, 8,0);
  292. image_draw_line(gray_img,cut_pt.x,cut_pt.y,lower_cut_pt.x,lower_cut_pt.y);
  293. imshow_wait("rs_fork_left_right_point", stem_img);
  294. imshow_wait("rs_fork_left_right_point", gray_img);
  295. }
  296. else{
  297. t = clock();
  298. if(1000.0*((float)(t-t0))/CLOCKS_PER_SEC>(float)m_cparam.timeout_proc){
  299. if(m_pLogger){
  300. m_pLogger->ERRORINFO(m_imgId+" rootstock timeout.");
  301. }
  302. throw_msg(m_imgId+" time out");
  303. }
  304. }
  305. if(m_pLogger){
  306. stringstream buff;
  307. buff<<m_imgId<<" rootstock image, upper_cut_point("<<cut_pt.x
  308. <<","<<cut_pt.y<<")"<<", lower_cut_pt( "<<lower_cut_pt.x
  309. <<","<<lower_cut_pt.y<<")";
  310. m_pLogger->INFO(buff.str());
  311. }
  312. //////////////////////////////////////start
  313. //v0.5.9.3 更改(2022-01-02)
  314. //cut_pt.x = stem_fork_left_x + 0.5*(stem_fork_right_x - stem_fork_left_x);
  315. //cut_pt.y = stem_fork_y;
  316. ///////////////////////////////////////end
  317. double rs_cut_upoint_x = (double)cut_pt.x;
  318. rs_cut_upoint_x -= (double)(img.cols/2.0);
  319. rs_cut_upoint_x *= m_cparam.rs_cut_pixel_ratio;
  320. double rs_cut_upoint_y = (double)cut_pt.y;
  321. rs_cut_upoint_y = (double)(img.rows/2.0) - rs_cut_upoint_y;
  322. rs_cut_upoint_y *= m_cparam.rs_cut_pixel_ratio;
  323. double rs_stem_diameter = stem_dia * m_cparam.rs_cut_pixel_ratio;
  324. double rs_cut_lpoint_x = lower_cut_pt.x;
  325. rs_cut_lpoint_x -= (double)(img.cols/2.0);
  326. rs_cut_lpoint_x *= m_cparam.rs_cut_pixel_ratio;
  327. double rs_cut_lpoint_y = lower_cut_pt.y;
  328. rs_cut_lpoint_y = (double)(img.rows/2.0) - rs_cut_lpoint_y;
  329. rs_cut_lpoint_y *= m_cparam.rs_cut_pixel_ratio;
  330. //posinfo.rs_cut_edge_length = 0.0;
  331. posinfo.rs_cut_upoint_x = rs_cut_upoint_x;
  332. posinfo.rs_cut_upoint_y = rs_cut_upoint_y;
  333. posinfo.rs_stem_diameter = rs_stem_diameter;
  334. posinfo.rs_cut_lpoint_x = rs_cut_lpoint_x;
  335. posinfo.rs_cut_lpoint_y = rs_cut_lpoint_y;
  336. if(m_pLogger){
  337. stringstream buff;
  338. buff<<m_imgId<<" rootstock image, rs_cut_upoint(mm)("<<rs_cut_upoint_x
  339. <<","<<rs_cut_upoint_y<<")"
  340. <<", rs_stem_diameter(mm)="<<rs_stem_diameter
  341. <<", lower_cut_pt(mm)("<<rs_cut_lpoint_x
  342. <<","<<rs_cut_lpoint_y<<")";
  343. m_pLogger->INFO(buff.str());
  344. }
  345. // return images: posinfo.pp_images
  346. if(m_cparam.image_return){
  347. this->clear_imginfo();
  348. //1)
  349. //stem x-range
  350. line(m_binImg,Point(stem_x0,0),Point(stem_x0,m_binImg.cols-1),Scalar(100),2);
  351. line(m_binImg,Point(stem_x1,0),Point(stem_x1,m_binImg.cols-1),Scalar(100),2);
  352. //fork right point
  353. circle(m_binImg, Point(stem_fork_left_x,stem_fork_y),5, Scalar(128,0,128), -1, 8,0);
  354. m_pImginfoBinFork=mat2imginfo(m_binImg);
  355. //3 cut point int gray image
  356. circle(m_grayImg, Point(stem_fork_left_x,stem_fork_y),5, Scalar(128,0,128), -1, 8,0);
  357. circle(m_grayImg, Point(stem_fork_right_x,stem_fork_y),5, Scalar(128,0,128), -1, 8,0);//v0.5.9.3 reference point
  358. circle(m_grayImg, Point(cut_pt.x,stem_fork_y),5, Scalar(128,0,128), -1, 8,0);//v0.5.9.3 reference point
  359. circle(m_grayImg, Point(cut_pt.x,stem_fork_y),2, Scalar(255,0,255), -1, 8,0);
  360. circle(m_grayImg, Point(lower_cut_pt.x,lower_cut_pt.y),5, Scalar(200,0,200), -1, 8,0);
  361. image_draw_line(m_grayImg,cut_pt.x,cut_pt.y,lower_cut_pt.x,lower_cut_pt.y);
  362. m_pImgCutPoint = mat2imginfo(m_grayImg);
  363. posinfo.pp_images[0]=m_pImginfoBinFork;
  364. posinfo.pp_images[1]=m_pImgCutPoint;
  365. if(m_ppImgSaver && *m_ppImgSaver){
  366. (*m_ppImgSaver)->saveImage(m_binImg, m_imgId+"_rst_0");
  367. (*m_ppImgSaver)->saveImage(m_grayImg, m_imgId+"_rst_1");
  368. }
  369. }
  370. if(m_pLogger){
  371. m_pLogger->INFO(m_imgId +" rootstock cut_pt detect finished.");
  372. }
  373. return 0;
  374. }
  375. void CRootStockCutPoint::get_stem_root_y(
  376. vector<int>&hist,
  377. double stem_dia_min,
  378. int & end_y)
  379. {
  380. end_y = -1;
  381. int start_idx, max_start_idx, max_end_idx, max_len;
  382. start_idx= max_start_idx= max_end_idx=-1;
  383. max_len=0;
  384. for(size_t i = 0; i<hist.size(); i++){
  385. if(i==0 && hist[i]>=stem_dia_min){
  386. start_idx=0;
  387. }
  388. if(i==0){continue;}
  389. if(hist[i]>=stem_dia_min && hist[i-1]<stem_dia_min){
  390. start_idx = i;
  391. continue;
  392. }
  393. if(hist[i]<stem_dia_min && hist[i-1]>=stem_dia_min){
  394. int lenth = i - start_idx;
  395. if(lenth>max_len){
  396. max_start_idx= start_idx;
  397. max_end_idx = i;
  398. max_len = lenth;
  399. }
  400. continue;
  401. }
  402. if(i==hist.size()-1){
  403. if(hist[i]>=stem_dia_min){
  404. int lenth = i - start_idx;
  405. if(lenth>max_len){
  406. max_start_idx= start_idx;
  407. max_end_idx = i;
  408. max_len = lenth;
  409. }
  410. }
  411. }
  412. }
  413. end_y = max_end_idx;
  414. }
  415. void CRootStockCutPoint::get_default_cutpoint(
  416. int fork_cent_x,
  417. int fork_cent_y,
  418. int fork_stem_dia,
  419. Point2f& cut_pt)
  420. {
  421. gcv_point<int> start_pt(fork_cent_x,fork_cent_y);
  422. gcv_point<int> lower_cut_pt = gcv_point<int>(-1,-1);
  423. find_lower_cut_point(m_binImg,start_pt,lower_cut_pt,m_cparam.rs_cut_angle+180.0, fork_stem_dia);
  424. if(lower_cut_pt.x<0 || lower_cut_pt.y<0){
  425. throw_msg(m_imgId+" invalid end points");
  426. }
  427. double dist = start_pt.distance(lower_cut_pt);
  428. if(dist>fork_stem_dia){
  429. double ca = (m_cparam.rs_cut_angle+180.0)*0.0174532925199433;
  430. cut_pt.x = start_pt.x+fork_stem_dia*cos(ca);
  431. cut_pt.y = start_pt.y-fork_stem_dia*sin(ca);
  432. }
  433. else{
  434. cut_pt.x = lower_cut_pt.x;
  435. cut_pt.y = lower_cut_pt.y;
  436. }
  437. }
  438. void CRootStockCutPoint::img_segment(Mat&img)
  439. {
  440. Mat b_img;
  441. if(img.channels()!=1){
  442. //color image ,bgr, for testing
  443. cvtColor(img,m_grayImg,COLOR_BGR2GRAY);
  444. }
  445. else{
  446. m_grayImg = img.clone();
  447. }
  448. Mat kernel = getStructuringElement(
  449. MORPH_ELLIPSE,
  450. Size( 2*m_cparam.rs_morph_radius + 1, 2*m_cparam.rs_morph_radius+1 ),
  451. Point( m_cparam.rs_morph_radius, m_cparam.rs_morph_radius )
  452. );
  453. /*
  454. morphologyEx(
  455. g_img,
  456. m_grayImg,
  457. MORPH_CLOSE,
  458. kernel,
  459. Point(-1,-1),
  460. m_cparam.rs_morph_iteration_gray
  461. );*/
  462. double th = threshold(m_grayImg, b_img, 255, 255,THRESH_OTSU);
  463. morphologyEx(
  464. b_img,
  465. m_binImg,
  466. MORPH_CLOSE,
  467. kernel,
  468. Point(-1,-1),
  469. m_cparam.rs_morph_iteration
  470. );
  471. //Canny(m_binImg, m_edgeImg,30,100);
  472. }
  473. int CRootStockCutPoint::get_optimal_corner(
  474. int ref_x,
  475. int ref_y,
  476. std::vector<Point2f>& corners,
  477. int stem_dia,
  478. double cand_corner_box_width_ratio,
  479. double cand_corner_box_xoffset_ratio,
  480. double opt_corner_xoffset_ratio,
  481. double opt_corner_yoffset_ratio,
  482. double corner_mask_radius_ratio,
  483. Point2f& cut_pt)
  484. {
  485. /*"""
  486. 通过茎左侧分叉点坐标( ref_x, ref_y)判别最优角点
  487. stem_img, 二值图像,茎部分的局部图像
  488. ref_x, ref_y, 检测到的茎分叉的左侧x,y坐标
  489. corners, 图像中的角点
  490. 方法:
  491. 1 reference点上区域的角点,定义区域
  492. 2 评价角点
  493. 角点与直线ref_x的距离
  494. 角点与参考点间联通性(是否都是前景)
  495. 角点与参考点的距离(1.5倍茎粗)
  496. """
  497. */
  498. cut_pt.x = -1;
  499. cut_pt.y = -1;
  500. int cand_corner_box_window = (int)(cand_corner_box_width_ratio*stem_dia);
  501. int bx = ref_x + (int)(cand_corner_box_xoffset_ratio*stem_dia) - cand_corner_box_window;
  502. int by = ref_y - cand_corner_box_window;
  503. roi_box<int> candidate_box = roi_box<int>(bx,by,cand_corner_box_window,cand_corner_box_window);
  504. vector<Point2f>cand_pts;
  505. for(size_t i=0; i<corners.size(); ++i){
  506. int pt_x = corners[i].x;
  507. int pt_y = corners[i].y;
  508. if (candidate_box.isInBox(pt_x,pt_y)){
  509. Point2f pt;
  510. pt.x=corners[i].x;
  511. pt.y=corners[i].y;
  512. cand_pts.push_back(pt);
  513. }
  514. }
  515. if(cand_pts.size()==0){
  516. return 1;
  517. }
  518. //calculate corner pts' corner score of binary image
  519. vector<double> cor_mag_scores;
  520. corner_magnificence(
  521. cand_pts,
  522. stem_dia,
  523. m_cparam.rs_corner_mask_rad_ratio,
  524. cor_mag_scores
  525. );
  526. //# estimate candidate points' score
  527. vector<double>cand_pts_score;
  528. int optx = ref_x + (int)(opt_corner_xoffset_ratio * stem_dia);
  529. int opty = ref_y + (int)(opt_corner_yoffset_ratio * stem_dia);
  530. for(size_t i=0; i<cand_pts.size(); ++i){
  531. int ptx = cand_pts[i].x;
  532. int pty = cand_pts[i].y;
  533. double score = (double)(abs(ptx - optx));
  534. double dist =sqrt((double)((ptx - optx)*(ptx - optx)) +
  535. (double)((pty - opty)* (pty - opty)));
  536. double mag_score = stem_dia * (1.0 - cor_mag_scores[i]);
  537. score += dist;
  538. score += mag_score;
  539. cand_pts_score.push_back(score);
  540. }
  541. auto smallest = min_element(begin(cand_pts_score),end(cand_pts_score));
  542. size_t min_idx = distance(begin(cand_pts_score), smallest);
  543. cut_pt.x = cand_pts[min_idx].x;
  544. cut_pt.y = cand_pts[min_idx].y;
  545. return 0;
  546. }
  547. void CRootStockCutPoint::corner_magnificence(
  548. const vector<Point2f>& cand_pts,
  549. int stem_dia,
  550. double corner_mask_radius_ratio,
  551. vector<double>& scores
  552. )
  553. {
  554. int r = int(stem_dia*corner_mask_radius_ratio);
  555. if (r<=0){r = 1;}
  556. int ptx,pty,x,y;
  557. double cnt,cnt_pos,score;
  558. for(size_t i=0; i<cand_pts.size();++i){
  559. ptx = (int)(cand_pts[i].x);
  560. pty = (int)(cand_pts[i].y);
  561. cnt = 0.0;
  562. cnt_pos = 0.0;
  563. for(int dx=-r;dx<=r;++dx){
  564. x = ptx+dx;
  565. if(x<0 || x>=m_binImg.cols){continue;}
  566. for(int dy = -r;dy<=r;++dy){
  567. y = pty+dy;
  568. if(y<0 || y>=m_binImg.rows){continue;}
  569. cnt+=1.0;
  570. if(m_binImg.at<unsigned char>(y,x)>0 ){
  571. cnt_pos+=1.0;
  572. }
  573. }
  574. }
  575. if(cnt>0){score = cnt_pos/cnt;}
  576. else{score=0.0;}
  577. scores.push_back(score);
  578. }
  579. }
  580. double CRootStockCutPoint::get_cut_edge_length
  581. (
  582. Mat& bin_img,
  583. Mat& edge_img,
  584. gcv_point<int>& start_pt, //up cut point
  585. gcv_point<int>& lower_cut_pt,//output
  586. gcv_point<int>& root_pt,//output
  587. double cut_angle,
  588. int y_max,
  589. int stem_dia,
  590. clock_t t0
  591. )
  592. {
  593. double curve_lenth = 0.0;
  594. gcv_point<int> pt_tmp = gcv_point<int>(-1,-1);
  595. lower_cut_pt = pt_tmp;
  596. find_lower_cut_point(bin_img,start_pt, lower_cut_pt, m_cparam.rs_cut_angle, stem_dia);
  597. curve_lenth = start_pt.distance(lower_cut_pt);
  598. if(m_pLogger){
  599. stringstream buff;
  600. buff<<m_imgId<<" rootstock image, found lower_cut_point("<<lower_cut_pt.x
  601. <<", "<<lower_cut_pt.y<<"), upper_cut_point("<<start_pt.x<<","<<start_pt.y<<") to lower_cut_point length: "<<curve_lenth;
  602. m_pLogger->INFO(buff.str());
  603. }
  604. //Mat edge_img;
  605. root_pt = pt_tmp;
  606. //Canny(bin_img, edge_img,30,100);
  607. double stem_curve_len = calculate_edge_length(edge_img,lower_cut_pt,root_pt,y_max,t0);
  608. curve_lenth+=stem_curve_len;
  609. return curve_lenth;
  610. }
  611. void CRootStockCutPoint::find_lower_cut_point(
  612. Mat& bin_img,
  613. gcv_point<int>&start_pt,
  614. gcv_point<int>&end_pt,//output
  615. double cut_angle,
  616. int stem_dia)
  617. {
  618. end_pt.x = -1;
  619. end_pt.y = -1;
  620. double step = 5.0;
  621. double polar_radius = step;
  622. int height,width;
  623. height = bin_img.rows;
  624. width = bin_img.cols;
  625. //ca = cut_angle*math.pi/180.0
  626. double ca = cut_angle*0.0174532925199433;
  627. int state = 0;
  628. int nxt_x,nxt_y;
  629. while(true) {
  630. nxt_x = int(start_pt.x+polar_radius*cos(ca));
  631. nxt_y = int(start_pt.y-polar_radius*sin(ca));
  632. if (nxt_x<0 || nxt_x >= width){
  633. state = 1;
  634. break;
  635. }
  636. if (nxt_y < 0 || nxt_y >= height){
  637. state = 1;
  638. break;
  639. }
  640. if (bin_img.at<unsigned char>(nxt_y,nxt_x)==0){
  641. //#在cur_pt和 nxt_pt两点间找到边缘点
  642. int small_step=1;
  643. double pr = polar_radius;
  644. int pre_x, pre_y;
  645. while (pr>0){
  646. pr-=small_step;
  647. pre_x = int(start_pt.x + pr * cos(ca));
  648. pre_y = int(start_pt.y - pr * sin(ca));
  649. if (bin_img.at<unsigned char>(pre_y, pre_x) > 0){
  650. end_pt.x = pre_x;
  651. end_pt.y = pre_y;
  652. double dd = start_pt.distance(end_pt);
  653. if (dd <(double)(stem_dia *0.66)){
  654. break;
  655. }
  656. else{
  657. state = 2;
  658. break;
  659. }
  660. }
  661. }
  662. }
  663. if(state==2){
  664. break;
  665. }
  666. polar_radius += step;
  667. }//while
  668. if(state==1){//超出图像范围,直接计算
  669. end_pt.x = start_pt.x + stem_dia/2;
  670. end_pt.y = start_pt.y + (int)(0.5*stem_dia*fabs(tan(ca))+0.5);
  671. }
  672. }
  673. double CRootStockCutPoint::calculate_edge_length(
  674. Mat& edge_img,
  675. gcv_point<int>&lower_cut_pt,
  676. gcv_point<int>&root_pt,//output
  677. int y_max,
  678. clock_t t0
  679. )
  680. {
  681. double sum_len = 0.0;
  682. //cur_x, cur_y = int(start_pt[0]), int(start_pt[1])
  683. root_pt.x = -1;
  684. root_pt.y = -1;
  685. gcv_point<int> cur_pt = gcv_point<int>(lower_cut_pt);
  686. gcv_point<int> pre_pt = gcv_point<int>(-1,-1);
  687. gcv_point<int> nxt_pt = gcv_point<int>(-1,-1);
  688. int state = 0;
  689. double root2 = sqrt(2.0);
  690. clock_t t;
  691. int cnt = 0;
  692. if(m_pLogger){
  693. stringstream buff;
  694. buff<<m_imgId<<" before curve crawling, finishing conditions: 1) ymax="<<y_max<<" 2) crawling to start_point("<<lower_cut_pt.x
  695. <<","<<lower_cut_pt.y<<")";
  696. m_pLogger->DEBUG(buff.str());
  697. }
  698. while (true){
  699. cnt +=1;
  700. if (!m_cparam.image_show && cnt%50 == 0){
  701. t = clock();
  702. if(1000.0*((float)(t-t0))/CLOCKS_PER_SEC>(float)m_cparam.timeout_proc){
  703. throw_msg(m_imgId+" time out");
  704. }
  705. }
  706. get_next_pt(edge_img, cur_pt, pre_pt,nxt_pt,false);
  707. if( nxt_pt.x == cur_pt.x || nxt_pt.y == cur_pt.y){
  708. sum_len += 1.0;
  709. }
  710. if( nxt_pt.x != cur_pt.x && nxt_pt.y != cur_pt.y){
  711. sum_len += root2;
  712. }
  713. pre_pt = cur_pt;
  714. cur_pt = nxt_pt;
  715. if(m_pLogger){
  716. stringstream buff;
  717. buff<<m_imgId<<" index: "<<cnt<<"\tcurrent point ("<<pre_pt.x
  718. <<", "<<pre_pt.y<<")";
  719. m_pLogger->DEBUG(buff.str());
  720. }
  721. if( nxt_pt.y == y_max){
  722. state = 1;
  723. root_pt = nxt_pt;
  724. break;
  725. }
  726. if (nxt_pt == lower_cut_pt){
  727. state = 2;
  728. break;
  729. }
  730. }
  731. if (m_pLogger){
  732. m_pLogger->DEBUG(m_imgId + " finished curve crawling!");
  733. }
  734. if (state == 2){
  735. return 0.0;
  736. }
  737. if (state == 1){
  738. return sum_len;
  739. }
  740. else{
  741. return 0.0;
  742. }
  743. }
  744. int CRootStockCutPoint::get_root_point_reid(
  745. int x0,
  746. int x1,
  747. int stem_end_y,
  748. bool is_right //是否右侧根部点, true-右侧,false-左侧
  749. )
  750. {
  751. int stem_end_x=-1;
  752. int start_x,end_x,max_start_x,max_end_x, max_len;
  753. start_x = end_x = -1;
  754. max_start_x = max_end_x = -1;
  755. max_len = -1;
  756. for(int i =x0; i<x1;++i){
  757. if(i==x0 && m_binImg.at<unsigned char>(stem_end_y,i)>0){
  758. start_x = i;
  759. continue;
  760. }
  761. if(i==x1-1 && m_binImg.at<unsigned char>(stem_end_y,i)>0){
  762. end_x = i;
  763. int length = end_x - start_x+1;
  764. if (length >max_len){
  765. max_len = length;
  766. max_start_x = start_x;
  767. max_end_x = end_x;
  768. }
  769. }
  770. if(m_binImg.at<unsigned char>(stem_end_y,i)>0 &&
  771. m_binImg.at<unsigned char>(stem_end_y,i-1)==0){
  772. start_x = i;
  773. continue;
  774. }
  775. if(m_binImg.at<unsigned char>(stem_end_y,i)==0 &&
  776. m_binImg.at<unsigned char>(stem_end_y,i-1)>0){
  777. end_x = i;
  778. int length = end_x - start_x+1;
  779. if (length >max_len){
  780. max_len = length;
  781. max_start_x = start_x;
  782. max_end_x = end_x;
  783. }
  784. }
  785. }
  786. if (is_right){
  787. return max_end_x;
  788. }
  789. return max_start_x;
  790. }
  791. void CRootStockCutPoint::find_upper_cut_point_reid(
  792. Mat& edge_img,
  793. gcv_point<int>&start_pt,
  794. gcv_point<int>&end_pt,//output
  795. double curve_length
  796. )
  797. {
  798. end_pt.x =-1;
  799. end_pt.y = -1;
  800. gcv_point<int> cur_pt = gcv_point<int>(start_pt);
  801. gcv_point<int> pre_pt = gcv_point<int>(-1,-1);
  802. gcv_point<int> nxt_pt = gcv_point<int>(-1,-1);
  803. //pre_x=-1
  804. //pre_y=-1
  805. double root2 = sqrt(2.0);
  806. double sum_len =0.0;
  807. int state = 0;
  808. if(m_pLogger){
  809. stringstream buff;
  810. buff<<m_imgId<<" reid, before curve crawling, finishing conditions: curve length="<<curve_length
  811. <<", start_point(root)("<<start_pt.x
  812. <<","<<start_pt.y<<")";
  813. m_pLogger->DEBUG(buff.str());
  814. }
  815. while (true){
  816. get_next_pt(edge_img,cur_pt,pre_pt,nxt_pt,true);
  817. if (nxt_pt.x==cur_pt.x || nxt_pt.y==cur_pt.y){
  818. sum_len+=1.0;
  819. }
  820. if ( nxt_pt.x!=cur_pt.x && nxt_pt.y!=cur_pt.y){
  821. sum_len += root2;
  822. }
  823. pre_pt = cur_pt;
  824. cur_pt = nxt_pt;
  825. if(m_pLogger){
  826. stringstream buff;
  827. buff<<m_imgId<<"current point ("<<pre_pt.x
  828. <<", "<<pre_pt.y<<")";
  829. m_pLogger->DEBUG(buff.str());
  830. }
  831. if (sum_len>=curve_length){
  832. state = 1;
  833. break;
  834. }
  835. if (nxt_pt == start_pt){
  836. state = 2;
  837. break;
  838. }
  839. }
  840. if (state==2){
  841. end_pt.x =-1;
  842. end_pt.y = -1;
  843. }
  844. if (state == 1){
  845. end_pt = cur_pt;
  846. }
  847. else{
  848. end_pt.x =-1;
  849. end_pt.y = -1;
  850. }
  851. }
  852. };