utils.cpp 44 KB

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