123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845 |
- #include "utils.h"
- #include <opencv.hpp>
- #include <time.h>
- #include <algorithm>
- #include <math.h>
- //#include "fork_rs.h"
- namespace graft_cv{
- cv::Mat imginfo2mat(ImgInfo* imginfo)
- {
- assert(imginfo->data);
- assert(imginfo->height>0 && imginfo->width>0);
- assert(imginfo->channel == 1 || imginfo->channel == 3);
- cv::Mat m;
- if (imginfo->channel == 1) {
- m = cv::Mat(imginfo->height, imginfo->width, CV_8UC1);
- }
- if (imginfo->channel == 3) {
- m = cv::Mat(imginfo->height, imginfo->width, CV_8UC3);
- }
- for (int h = 0; h<imginfo->height; ++h)
- {
- memcpy((void*)(m.ptr(h)),
- (void*)(imginfo->data + h*imginfo->width * imginfo->channel),
- imginfo->width * imginfo->channel);
- };
- return m;
- };
- ImgInfo* mat2imginfo(const cv::Mat&img){
- assert(img.channels() == 1 || img.channels() == 3);
- ImgInfo* imginfo = new ImgInfo();
- imginfo->channel = img.channels();
- imginfo->height = img.rows;
- imginfo->width = img.cols;
- imginfo->data = 0;
- imginfo->data = new byte[imginfo->height * imginfo->width * imginfo->channel];
- memset(imginfo->data, 0, imginfo->height * imginfo->width * imginfo->channel);
- //IplImage ipl_img = cvIplImage(img);
- for (int i = 0; i<imginfo->height; ++i) {
- memcpy(imginfo->data + i*imginfo->width * imginfo->channel, img.ptr(i), imginfo->width * imginfo->channel);
- }
- return imginfo;
- };
- void imginfo_release(ImgInfo**ppImgInfo){
- ImgInfo* pImgInfo = *ppImgInfo;
- if(pImgInfo->data){
- delete [] pImgInfo->data;
- pImgInfo->data=0;
- }
- delete pImgInfo;
- pImgInfo = 0;
- };
- bool isvalid(const ImgInfo*imginfo)
- {
- if(!imginfo){return false;}
- if(!imginfo->data){return false;}
- if(imginfo->height*imginfo->width<=0){return false;}
- return true;
- }
- void image_set_bottom(
- cv::Mat& img,
- unsigned char value,
- int rows_cnt)
- {
- for(size_t r=(img.rows-rows_cnt);r<img.rows;++r){
- for(size_t c=0;c<img.cols;++c){
- img.at<unsigned char>(r,c) = value;
- }
- }
- }
- void image_set_top(
- cv::Mat& img,
- unsigned char value,
- int rows_cnt)
- {
- for(size_t r=0;r<rows_cnt;++r){
- for(size_t c=0;c<img.cols;++c){
- img.at<unsigned char>(r,c) = value;
- }
- }
- }
- void image_draw_line(
- cv::Mat& img,
- int x0,int y0,
- int x1,int y1)
- {
- cv::Point p0,p1;
- if(x0==x1){
- p0.x=x0;
- p0.y=0;
- p1.x=x0;
- p1.y=img.rows-1;
- }
- else{
- double k = (double)(y1-y0)/(double)(x1-x0);
- double b = (double)y0 - k * x0;
- p0.x=0;
- p0.y=b;
- p1.x=img.cols;
- p1.y = (int)(b + k * p1.x);
- }
- cv::line(img,p0,p1, cv::Scalar(255,255,255));
-
- }
- string currTime(){
- char tmp[64];
- struct tm ptime;
- time_t time_seconds = time(0);
- localtime_s(&ptime, &time_seconds);
- strftime(
- tmp,
- sizeof(tmp),
- "%Y-%m-%d %H:%M:%S",
- &ptime
- );
- return tmp;
- }
- // function getImgId
- // input im_type, img_type
- static unsigned int ID_COUNTER = 0;
- string getImgId(int im_type)
- {
- char tmp[64];
- struct tm ptime;
- time_t time_seconds = time(0);
- localtime_s(&ptime, &time_seconds);
- strftime(
- tmp,
- sizeof(tmp),
- "%Y%m%d%H%M%S",
- &ptime
- );
- unsigned int id_serial = ID_COUNTER % 100;
- stringstream buff;
- buff<<tmp;
- buff.width(2);
- buff.fill('0');
- buff<<id_serial;
- buff<<"_"<<im_type;
- ID_COUNTER+=1;
- return buff.str();
- }
- string getImgIdOa(string iid, int im_idx)
- {
- stringstream buff;
- buff<<im_idx;
- return iid+"_"+buff.str();
- }
- inline void throw_msg(string& msg){
- stringstream buff;
- buff<<"Error:"<<msg<<"\tfile:"<<__FILE__<<"\tline:"<<__LINE__;
- throw(buff.str());
- }
- void drawgrid(
- cv::Mat&img,
- int grid_col/*=50*/,
- int grid_row/*=50*/,
- int line_thick/*=2*/
- )
- {
- if(img.empty()){return;}
- //horizontal grid
- for(int row=img.rows-1; row>=0; row-=grid_row){
- cv::line(img, cv::Point(0,row), cv::Point(img.cols-1,row), cv::Scalar(100),line_thick);
- }
- //veritcal grid
- for(int col=0; col<img.cols; col+=grid_col){
- cv::line(img, cv::Point(col,0), cv::Point(col,img.rows-1), cv::Scalar(100),line_thick);
- }
- }
- void hist2image(
- vector<int>& hist,
- cv::Mat&img,
- int aix, /*=1*/
- int grid_col/*=50*/,
- int grid_row/*=50*/
- )
- {
- vector<int>::iterator maxit = max_element(begin(hist),end(hist));
- if( grid_col<10){grid_col=10;}
- if( grid_row<10){grid_row=10;}
- // aix = 0, column hist, aix=1, row histogtam
- if (aix==0){
- img = cv::Mat::zeros(hist.size(),*maxit, CV_8UC1);
- drawgrid(img,grid_col, grid_row,1);
- for(size_t i=0; i<hist.size();++i){
- if(i>=img.rows){break;}
- for(size_t j=0;j<=hist[i];++j){
- if(j>=img.cols){break;}
- img.at<unsigned char>(i,j)=255;
- }
- }
- }
- else{
- img = cv::Mat::zeros(*maxit,hist.size(), CV_8UC1);
- drawgrid(img,grid_col, grid_row,1);
- for(size_t i=0; i<hist.size();++i){
- int y = *maxit-hist[i]-1;
- if (y<0){y=0;}
- for(size_t j=y;j<img.rows;++j){
- img.at<unsigned char>(j,i)=255;
- }
- }
- }
- };
- //matHistogram
- // axis == 0, statistic histogram for columns;
- // others, statistic histogram for rows
- void mat_histogram(
- cv::Mat& img,
- std::vector<int>&hist,
- int axis,
- int r0,
- int r1,
- int c0,
- int c1
- )
- {
- hist.clear();
- if(r0<0){r0 = 0;}
- if(r1<0){r1 = img.rows;}
- if(c0<0){c0 = 0;}
- if(c1<0){c1 = img.cols;}
- if (axis==0){
- for(int c=c0;c<c1;++c){
- int cnt = 0;
- for(int r=r0;r<r1; ++r){
- if (img.at<unsigned char>(r,c)>0){cnt+=1;}
- }
- hist.push_back(cnt);
- }
- }
- else{
- for(int r=r0;r<r1; ++r){
- int cnt = 0;
- for(int c=c0;c<c1;++c){
- if (img.at<unsigned char>(r,c)>0){cnt+=1;}
- }
- hist.push_back(cnt);
- }
- }
- };
- //matHistogram
- // axis == 0, statistic histogram for columns;
- // others, statistic histogram for rows
- // with weight
- void mat_histogram_w(
- cv::Mat& img,
- std::vector<int>&hist,
- int axis,
- int r0,
- int r1,
- int c0,
- int c1,
- bool asc_w //true---ascending weight, [0-1.0], false --- descending weight [1.0--0.0]
- )
- {
- hist.clear();
- if(r0<0){r0 = 0;}
- if(r1<0){r1 = img.rows;}
- if(c0<0){c0 = 0;}
- if(c1<0){c1 = img.cols;}
-
- if (axis==0){
- double step = 1.0/(double)(r1-1);
- for(int c=c0;c<c1;++c){
- double cnt = 0.0;
- double hi=0.0;
- for(int r=r0;r<r1; ++r){
- if (img.at<unsigned char>(r,c)>0){
- if(asc_w){
- hi = (r-r0)*step;
- if(hi>=0.66666){
- cnt+=hi;
- }
- }
- else{
- hi = (r1-r+r0)*step;
- if(hi>=0.66666){
- cnt+=hi;
- }
- }
- }
- }
- hist.push_back((int)cnt);
- }
- }
- else{
- double step = 1.0/(double)(c1-1);
- for(int r=r0;r<r1; ++r){
- double cnt = 0.0;
- double hi=0.0;
- for(int c=c0;c<c1;++c){
- if (img.at<unsigned char>(r,c)>0){
- if(asc_w){
- hi = (c-c0)*step;
- if(hi>=0.66666){
- cnt+=hi;
- }
- }
- else{
- hi = (c1-c+c0)*step;
- if(hi>=0.66666){
- cnt+=hi;
- }
- }
- }
- }
- hist.push_back((int)cnt);
- }
- }
- };
- void mat_histogram_yfork(
- cv::Mat& img,
- std::vector<int>&hist,
- int r0,
- int r1
- )
- {
- hist.clear();
- if(r0<0){r0 = 0;}
- if(r1<0){r1 = img.rows;}
-
- double step = 1.0/(double)(r1-r0);
- for(int c=0;c<img.cols;++c){
- double cnt = 0.0;
- double hi=0.0;
- for(int r=r0;r<r1; ++r){
- if (img.at<unsigned char>(r,c)>0){
- hi = (r-r0)*step;
- //if(hi>=0.66666){
- cnt+=hi;
- //}
-
-
- }
- }
- hist.push_back((int)cnt);
- }
-
- };
- //get_stem_x_range() 确定茎的位置,x方向
- // 2022/1/4 chenhj 修改,大叶子下垂,将叶子识别成茎,增加histogram满足阈值情况下,两侧数据方差检测
- void get_stem_x_range_oa(
- const std::vector<int>&hist_h,
- double h_ratio,
- int padding,
- int cent_x,
- int width_x,
- int&x0,
- int&x1,
- int&stem_x0,
- int&stem_x1
- )
- {
- //hist_h: histogram in column
- //h_ratio: ratio for generating threshold
- // x0,x1: sub-image x-range
- // stem_x0,1: stem x-range
- // padding: pad for stem x-range
- // calculate the max-value of hist_h
- int min_segment_len = 0;//最小线段长度,如果小于此数值,不计为有效数据,跳过
- int min_segment_dist = 20;//多峰情况下,间隔像素数量
- int var_padding = 20;
- auto max_it = max_element(hist_h.begin(), hist_h.end());
- size_t max_idx = max_it - hist_h.begin();
- int th = int(h_ratio * (double)(*max_it));
- if(th<=0){
- throw_msg(string("error: threshold is 0 in get_stem_x_range()"));
- }
- // 1 计算小线段的起始位置
- vector<gcv_point<int>>segments;
- get_hist_segment(hist_h,th,segments);
- // 2 获取多峰位置
- vector<gcv_point<int>>peaks;
-
- if(segments.size()==1){
- peaks = segments; //单峰
- }
- else{// 小线段删除
- vector<gcv_point<int>>big_segments;
- for(size_t i=0;i<segments.size();++i){
- if(segments[i].y - segments[i].x>=min_segment_len){
- big_segments.push_back(segments[i]);
- }
- }
- if(big_segments.size()==1){
- peaks = big_segments;//单峰
- }
- else{// 合并
- peaks.push_back(big_segments[0]);
- for(size_t j=1;j<big_segments.size();++j){
- int dist = big_segments[j].x - peaks.back().y;
- if(dist>min_segment_dist){
- peaks.push_back(big_segments[j]);
- }
- else{
- peaks.back().y = big_segments[j].y;
- }
- }
- }
- }
- // 3 多峰情况,先计算center_ratio, 统计峰段两侧数据的方差,方差大的为茎
- int opt_index=0;
- if(peaks.size()>1){
- vector<double>center_r;
- for(size_t i=0;i<peaks.size();++i){
- double r = fabs(0.5*(double)(peaks[i].x+ peaks[i].y) - cent_x)/(double)(width_x/2.0);
- center_r.push_back(1.0-r);
- }
- vector<size_t>sort_idx = sort_indexes_e(center_r,false);
- double cr_1 = center_r[sort_idx[0]];
- double cr_2 = center_r[sort_idx[1]];
- if(cr_1-cr_2>0.2 || cr_1>=0.65){//如果存在居中显著的,按居中优先
- opt_index = sort_idx[0];
- }
- else{
- vector<int> candidate_idx;
- candidate_idx.push_back(sort_idx[0]);
- candidate_idx.push_back(sort_idx[1]);
- for(size_t id = 2;id<sort_idx.size();++id){
- if(cr_1 - center_r[sort_idx[id]]<=0.3){
- candidate_idx.push_back(sort_idx[id]);
- }
- }
- vector<double>vars;
- int vx0,vx1;
- for(size_t i=0;i<peaks.size();++i){
- vector<int>::iterator it = find(candidate_idx.begin(),candidate_idx.end(),i);
- if(it==candidate_idx.end()){
- vars.push_back(0.0);
- continue;
- }
- vx0 = peaks[i].x - var_padding;
- vx1 = peaks[i].y + var_padding;
- if(vx0<0){vx0=0;}
- if(vx1>hist_h.size()-1){vx1=hist_h.size()-1;}
- double mean,var;
- mean = 0.0;
- var=-1;
- get_hist_mean_var_local(hist_h,vx0,vx1,mean,var);
- if(var<0){var=0.0;}
- vars.push_back(var);
- }
- auto var_max_it = max_element(vars.begin(), vars.end());
- size_t var_max_idx = var_max_it - vars.begin();
- opt_index = var_max_idx;
- }
- }
- stem_x0 = peaks[opt_index].x;
- stem_x1 = peaks[opt_index].y;
- x0 = stem_x0 - padding;
- x1 = stem_x1 + padding;
- if(x0<0){x0=0;};
- if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
- //////////////////////////////////////
- //int global_start_idx=-1;
- //int global_end_idx=-1;
- //int start_idx=-1;
- //int end_idx=-1;
- //for(size_t i=0;i<hist_h.size();++i){
- // if(i==0){
- // if(hist_h[i]>=th){
- // start_idx=i;
- // }
- // continue;
- // }
- // if(hist_h[i]>=th && hist_h[i-1]<th){
- // start_idx=i;
- // continue;
- // }
- // if(i==hist_h.size()-1){
- // if(hist_h[i]>=th && hist_h[i-1]>=th){
- // if((i-start_idx+1)>=min_segment_len){
- // global_end_idx=i;
- // }
- // }
- // }
- // if(hist_h[i]<th && hist_h[i-1]>=th){
- // if((i-start_idx+1)>=min_segment_len){
- // if( global_start_idx<0){
- // global_start_idx = start_idx;
- // }
- // global_end_idx = i;
- // }
- // }
- //}
- ///*stem_x0 = 0;
- //stem_x1 = hist_h.size()-1;
- //for(size_t i=max_idx; i < hist_h.size(); ++i){
- // if(hist_h[i]>=th){
- // stem_x1 = i;
- // }
- // else{
- // break;
- // }
- //}
- //for(int i=max_idx; i >= 0; i--){
- // if(hist_h[i]>=th){
- // stem_x0 = i;
- // }
- // else{
- // break;
- // }
- //}*/
- //stem_x0 = global_start_idx;
- //stem_x1 = global_end_idx;
- //x0 = stem_x0 - padding;
- //x1 = stem_x1 + padding;
- //if(x0<0){x0=0;};
- //if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
- };
- void get_stem_x_range_rscut(
- const std::vector<int>&hist_h,
- double h_ratio,
- int padding,
- int cent_x,
- int width_x,
- int&x0,
- int&x1,
- int&stem_x0,
- int&stem_x1
- )
- {
- //hist_h: histogram in column
- //h_ratio: ratio for generating threshold
- // x0,x1: sub-image x-range
- // stem_x0,1: stem x-range
- // padding: pad for stem x-range
- // calculate the max-value of hist_h
- int min_segment_len = 0;//最小线段长度,如果小于此数值,不计为有效数据,跳过
- int min_segment_dist = 20;//多峰情况下,间隔像素数量
- int var_padding = 20;
- auto max_it = max_element(hist_h.begin(), hist_h.end());
- size_t max_idx = max_it - hist_h.begin();
- int th = int(h_ratio * (double)(*max_it));
- if(th<=0){
- throw_msg(string("error: threshold is 0 in get_stem_x_range()"));
- }
- // 1 计算小线段的起始位置
- vector<gcv_point<int>>segments;
- get_hist_segment(hist_h,th,segments);
- // 2 获取多峰位置
- vector<gcv_point<int>>peaks;
-
- if(segments.size()==1){
- peaks = segments; //单峰
- }
- else{// 小线段删除
- vector<gcv_point<int>>big_segments;
- for(size_t i=0;i<segments.size();++i){
- if(segments[i].y - segments[i].x>=min_segment_len){
- big_segments.push_back(segments[i]);
- }
- }
- if(big_segments.size()==1){
- peaks = big_segments;//单峰
- }
- else{// 合并
- peaks.push_back(big_segments[0]);
- for(size_t j=1;j<big_segments.size();++j){
- int dist = big_segments[j].x - peaks.back().y;
- if(dist>min_segment_dist){
- peaks.push_back(big_segments[j]);
- }
- else{
- peaks.back().y = big_segments[j].y;
- }
- }
- }
- }
- // 3 多峰情况,先计算center_ratio, 统计峰段两侧数据的方差,方差大的为茎
- int opt_index=0;
- if(peaks.size()>1){
- vector<double>center_r;
- for(size_t i=0;i<peaks.size();++i){
- double r = fabs(0.5*(double)(peaks[i].x+ peaks[i].y) - cent_x)/(double)(width_x/2.0);
- center_r.push_back(1.0-r);
- }
- vector<size_t>sort_idx = sort_indexes_e(center_r,false);
- double cr_1 = center_r[sort_idx[0]];
- double cr_2 = center_r[sort_idx[1]];
- if(cr_1-cr_2>0.2 || cr_1>=0.65){//如果存在居中显著的,按居中优先
- opt_index = sort_idx[0];
- }
- else{
- vector<int> candidate_idx;
- candidate_idx.push_back(sort_idx[0]);
- candidate_idx.push_back(sort_idx[1]);
- for(size_t id = 2;id<sort_idx.size();++id){
- if(cr_1 - center_r[sort_idx[id]]<=0.3){
- candidate_idx.push_back(sort_idx[id]);
- }
- }
- vector<double>vars;
- int vx0,vx1;
- for(size_t i=0;i<peaks.size();++i){
- vector<int>::iterator it = find(candidate_idx.begin(),candidate_idx.end(),i);
- if(it==candidate_idx.end()){
- vars.push_back(0.0);
- continue;
- }
- vx0 = peaks[i].x - var_padding;
- vx1 = peaks[i].y + var_padding;
- if(vx0<0){vx0=0;}
- if(vx1>hist_h.size()-1){vx1=hist_h.size()-1;}
- double mean,var;
- mean = 0.0;
- var=-1;
- get_hist_mean_var_local(hist_h,vx0,vx1,mean,var);
- if(var<0){var=0.0;}
- vars.push_back(var);
- }
- auto var_max_it = max_element(vars.begin(), vars.end());
- size_t var_max_idx = var_max_it - vars.begin();
- opt_index = var_max_idx;
- }
- }
- stem_x0 = peaks[opt_index].x;
- stem_x1 = peaks[opt_index].y;
- x0 = stem_x0 - padding;
- x1 = stem_x1 + padding;
- if(x0<0){x0=0;};
- if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
- //////////////////////////////////////
- //int global_start_idx=-1;
- //int global_end_idx=-1;
- //int start_idx=-1;
- //int end_idx=-1;
- //for(size_t i=0;i<hist_h.size();++i){
- // if(i==0){
- // if(hist_h[i]>=th){
- // start_idx=i;
- // }
- // continue;
- // }
- // if(hist_h[i]>=th && hist_h[i-1]<th){
- // start_idx=i;
- // continue;
- // }
- // if(i==hist_h.size()-1){
- // if(hist_h[i]>=th && hist_h[i-1]>=th){
- // if((i-start_idx+1)>=min_segment_len){
- // global_end_idx=i;
- // }
- // }
- // }
- // if(hist_h[i]<th && hist_h[i-1]>=th){
- // if((i-start_idx+1)>=min_segment_len){
- // if( global_start_idx<0){
- // global_start_idx = start_idx;
- // }
- // global_end_idx = i;
- // }
- // }
- //}
- ///*stem_x0 = 0;
- //stem_x1 = hist_h.size()-1;
- //for(size_t i=max_idx; i < hist_h.size(); ++i){
- // if(hist_h[i]>=th){
- // stem_x1 = i;
- // }
- // else{
- // break;
- // }
- //}
- //for(int i=max_idx; i >= 0; i--){
- // if(hist_h[i]>=th){
- // stem_x0 = i;
- // }
- // else{
- // break;
- // }
- //}*/
- //stem_x0 = global_start_idx;
- //stem_x1 = global_end_idx;
- //x0 = stem_x0 - padding;
- //x1 = stem_x1 + padding;
- //if(x0<0){x0=0;};
- //if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
- };
- void get_stem_x_range_scion(
- const std::vector<int>&hist_h,
- double h_ratio,
- int padding,
- int&x0,
- int&x1,
- int&stem_x0,
- int&stem_x1
- )
- {
- //hist_h: histogram in column
- //h_ratio: ratio for generating threshold
- // x0,x1: sub-image x-range
- // stem_x0,1: stem x-range
- // padding: pad for stem x-range
- // calculate the max-value of hist_h
- auto max_it = max_element(hist_h.begin(), hist_h.end());
- size_t max_idx = max_it - hist_h.begin();
- int th = int(h_ratio * (double)(*max_it));
- stem_x0 = 0;
- stem_x1 = hist_h.size()-1;
- for(size_t i=max_idx; i < hist_h.size(); ++i){
- if(hist_h[i]>=th){
- stem_x1 = i;
- }
- else{
- break;
- }
- }
- for(int i=max_idx; i >= 0; i--){
- if(hist_h[i]>=th){
- stem_x0 = i;
- }
- else{
- break;
- }
- }
- x0 = stem_x0 - padding;
- x1 = stem_x1 + padding;
- if(x0<0){x0=0;};
- if(x1>hist_h.size()-1){x1 = hist_h.size()-1;};
- };
- void get_hist_segment(
- const std::vector<int>& hist,
- int th,//threshold
- std::vector<gcv_point<int>>& segments
- )
- {
- segments.clear();
- int start_idx=-1;
- int end_idx=-1;
- for(size_t i=0;i<hist.size();++i){
- if(i==0){
- if(hist[i]>=th){
- start_idx=i;
- }
- continue;
- }
- if(hist[i]>=th && hist[i-1]<th){
- start_idx=i;
- continue;
- }
- if(i==hist.size()-1){
- if(hist[i]>=th && hist[i-1]>=th){
- segments.push_back(gcv_point<int>(start_idx,i));
- }
- }
- if(hist[i]<th && hist[i-1]>=th){
- segments.push_back(gcv_point<int>(start_idx,i-1));
- }
- }
- }
- void get_hist_mean_var_local(
- const std::vector<int>& hist,
- int x0,
- int x1,
- double& mean,
- double& var
- )
- {
- mean=0.0;
- var = 0.0;
- if(hist.size()==0){
- return;
- }
- if(x0<0){x0=0;}
- if(x1<0 || x1>hist.size()-1){hist.size()-1;}
-
- for(int i=x0;i<=x1;++i){
- mean+=hist[i];
- }
- mean /=(double)(x1-x0+1);
-
- for(int i=x0;i<=x1;++i){
- var+=((double)(hist[i])-mean) * ((double)(hist[i])-mean);
- }
- var /= (double)(x1-x0+1);
-
- }
- void get_stem_y_fork(
- const std::vector<int>& hist,
- double ratio,
- int stem_dia_min,
- int stem_fork_y_min,
- double stem_dia_mp,
- int& fork_y, //output
- int& end_y, //output
- int& stem_dia //output
- )
- {
- //# 由y的底部向上检测是否有茎,有的话计算移动均值
- //# 通过当前值和移动均值的比值识别分叉点
- //# stem_dia_min = 10 # 茎粗最小值
- //# stem_fork_y_min = 10 # 茎分叉位置与茎根的最小像素高度
- //# stem_dia_mp = 0.8 # moving power
- fork_y = end_y = stem_dia = -1;
- std::vector<int>reversed_hist;
- for(int ii=hist.size()-1; ii>=0;ii--){
- reversed_hist.push_back(hist[ii]);
- }
- int stem_root_y = -1;
- double stem_dia_ma = -1;//# stem diameter moving-average
- size_t i=0;
- for(; i<hist.size(); ++i){
- if (i==0){continue;}
- if ((reversed_hist[i-1]<stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0)
- ||(reversed_hist[i-1]>=stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0))
- {
- //# stem root begin
- stem_root_y = i;
- stem_dia_ma = (double)reversed_hist[i];
- }
- else {
- if (reversed_hist[i-1]>=stem_dia_min &&
- reversed_hist[i]>=stem_dia_min)
- {
- double fork_ratio = reversed_hist[i] / stem_dia_ma;
- //if (i<150){
- // cout<<fork_ratio<<"\t"<<reversed_hist[i]<<"\t"<<stem_dia_ma<<endl;
- //}
- if( i - stem_root_y > stem_fork_y_min &&
- fork_ratio >= ratio)
- {
- //# found the fork y level
- fork_y = hist.size() - i;
- stem_dia = reversed_hist[i-1];
- break;
- }
- //# update stem_dia_ma
- stem_dia_ma = stem_dia_mp *stem_dia_ma + (1.0-stem_dia_mp) * reversed_hist[i];
- }
- }
- };
- if(stem_root_y<0){
- throw_msg(string("not exists diameter bigger than stem_dia_min"));
- }
- if(fork_y<0){
- throw_msg(string("not exists diameter fork_ratio bigger than threshold"));
- }
- //end_y
- end_y = hist.size() - stem_root_y-1;
- //统计stem_root_y 到 i 间reversed_hist的数值,得到stem_dia。方法可用平均值,75%分位数
- vector<int> tmp;
- for(size_t j=stem_root_y; j<=i;++j){
- tmp.push_back(reversed_hist[j]);
- }
- sort(tmp.begin(), tmp.end());
- int tar_idx = (int)((float)tmp.size()*0.75);
- stem_dia = tmp[tar_idx];
- };
- void get_stem_y_fork_3sigma(
- const std::vector<int>& hist,
- double ratio,
- int stem_dia_min,
- int stem_fork_y_min,
- double stem_dia_mp,
- int& fork_y, //output
- int& end_y, //output
- int& stem_dia //output
- )
- {
-
-
- fork_y = end_y = stem_dia = -1;
- std::vector<int>reversed_hist;
- for(int ii=hist.size()-1; ii>=0;ii--){
- reversed_hist.push_back(hist[ii]);
- }
- int mean_radius =2;
- double mean_dia = 2.0*mean_radius +1.0;
- double mean = 0.0;
- double min_m, max_m;
- min_m = max_m = 0.0;
- std::vector<double>reversed_mean;
- for(int ii=0; ii<reversed_hist.size();ii++){
- if(ii-mean_radius<0 || ii + mean_radius>=reversed_hist.size()){
- reversed_mean.push_back(0.0);
- continue;
- }
- mean = 0.0;
- for(int k = -mean_radius;k<=mean_radius;++k){
- mean+=(double)reversed_hist[ii+k];
- }
- mean /= mean_dia;
- if(mean>max_m){max_m = mean;}
- reversed_mean.push_back(mean);
- }
- cv::Mat tmp;
- hist2image_scale_line(reversed_mean,tmp,min_m,max_m,1.0,min_m,50,50);
- imshow_wait("mean5",tmp);
- return;
- int stem_root_y = -1;
- double stem_dia_ma = -1;//# stem diameter moving-average
- size_t i=0;
- for(; i<reversed_mean.size(); ++i){
- if (i==0){continue;}
- if ((reversed_hist[i-1]<stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0)
- ||(reversed_hist[i-1]>=stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0))
- {
- //# stem root begin
- stem_root_y = i;
- stem_dia_ma = (double)reversed_hist[i];
- }
- else {
- if (reversed_hist[i-1]>=stem_dia_min &&
- reversed_hist[i]>=stem_dia_min)
- {
- double fork_ratio = reversed_hist[i] / stem_dia_ma;
- //if (i<150){
- // cout<<fork_ratio<<"\t"<<reversed_hist[i]<<"\t"<<stem_dia_ma<<endl;
- //}
- if( i - stem_root_y > stem_fork_y_min &&
- fork_ratio >= ratio)
- {
- //# found the fork y level
- fork_y = hist.size() - i;
- stem_dia = reversed_hist[i-1];
- break;
- }
- //# update stem_dia_ma
- stem_dia_ma = stem_dia_mp *stem_dia_ma + (1.0-stem_dia_mp) * reversed_hist[i];
- }
- }
- };
- //r2index(
- /*
- int stem_root_y = -1;
- double stem_dia_ma = -1;//# stem diameter moving-average
- size_t i=0;
- for(; i<hist.size(); ++i){
- if (i==0){continue;}
- if ((reversed_hist[i-1]<stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0)
- ||(reversed_hist[i-1]>=stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0))
- {
- //# stem root begin
- stem_root_y = i;
- stem_dia_ma = (double)reversed_hist[i];
- }
- else {
- if (reversed_hist[i-1]>=stem_dia_min &&
- reversed_hist[i]>=stem_dia_min)
- {
- double fork_ratio = reversed_hist[i] / stem_dia_ma;
- //if (i<150){
- // cout<<fork_ratio<<"\t"<<reversed_hist[i]<<"\t"<<stem_dia_ma<<endl;
- //}
- if( i - stem_root_y > stem_fork_y_min &&
- fork_ratio >= ratio)
- {
- //# found the fork y level
- fork_y = hist.size() - i;
- stem_dia = reversed_hist[i-1];
- break;
- }
- //# update stem_dia_ma
- stem_dia_ma = stem_dia_mp *stem_dia_ma + (1.0-stem_dia_mp) * reversed_hist[i];
- }
- }
- };
- if(stem_root_y<0){
- throw_msg(string("not exists diameter bigger than stem_dia_min"));
- }
- if(fork_y<0){
- throw_msg(string("not exists diameter fork_ratio bigger than threshold"));
- }
- //end_y
- end_y = hist.size() - stem_root_y-1;
- //统计stem_root_y 到 i 间reversed_hist的数值,得到stem_dia。方法可用平均值,75%分位数
- vector<int> tmp;
- for(size_t j=stem_root_y; j<=i;++j){
- tmp.push_back(reversed_hist[j]);
- }
- sort(tmp.begin(), tmp.end());
- int tar_idx = (int)((float)tmp.size()*0.75);
- stem_dia = tmp[tar_idx];
- */
- };
- void get_stem_y_fork_rs(
- const std::vector<int>& hist,
- double ratio,
- int stem_dia_min,
- int stem_fork_y_min,
- double stem_dia_mp,
- int& fork_y, //output
- int& end_y, //output
- int& stem_dia, //output
- int& roi_max_y //output
- )
- {
- //# 由y的底部向上检测是否有茎,有的话计算移动均值
- //# 通过当前值和移动均值的比值识别分叉点
- //# stem_dia_min = 10 # 茎粗最小值
- //# stem_fork_y_min = 10 # 茎分叉位置与茎根的最小像素高度
- //# stem_dia_mp = 0.8 # moving power
- fork_y = end_y = stem_dia = -1;
- std::vector<int>reversed_hist;
- for(int ii=hist.size()-1; ii>=0;ii--){
- reversed_hist.push_back(hist[ii]);
- }
- int stem_root_y = -1;
- double stem_dia_ma = -1;//# stem diameter moving-average
- int max_y = 0;
- int max_val = reversed_hist[0];
- size_t i=0;
- vector<double> index_of_diachange;
- vector<double> index_of_diameter;
- for(; i<reversed_hist.size(); ++i){
- //find max value index
- if(reversed_hist[i]>max_val){
- max_val = reversed_hist[i];
- max_y = i;
- }
- if (i==0){
- index_of_diachange.push_back(0.0);
- continue;
- }
- if ((reversed_hist[i-1]<stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0)
- ||(reversed_hist[i-1]>=stem_dia_min && reversed_hist[i]>=stem_dia_min && stem_root_y<0))
- {
- //# stem root begin
- stem_root_y = i;
- stem_dia_ma = (double)reversed_hist[i];
- index_of_diachange.push_back(0.0);
- }
- else {
- if (reversed_hist[i-1]>=stem_dia_min &&
- reversed_hist[i]>=stem_dia_min)
- {
- double fork_ratio = reversed_hist[i] / stem_dia_ma;
- if(fork_ratio>1.5){fork_ratio=1.5;}
- index_of_diachange.push_back(fork_ratio);
- stem_dia_ma = stem_dia_mp *stem_dia_ma + (1.0-stem_dia_mp) * reversed_hist[i];
- }
- else{
- index_of_diachange.push_back(0.0);
- }
- }
- };
- if(stem_root_y<0){
- throw_msg(string("not exists diameter bigger than stem_dia_min"));
- }
-
- //end_y
- end_y = hist.size() - stem_root_y-1;
- //统计stem_root_y 到 max_y 间reversed_hist的数值,得到stem_dia。方法可用平均值,40%分位数
- vector<int> tmp;
- for(size_t j=stem_root_y; j<max_y;++j){
- if(j<0 || j>=reversed_hist.size()){continue;}
- tmp.push_back(reversed_hist[j]);
- }
- sort(tmp.begin(), tmp.end());
- int tar_idx = (int)((float)tmp.size()*0.40);
- if(tmp.size()==0 || tar_idx>=tmp.size()){
- throw_msg(string("not exists y fork point"));
- }
- stem_dia = tmp[tar_idx];
- ////////////////////////////////////////
- //update 重新检验max_y,因为max_y是全局最大,由于苗的倾斜,可能出现局部最大为分叉点位置
- // 2022-2-21 将比例参数1.5调整至2.0
- int local_max_y = max_y;
- for(size_t j=stem_root_y; j<max_y;++j){
- if((double)(reversed_hist[j]/(double)stem_dia) >=2.0){
- local_max_y = j;
- break;
- }
- }
- max_y = local_max_y ;
- roi_max_y = hist.size() - max_y-1;
-
- //////////////////////////////////////////////////////////////////
- // 计算vector<double>index_of_diameter
- for(int idx = 0;idx<reversed_hist.size();++idx){
-
- if(reversed_hist[idx]==0){
- index_of_diameter.push_back(0.0);
- }
- else {
- if(idx>=max_y){
- index_of_diameter.push_back(0.0);
- }
- else{
- double r = yfork_validity_stemdiaratio(stem_dia,reversed_hist[idx],ratio);
- index_of_diameter.push_back(r);
- }
- }
- }
- //////////////////////////////////////////////////////////////////
- // 计算fork_y
- vector<double> index_yfork;
- for(int idx = 0;idx<reversed_hist.size();++idx){
- index_yfork.push_back(index_of_diachange[idx] * index_of_diameter[idx]);
- }
- vector<double>::iterator max_it = max_element(begin(index_yfork),end(index_yfork));
- int max_idx_y = max_it - index_yfork.begin();
- fork_y = hist.size() - max_idx_y;
- };
- void get_stem_y_fork_rs_update(
- const cv::Mat& bin_img,
- int stem_x_padding,
- int x0,
- int x1,
- int max_y,
- int stem_root_y,
- int stem_dia,
- bool image_show,
- double cut_point_offset_ratio,
- cv::Point& fork_cent,
- double& max_radius,
- double& stem_angle
- )
- {
- //1 通过bin_img的二值图像,找到茎中心线
- //2 通过中心线,更新二值图像采集区域(有可能倾斜)
- //3 在max_y附近找最大内接圆中心(在茎中心线上)
- fork_cent.x = fork_cent.y = -1;
- max_radius = 0.0;
- stem_angle = 90.0;
- // 茎中心点提取, 在图像max_y 和 stem_root_y之间
- int max_len,start_x,cent_x, pre_val, cur_val;
- vector<int>cent_xs;
- vector<int>cent_ys;
- for(int i=0;i<bin_img.rows;++i){
- if(i<=max_y || i>=stem_root_y){continue;}
- max_len = start_x = cent_x = -1;
- for(int j=x0;j<=x1;++j){
- if(j==x0 && bin_img.at<unsigned char>(i,j)==0){continue;}
- if(j==x0 && bin_img.at<unsigned char>(i,j)>0){start_x=j;continue;}
- pre_val = bin_img.at<unsigned char>(i,j-1);
- cur_val = bin_img.at<unsigned char>(i,j);
- if(pre_val==0 && cur_val>0){
- start_x = j;
- }
- if((pre_val>0 && cur_val==0) ||(j==x1 && cur_val>0)){
- int len = j-start_x +1;
- if(len>max_len){
- max_len = len;
- cent_x = (start_x+j)/2;
- //cout<<i<<"\t"<<"x0="<<start_x<<", x1="<<j<<", centx="<<cent_x<<endl;
- }
- }
- }
- if(cent_x>0){
- cent_xs.push_back(cent_x);
- cent_ys.push_back(i);
- }
- }
- if(cent_xs.size()!=cent_ys.size() || cent_xs.size()<3){
- throw_msg(string("stem length < 3, in function get_stem_y_fork_rs_update()"));
- }
- //茎中心线拟合
- double beta0, beta1;
- beta0 = beta1 = 0.0;
- // x = beta0 + beta1 * y
- double r2 = r2index(cent_ys,cent_xs,0,cent_xs.size()-1, beta0, beta1);
- double th = (double)stem_dia/2.0 + stem_x_padding;
- cv::Mat roi_img = bin_img.clone();
- //提取兴趣区域图片
- stem_angle = atan(beta1)*57.2957795130 + 90.0;
- if(fabs(stem_angle-90.0)<2.0){
- for(int r=0;r<roi_img.rows;++r){
- for(int c=0;c<roi_img.cols;++c){
- if(c<x0 || c>x1){
- roi_img.at<unsigned char>(r,c) = 0;
- }
- }
- }
- }
- else{
- for(int r=0;r<roi_img.rows;++r){
- double cx = beta0 + beta1 * r;
- int cx0 = (int)(cx-th);
- int cx1 = (int)(cx+th);
- for(int c=0;c<roi_img.cols;++c){
- if(c<cx0 || c>cx1){
- roi_img.at<unsigned char>(r,c) = 0;
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////
- //
- if(image_show){
- cv::Mat tmp = roi_img.clone();
- cv::Point p0((int)(beta0),0);
- cv::Point p1((int)((double)tmp.rows *beta1 +beta0),tmp.rows);
- cv::line(tmp,p0,p1, cv::Scalar(128,0,0));
- cv::line(tmp, cv::Point(cent_xs[0],cent_ys[0]),
- cv::Point(cent_xs.back(),cent_ys.back()), cv::Scalar(128,0,0));
-
- imshow_wait("rs_bin_roi", tmp);
- }
- ///////////////////////////////////////////////////////////
- //兴趣区域图片findContours
- vector<vector<cv::Point>> contours;
- vector<cv::Vec4i> hierarchy;
- findContours(roi_img,contours,hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
- //找到周长最长的区域作为目标区域
- int max_length_idx = 0;
- int max_length_val = contours[0].size();
- for(int i=0;i<contours.size();++i){
- if(i==0){continue;}
- if(contours[i].size()>max_length_val){
- max_length_val = contours[i].size();
- max_length_idx = i;
- }
- }
- vector<float>inner_max_radius;
- vector<int> nnindex;
- vector<int> xs;
- /*
- //////////////////////////////////////////
- //基于flann的方法
- vector<Point> contours_serial;
- for(size_t i=0;i<contours.size();++i){
- if(contours_serial.size()==0){
- contours_serial.assign(contours[i].begin(),contours[i].end());
- }
- else{
- for(size_t j=0;j<contours[i].size();++j){
- contours_serial.push_back(contours[i][j]);
- }
- }
- }
- //find inner max radius
- Mat source = Mat(contours_serial).reshape(1);
- source.convertTo(source,CV_32F);
- flann::KDTreeIndexParams indexParams(2);
- flann::Index kdtree(source, indexParams);
- unsigned queryNum=1;
- vector<float> vecQuery(2);
- vector<int> vecIndex(queryNum);
- vector<float> vecDist(queryNum);
- flann::SearchParams params(32);
- //在茎中心线上计算最优点指标:
- // 1) inner_max_radius --- 中心点对应的最小内接圆半径
- for(size_t r =0; r<roi_img.rows;++r){
- if(r < max_y - 50){
- inner_max_radius.push_back(0.0);
- nnindex.push_back(-1);
- xs.push_back(-1);
- continue;
- }
- // x = beta0 + beta1 * y
- float x = (float) (beta0 + beta1 * r);
- int xi = (int)(x+0.5);
- if(xi<0 || xi>=roi_img.cols){
- inner_max_radius.push_back(0.0);
- nnindex.push_back(-1);
- xs.push_back(-1);
- continue;
- }
- if(roi_img.at<unsigned char>(r,xi)==0){
- inner_max_radius.push_back(0.0);
- nnindex.push_back(-1);
- xs.push_back(xi);
- continue;
- }
- vecQuery[0] = x;//x
- vecQuery[1] = (float)r;//y
- kdtree.knnSearch(vecQuery, vecIndex, vecDist,queryNum, params);
- if(vecDist.size()<1 || vecIndex.size()<1){
- inner_max_radius.push_back(0.0);
- nnindex.push_back(-1);
- xs.push_back(xi);
- continue;
- }
- inner_max_radius.push_back(vecDist[0]);
- nnindex.push_back(vecIndex[0]);
- xs.push_back(xi);
- }
- */
- ////////////////////////////////////////////////////////////////
- //基于pointPolygonTest的方法
- //在茎中心线上计算最优点指标:
- // 1) inner_max_radius --- 中心点对应的最小内接圆半径
- for(size_t r =0; r<roi_img.rows;++r){
- if(r < max_y - 50){
- inner_max_radius.push_back(0.0);
- nnindex.push_back(-1);
- xs.push_back(-1);
- continue;
- }
- // x = beta0 + beta1 * y
- float x = (float) (beta0 + beta1 * r);
- int xi = (int)(x+0.5);
- if(xi<0 || xi>=roi_img.cols){
- inner_max_radius.push_back(0.0);
- nnindex.push_back(-1);
- xs.push_back(-1);
- continue;
- }
- if(roi_img.at<unsigned char>(r,xi)==0){
- inner_max_radius.push_back(0.0);
- nnindex.push_back(-1);
- xs.push_back(xi);
- continue;
- }
- float d = cv::pointPolygonTest( contours[max_length_idx], cv::Point2f(x,r), true );
- if(d<0){
- inner_max_radius.push_back(0.0);
- nnindex.push_back(-1);
- xs.push_back(xi);
- continue;
- }
- inner_max_radius.push_back(d);
- nnindex.push_back(-1);
- xs.push_back(xi);
- }
- //find max radius point
- vector<float>::iterator max_it = max_element(inner_max_radius.begin(),inner_max_radius.end());
- int max_idx = max_it - inner_max_radius.begin();
- //基于flann的方法
- //max_radius = sqrt(inner_max_radius[max_idx]);
- max_radius = inner_max_radius[max_idx];
- //offset
- double r_offset = cut_point_offset_ratio * max_radius;
- double dy = r_offset * (1.0 / sqrt(1.0 + beta1 * beta1));
- double x_offset = beta0 + beta1 * (max_idx + dy);
- fork_cent.x = (int)(x_offset+0.5);
- fork_cent.y =(int)(max_idx + dy + 0.5);
- ///////////////////////////////////////////////
- //test CForkDetectOptimal 2022-2-17
- /*CForkDetectOptimal fdo(100,20);
- vector<float>opt_max;
- int opt_max_idx = 0;
- double optm=0;
- for(size_t r =0; r<xs.size();++r){
- if(abs((int)r-max_idx)>50){
- opt_max.push_back(0.0);
- continue;
- }
- else{
- if(xs[r]<0){
- opt_max.push_back(0.0);
- continue;
- }
- Point cent_pt = Point2f(xs[r],r);
- double rt = fdo.center_pt_index(bin_img,cent_pt,-60.0);
- opt_max.push_back((float)rt);
- if(rt>optm){
- optm=rt;
- opt_max_idx=r;
- }
- }
- }*/
- ///////////////////////////////////////////////
- ///////////////////////////////////////////////
- //test
- if(image_show){
- cv::Mat edge;
- edge = cv::Mat::zeros(roi_img.rows, roi_img.cols, CV_8UC1);
- cv::drawContours(edge, contours, -1, cv::Scalar(255, 255, 0), 1);
- for(int r=0;r<xs.size();++r){
- int c = xs[r];
- if(c<0){continue;}
- if(inner_max_radius[r]<=0){continue;}
- /*
- //基于flann的方法
- if(nnindex[r]<0){continue;}
- line(edge,Point(c,r),contours_serial[nnindex[r]],Scalar(128,0,0));
- */
- int rad = int(inner_max_radius[r]);
- cv::line(edge, cv::Point(c-rad,r), cv::Point(c+rad,r), cv::Scalar(80,0,0));
- }
- cv::Point fork_cent_origin;
- fork_cent_origin.x = xs[max_idx];
- fork_cent_origin.y =max_idx;
- cv::circle(edge,fork_cent_origin,3, cv::Scalar(200,0,0));
- cv::circle(edge,fork_cent_origin,(int)(max_radius), cv::Scalar(200,0,0));
- //circle(edge,Point(xs[opt_max_idx],opt_max_idx),5,Scalar(200,0,0));
- cv::circle(edge,fork_cent,3, cv::Scalar(128,0,0));
- cv::circle(edge,fork_cent,(int)(max_radius), cv::Scalar(128,0,0));
- imshow_wait("rs_bin_roi_radius_circle", edge);
- }
- ///////////////////////////////////////////////
- };
- //分叉位置指标 yfork_validity_position()
- // max_y---hist中最大点的y-index,
- // end_y ----hist中茎最底端点的y-index
- // fork_y --- 预期分叉点y-index
- // max_y > fork_y > end_y
- double yfork_validity_position(int max_y, int end_y, int fork_y){
- //确定hist最大点位置向下到end_y点最优分叉点位置系数
- double validity = 0.0;
- double opt_pos = 0.85;
- if(fork_y<end_y || fork_y>max_y){return validity;}
- double opt_y = (max_y-end_y+1)*opt_pos + end_y;
- if(fork_y<=end_y || fork_y>=max_y){return validity;}
- if(fork_y>end_y && fork_y<opt_y){
- validity =(double)( (fork_y-end_y+1) / (opt_y-end_y+1));
- }
- else{
- validity =(double)( (max_y -fork_y+1) / (max_y -opt_y+1));
- }
- return validity;
- }
- //茎粗比指标 yfork_validity_stemdiaratio()
- // stem_dia---茎粗,
- // dia_y ----hist中y-index对应的茎粗
- double yfork_validity_stemdiaratio(int stem_dia, int dia_y,double opt_dia_ratio){
- //确定hist最大点位置向下到end_y点最优分叉点位置系数
- double validity = 0.0;
- //double opt_pos = 1.2;
- double opt_dia = stem_dia*opt_dia_ratio;
- if(dia_y<opt_dia){return dia_y/opt_dia;}
- else{
- return opt_dia/dia_y;
- }
- }
- //计算上下窗口茎粗变化率
- void yfork_validity_stemdiachange(
- const std::vector<int>& hist,
- int stem_dia_min,
- std::vector<double>& var_ratio
- )
- {
- var_ratio.clear();
- int smooth_width = 5;
- double mean_pre=0.0;
- double mean_nxt=0.0;
- int low_y,up_y;
- low_y=up_y=-1;
- for(int i=0;i<hist.size();++i){
- if(hist[i]>=stem_dia_min){
- if( low_y<0){low_y=i;}
- if(i>up_y){up_y = i;}
- }
- }
- for(int i=0;i<hist.size();++i){
- if(i-smooth_width<0 || i+smooth_width>=hist.size()){
- var_ratio.push_back(0.0);
- continue;
- }
- if(i<low_y+smooth_width || i>up_y-smooth_width){
- var_ratio.push_back(0.0);
- continue;
- }
- mean_pre = mean_nxt=0.0;
- //low sum
- for(int di = -smooth_width;di<0;++di){
- mean_pre += hist[i+di];
- }
- //up sum
- for(int di = 0;di<smooth_width;++di){
- mean_nxt += hist[i+di];
- }
- if(mean_pre<1.0e-5){
- var_ratio.push_back(0.0);
- continue;
- }
- else{
- double ratio = mean_nxt / mean_pre;
- if(ratio>3.0){ratio=3.0;}
- var_ratio.push_back(ratio);
- }
- }
- }
- void imshow_wait(
- const char* winname,
- cv::Mat&img,
- int waittype/*=-1*/
- )
- {
- cv::namedWindow(winname, cv::WINDOW_NORMAL);
- cv::imshow(winname, img);
- cv::waitKey(waittype);
- };
- int get_stem_fork_right(
- cv::Mat&stem_img,
- int stem_fork_y,
- int stem_x0,
- int stem_x1,
- int detect_window
- )
- {
- /*
- 通过茎分叉坐标找到右侧茎分叉点坐标
- stem_img, 二值图像,茎部分的局部图像
- stem_fork_y, 检测到的茎分叉的y坐标
- stem_x0, 茎左侧边缘(通过hist检测,针对stem_fork_y不一定精确)
- stem_x1, 茎右侧边缘(通过hist检测,针对stem_fork_y不一定精确)
- */
-
- // 注释掉 2021-11-3 开始
- /*double min_dist = 1.0;
- int min_x1 = -1;
- for(size_t i=stem_x0; i<stem_x1+2*detect_window; ++i){
- double left_cnt = 0.0;
- double right_cnt = 0.0;
- for(size_t j=i-detect_window+1; j<i+1; ++j){
- if (stem_img.at<unsigned char>(stem_fork_y,j)>0){
- left_cnt+=1;
- }
- }
- for(size_t j=i+1; j<i+detect_window+1; ++j){
- if( stem_img.at<unsigned char>(stem_fork_y,j)==0){
- right_cnt+=1;
- }
- }
- if( left_cnt==0 || right_cnt ==0){continue;}
- double dist = 1.0 - min(left_cnt/right_cnt, right_cnt/left_cnt);
- if (dist < min_dist){
- min_dist = dist;
- min_x1 = i;
- }
- }
- return min_x1;
- */
- // 注释掉 2021-11-3 结束
- size_t left_min = stem_x0-4*detect_window;
- size_t right_max = stem_x1+4*detect_window;
- if(left_min<0){left_min = 0;}
- if(right_max>stem_img.cols){right_max = stem_img.cols;}
- int max_len = -1;
- int max_idx = -1;
- int start_x=left_min;
- for(size_t i=left_min; i<right_max; ++i){
- if(i==left_min){
- if(stem_img.at<unsigned char>(stem_fork_y,i)>0){
- start_x =i;
- }
- continue;
- }
- if (stem_img.at<unsigned char>(stem_fork_y,i-1)==0 &&
- stem_img.at<unsigned char>(stem_fork_y,i)>0){
- start_x =i;
- continue;
- }
-
- if ((stem_img.at<unsigned char>(stem_fork_y,i-1)>0 && stem_img.at<unsigned char>(stem_fork_y,i)==0) ||
- (stem_img.at<unsigned char>(stem_fork_y,i)>0 && i==right_max-1)){
- if(((int)i-start_x) > max_len){
- max_len = i-start_x;
- max_idx = i;
- }
- }
- }
- if(max_idx<0){max_idx = stem_x1;}
- return max_idx;
- };
- int get_stem_fork_left(
- cv::Mat&stem_img,
- int stem_fork_y,
- int stem_x0,
- int stem_x1,
- int detect_window
- )
- {
- /*
- 通过茎分叉坐标找到右侧茎分叉点坐标
- stem_img, 二值图像,茎部分的局部图像
- stem_fork_y, 检测到的茎分叉的y坐标
- stem_x0, 茎左侧边缘(通过hist检测,针对stem_fork_y不一定精确)
- stem_x1, 茎右侧边缘(通过hist检测,针对stem_fork_y不一定精确)
- */
-
- size_t left_min = stem_x0-8*detect_window;
- size_t right_max = stem_x1+8*detect_window;
- if(left_min<0){left_min = 0;}
- if(right_max>stem_img.cols-1){right_max=stem_img.cols;}
- int max_len = -1;
- int max_idx = -1;
- int start_x=left_min;
- for(size_t i=left_min; i<right_max; ++i){
- if(i==left_min){
- if(stem_img.at<unsigned char>(stem_fork_y,i)>0){
- start_x =i;
- }
- continue;
- }
- if (stem_img.at<unsigned char>(stem_fork_y,i-1)==0 &&
- stem_img.at<unsigned char>(stem_fork_y,i)>0)
- {
- start_x =i;
- continue;
- }
- if ((stem_img.at<unsigned char>(stem_fork_y,i-1)>0 && stem_img.at<unsigned char>(stem_fork_y,i)==0) ||
- (stem_img.at<unsigned char>(stem_fork_y,i)>0 && i==right_max-1)){
- if(((int)i-start_x) > max_len){
- max_len = i-start_x;
- max_idx = start_x;
- }
- }
- }
- if(max_idx<0){max_idx = stem_x0;}
- return max_idx;
- }
- void get_stem_fork_xs(
- cv::Mat&stem_img,
- int stem_fork_y,
- int stem_x0,
- int stem_x1,
- int x0,
- int x1,
- int & fork_x0,
- int & fork_x1
- )
- {
- size_t left_min = x0;
- size_t right_max = x1;
- if(left_min<0){left_min = 0;}
- if(right_max>=stem_img.cols){right_max = stem_img.cols-1;}
- int max_len = -1;
- int max_idx = -1;
- int max_idx_right = -1;
- int start_x=left_min;
- for(size_t i=left_min; i<right_max; ++i){
- if ( i==0 &&
- stem_img.at<unsigned char>(stem_fork_y,i)>0)
- {
- start_x =i;
- continue;
- }
- if (stem_img.at<unsigned char>(stem_fork_y,i)==0 &&
- stem_img.at<unsigned char>(stem_fork_y,i+1)>0)
- {
- start_x =i;
- }
-
- if ((stem_img.at<unsigned char>(stem_fork_y,i)>0 && stem_img.at<unsigned char>(stem_fork_y,i+1)==0) ||
- (stem_img.at<unsigned char>(stem_fork_y,i)>0 && i==right_max-1)){
- if(((int)i-start_x) > max_len)
- {
- max_len = i-start_x;
- max_idx = start_x;
- max_idx_right = i;
- }
- }
- }
- if(max_idx<0){max_idx = stem_x0;}
- fork_x0 = max_idx;
- if(max_idx_right<0){max_idx_right = stem_x1;}
- fork_x1 = max_idx_right;
- }
- // 根据边缘上的一点,爬取下一点
- void get_next_pt(
- cv::Mat& edge_img,
- gcv_point<int>&start_pt,
- gcv_point<int>&pre_pt,
- gcv_point<int>&nxt_pt,//output
- bool is_up //true 向上爬取, false 向下爬取
- )
- {
- nxt_pt.x=-1;
- nxt_pt.y=-1;
- int dy_range[3] = {-1,0,1};
- int dx_range[3] = {1,0,-1};
- if (!is_up){
- dy_range[0] = 1;
- dy_range[2] = -1;
- }
-
- for (int dyi =0; dyi<3;++dyi){
- int dy = dy_range[dyi];
- bool break_x = false;
- for( int dxi=0;dxi<3;++dxi){
- int dx = dx_range[dxi];
- int x = start_pt.x+dx;
- int y = start_pt.y+dy;
- if (dx==0 && dy == 0){continue;}
- if (x==pre_pt.x && y==pre_pt.y){continue;}
- if (edge_img.at<unsigned char>(y,x)>0){
- nxt_pt.x = x;
- nxt_pt.y = y;
- break_x = true;
- break;
- }
- }
- if (break_x){
- break;
- }
- }
- }
- // qua_fitting
- // input: xx ---- sorted x values, ascending
- // yy ----- y values
- //
- // return: oa --- optimal angle, which is the x coordinates of center line of quadratic fitting line
- double qua_fitting(vector<double>&xx, vector<double>&yy)
- {
- double oa = 0.0;
- // fit quadratic function with opencv
- cv::Mat A = cv::Mat::zeros(cv::Size(3,xx.size()), CV_64FC1);
- for(int i=0; i<xx.size();++i){
- A.at<double>(i,0) = 1;
- A.at<double>(i,1) = xx[i];
- A.at<double>(i,2) = xx[i] * xx[i];
- }
- cv::Mat b = cv::Mat::zeros(cv::Size(1,yy.size()), CV_64FC1);
- for(int i = 0; i<yy.size(); ++i){
- b.at<double>(i,0) = yy[i];
- }
- cv::Mat c, d;
- c = A.t() * A;
- d = A.t() * b;
- cv::Mat result = cv::Mat::zeros(cv::Size(1,3),CV_64FC1);
- cv::solve(c,d,result);
-
- double a0 = result.at<double>(0, 0);
- double a1 = result.at<double>(1, 0);
- double a2 = result.at<double>(2, 0);
- if(fabs(a2)<1.0e-6){
- throw_msg(string("Error: a2 = 0 in solver"));
- }
- oa = -a1 / a2 / 2;
- if(oa >180.0){oa -= 180.0;}
- return oa;
- }
- };
|