cut_point_sc.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. #include <opencv2\imgproc\imgproc.hpp>
  2. #include "cut_point_sc.h"
  3. #include "utils.h"
  4. using namespace cv;
  5. namespace graft_cv{
  6. CScionCutPoint::CScionCutPoint(ConfigParam&cp,CGcvLogger*pLog/*=0*/)
  7. :m_cparam(cp),
  8. m_pLogger(pLog),
  9. m_pImginfoBinFork(0),
  10. m_imgId(""),
  11. m_ppImgSaver(0),
  12. m_folder_th(-1)
  13. {
  14. }
  15. CScionCutPoint::~CScionCutPoint()
  16. {
  17. this->clear_imginfo();
  18. }
  19. void CScionCutPoint::clear_imginfo(){
  20. if (m_pImginfoBinFork){
  21. imginfo_release(&m_pImginfoBinFork);
  22. m_pImginfoBinFork=0;
  23. }
  24. }
  25. int CScionCutPoint::up_point_detect(
  26. ImgInfo* imginfo,
  27. Mat&cimg,
  28. PositionInfo& posinfo
  29. )
  30. {
  31. clock_t t;
  32. clock_t t0 = clock();
  33. m_imgId = getImgId(img_type::sc);
  34. if(m_pLogger){
  35. m_pLogger->INFO(m_imgId +" scion cut_pt detect begin.");
  36. }
  37. // cimg --- color image, bgr
  38. Mat img;
  39. if(imginfo){
  40. if(m_pLogger){
  41. stringstream buff;
  42. buff<<m_imgId<<" scion image, width="<<imginfo->width
  43. <<"\theight="<<imginfo->height;
  44. m_pLogger->INFO(buff.str());
  45. }
  46. if(!isvalid(imginfo)){
  47. if(m_pLogger){
  48. m_pLogger->ERRORINFO(m_imgId+" scion input image invalid.");
  49. }
  50. throw_msg( m_imgId+" invalid input image");
  51. }
  52. img = imginfo2mat(imginfo);
  53. }
  54. else{
  55. if(m_pLogger){
  56. stringstream buff;
  57. buff<<m_imgId<<" scion image, width="<<cimg.cols
  58. <<"\theight="<<cimg.rows;
  59. m_pLogger->INFO(buff.str());
  60. }
  61. if(cimg.empty()){
  62. if(m_pLogger){
  63. m_pLogger->ERRORINFO(m_imgId+" scion input image invalid.");
  64. }
  65. throw_msg(m_imgId+" invalid input image");
  66. }
  67. img = cimg;
  68. }
  69. if(m_cparam.self_camera){
  70. image_set_bottom(img,20,8);
  71. if(m_pLogger){
  72. m_pLogger->DEBUG(m_imgId+" scion image set bottom with pixel value 20.");
  73. }
  74. }
  75. if(m_cparam.sc_y_flip){
  76. flip(img,img,0);
  77. if(m_pLogger){
  78. m_pLogger->DEBUG(m_imgId+" scion image y fliped.");
  79. }
  80. }
  81. //image saver
  82. if(m_ppImgSaver && *m_ppImgSaver){
  83. (*m_ppImgSaver)->saveImage(img, m_imgId);
  84. if(m_pLogger){
  85. m_pLogger->DEBUG(m_imgId+" scion after image save.");
  86. }
  87. }
  88. if(m_pLogger){
  89. m_pLogger->DEBUG(m_imgId+" scion before image segment.");
  90. }
  91. //////////////////////////////////////////////////////////////////
  92. //image segment
  93. img_segment(img);
  94. if(m_pLogger){
  95. m_pLogger->DEBUG(m_imgId+" scion after scion image segment.");
  96. }
  97. if(m_cparam.image_show){
  98. destroyAllWindows();
  99. imshow_wait("sc_gray", m_grayImg);
  100. imshow_wait("sc_bin", m_binImg);
  101. }
  102. if(m_pLogger){
  103. m_pLogger->DEBUG(m_imgId+" scion after m_binImg\m_grayImg image show.");
  104. }
  105. //////////////////////////////////////////////////////////////////
  106. //stem x-range
  107. vector<int> hist_col;
  108. mat_histogram(m_binImg,hist_col);
  109. if(m_cparam.image_show){
  110. Mat hist_col_img;
  111. hist2image(hist_col,hist_col_img,1,m_cparam.image_col_grid,m_cparam.image_row_grid);
  112. imshow_wait("sc_hist_col", hist_col_img);
  113. }
  114. if(m_pLogger){
  115. m_pLogger->DEBUG(m_imgId+" scion after histogram col.");
  116. }
  117. int x0,x1,stem_x0, stem_x1;
  118. get_stem_x_range_scion(hist_col,
  119. m_cparam.sc_col_th_ratio,
  120. m_cparam.sc_stem_x_padding,
  121. x0,
  122. x1,
  123. stem_x0,
  124. stem_x1
  125. );
  126. if(m_pLogger){
  127. stringstream buff;
  128. buff<<m_imgId<<" scion image, x0="<<x0
  129. <<"\tx1="<<x1
  130. <<"\tstem_x0="<<stem_x0
  131. <<"\tstem_x1="<<stem_x1;
  132. m_pLogger->INFO(buff.str());
  133. }
  134. if(m_cparam.image_show){
  135. Mat tmp_img = m_binImg.clone();
  136. line(tmp_img,Point(x0,0),Point(x0,m_binImg.rows-1),Scalar(100),2);
  137. line(tmp_img,Point(x1,0),Point(x1,m_binImg.rows-1),Scalar(100),2);
  138. //fork right point
  139. imshow_wait("sc_x_field", tmp_img);
  140. }
  141. /////////////////////////////////////////////////////
  142. // 茎分离,通过高亮夹子分割
  143. vector<int> hist_row;
  144. mat_histogram(m_binImg,hist_row,1,-1,-1,x0,x1+1);
  145. if(m_pLogger){
  146. m_pLogger->DEBUG(m_imgId+" scion after histogram row.");
  147. }
  148. if(m_cparam.image_show){
  149. Mat hist_row_img;
  150. hist2image(hist_row,hist_row_img, 0,m_cparam.image_col_grid,m_cparam.image_row_grid);
  151. imshow_wait("sc_hist_row", hist_row_img);
  152. }
  153. else{
  154. t = clock();
  155. if(1000.0*((float)(t-t0))/CLOCKS_PER_SEC>(float)m_cparam.timeout_proc){
  156. throw_msg(m_imgId+" time out");
  157. }
  158. }
  159. int stem_y_min=-1;//,stem_dia=-1,stem_root_y=-1;
  160. vector<int> sub_hist_col;
  161. vector<int> sub_hist_row;
  162. int sub_stem_x0,sub_stem_x1,sub_x0,sub_x1;
  163. sub_x0 = sub_x1=sub_stem_x0=sub_stem_x1=-1;
  164. Mat scionBinImg;
  165. get_stem_local_img(
  166. hist_row,
  167. m_cparam.sc_stem_dia_min,
  168. x0,
  169. x1,
  170. stem_y_min,
  171. sub_hist_col,
  172. sub_hist_row,
  173. scionBinImg);
  174. get_stem_x_range_scion(
  175. sub_hist_col,
  176. m_cparam.sc_col_th_ratio,
  177. m_cparam.sc_stem_x_padding,
  178. sub_x0,
  179. sub_x1,
  180. sub_stem_x0,
  181. sub_stem_x1);
  182. int ymin=-1,ymax=-1;
  183. get_cut_up_point(
  184. sub_hist_row,
  185. stem_y_min,
  186. m_cparam.sc_r2_th,
  187. m_cparam.sc_average_window,
  188. m_cparam.sc_r2_window,
  189. ymin,
  190. ymax);
  191. int slop_fold_x = get_stem_fork_left(
  192. scionBinImg,
  193. ymin,
  194. sub_stem_x0,
  195. sub_stem_x1,
  196. m_cparam.sc_stem_edge_detect_window);
  197. int slop_cent_y = (int)((float)(ymin+ymax)/2.0);
  198. int slop_cent_x = get_stem_fork_left(
  199. scionBinImg,
  200. slop_cent_y,
  201. sub_stem_x0,
  202. sub_stem_x1,
  203. m_cparam.sc_stem_edge_detect_window);
  204. int slop_lower_x = get_stem_fork_left(
  205. scionBinImg,
  206. ymax,
  207. sub_stem_x0,
  208. sub_stem_x1,
  209. m_cparam.sc_stem_edge_detect_window);
  210. //update sub_stem_x0, sub_stem_x1
  211. stem_x0 = x0 + sub_stem_x0;
  212. stem_x1 = x0 + sub_stem_x1;
  213. slop_fold_x += x0;
  214. slop_cent_x += x0;
  215. slop_lower_x += x0;
  216. //update ymin,ymax
  217. ymin += stem_y_min;
  218. ymax += stem_y_min;
  219. slop_cent_y += stem_y_min;
  220. Mat binRoi = m_binImg(Rect(x0,stem_y_min,scionBinImg.cols,scionBinImg.rows));
  221. scionBinImg.copyTo(binRoi);
  222. //imshow_wait("m_binImg", m_binImg);
  223. if(m_pLogger){
  224. stringstream buff;
  225. buff<<m_imgId<<" scion image result(pixel) x0="<<x0
  226. <<", x1="<<x1
  227. <<", stem_x0="<<stem_x0
  228. <<", stem_x1="<<stem_x1
  229. <<", cut_pt=("<<slop_fold_x<<","<<ymin<<")"
  230. <<", cut_cent_pt=("<<slop_cent_x<<","<<slop_cent_y<<")"
  231. <<", cut_lower_pt=("<<slop_lower_x<<","<<ymax<<")"
  232. <<", stem_y_max="<<ymax;
  233. m_pLogger->INFO(buff.str());
  234. }
  235. if(m_cparam.image_show){
  236. Mat result_img = m_binImg.clone();
  237. //stem x-range
  238. line(result_img,Point(stem_x0,0),Point(stem_x0,m_binImg.rows-1),Scalar(100),2);
  239. line(result_img,Point(stem_x1,0),Point(stem_x1,m_binImg.rows-1),Scalar(100),2);
  240. //fork y line
  241. line(result_img,Point(stem_x0,stem_y_min),Point(stem_x1,stem_y_min),Scalar(100),2);
  242. line(result_img,Point(stem_x0,ymax),Point(stem_x1,ymax),Scalar(100),2);
  243. //fold right point
  244. circle(result_img, Point(slop_fold_x,ymin),2, Scalar(150,0,128), -1, 8,0);
  245. circle(result_img, Point(slop_cent_x,slop_cent_y),2, Scalar(150,0,128), -1, 8,0);
  246. circle(result_img, Point(slop_lower_x,ymax),2, Scalar(150,0,128), -1, 8,0);
  247. imshow_wait("sc_result", result_img);
  248. }
  249. double sc_cut_upoint_x, sc_cut_upoint_y,sc_cut_cpoint_x, sc_cut_cpoint_y, sc_cut_lpoint_x, sc_cut_lpoint_y;
  250. sc_cut_upoint_x = (double)slop_fold_x;
  251. sc_cut_upoint_x -= (double)(img.cols/2.0);
  252. sc_cut_upoint_x *= m_cparam.sc_cut_pixel_ratio;
  253. sc_cut_upoint_y = (double)ymin;
  254. sc_cut_upoint_y = (double)(img.rows/2.0) - sc_cut_upoint_y;
  255. sc_cut_upoint_y *= m_cparam.sc_cut_pixel_ratio;
  256. sc_cut_cpoint_x = (double)slop_cent_x;
  257. sc_cut_cpoint_x -= (double)(img.cols/2.0);
  258. sc_cut_cpoint_x *= m_cparam.sc_cut_pixel_ratio;
  259. sc_cut_cpoint_y = (double)slop_cent_y;
  260. sc_cut_cpoint_y = (double)(img.rows/2.0) - sc_cut_cpoint_y;
  261. sc_cut_cpoint_y *= m_cparam.sc_cut_pixel_ratio;
  262. sc_cut_lpoint_x = (double)slop_lower_x;
  263. sc_cut_lpoint_x -= (double)(img.cols/2.0);
  264. sc_cut_lpoint_x *= m_cparam.sc_cut_pixel_ratio;
  265. sc_cut_lpoint_y = (double)ymax;
  266. sc_cut_lpoint_y = (double)(img.rows/2.0) - sc_cut_lpoint_y;
  267. sc_cut_lpoint_y *= m_cparam.sc_cut_pixel_ratio;
  268. posinfo.sc_cut_upoint_x = sc_cut_upoint_x;
  269. posinfo.sc_cut_upoint_y = sc_cut_upoint_y;
  270. posinfo.sc_cut_cpoint_x = sc_cut_cpoint_x;
  271. posinfo.sc_cut_cpoint_y = sc_cut_cpoint_y;
  272. posinfo.sc_cut_lpoint_x = sc_cut_lpoint_x;
  273. posinfo.sc_cut_lpoint_y = sc_cut_lpoint_y;
  274. if(m_pLogger){
  275. stringstream buff;
  276. buff<<m_imgId<<" scion image result(mm)"
  277. <<", cut_pt=("<<sc_cut_upoint_x<<","<<sc_cut_upoint_y<<")"
  278. <<", cut_cent_pt=("<<sc_cut_cpoint_x<<","<<sc_cut_cpoint_y<<")"
  279. <<", cut_lower_pt=("<<sc_cut_lpoint_x<<","<<sc_cut_lpoint_y<<")";
  280. m_pLogger->INFO(buff.str());
  281. }
  282. // posinfo.pp_images
  283. if(m_cparam.image_return){
  284. this->clear_imginfo();
  285. //1)
  286. //stem x-range
  287. line(m_binImg,Point(stem_x0,0),Point(stem_x0,m_binImg.cols-1),Scalar(100),2);
  288. line(m_binImg,Point(stem_x1,0),Point(stem_x1,m_binImg.cols-1),Scalar(100),2);
  289. //fork y line
  290. line(m_binImg,Point(stem_x0,stem_y_min),Point(stem_x1,stem_y_min),Scalar(100),2);
  291. line(m_binImg,Point(stem_x0,ymax),Point(stem_x1,ymax),Scalar(100),2);
  292. //fold right point
  293. circle(m_binImg, Point(slop_fold_x,ymin),5, Scalar(128,0,128), -1, 8,0);
  294. circle(m_binImg, Point(slop_cent_x,slop_cent_y),5, Scalar(128,0,128), -1, 8,0);
  295. circle(m_binImg, Point(slop_lower_x,ymax),5, Scalar(128,0,128), -1, 8,0);
  296. m_pImginfoBinFork=mat2imginfo(m_binImg);
  297. //
  298. posinfo.pp_images[0]=m_pImginfoBinFork;
  299. if(m_ppImgSaver && *m_ppImgSaver){
  300. (*m_ppImgSaver)->saveImage(m_binImg, m_imgId+"_rst_0");
  301. }
  302. }
  303. if(m_pLogger){
  304. m_pLogger->INFO(m_imgId +" scion cut_pt detect finished.");
  305. }
  306. return ymin;
  307. }
  308. void CScionCutPoint::img_segment(Mat&img)
  309. {
  310. if(img.channels()!=1){
  311. //color image ,bgr, for testing
  312. Mat b_img;
  313. cvtColor(img,m_grayImg,COLOR_BGR2GRAY);
  314. double th = threshold(
  315. m_grayImg,
  316. b_img,
  317. 255,
  318. 255,
  319. THRESH_OTSU
  320. );
  321. Mat kernel = getStructuringElement(
  322. MORPH_RECT,
  323. Size( 2*m_cparam.sc_morph_radius + 1, 2*m_cparam.sc_morph_radius+1),
  324. Point( m_cparam.sc_morph_radius, m_cparam.sc_morph_radius)
  325. );
  326. morphologyEx(
  327. b_img,
  328. m_binImg,
  329. MORPH_CLOSE,
  330. kernel,
  331. Point(-1,-1),
  332. m_cparam.sc_morph_iteration);
  333. }
  334. else{
  335. //from imginfo image, gray image
  336. //int morph_size = 1;
  337. m_grayImg = img.clone();
  338. Mat b_img;
  339. double th = threshold(img, b_img, 255, 255,THRESH_OTSU);
  340. Mat kernel = getStructuringElement(
  341. MORPH_RECT,
  342. Size( 2*m_cparam.sc_morph_radius + 1, 2*m_cparam.sc_morph_radius+1 ),
  343. Point( m_cparam.sc_morph_radius, m_cparam.sc_morph_radius)
  344. );
  345. morphologyEx(
  346. b_img,
  347. m_binImg,
  348. MORPH_OPEN,
  349. kernel,
  350. Point(-1,-1),
  351. m_cparam.sc_morph_iteration
  352. );
  353. }
  354. }
  355. void CScionCutPoint::get_stem_local_img(
  356. const std::vector<int>& hist,
  357. int stem_dia_min,
  358. int x0,
  359. int x1,
  360. int& stem_y_min,
  361. vector<int>& hist_col,
  362. vector<int>& hist_row,
  363. Mat& scionBinImg
  364. )
  365. {
  366. //1 夹子被罩上背景板,不能以高亮夹子定位
  367. //2 直接找x范围内下方的大目标作为识别对象,目标像素面积
  368. //3 截取部分图像单独做二值化得到二值图像
  369. //4 局部图的histogram
  370. //0 设定目标面积最小值
  371. int min_obj_area = 100;
  372. //1 reverse histogram
  373. std::vector<int>reversed_hist;
  374. for(int i=hist.size()-1; i>=0;i--){
  375. reversed_hist.push_back(hist[i]);
  376. }
  377. stem_y_min = -1;
  378. int stem_y_max = -1;
  379. int start_idx, end_idx;
  380. start_idx = end_idx = -1;
  381. for(size_t i=0; i < reversed_hist.size(); ++i){
  382. if(i==0){
  383. if(reversed_hist[i]>=stem_dia_min){start_idx=i;}
  384. continue;
  385. }
  386. if(reversed_hist[i]>=stem_dia_min && reversed_hist[i-1]<stem_dia_min){
  387. start_idx=i;
  388. continue;
  389. }
  390. if(reversed_hist[i]<stem_dia_min && reversed_hist[i-1]>=stem_dia_min){
  391. //计算面积
  392. int area = 0;
  393. for(int oi=start_idx;oi<i;++oi){
  394. area +=reversed_hist[oi];
  395. }
  396. if(area >= min_obj_area){
  397. end_idx = i-1;
  398. break;
  399. }
  400. }
  401. if(i==reversed_hist.size()-1){
  402. if(reversed_hist[i]>=stem_dia_min){
  403. int area = 0;
  404. for(int oi=start_idx;oi<=i;++oi){
  405. area +=reversed_hist[oi];
  406. }
  407. if(area >= min_obj_area){
  408. end_idx = i;
  409. break;
  410. }
  411. }
  412. }
  413. }
  414. if(start_idx<0 || end_idx<0 || end_idx<=start_idx){
  415. throw_msg( m_imgId+string(" scion stem NOT exists valid sub-image"));
  416. }
  417. stem_y_min = end_idx + m_cparam.sc_clip_padding;
  418. stem_y_max = start_idx - 10* m_cparam.sc_clip_padding;
  419. if(stem_y_min>=m_grayImg.rows){stem_y_min = m_grayImg.rows;}
  420. if(stem_y_max<0){stem_y_max=0;}
  421. //2
  422. if(m_cparam.image_show){
  423. Mat grayimg = m_grayImg.clone();
  424. rectangle(grayimg,
  425. Point(x0,grayimg.rows-stem_y_min),
  426. Point(x1,grayimg.rows-stem_y_max),
  427. Scalar(128));
  428. imshow_wait("gray_rect",grayimg);
  429. }
  430. Mat scionImg = m_grayImg(Rect(x0,
  431. m_grayImg.rows-stem_y_min,
  432. x1-x0,
  433. stem_y_min-stem_y_max));
  434. if(m_pLogger){
  435. stringstream buff;
  436. buff<<m_imgId<<" scion image, sub image(x0,y0,width,height), x0="<<x0
  437. <<", y0="<<m_grayImg.rows-stem_y_min
  438. <<", width="<<x1-x0
  439. <<", height="<<stem_y_min-stem_y_max;
  440. m_pLogger->INFO(buff.str());
  441. }
  442. //Mat scionBinImg;
  443. double th = threshold(scionImg, scionBinImg, 255, 255,THRESH_OTSU);
  444. if(m_cparam.image_show){
  445. imshow_wait("scion_sub_bin",scionBinImg);
  446. vector<int>hist;
  447. mat_histogram(scionBinImg,hist,1);
  448. Mat hist_col_img;
  449. hist2image(hist,hist_col_img,1,m_cparam.image_col_grid,m_cparam.image_row_grid);
  450. imshow_wait("sc_sub_hist_col", hist_col_img);
  451. }
  452. //3
  453. hist_row.clear();
  454. mat_histogram(scionBinImg,hist_row,1,-1,-1,-1,-1);
  455. hist_col.clear();
  456. mat_histogram(scionBinImg,hist_col,0,-1,-1,-1,-1);
  457. stem_y_min = m_grayImg.rows-stem_y_min;
  458. };
  459. void CScionCutPoint::get_cut_up_point(
  460. const std::vector<int>& sub_hist,
  461. int stem_y_min,
  462. double r2_ratio_th,
  463. int average_window,
  464. int r2_window,
  465. int& ymin,
  466. int& ymax)
  467. {
  468. int start_idx=-1;
  469. int max_len=0;
  470. int max_start_idx = -1;
  471. int max_end_idx = -1;
  472. for(size_t i=0;i<sub_hist.size();++i){
  473. if(i==0 && sub_hist[i]>0){
  474. start_idx=i;
  475. continue;
  476. }
  477. if(i==0){continue;}
  478. if(sub_hist[i]>0 && sub_hist[i-1]==0){
  479. start_idx=i;
  480. continue;
  481. }
  482. if((sub_hist[i]==0 && sub_hist[i-1]>0) ||
  483. (i==sub_hist.size()-1 && sub_hist[i]>0)){
  484. if((i-start_idx) >max_len ){
  485. max_end_idx=i;
  486. max_start_idx = start_idx;
  487. max_len = i-start_idx;
  488. }
  489. continue;
  490. }
  491. }
  492. if(max_end_idx<0){
  493. throw_msg(m_imgId+" scion stem sub-image histogram is all zero");
  494. }
  495. ymin = max_start_idx;
  496. ymax = max_end_idx-1;
  497. vector<int>ys;
  498. for (size_t i = ymin; i < ymax+1; ++i){
  499. ys.insert(ys.begin(),sub_hist[i]);
  500. }
  501. int fold_point = -1;
  502. vector<int> ys_tmp;
  503. for(size_t i=0;i<ys.size();++i){ys_tmp.push_back(ys[i]);}
  504. int mid_idx = (int)((float)ys.size() * 0.5);
  505. sort(ys_tmp.begin(), ys_tmp.end());
  506. int mid_stem_dia = ys_tmp[mid_idx];
  507. int th_stem_dia = (int)((double)mid_stem_dia*0.95 +0.5);
  508. for(size_t i=0;i<ys.size();++i){
  509. if(ys[i]>=th_stem_dia){
  510. fold_point = i;
  511. break;
  512. }
  513. }
  514. int offset=10;
  515. if(m_folder_th<0){
  516. double ca = (75.+m_cparam.rs_cut_angle)*0.0174532925199433;
  517. m_folder_th = (int)(offset * fabs(tan(ca)) + 0.5);
  518. }
  519. while(true){
  520. int pre_idx = fold_point-offset;
  521. if(pre_idx<=0){break;}
  522. if(ys[fold_point] - ys[pre_idx] >=m_folder_th){break;}
  523. fold_point--;
  524. }
  525. ymin = ymax-fold_point;
  526. }
  527. };