cut_point_rs.cpp 24 KB

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