12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910 |
- #include "utils.h"
- #include <opencv.hpp>
- #include <time.h>
- #include <algorithm>
- #include <math.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;
- }
- int nms_bbox(
- vector<Bbox>& candidate_droplets,
- float th,
- vector<int>& keep
- )
- {
- keep.clear();
- if (candidate_droplets.size() == 1) {
- keep.push_back(0);
- return 0;
- }
- sort(candidate_droplets.begin(), candidate_droplets.end(),
- [=](const Bbox& left, const Bbox& right) {
- return left.score > right.score;
- });
- vector<int> order(candidate_droplets.size());
- for (size_t i = 0; i < candidate_droplets.size(); ++i) {
- order[i] = (int)i;
- }
- while (order.size()) {
- int i = order[0];
- keep.push_back(i);
- vector<int> del_idx;
- for (size_t j = 1; j < order.size(); ++j) {
- float iou = iou_bbox(
- candidate_droplets[i],
- candidate_droplets[order[j]]);
- if (iou > th) {
- del_idx.push_back((int)j);
- }
- }
- vector<int> order_update;
- for (size_t j = 1; j < order.size(); ++j) {
- vector<int>::iterator it = find(del_idx.begin(), del_idx.end(), j);
- if (it == del_idx.end()) {
- order_update.push_back(order[j]);
- }
- }
- order.clear();
- order.assign(order_update.begin(), order_update.end());
- }
- return 0;
- }
- float iou_bbox(
- const Bbox & det_a,
- const Bbox & det_b)
- {
- float aa = (float)(det_a.x2 - det_a.x1 + 1) * (det_a.y2 - det_a.y1 + 1);
- float ab = (float)(det_b.x2 - det_b.x1 + 1) * (det_b.y2 - det_b.y1 + 1);
- float xx1 = (float)max(det_a.x1, det_b.x1);
- float yy1 = (float)max(det_a.y1, det_b.y1);
- float xx2 = (float)min(det_a.x2, det_b.x2);
- float yy2 = (float)min(det_a.y2, det_b.y2);
- float w = (float)max(0.0, xx2 - xx1 + 1.0);
- float h = (float)max(0.0, yy2 - yy1 + 1.0);
- float inter = w * h;
- float ovr = inter / (aa + ab - inter);
- return ovr;
- }
- };
|