utils.cpp 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837
  1. #include "utils.h"
  2. #include <opencv.hpp>
  3. #include <time.h>
  4. #include <algorithm>
  5. #include <math.h>
  6. //#include "fork_rs.h"
  7. namespace graft_cv{
  8. cv::Mat imginfo2mat(ImgInfo* imginfo)
  9. {
  10. assert(imginfo->data);
  11. assert(imginfo->height>0 && imginfo->width>0);
  12. cv::Mat m = cv::Mat(imginfo->height,imginfo->width,CV_8UC1);
  13. for(int h=0; h<imginfo->height; ++h)
  14. {
  15. memcpy((void*)(m.ptr(h)),
  16. (void*)(imginfo->data+h*imginfo->width),
  17. imginfo->width);
  18. };
  19. return m;
  20. };
  21. ImgInfo* mat2imginfo(const cv::Mat&img){
  22. assert(img.channels()==1);
  23. ImgInfo* imginfo = new ImgInfo();
  24. imginfo->angle=0;
  25. imginfo->height=img.rows;
  26. imginfo->width = img.cols;
  27. imginfo->data = 0;
  28. imginfo->data = new byte[imginfo->height * imginfo->width];
  29. memset(imginfo->data, 0,imginfo->height * imginfo->width);
  30. //IplImage ipl_img = cvIplImage(img);
  31. for(int i =0; i<imginfo->height;++i){
  32. memcpy(imginfo->data+i*imginfo->width, img.ptr(i), imginfo->width);
  33. }
  34. return imginfo;
  35. };
  36. void imginfo_release(ImgInfo**ppImgInfo){
  37. ImgInfo* pImgInfo = *ppImgInfo;
  38. if(pImgInfo->data){
  39. delete [] pImgInfo->data;
  40. pImgInfo->data=0;
  41. }
  42. delete pImgInfo;
  43. pImgInfo = 0;
  44. };
  45. bool isvalid(const ImgInfo*imginfo)
  46. {
  47. if(!imginfo){return false;}
  48. if(!imginfo->data){return false;}
  49. if(imginfo->height*imginfo->width<=0){return false;}
  50. return true;
  51. }
  52. void image_set_bottom(
  53. cv::Mat& img,
  54. unsigned char value,
  55. int rows_cnt)
  56. {
  57. for(size_t r=(img.rows-rows_cnt);r<img.rows;++r){
  58. for(size_t c=0;c<img.cols;++c){
  59. img.at<unsigned char>(r,c) = value;
  60. }
  61. }
  62. }
  63. void image_set_top(
  64. cv::Mat& img,
  65. unsigned char value,
  66. int rows_cnt)
  67. {
  68. for(size_t r=0;r<rows_cnt;++r){
  69. for(size_t c=0;c<img.cols;++c){
  70. img.at<unsigned char>(r,c) = value;
  71. }
  72. }
  73. }
  74. void image_draw_line(
  75. cv::Mat& img,
  76. int x0,int y0,
  77. int x1,int y1)
  78. {
  79. cv::Point p0,p1;
  80. if(x0==x1){
  81. p0.x=x0;
  82. p0.y=0;
  83. p1.x=x0;
  84. p1.y=img.rows-1;
  85. }
  86. else{
  87. double k = (double)(y1-y0)/(double)(x1-x0);
  88. double b = (double)y0 - k * x0;
  89. p0.x=0;
  90. p0.y=b;
  91. p1.x=img.cols;
  92. p1.y = (int)(b + k * p1.x);
  93. }
  94. cv::line(img,p0,p1, cv::Scalar(255,255,255));
  95. }
  96. string currTime(){
  97. char tmp[64];
  98. struct tm ptime;
  99. time_t time_seconds = time(0);
  100. localtime_s(&ptime, &time_seconds);
  101. strftime(
  102. tmp,
  103. sizeof(tmp),
  104. "%Y-%m-%d %H:%M:%S",
  105. &ptime
  106. );
  107. return tmp;
  108. }
  109. // function getImgId
  110. // input im_type, img_type
  111. static unsigned int ID_COUNTER = 0;
  112. string getImgId(int im_type)
  113. {
  114. char tmp[64];
  115. struct tm ptime;
  116. time_t time_seconds = time(0);
  117. localtime_s(&ptime, &time_seconds);
  118. strftime(
  119. tmp,
  120. sizeof(tmp),
  121. "%Y%m%d%H%M%S",
  122. &ptime
  123. );
  124. unsigned int id_serial = ID_COUNTER % 100;
  125. stringstream buff;
  126. buff<<tmp;
  127. buff.width(2);
  128. buff.fill('0');
  129. buff<<id_serial;
  130. buff<<"_"<<im_type;
  131. ID_COUNTER+=1;
  132. return buff.str();
  133. }
  134. string getImgIdOa(string iid, int im_idx)
  135. {
  136. stringstream buff;
  137. buff<<im_idx;
  138. return iid+"_"+buff.str();
  139. }
  140. inline void throw_msg(string& msg){
  141. stringstream buff;
  142. buff<<"Error:"<<msg<<"\tfile:"<<__FILE__<<"\tline:"<<__LINE__;
  143. throw(buff.str());
  144. }
  145. void drawgrid(
  146. cv::Mat&img,
  147. int grid_col/*=50*/,
  148. int grid_row/*=50*/,
  149. int line_thick/*=2*/
  150. )
  151. {
  152. if(img.empty()){return;}
  153. //horizontal grid
  154. for(int row=img.rows-1; row>=0; row-=grid_row){
  155. cv::line(img, cv::Point(0,row), cv::Point(img.cols-1,row), cv::Scalar(100),line_thick);
  156. }
  157. //veritcal grid
  158. for(int col=0; col<img.cols; col+=grid_col){
  159. cv::line(img, cv::Point(col,0), cv::Point(col,img.rows-1), cv::Scalar(100),line_thick);
  160. }
  161. }
  162. void hist2image(
  163. vector<int>& hist,
  164. cv::Mat&img,
  165. int aix, /*=1*/
  166. int grid_col/*=50*/,
  167. int grid_row/*=50*/
  168. )
  169. {
  170. vector<int>::iterator maxit = max_element(begin(hist),end(hist));
  171. if( grid_col<10){grid_col=10;}
  172. if( grid_row<10){grid_row=10;}
  173. // aix = 0, column hist, aix=1, row histogtam
  174. if (aix==0){
  175. img = cv::Mat::zeros(hist.size(),*maxit, CV_8UC1);
  176. drawgrid(img,grid_col, grid_row,1);
  177. for(size_t i=0; i<hist.size();++i){
  178. if(i>=img.rows){break;}
  179. for(size_t j=0;j<=hist[i];++j){
  180. if(j>=img.cols){break;}
  181. img.at<unsigned char>(i,j)=255;
  182. }
  183. }
  184. }
  185. else{
  186. img = cv::Mat::zeros(*maxit,hist.size(), CV_8UC1);
  187. drawgrid(img,grid_col, grid_row,1);
  188. for(size_t i=0; i<hist.size();++i){
  189. int y = *maxit-hist[i]-1;
  190. if (y<0){y=0;}
  191. for(size_t j=y;j<img.rows;++j){
  192. img.at<unsigned char>(j,i)=255;
  193. }
  194. }
  195. }
  196. };
  197. //matHistogram
  198. // axis == 0, statistic histogram for columns;
  199. // others, statistic histogram for rows
  200. void mat_histogram(
  201. cv::Mat& img,
  202. std::vector<int>&hist,
  203. int axis,
  204. int r0,
  205. int r1,
  206. int c0,
  207. int c1
  208. )
  209. {
  210. hist.clear();
  211. if(r0<0){r0 = 0;}
  212. if(r1<0){r1 = img.rows;}
  213. if(c0<0){c0 = 0;}
  214. if(c1<0){c1 = img.cols;}
  215. if (axis==0){
  216. for(int c=c0;c<c1;++c){
  217. int cnt = 0;
  218. for(int r=r0;r<r1; ++r){
  219. if (img.at<unsigned char>(r,c)>0){cnt+=1;}
  220. }
  221. hist.push_back(cnt);
  222. }
  223. }
  224. else{
  225. for(int r=r0;r<r1; ++r){
  226. int cnt = 0;
  227. for(int c=c0;c<c1;++c){
  228. if (img.at<unsigned char>(r,c)>0){cnt+=1;}
  229. }
  230. hist.push_back(cnt);
  231. }
  232. }
  233. };
  234. //matHistogram
  235. // axis == 0, statistic histogram for columns;
  236. // others, statistic histogram for rows
  237. // with weight
  238. void mat_histogram_w(
  239. cv::Mat& img,
  240. std::vector<int>&hist,
  241. int axis,
  242. int r0,
  243. int r1,
  244. int c0,
  245. int c1,
  246. bool asc_w //true---ascending weight, [0-1.0], false --- descending weight [1.0--0.0]
  247. )
  248. {
  249. hist.clear();
  250. if(r0<0){r0 = 0;}
  251. if(r1<0){r1 = img.rows;}
  252. if(c0<0){c0 = 0;}
  253. if(c1<0){c1 = img.cols;}
  254. if (axis==0){
  255. double step = 1.0/(double)(r1-1);
  256. for(int c=c0;c<c1;++c){
  257. double cnt = 0.0;
  258. double hi=0.0;
  259. for(int r=r0;r<r1; ++r){
  260. if (img.at<unsigned char>(r,c)>0){
  261. if(asc_w){
  262. hi = (r-r0)*step;
  263. if(hi>=0.66666){
  264. cnt+=hi;
  265. }
  266. }
  267. else{
  268. hi = (r1-r+r0)*step;
  269. if(hi>=0.66666){
  270. cnt+=hi;
  271. }
  272. }
  273. }
  274. }
  275. hist.push_back((int)cnt);
  276. }
  277. }
  278. else{
  279. double step = 1.0/(double)(c1-1);
  280. for(int r=r0;r<r1; ++r){
  281. double cnt = 0.0;
  282. double hi=0.0;
  283. for(int c=c0;c<c1;++c){
  284. if (img.at<unsigned char>(r,c)>0){
  285. if(asc_w){
  286. hi = (c-c0)*step;
  287. if(hi>=0.66666){
  288. cnt+=hi;
  289. }
  290. }
  291. else{
  292. hi = (c1-c+c0)*step;
  293. if(hi>=0.66666){
  294. cnt+=hi;
  295. }
  296. }
  297. }
  298. }
  299. hist.push_back((int)cnt);
  300. }
  301. }
  302. };
  303. void mat_histogram_yfork(
  304. cv::Mat& img,
  305. std::vector<int>&hist,
  306. int r0,
  307. int r1
  308. )
  309. {
  310. hist.clear();
  311. if(r0<0){r0 = 0;}
  312. if(r1<0){r1 = img.rows;}
  313. double step = 1.0/(double)(r1-r0);
  314. for(int c=0;c<img.cols;++c){
  315. double cnt = 0.0;
  316. double hi=0.0;
  317. for(int r=r0;r<r1; ++r){
  318. if (img.at<unsigned char>(r,c)>0){
  319. hi = (r-r0)*step;
  320. //if(hi>=0.66666){
  321. cnt+=hi;
  322. //}
  323. }
  324. }
  325. hist.push_back((int)cnt);
  326. }
  327. };
  328. //get_stem_x_range() 确定茎的位置,x方向
  329. // 2022/1/4 chenhj 修改,大叶子下垂,将叶子识别成茎,增加histogram满足阈值情况下,两侧数据方差检测
  330. void get_stem_x_range_oa(
  331. const std::vector<int>&hist_h,
  332. double h_ratio,
  333. int padding,
  334. int cent_x,
  335. int width_x,
  336. int&x0,
  337. int&x1,
  338. int&stem_x0,
  339. int&stem_x1
  340. )
  341. {
  342. //hist_h: histogram in column
  343. //h_ratio: ratio for generating threshold
  344. // x0,x1: sub-image x-range
  345. // stem_x0,1: stem x-range
  346. // padding: pad for stem x-range
  347. // calculate the max-value of hist_h
  348. int min_segment_len = 0;//最小线段长度,如果小于此数值,不计为有效数据,跳过
  349. int min_segment_dist = 20;//多峰情况下,间隔像素数量
  350. int var_padding = 20;
  351. auto max_it = max_element(hist_h.begin(), hist_h.end());
  352. size_t max_idx = max_it - hist_h.begin();
  353. int th = int(h_ratio * (double)(*max_it));
  354. if(th<=0){
  355. throw_msg(string("error: threshold is 0 in get_stem_x_range()"));
  356. }
  357. // 1 计算小线段的起始位置
  358. vector<gcv_point<int>>segments;
  359. get_hist_segment(hist_h,th,segments);
  360. // 2 获取多峰位置
  361. vector<gcv_point<int>>peaks;
  362. if(segments.size()==1){
  363. peaks = segments; //单峰
  364. }
  365. else{// 小线段删除
  366. vector<gcv_point<int>>big_segments;
  367. for(size_t i=0;i<segments.size();++i){
  368. if(segments[i].y - segments[i].x>=min_segment_len){
  369. big_segments.push_back(segments[i]);
  370. }
  371. }
  372. if(big_segments.size()==1){
  373. peaks = big_segments;//单峰
  374. }
  375. else{// 合并
  376. peaks.push_back(big_segments[0]);
  377. for(size_t j=1;j<big_segments.size();++j){
  378. int dist = big_segments[j].x - peaks.back().y;
  379. if(dist>min_segment_dist){
  380. peaks.push_back(big_segments[j]);
  381. }
  382. else{
  383. peaks.back().y = big_segments[j].y;
  384. }
  385. }
  386. }
  387. }
  388. // 3 多峰情况,先计算center_ratio, 统计峰段两侧数据的方差,方差大的为茎
  389. int opt_index=0;
  390. if(peaks.size()>1){
  391. vector<double>center_r;
  392. for(size_t i=0;i<peaks.size();++i){
  393. double r = fabs(0.5*(double)(peaks[i].x+ peaks[i].y) - cent_x)/(double)(width_x/2.0);
  394. center_r.push_back(1.0-r);
  395. }
  396. vector<size_t>sort_idx = sort_indexes_e(center_r,false);
  397. double cr_1 = center_r[sort_idx[0]];
  398. double cr_2 = center_r[sort_idx[1]];
  399. if(cr_1-cr_2>0.2 || cr_1>=0.65){//如果存在居中显著的,按居中优先
  400. opt_index = sort_idx[0];
  401. }
  402. else{
  403. vector<int> candidate_idx;
  404. candidate_idx.push_back(sort_idx[0]);
  405. candidate_idx.push_back(sort_idx[1]);
  406. for(size_t id = 2;id<sort_idx.size();++id){
  407. if(cr_1 - center_r[sort_idx[id]]<=0.3){
  408. candidate_idx.push_back(sort_idx[id]);
  409. }
  410. }
  411. vector<double>vars;
  412. int vx0,vx1;
  413. for(size_t i=0;i<peaks.size();++i){
  414. vector<int>::iterator it = find(candidate_idx.begin(),candidate_idx.end(),i);
  415. if(it==candidate_idx.end()){
  416. vars.push_back(0.0);
  417. continue;
  418. }
  419. vx0 = peaks[i].x - var_padding;
  420. vx1 = peaks[i].y + var_padding;
  421. if(vx0<0){vx0=0;}
  422. if(vx1>hist_h.size()-1){vx1=hist_h.size()-1;}
  423. double mean,var;
  424. mean = 0.0;
  425. var=-1;
  426. get_hist_mean_var_local(hist_h,vx0,vx1,mean,var);
  427. if(var<0){var=0.0;}
  428. vars.push_back(var);
  429. }
  430. auto var_max_it = max_element(vars.begin(), vars.end());
  431. size_t var_max_idx = var_max_it - vars.begin();
  432. opt_index = var_max_idx;
  433. }
  434. }
  435. stem_x0 = peaks[opt_index].x;
  436. stem_x1 = peaks[opt_index].y;
  437. x0 = stem_x0 - padding;
  438. x1 = stem_x1 + padding;
  439. if(x0<0){x0=0;};
  440. if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
  441. //////////////////////////////////////
  442. //int global_start_idx=-1;
  443. //int global_end_idx=-1;
  444. //int start_idx=-1;
  445. //int end_idx=-1;
  446. //for(size_t i=0;i<hist_h.size();++i){
  447. // if(i==0){
  448. // if(hist_h[i]>=th){
  449. // start_idx=i;
  450. // }
  451. // continue;
  452. // }
  453. // if(hist_h[i]>=th && hist_h[i-1]<th){
  454. // start_idx=i;
  455. // continue;
  456. // }
  457. // if(i==hist_h.size()-1){
  458. // if(hist_h[i]>=th && hist_h[i-1]>=th){
  459. // if((i-start_idx+1)>=min_segment_len){
  460. // global_end_idx=i;
  461. // }
  462. // }
  463. // }
  464. // if(hist_h[i]<th && hist_h[i-1]>=th){
  465. // if((i-start_idx+1)>=min_segment_len){
  466. // if( global_start_idx<0){
  467. // global_start_idx = start_idx;
  468. // }
  469. // global_end_idx = i;
  470. // }
  471. // }
  472. //}
  473. ///*stem_x0 = 0;
  474. //stem_x1 = hist_h.size()-1;
  475. //for(size_t i=max_idx; i < hist_h.size(); ++i){
  476. // if(hist_h[i]>=th){
  477. // stem_x1 = i;
  478. // }
  479. // else{
  480. // break;
  481. // }
  482. //}
  483. //for(int i=max_idx; i >= 0; i--){
  484. // if(hist_h[i]>=th){
  485. // stem_x0 = i;
  486. // }
  487. // else{
  488. // break;
  489. // }
  490. //}*/
  491. //stem_x0 = global_start_idx;
  492. //stem_x1 = global_end_idx;
  493. //x0 = stem_x0 - padding;
  494. //x1 = stem_x1 + padding;
  495. //if(x0<0){x0=0;};
  496. //if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
  497. };
  498. void get_stem_x_range_rscut(
  499. const std::vector<int>&hist_h,
  500. double h_ratio,
  501. int padding,
  502. int cent_x,
  503. int width_x,
  504. int&x0,
  505. int&x1,
  506. int&stem_x0,
  507. int&stem_x1
  508. )
  509. {
  510. //hist_h: histogram in column
  511. //h_ratio: ratio for generating threshold
  512. // x0,x1: sub-image x-range
  513. // stem_x0,1: stem x-range
  514. // padding: pad for stem x-range
  515. // calculate the max-value of hist_h
  516. int min_segment_len = 0;//最小线段长度,如果小于此数值,不计为有效数据,跳过
  517. int min_segment_dist = 20;//多峰情况下,间隔像素数量
  518. int var_padding = 20;
  519. auto max_it = max_element(hist_h.begin(), hist_h.end());
  520. size_t max_idx = max_it - hist_h.begin();
  521. int th = int(h_ratio * (double)(*max_it));
  522. if(th<=0){
  523. throw_msg(string("error: threshold is 0 in get_stem_x_range()"));
  524. }
  525. // 1 计算小线段的起始位置
  526. vector<gcv_point<int>>segments;
  527. get_hist_segment(hist_h,th,segments);
  528. // 2 获取多峰位置
  529. vector<gcv_point<int>>peaks;
  530. if(segments.size()==1){
  531. peaks = segments; //单峰
  532. }
  533. else{// 小线段删除
  534. vector<gcv_point<int>>big_segments;
  535. for(size_t i=0;i<segments.size();++i){
  536. if(segments[i].y - segments[i].x>=min_segment_len){
  537. big_segments.push_back(segments[i]);
  538. }
  539. }
  540. if(big_segments.size()==1){
  541. peaks = big_segments;//单峰
  542. }
  543. else{// 合并
  544. peaks.push_back(big_segments[0]);
  545. for(size_t j=1;j<big_segments.size();++j){
  546. int dist = big_segments[j].x - peaks.back().y;
  547. if(dist>min_segment_dist){
  548. peaks.push_back(big_segments[j]);
  549. }
  550. else{
  551. peaks.back().y = big_segments[j].y;
  552. }
  553. }
  554. }
  555. }
  556. // 3 多峰情况,先计算center_ratio, 统计峰段两侧数据的方差,方差大的为茎
  557. int opt_index=0;
  558. if(peaks.size()>1){
  559. vector<double>center_r;
  560. for(size_t i=0;i<peaks.size();++i){
  561. double r = fabs(0.5*(double)(peaks[i].x+ peaks[i].y) - cent_x)/(double)(width_x/2.0);
  562. center_r.push_back(1.0-r);
  563. }
  564. vector<size_t>sort_idx = sort_indexes_e(center_r,false);
  565. double cr_1 = center_r[sort_idx[0]];
  566. double cr_2 = center_r[sort_idx[1]];
  567. if(cr_1-cr_2>0.2 || cr_1>=0.65){//如果存在居中显著的,按居中优先
  568. opt_index = sort_idx[0];
  569. }
  570. else{
  571. vector<int> candidate_idx;
  572. candidate_idx.push_back(sort_idx[0]);
  573. candidate_idx.push_back(sort_idx[1]);
  574. for(size_t id = 2;id<sort_idx.size();++id){
  575. if(cr_1 - center_r[sort_idx[id]]<=0.3){
  576. candidate_idx.push_back(sort_idx[id]);
  577. }
  578. }
  579. vector<double>vars;
  580. int vx0,vx1;
  581. for(size_t i=0;i<peaks.size();++i){
  582. vector<int>::iterator it = find(candidate_idx.begin(),candidate_idx.end(),i);
  583. if(it==candidate_idx.end()){
  584. vars.push_back(0.0);
  585. continue;
  586. }
  587. vx0 = peaks[i].x - var_padding;
  588. vx1 = peaks[i].y + var_padding;
  589. if(vx0<0){vx0=0;}
  590. if(vx1>hist_h.size()-1){vx1=hist_h.size()-1;}
  591. double mean,var;
  592. mean = 0.0;
  593. var=-1;
  594. get_hist_mean_var_local(hist_h,vx0,vx1,mean,var);
  595. if(var<0){var=0.0;}
  596. vars.push_back(var);
  597. }
  598. auto var_max_it = max_element(vars.begin(), vars.end());
  599. size_t var_max_idx = var_max_it - vars.begin();
  600. opt_index = var_max_idx;
  601. }
  602. }
  603. stem_x0 = peaks[opt_index].x;
  604. stem_x1 = peaks[opt_index].y;
  605. x0 = stem_x0 - padding;
  606. x1 = stem_x1 + padding;
  607. if(x0<0){x0=0;};
  608. if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
  609. //////////////////////////////////////
  610. //int global_start_idx=-1;
  611. //int global_end_idx=-1;
  612. //int start_idx=-1;
  613. //int end_idx=-1;
  614. //for(size_t i=0;i<hist_h.size();++i){
  615. // if(i==0){
  616. // if(hist_h[i]>=th){
  617. // start_idx=i;
  618. // }
  619. // continue;
  620. // }
  621. // if(hist_h[i]>=th && hist_h[i-1]<th){
  622. // start_idx=i;
  623. // continue;
  624. // }
  625. // if(i==hist_h.size()-1){
  626. // if(hist_h[i]>=th && hist_h[i-1]>=th){
  627. // if((i-start_idx+1)>=min_segment_len){
  628. // global_end_idx=i;
  629. // }
  630. // }
  631. // }
  632. // if(hist_h[i]<th && hist_h[i-1]>=th){
  633. // if((i-start_idx+1)>=min_segment_len){
  634. // if( global_start_idx<0){
  635. // global_start_idx = start_idx;
  636. // }
  637. // global_end_idx = i;
  638. // }
  639. // }
  640. //}
  641. ///*stem_x0 = 0;
  642. //stem_x1 = hist_h.size()-1;
  643. //for(size_t i=max_idx; i < hist_h.size(); ++i){
  644. // if(hist_h[i]>=th){
  645. // stem_x1 = i;
  646. // }
  647. // else{
  648. // break;
  649. // }
  650. //}
  651. //for(int i=max_idx; i >= 0; i--){
  652. // if(hist_h[i]>=th){
  653. // stem_x0 = i;
  654. // }
  655. // else{
  656. // break;
  657. // }
  658. //}*/
  659. //stem_x0 = global_start_idx;
  660. //stem_x1 = global_end_idx;
  661. //x0 = stem_x0 - padding;
  662. //x1 = stem_x1 + padding;
  663. //if(x0<0){x0=0;};
  664. //if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
  665. };
  666. void get_stem_x_range_scion(
  667. const std::vector<int>&hist_h,
  668. double h_ratio,
  669. int padding,
  670. int&x0,
  671. int&x1,
  672. int&stem_x0,
  673. int&stem_x1
  674. )
  675. {
  676. //hist_h: histogram in column
  677. //h_ratio: ratio for generating threshold
  678. // x0,x1: sub-image x-range
  679. // stem_x0,1: stem x-range
  680. // padding: pad for stem x-range
  681. // calculate the max-value of hist_h
  682. auto max_it = max_element(hist_h.begin(), hist_h.end());
  683. size_t max_idx = max_it - hist_h.begin();
  684. int th = int(h_ratio * (double)(*max_it));
  685. stem_x0 = 0;
  686. stem_x1 = hist_h.size()-1;
  687. for(size_t i=max_idx; i < hist_h.size(); ++i){
  688. if(hist_h[i]>=th){
  689. stem_x1 = i;
  690. }
  691. else{
  692. break;
  693. }
  694. }
  695. for(int i=max_idx; i >= 0; i--){
  696. if(hist_h[i]>=th){
  697. stem_x0 = i;
  698. }
  699. else{
  700. break;
  701. }
  702. }
  703. x0 = stem_x0 - padding;
  704. x1 = stem_x1 + padding;
  705. if(x0<0){x0=0;};
  706. if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
  707. };
  708. void get_hist_segment(
  709. const std::vector<int>& hist,
  710. int th,//threshold
  711. std::vector<gcv_point<int>>& segments
  712. )
  713. {
  714. segments.clear();
  715. int start_idx=-1;
  716. int end_idx=-1;
  717. for(size_t i=0;i<hist.size();++i){
  718. if(i==0){
  719. if(hist[i]>=th){
  720. start_idx=i;
  721. }
  722. continue;
  723. }
  724. if(hist[i]>=th && hist[i-1]<th){
  725. start_idx=i;
  726. continue;
  727. }
  728. if(i==hist.size()-1){
  729. if(hist[i]>=th && hist[i-1]>=th){
  730. segments.push_back(gcv_point<int>(start_idx,i));
  731. }
  732. }
  733. if(hist[i]<th && hist[i-1]>=th){
  734. segments.push_back(gcv_point<int>(start_idx,i-1));
  735. }
  736. }
  737. }
  738. void get_hist_mean_var_local(
  739. const std::vector<int>& hist,
  740. int x0,
  741. int x1,
  742. double& mean,
  743. double& var
  744. )
  745. {
  746. mean=0.0;
  747. var = 0.0;
  748. if(hist.size()==0){
  749. return;
  750. }
  751. if(x0<0){x0=0;}
  752. if(x1<0 || x1>hist.size()-1){hist.size()-1;}
  753. for(int i=x0;i<=x1;++i){
  754. mean+=hist[i];
  755. }
  756. mean /=(double)(x1-x0+1);
  757. for(int i=x0;i<=x1;++i){
  758. var+=((double)(hist[i])-mean) * ((double)(hist[i])-mean);
  759. }
  760. var /= (double)(x1-x0+1);
  761. }
  762. void get_stem_y_fork(
  763. const std::vector<int>& hist,
  764. double ratio,
  765. int stem_dia_min,
  766. int stem_fork_y_min,
  767. double stem_dia_mp,
  768. int& fork_y, //output
  769. int& end_y, //output
  770. int& stem_dia //output
  771. )
  772. {
  773. //# 由y的底部向上检测是否有茎,有的话计算移动均值
  774. //# 通过当前值和移动均值的比值识别分叉点
  775. //# stem_dia_min = 10 # 茎粗最小值
  776. //# stem_fork_y_min = 10 # 茎分叉位置与茎根的最小像素高度
  777. //# stem_dia_mp = 0.8 # moving power
  778. fork_y = end_y = stem_dia = -1;
  779. std::vector<int>reversed_hist;
  780. for(int ii=hist.size()-1; ii>=0;ii--){
  781. reversed_hist.push_back(hist[ii]);
  782. }
  783. int stem_root_y = -1;
  784. double stem_dia_ma = -1;//# stem diameter moving-average
  785. size_t i=0;
  786. for(; i<hist.size(); ++i){
  787. if (i==0){continue;}
  788. if ((reversed_hist[i-1]<stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0)
  789. ||(reversed_hist[i-1]>=stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0))
  790. {
  791. //# stem root begin
  792. stem_root_y = i;
  793. stem_dia_ma = (double)reversed_hist[i];
  794. }
  795. else {
  796. if (reversed_hist[i-1]>=stem_dia_min &&
  797. reversed_hist[i]>=stem_dia_min)
  798. {
  799. double fork_ratio = reversed_hist[i] / stem_dia_ma;
  800. //if (i<150){
  801. // cout<<fork_ratio<<"\t"<<reversed_hist[i]<<"\t"<<stem_dia_ma<<endl;
  802. //}
  803. if( i - stem_root_y > stem_fork_y_min &&
  804. fork_ratio >= ratio)
  805. {
  806. //# found the fork y level
  807. fork_y = hist.size() - i;
  808. stem_dia = reversed_hist[i-1];
  809. break;
  810. }
  811. //# update stem_dia_ma
  812. stem_dia_ma = stem_dia_mp *stem_dia_ma + (1.0-stem_dia_mp) * reversed_hist[i];
  813. }
  814. }
  815. };
  816. if(stem_root_y<0){
  817. throw_msg(string("not exists diameter bigger than stem_dia_min"));
  818. }
  819. if(fork_y<0){
  820. throw_msg(string("not exists diameter fork_ratio bigger than threshold"));
  821. }
  822. //end_y
  823. end_y = hist.size() - stem_root_y-1;
  824. //统计stem_root_y 到 i 间reversed_hist的数值,得到stem_dia。方法可用平均值,75%分位数
  825. vector<int> tmp;
  826. for(size_t j=stem_root_y; j<=i;++j){
  827. tmp.push_back(reversed_hist[j]);
  828. }
  829. sort(tmp.begin(), tmp.end());
  830. int tar_idx = (int)((float)tmp.size()*0.75);
  831. stem_dia = tmp[tar_idx];
  832. };
  833. void get_stem_y_fork_3sigma(
  834. const std::vector<int>& hist,
  835. double ratio,
  836. int stem_dia_min,
  837. int stem_fork_y_min,
  838. double stem_dia_mp,
  839. int& fork_y, //output
  840. int& end_y, //output
  841. int& stem_dia //output
  842. )
  843. {
  844. fork_y = end_y = stem_dia = -1;
  845. std::vector<int>reversed_hist;
  846. for(int ii=hist.size()-1; ii>=0;ii--){
  847. reversed_hist.push_back(hist[ii]);
  848. }
  849. int mean_radius =2;
  850. double mean_dia = 2.0*mean_radius +1.0;
  851. double mean = 0.0;
  852. double min_m, max_m;
  853. min_m = max_m = 0.0;
  854. std::vector<double>reversed_mean;
  855. for(int ii=0; ii<reversed_hist.size();ii++){
  856. if(ii-mean_radius<0 || ii + mean_radius>=reversed_hist.size()){
  857. reversed_mean.push_back(0.0);
  858. continue;
  859. }
  860. mean = 0.0;
  861. for(int k = -mean_radius;k<=mean_radius;++k){
  862. mean+=(double)reversed_hist[ii+k];
  863. }
  864. mean /= mean_dia;
  865. if(mean>max_m){max_m = mean;}
  866. reversed_mean.push_back(mean);
  867. }
  868. cv::Mat tmp;
  869. hist2image_scale_line(reversed_mean,tmp,min_m,max_m,1.0,min_m,50,50);
  870. imshow_wait("mean5",tmp);
  871. return;
  872. int stem_root_y = -1;
  873. double stem_dia_ma = -1;//# stem diameter moving-average
  874. size_t i=0;
  875. for(; i<reversed_mean.size(); ++i){
  876. if (i==0){continue;}
  877. if ((reversed_hist[i-1]<stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0)
  878. ||(reversed_hist[i-1]>=stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0))
  879. {
  880. //# stem root begin
  881. stem_root_y = i;
  882. stem_dia_ma = (double)reversed_hist[i];
  883. }
  884. else {
  885. if (reversed_hist[i-1]>=stem_dia_min &&
  886. reversed_hist[i]>=stem_dia_min)
  887. {
  888. double fork_ratio = reversed_hist[i] / stem_dia_ma;
  889. //if (i<150){
  890. // cout<<fork_ratio<<"\t"<<reversed_hist[i]<<"\t"<<stem_dia_ma<<endl;
  891. //}
  892. if( i - stem_root_y > stem_fork_y_min &&
  893. fork_ratio >= ratio)
  894. {
  895. //# found the fork y level
  896. fork_y = hist.size() - i;
  897. stem_dia = reversed_hist[i-1];
  898. break;
  899. }
  900. //# update stem_dia_ma
  901. stem_dia_ma = stem_dia_mp *stem_dia_ma + (1.0-stem_dia_mp) * reversed_hist[i];
  902. }
  903. }
  904. };
  905. //r2index(
  906. /*
  907. int stem_root_y = -1;
  908. double stem_dia_ma = -1;//# stem diameter moving-average
  909. size_t i=0;
  910. for(; i<hist.size(); ++i){
  911. if (i==0){continue;}
  912. if ((reversed_hist[i-1]<stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0)
  913. ||(reversed_hist[i-1]>=stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0))
  914. {
  915. //# stem root begin
  916. stem_root_y = i;
  917. stem_dia_ma = (double)reversed_hist[i];
  918. }
  919. else {
  920. if (reversed_hist[i-1]>=stem_dia_min &&
  921. reversed_hist[i]>=stem_dia_min)
  922. {
  923. double fork_ratio = reversed_hist[i] / stem_dia_ma;
  924. //if (i<150){
  925. // cout<<fork_ratio<<"\t"<<reversed_hist[i]<<"\t"<<stem_dia_ma<<endl;
  926. //}
  927. if( i - stem_root_y > stem_fork_y_min &&
  928. fork_ratio >= ratio)
  929. {
  930. //# found the fork y level
  931. fork_y = hist.size() - i;
  932. stem_dia = reversed_hist[i-1];
  933. break;
  934. }
  935. //# update stem_dia_ma
  936. stem_dia_ma = stem_dia_mp *stem_dia_ma + (1.0-stem_dia_mp) * reversed_hist[i];
  937. }
  938. }
  939. };
  940. if(stem_root_y<0){
  941. throw_msg(string("not exists diameter bigger than stem_dia_min"));
  942. }
  943. if(fork_y<0){
  944. throw_msg(string("not exists diameter fork_ratio bigger than threshold"));
  945. }
  946. //end_y
  947. end_y = hist.size() - stem_root_y-1;
  948. //统计stem_root_y 到 i 间reversed_hist的数值,得到stem_dia。方法可用平均值,75%分位数
  949. vector<int> tmp;
  950. for(size_t j=stem_root_y; j<=i;++j){
  951. tmp.push_back(reversed_hist[j]);
  952. }
  953. sort(tmp.begin(), tmp.end());
  954. int tar_idx = (int)((float)tmp.size()*0.75);
  955. stem_dia = tmp[tar_idx];
  956. */
  957. };
  958. void get_stem_y_fork_rs(
  959. const std::vector<int>& hist,
  960. double ratio,
  961. int stem_dia_min,
  962. int stem_fork_y_min,
  963. double stem_dia_mp,
  964. int& fork_y, //output
  965. int& end_y, //output
  966. int& stem_dia, //output
  967. int& roi_max_y //output
  968. )
  969. {
  970. //# 由y的底部向上检测是否有茎,有的话计算移动均值
  971. //# 通过当前值和移动均值的比值识别分叉点
  972. //# stem_dia_min = 10 # 茎粗最小值
  973. //# stem_fork_y_min = 10 # 茎分叉位置与茎根的最小像素高度
  974. //# stem_dia_mp = 0.8 # moving power
  975. fork_y = end_y = stem_dia = -1;
  976. std::vector<int>reversed_hist;
  977. for(int ii=hist.size()-1; ii>=0;ii--){
  978. reversed_hist.push_back(hist[ii]);
  979. }
  980. int stem_root_y = -1;
  981. double stem_dia_ma = -1;//# stem diameter moving-average
  982. int max_y = 0;
  983. int max_val = reversed_hist[0];
  984. size_t i=0;
  985. vector<double> index_of_diachange;
  986. vector<double> index_of_diameter;
  987. for(; i<reversed_hist.size(); ++i){
  988. //find max value index
  989. if(reversed_hist[i]>max_val){
  990. max_val = reversed_hist[i];
  991. max_y = i;
  992. }
  993. if (i==0){
  994. index_of_diachange.push_back(0.0);
  995. continue;
  996. }
  997. if ((reversed_hist[i-1]<stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0)
  998. ||(reversed_hist[i-1]>=stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0))
  999. {
  1000. //# stem root begin
  1001. stem_root_y = i;
  1002. stem_dia_ma = (double)reversed_hist[i];
  1003. index_of_diachange.push_back(0.0);
  1004. }
  1005. else {
  1006. if (reversed_hist[i-1]>=stem_dia_min &&
  1007. reversed_hist[i]>=stem_dia_min)
  1008. {
  1009. double fork_ratio = reversed_hist[i] / stem_dia_ma;
  1010. if(fork_ratio>1.5){fork_ratio=1.5;}
  1011. index_of_diachange.push_back(fork_ratio);
  1012. stem_dia_ma = stem_dia_mp *stem_dia_ma + (1.0-stem_dia_mp) * reversed_hist[i];
  1013. }
  1014. else{
  1015. index_of_diachange.push_back(0.0);
  1016. }
  1017. }
  1018. };
  1019. if(stem_root_y<0){
  1020. throw_msg(string("not exists diameter bigger than stem_dia_min"));
  1021. }
  1022. //end_y
  1023. end_y = hist.size() - stem_root_y-1;
  1024. //统计stem_root_y 到 max_y 间reversed_hist的数值,得到stem_dia。方法可用平均值,40%分位数
  1025. vector<int> tmp;
  1026. for(size_t j=stem_root_y; j<max_y;++j){
  1027. if(j<0 || j>=reversed_hist.size()){continue;}
  1028. tmp.push_back(reversed_hist[j]);
  1029. }
  1030. sort(tmp.begin(), tmp.end());
  1031. int tar_idx = (int)((float)tmp.size()*0.40);
  1032. if(tmp.size()==0 || tar_idx>=tmp.size()){
  1033. throw_msg(string("not exists y fork point"));
  1034. }
  1035. stem_dia = tmp[tar_idx];
  1036. ////////////////////////////////////////
  1037. //update 重新检验max_y,因为max_y是全局最大,由于苗的倾斜,可能出现局部最大为分叉点位置
  1038. // 2022-2-21 将比例参数1.5调整至2.0
  1039. int local_max_y = max_y;
  1040. for(size_t j=stem_root_y; j<max_y;++j){
  1041. if((double)(reversed_hist[j]/(double)stem_dia) >=2.0){
  1042. local_max_y = j;
  1043. break;
  1044. }
  1045. }
  1046. max_y = local_max_y ;
  1047. roi_max_y = hist.size() - max_y-1;
  1048. //////////////////////////////////////////////////////////////////
  1049. // 计算vector<double>index_of_diameter
  1050. for(int idx = 0;idx<reversed_hist.size();++idx){
  1051. if(reversed_hist[idx]==0){
  1052. index_of_diameter.push_back(0.0);
  1053. }
  1054. else {
  1055. if(idx>=max_y){
  1056. index_of_diameter.push_back(0.0);
  1057. }
  1058. else{
  1059. double r = yfork_validity_stemdiaratio(stem_dia,reversed_hist[idx],ratio);
  1060. index_of_diameter.push_back(r);
  1061. }
  1062. }
  1063. }
  1064. //////////////////////////////////////////////////////////////////
  1065. // 计算fork_y
  1066. vector<double> index_yfork;
  1067. for(int idx = 0;idx<reversed_hist.size();++idx){
  1068. index_yfork.push_back(index_of_diachange[idx] * index_of_diameter[idx]);
  1069. }
  1070. vector<double>::iterator max_it = max_element(begin(index_yfork),end(index_yfork));
  1071. int max_idx_y = max_it - index_yfork.begin();
  1072. fork_y = hist.size() - max_idx_y;
  1073. };
  1074. void get_stem_y_fork_rs_update(
  1075. const cv::Mat& bin_img,
  1076. int stem_x_padding,
  1077. int x0,
  1078. int x1,
  1079. int max_y,
  1080. int stem_root_y,
  1081. int stem_dia,
  1082. bool image_show,
  1083. double cut_point_offset_ratio,
  1084. cv::Point& fork_cent,
  1085. double& max_radius,
  1086. double& stem_angle
  1087. )
  1088. {
  1089. //1 通过bin_img的二值图像,找到茎中心线
  1090. //2 通过中心线,更新二值图像采集区域(有可能倾斜)
  1091. //3 在max_y附近找最大内接圆中心(在茎中心线上)
  1092. fork_cent.x = fork_cent.y = -1;
  1093. max_radius = 0.0;
  1094. stem_angle = 90.0;
  1095. // 茎中心点提取, 在图像max_y 和 stem_root_y之间
  1096. int max_len,start_x,cent_x, pre_val, cur_val;
  1097. vector<int>cent_xs;
  1098. vector<int>cent_ys;
  1099. for(int i=0;i<bin_img.rows;++i){
  1100. if(i<=max_y || i>=stem_root_y){continue;}
  1101. max_len = start_x = cent_x = -1;
  1102. for(int j=x0;j<=x1;++j){
  1103. if(j==x0 && bin_img.at<unsigned char>(i,j)==0){continue;}
  1104. if(j==x0 && bin_img.at<unsigned char>(i,j)>0){start_x=j;continue;}
  1105. pre_val = bin_img.at<unsigned char>(i,j-1);
  1106. cur_val = bin_img.at<unsigned char>(i,j);
  1107. if(pre_val==0 && cur_val>0){
  1108. start_x = j;
  1109. }
  1110. if((pre_val>0 && cur_val==0) ||(j==x1 && cur_val>0)){
  1111. int len = j-start_x +1;
  1112. if(len>max_len){
  1113. max_len = len;
  1114. cent_x = (start_x+j)/2;
  1115. //cout<<i<<"\t"<<"x0="<<start_x<<", x1="<<j<<", centx="<<cent_x<<endl;
  1116. }
  1117. }
  1118. }
  1119. if(cent_x>0){
  1120. cent_xs.push_back(cent_x);
  1121. cent_ys.push_back(i);
  1122. }
  1123. }
  1124. if(cent_xs.size()!=cent_ys.size() || cent_xs.size()<3){
  1125. throw_msg(string("stem length < 3, in function get_stem_y_fork_rs_update()"));
  1126. }
  1127. //茎中心线拟合
  1128. double beta0, beta1;
  1129. beta0 = beta1 = 0.0;
  1130. // x = beta0 + beta1 * y
  1131. double r2 = r2index(cent_ys,cent_xs,0,cent_xs.size()-1, beta0, beta1);
  1132. double th = (double)stem_dia/2.0 + stem_x_padding;
  1133. cv::Mat roi_img = bin_img.clone();
  1134. //提取兴趣区域图片
  1135. stem_angle = atan(beta1)*57.2957795130 + 90.0;
  1136. if(fabs(stem_angle-90.0)<2.0){
  1137. for(int r=0;r<roi_img.rows;++r){
  1138. for(int c=0;c<roi_img.cols;++c){
  1139. if(c<x0 || c>x1){
  1140. roi_img.at<unsigned char>(r,c) = 0;
  1141. }
  1142. }
  1143. }
  1144. }
  1145. else{
  1146. for(int r=0;r<roi_img.rows;++r){
  1147. double cx = beta0 + beta1 * r;
  1148. int cx0 = (int)(cx-th);
  1149. int cx1 = (int)(cx+th);
  1150. for(int c=0;c<roi_img.cols;++c){
  1151. if(c<cx0 || c>cx1){
  1152. roi_img.at<unsigned char>(r,c) = 0;
  1153. }
  1154. }
  1155. }
  1156. }
  1157. ////////////////////////////////////////////////////////////
  1158. //
  1159. if(image_show){
  1160. cv::Mat tmp = roi_img.clone();
  1161. cv::Point p0((int)(beta0),0);
  1162. cv::Point p1((int)((double)tmp.rows *beta1 +beta0),tmp.rows);
  1163. cv::line(tmp,p0,p1, cv::Scalar(128,0,0));
  1164. cv::line(tmp, cv::Point(cent_xs[0],cent_ys[0]),
  1165. cv::Point(cent_xs.back(),cent_ys.back()), cv::Scalar(128,0,0));
  1166. imshow_wait("rs_bin_roi", tmp);
  1167. }
  1168. ///////////////////////////////////////////////////////////
  1169. //兴趣区域图片findContours
  1170. vector<vector<cv::Point>> contours;
  1171. vector<cv::Vec4i> hierarchy;
  1172. findContours(roi_img,contours,hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
  1173. //找到周长最长的区域作为目标区域
  1174. int max_length_idx = 0;
  1175. int max_length_val = contours[0].size();
  1176. for(int i=0;i<contours.size();++i){
  1177. if(i==0){continue;}
  1178. if(contours[i].size()>max_length_val){
  1179. max_length_val = contours[i].size();
  1180. max_length_idx = i;
  1181. }
  1182. }
  1183. vector<float>inner_max_radius;
  1184. vector<int> nnindex;
  1185. vector<int> xs;
  1186. /*
  1187. //////////////////////////////////////////
  1188. //基于flann的方法
  1189. vector<Point> contours_serial;
  1190. for(size_t i=0;i<contours.size();++i){
  1191. if(contours_serial.size()==0){
  1192. contours_serial.assign(contours[i].begin(),contours[i].end());
  1193. }
  1194. else{
  1195. for(size_t j=0;j<contours[i].size();++j){
  1196. contours_serial.push_back(contours[i][j]);
  1197. }
  1198. }
  1199. }
  1200. //find inner max radius
  1201. Mat source = Mat(contours_serial).reshape(1);
  1202. source.convertTo(source,CV_32F);
  1203. flann::KDTreeIndexParams indexParams(2);
  1204. flann::Index kdtree(source, indexParams);
  1205. unsigned queryNum=1;
  1206. vector<float> vecQuery(2);
  1207. vector<int> vecIndex(queryNum);
  1208. vector<float> vecDist(queryNum);
  1209. flann::SearchParams params(32);
  1210. //在茎中心线上计算最优点指标:
  1211. // 1) inner_max_radius --- 中心点对应的最小内接圆半径
  1212. for(size_t r =0; r<roi_img.rows;++r){
  1213. if(r < max_y - 50){
  1214. inner_max_radius.push_back(0.0);
  1215. nnindex.push_back(-1);
  1216. xs.push_back(-1);
  1217. continue;
  1218. }
  1219. // x = beta0 + beta1 * y
  1220. float x = (float) (beta0 + beta1 * r);
  1221. int xi = (int)(x+0.5);
  1222. if(xi<0 || xi>=roi_img.cols){
  1223. inner_max_radius.push_back(0.0);
  1224. nnindex.push_back(-1);
  1225. xs.push_back(-1);
  1226. continue;
  1227. }
  1228. if(roi_img.at<unsigned char>(r,xi)==0){
  1229. inner_max_radius.push_back(0.0);
  1230. nnindex.push_back(-1);
  1231. xs.push_back(xi);
  1232. continue;
  1233. }
  1234. vecQuery[0] = x;//x
  1235. vecQuery[1] = (float)r;//y
  1236. kdtree.knnSearch(vecQuery, vecIndex, vecDist,queryNum, params);
  1237. if(vecDist.size()<1 || vecIndex.size()<1){
  1238. inner_max_radius.push_back(0.0);
  1239. nnindex.push_back(-1);
  1240. xs.push_back(xi);
  1241. continue;
  1242. }
  1243. inner_max_radius.push_back(vecDist[0]);
  1244. nnindex.push_back(vecIndex[0]);
  1245. xs.push_back(xi);
  1246. }
  1247. */
  1248. ////////////////////////////////////////////////////////////////
  1249. //基于pointPolygonTest的方法
  1250. //在茎中心线上计算最优点指标:
  1251. // 1) inner_max_radius --- 中心点对应的最小内接圆半径
  1252. for(size_t r =0; r<roi_img.rows;++r){
  1253. if(r < max_y - 50){
  1254. inner_max_radius.push_back(0.0);
  1255. nnindex.push_back(-1);
  1256. xs.push_back(-1);
  1257. continue;
  1258. }
  1259. // x = beta0 + beta1 * y
  1260. float x = (float) (beta0 + beta1 * r);
  1261. int xi = (int)(x+0.5);
  1262. if(xi<0 || xi>=roi_img.cols){
  1263. inner_max_radius.push_back(0.0);
  1264. nnindex.push_back(-1);
  1265. xs.push_back(-1);
  1266. continue;
  1267. }
  1268. if(roi_img.at<unsigned char>(r,xi)==0){
  1269. inner_max_radius.push_back(0.0);
  1270. nnindex.push_back(-1);
  1271. xs.push_back(xi);
  1272. continue;
  1273. }
  1274. float d = cv::pointPolygonTest( contours[max_length_idx], cv::Point2f(x,r), true );
  1275. if(d<0){
  1276. inner_max_radius.push_back(0.0);
  1277. nnindex.push_back(-1);
  1278. xs.push_back(xi);
  1279. continue;
  1280. }
  1281. inner_max_radius.push_back(d);
  1282. nnindex.push_back(-1);
  1283. xs.push_back(xi);
  1284. }
  1285. //find max radius point
  1286. vector<float>::iterator max_it = max_element(inner_max_radius.begin(),inner_max_radius.end());
  1287. int max_idx = max_it - inner_max_radius.begin();
  1288. //基于flann的方法
  1289. //max_radius = sqrt(inner_max_radius[max_idx]);
  1290. max_radius = inner_max_radius[max_idx];
  1291. //offset
  1292. double r_offset = cut_point_offset_ratio * max_radius;
  1293. double dy = r_offset * (1.0 / sqrt(1.0 + beta1 * beta1));
  1294. double x_offset = beta0 + beta1 * (max_idx + dy);
  1295. fork_cent.x = (int)(x_offset+0.5);
  1296. fork_cent.y =(int)(max_idx + dy + 0.5);
  1297. ///////////////////////////////////////////////
  1298. //test CForkDetectOptimal 2022-2-17
  1299. /*CForkDetectOptimal fdo(100,20);
  1300. vector<float>opt_max;
  1301. int opt_max_idx = 0;
  1302. double optm=0;
  1303. for(size_t r =0; r<xs.size();++r){
  1304. if(abs((int)r-max_idx)>50){
  1305. opt_max.push_back(0.0);
  1306. continue;
  1307. }
  1308. else{
  1309. if(xs[r]<0){
  1310. opt_max.push_back(0.0);
  1311. continue;
  1312. }
  1313. Point cent_pt = Point2f(xs[r],r);
  1314. double rt = fdo.center_pt_index(bin_img,cent_pt,-60.0);
  1315. opt_max.push_back((float)rt);
  1316. if(rt>optm){
  1317. optm=rt;
  1318. opt_max_idx=r;
  1319. }
  1320. }
  1321. }*/
  1322. ///////////////////////////////////////////////
  1323. ///////////////////////////////////////////////
  1324. //test
  1325. if(image_show){
  1326. cv::Mat edge;
  1327. edge = cv::Mat::zeros(roi_img.rows, roi_img.cols, CV_8UC1);
  1328. cv::drawContours(edge, contours, -1, cv::Scalar(255, 255, 0), 1);
  1329. for(int r=0;r<xs.size();++r){
  1330. int c = xs[r];
  1331. if(c<0){continue;}
  1332. if(inner_max_radius[r]<=0){continue;}
  1333. /*
  1334. //基于flann的方法
  1335. if(nnindex[r]<0){continue;}
  1336. line(edge,Point(c,r),contours_serial[nnindex[r]],Scalar(128,0,0));
  1337. */
  1338. int rad = int(inner_max_radius[r]);
  1339. cv::line(edge, cv::Point(c-rad,r), cv::Point(c+rad,r), cv::Scalar(80,0,0));
  1340. }
  1341. cv::Point fork_cent_origin;
  1342. fork_cent_origin.x = xs[max_idx];
  1343. fork_cent_origin.y =max_idx;
  1344. cv::circle(edge,fork_cent_origin,3, cv::Scalar(200,0,0));
  1345. cv::circle(edge,fork_cent_origin,(int)(max_radius), cv::Scalar(200,0,0));
  1346. //circle(edge,Point(xs[opt_max_idx],opt_max_idx),5,Scalar(200,0,0));
  1347. cv::circle(edge,fork_cent,3, cv::Scalar(128,0,0));
  1348. cv::circle(edge,fork_cent,(int)(max_radius), cv::Scalar(128,0,0));
  1349. imshow_wait("rs_bin_roi_radius_circle", edge);
  1350. }
  1351. ///////////////////////////////////////////////
  1352. };
  1353. //分叉位置指标 yfork_validity_position()
  1354. // max_y---hist中最大点的y-index,
  1355. // end_y ----hist中茎最底端点的y-index
  1356. // fork_y --- 预期分叉点y-index
  1357. // max_y > fork_y > end_y
  1358. double yfork_validity_position(int max_y, int end_y, int fork_y){
  1359. //确定hist最大点位置向下到end_y点最优分叉点位置系数
  1360. double validity = 0.0;
  1361. double opt_pos = 0.85;
  1362. if(fork_y<end_y || fork_y>max_y){return validity;}
  1363. double opt_y = (max_y-end_y+1)*opt_pos + end_y;
  1364. if(fork_y<=end_y || fork_y>=max_y){return validity;}
  1365. if(fork_y>end_y && fork_y<opt_y){
  1366. validity =(double)( (fork_y-end_y+1) / (opt_y-end_y+1));
  1367. }
  1368. else{
  1369. validity =(double)( (max_y -fork_y+1) / (max_y -opt_y+1));
  1370. }
  1371. return validity;
  1372. }
  1373. //茎粗比指标 yfork_validity_stemdiaratio()
  1374. // stem_dia---茎粗,
  1375. // dia_y ----hist中y-index对应的茎粗
  1376. double yfork_validity_stemdiaratio(int stem_dia, int dia_y,double opt_dia_ratio){
  1377. //确定hist最大点位置向下到end_y点最优分叉点位置系数
  1378. double validity = 0.0;
  1379. //double opt_pos = 1.2;
  1380. double opt_dia = stem_dia*opt_dia_ratio;
  1381. if(dia_y<opt_dia){return dia_y/opt_dia;}
  1382. else{
  1383. return opt_dia/dia_y;
  1384. }
  1385. }
  1386. //计算上下窗口茎粗变化率
  1387. void yfork_validity_stemdiachange(
  1388. const std::vector<int>& hist,
  1389. int stem_dia_min,
  1390. std::vector<double>& var_ratio
  1391. )
  1392. {
  1393. var_ratio.clear();
  1394. int smooth_width = 5;
  1395. double mean_pre=0.0;
  1396. double mean_nxt=0.0;
  1397. int low_y,up_y;
  1398. low_y=up_y=-1;
  1399. for(int i=0;i<hist.size();++i){
  1400. if(hist[i]>=stem_dia_min){
  1401. if( low_y<0){low_y=i;}
  1402. if(i>up_y){up_y = i;}
  1403. }
  1404. }
  1405. for(int i=0;i<hist.size();++i){
  1406. if(i-smooth_width<0 || i+smooth_width>=hist.size()){
  1407. var_ratio.push_back(0.0);
  1408. continue;
  1409. }
  1410. if(i<low_y+smooth_width || i>up_y-smooth_width){
  1411. var_ratio.push_back(0.0);
  1412. continue;
  1413. }
  1414. mean_pre = mean_nxt=0.0;
  1415. //low sum
  1416. for(int di = -smooth_width;di<0;++di){
  1417. mean_pre += hist[i+di];
  1418. }
  1419. //up sum
  1420. for(int di = 0;di<smooth_width;++di){
  1421. mean_nxt += hist[i+di];
  1422. }
  1423. if(mean_pre<1.0e-5){
  1424. var_ratio.push_back(0.0);
  1425. continue;
  1426. }
  1427. else{
  1428. double ratio = mean_nxt / mean_pre;
  1429. if(ratio>3.0){ratio=3.0;}
  1430. var_ratio.push_back(ratio);
  1431. }
  1432. }
  1433. }
  1434. void imshow_wait(
  1435. const char* winname,
  1436. cv::Mat&img,
  1437. int waittype/*=-1*/
  1438. )
  1439. {
  1440. cv::namedWindow(winname, cv::WINDOW_NORMAL);
  1441. cv::imshow(winname, img);
  1442. cv::waitKey(waittype);
  1443. };
  1444. int get_stem_fork_right(
  1445. cv::Mat&stem_img,
  1446. int stem_fork_y,
  1447. int stem_x0,
  1448. int stem_x1,
  1449. int detect_window
  1450. )
  1451. {
  1452. /*
  1453. 通过茎分叉坐标找到右侧茎分叉点坐标
  1454. stem_img, 二值图像,茎部分的局部图像
  1455. stem_fork_y, 检测到的茎分叉的y坐标
  1456. stem_x0, 茎左侧边缘(通过hist检测,针对stem_fork_y不一定精确)
  1457. stem_x1, 茎右侧边缘(通过hist检测,针对stem_fork_y不一定精确)
  1458. */
  1459. // 注释掉 2021-11-3 开始
  1460. /*double min_dist = 1.0;
  1461. int min_x1 = -1;
  1462. for(size_t i=stem_x0; i<stem_x1+2*detect_window; ++i){
  1463. double left_cnt = 0.0;
  1464. double right_cnt = 0.0;
  1465. for(size_t j=i-detect_window+1; j<i+1; ++j){
  1466. if (stem_img.at<unsigned char>(stem_fork_y,j)>0){
  1467. left_cnt+=1;
  1468. }
  1469. }
  1470. for(size_t j=i+1; j<i+detect_window+1; ++j){
  1471. if( stem_img.at<unsigned char>(stem_fork_y,j)==0){
  1472. right_cnt+=1;
  1473. }
  1474. }
  1475. if( left_cnt==0 || right_cnt ==0){continue;}
  1476. double dist = 1.0 - min(left_cnt/right_cnt, right_cnt/left_cnt);
  1477. if (dist < min_dist){
  1478. min_dist = dist;
  1479. min_x1 = i;
  1480. }
  1481. }
  1482. return min_x1;
  1483. */
  1484. // 注释掉 2021-11-3 结束
  1485. size_t left_min = stem_x0-4*detect_window;
  1486. size_t right_max = stem_x1+4*detect_window;
  1487. if(left_min<0){left_min = 0;}
  1488. if(right_max>stem_img.cols){right_max = stem_img.cols;}
  1489. int max_len = -1;
  1490. int max_idx = -1;
  1491. int start_x=left_min;
  1492. for(size_t i=left_min; i<right_max; ++i){
  1493. if(i==left_min){
  1494. if(stem_img.at<unsigned char>(stem_fork_y,i)>0){
  1495. start_x =i;
  1496. }
  1497. continue;
  1498. }
  1499. if (stem_img.at<unsigned char>(stem_fork_y,i-1)==0 &&
  1500. stem_img.at<unsigned char>(stem_fork_y,i)>0){
  1501. start_x =i;
  1502. continue;
  1503. }
  1504. if ((stem_img.at<unsigned char>(stem_fork_y,i-1)>0 && stem_img.at<unsigned char>(stem_fork_y,i)==0) ||
  1505. (stem_img.at<unsigned char>(stem_fork_y,i)>0 && i==right_max-1)){
  1506. if(((int)i-start_x) > max_len){
  1507. max_len = i-start_x;
  1508. max_idx = i;
  1509. }
  1510. }
  1511. }
  1512. if(max_idx<0){max_idx = stem_x1;}
  1513. return max_idx;
  1514. };
  1515. int get_stem_fork_left(
  1516. cv::Mat&stem_img,
  1517. int stem_fork_y,
  1518. int stem_x0,
  1519. int stem_x1,
  1520. int detect_window
  1521. )
  1522. {
  1523. /*
  1524. 通过茎分叉坐标找到右侧茎分叉点坐标
  1525. stem_img, 二值图像,茎部分的局部图像
  1526. stem_fork_y, 检测到的茎分叉的y坐标
  1527. stem_x0, 茎左侧边缘(通过hist检测,针对stem_fork_y不一定精确)
  1528. stem_x1, 茎右侧边缘(通过hist检测,针对stem_fork_y不一定精确)
  1529. */
  1530. size_t left_min = stem_x0-8*detect_window;
  1531. size_t right_max = stem_x1+8*detect_window;
  1532. if(left_min<0){left_min = 0;}
  1533. if(right_max>stem_img.cols-1){right_max=stem_img.cols;}
  1534. int max_len = -1;
  1535. int max_idx = -1;
  1536. int start_x=left_min;
  1537. for(size_t i=left_min; i<right_max; ++i){
  1538. if(i==left_min){
  1539. if(stem_img.at<unsigned char>(stem_fork_y,i)>0){
  1540. start_x =i;
  1541. }
  1542. continue;
  1543. }
  1544. if (stem_img.at<unsigned char>(stem_fork_y,i-1)==0 &&
  1545. stem_img.at<unsigned char>(stem_fork_y,i)>0)
  1546. {
  1547. start_x =i;
  1548. continue;
  1549. }
  1550. if ((stem_img.at<unsigned char>(stem_fork_y,i-1)>0 && stem_img.at<unsigned char>(stem_fork_y,i)==0) ||
  1551. (stem_img.at<unsigned char>(stem_fork_y,i)>0 && i==right_max-1)){
  1552. if(((int)i-start_x) > max_len){
  1553. max_len = i-start_x;
  1554. max_idx = start_x;
  1555. }
  1556. }
  1557. }
  1558. if(max_idx<0){max_idx = stem_x0;}
  1559. return max_idx;
  1560. }
  1561. void get_stem_fork_xs(
  1562. cv::Mat&stem_img,
  1563. int stem_fork_y,
  1564. int stem_x0,
  1565. int stem_x1,
  1566. int x0,
  1567. int x1,
  1568. int & fork_x0,
  1569. int & fork_x1
  1570. )
  1571. {
  1572. size_t left_min = x0;
  1573. size_t right_max = x1;
  1574. if(left_min<0){left_min = 0;}
  1575. if(right_max>=stem_img.cols){right_max = stem_img.cols-1;}
  1576. int max_len = -1;
  1577. int max_idx = -1;
  1578. int max_idx_right = -1;
  1579. int start_x=left_min;
  1580. for(size_t i=left_min; i<right_max; ++i){
  1581. if ( i==0 &&
  1582. stem_img.at<unsigned char>(stem_fork_y,i)>0)
  1583. {
  1584. start_x =i;
  1585. continue;
  1586. }
  1587. if (stem_img.at<unsigned char>(stem_fork_y,i)==0 &&
  1588. stem_img.at<unsigned char>(stem_fork_y,i+1)>0)
  1589. {
  1590. start_x =i;
  1591. }
  1592. if ((stem_img.at<unsigned char>(stem_fork_y,i)>0 && stem_img.at<unsigned char>(stem_fork_y,i+1)==0) ||
  1593. (stem_img.at<unsigned char>(stem_fork_y,i)>0 && i==right_max-1)){
  1594. if(((int)i-start_x) > max_len)
  1595. {
  1596. max_len = i-start_x;
  1597. max_idx = start_x;
  1598. max_idx_right = i;
  1599. }
  1600. }
  1601. }
  1602. if(max_idx<0){max_idx = stem_x0;}
  1603. fork_x0 = max_idx;
  1604. if(max_idx_right<0){max_idx_right = stem_x1;}
  1605. fork_x1 = max_idx_right;
  1606. }
  1607. // 根据边缘上的一点,爬取下一点
  1608. void get_next_pt(
  1609. cv::Mat& edge_img,
  1610. gcv_point<int>&start_pt,
  1611. gcv_point<int>&pre_pt,
  1612. gcv_point<int>&nxt_pt,//output
  1613. bool is_up //true 向上爬取, false 向下爬取
  1614. )
  1615. {
  1616. nxt_pt.x=-1;
  1617. nxt_pt.y=-1;
  1618. int dy_range[3] = {-1,0,1};
  1619. int dx_range[3] = {1,0,-1};
  1620. if (!is_up){
  1621. dy_range[0] = 1;
  1622. dy_range[2] = -1;
  1623. }
  1624. for (int dyi =0; dyi<3;++dyi){
  1625. int dy = dy_range[dyi];
  1626. bool break_x = false;
  1627. for( int dxi=0;dxi<3;++dxi){
  1628. int dx = dx_range[dxi];
  1629. int x = start_pt.x+dx;
  1630. int y = start_pt.y+dy;
  1631. if (dx==0 && dy == 0){continue;}
  1632. if (x==pre_pt.x && y==pre_pt.y){continue;}
  1633. if (edge_img.at<unsigned char>(y,x)>0){
  1634. nxt_pt.x = x;
  1635. nxt_pt.y = y;
  1636. break_x = true;
  1637. break;
  1638. }
  1639. }
  1640. if (break_x){
  1641. break;
  1642. }
  1643. }
  1644. }
  1645. // qua_fitting
  1646. // input: xx ---- sorted x values, ascending
  1647. // yy ----- y values
  1648. //
  1649. // return: oa --- optimal angle, which is the x coordinates of center line of quadratic fitting line
  1650. double qua_fitting(vector<double>&xx, vector<double>&yy)
  1651. {
  1652. double oa = 0.0;
  1653. // fit quadratic function with opencv
  1654. cv::Mat A = cv::Mat::zeros(cv::Size(3,xx.size()), CV_64FC1);
  1655. for(int i=0; i<xx.size();++i){
  1656. A.at<double>(i,0) = 1;
  1657. A.at<double>(i,1) = xx[i];
  1658. A.at<double>(i,2) = xx[i] * xx[i];
  1659. }
  1660. cv::Mat b = cv::Mat::zeros(cv::Size(1,yy.size()), CV_64FC1);
  1661. for(int i = 0; i<yy.size(); ++i){
  1662. b.at<double>(i,0) = yy[i];
  1663. }
  1664. cv::Mat c, d;
  1665. c = A.t() * A;
  1666. d = A.t() * b;
  1667. cv::Mat result = cv::Mat::zeros(cv::Size(1,3),CV_64FC1);
  1668. cv::solve(c,d,result);
  1669. double a0 = result.at<double>(0, 0);
  1670. double a1 = result.at<double>(1, 0);
  1671. double a2 = result.at<double>(2, 0);
  1672. if(fabs(a2)<1.0e-6){
  1673. throw_msg(string("Error: a2 = 0 in solver"));
  1674. }
  1675. oa = -a1 / a2 / 2;
  1676. if(oa >180.0){oa -= 180.0;}
  1677. return oa;
  1678. }
  1679. };