utils.cpp 44 KB

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