cut_point_rs.cpp 24 KB

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