utils.cpp 46 KB

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