#include "utils.h" #include #include #include #include //#include "fork_rs.h" namespace graft_cv{ cv::Mat imginfo2mat(ImgInfo* imginfo) { assert(imginfo->data); assert(imginfo->height>0 && imginfo->width>0); cv::Mat m = cv::Mat(imginfo->height,imginfo->width,CV_8UC1); for(int h=0; hheight; ++h) { memcpy((void*)(m.ptr(h)), (void*)(imginfo->data+h*imginfo->width), imginfo->width); }; return m; }; ImgInfo* mat2imginfo(const cv::Mat&img){ assert(img.channels()==1); ImgInfo* imginfo = new ImgInfo(); imginfo->angle=0; imginfo->height=img.rows; imginfo->width = img.cols; imginfo->data = 0; imginfo->data = new byte[imginfo->height * imginfo->width]; memset(imginfo->data, 0,imginfo->height * imginfo->width); //IplImage ipl_img = cvIplImage(img); for(int i =0; iheight;++i){ memcpy(imginfo->data+i*imginfo->width, img.ptr(i), imginfo->width); } 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(r,c) = value; } } } void image_set_top( cv::Mat& img, unsigned char value, int rows_cnt) { for(size_t r=0;r(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<=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& hist, cv::Mat&img, int aix, /*=1*/ int grid_col/*=50*/, int grid_row/*=50*/ ) { vector::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=img.rows){break;} for(size_t j=0;j<=hist[i];++j){ if(j>=img.cols){break;} img.at(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(j,i)=255; } } } }; //matHistogram // axis == 0, statistic histogram for columns; // others, statistic histogram for rows void mat_histogram( cv::Mat& img, std::vector&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(r,c)>0){cnt+=1;} } hist.push_back(cnt); } } else{ for(int r=r0;r(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&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(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(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&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(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&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>segments; get_hist_segment(hist_h,th,segments); // 2 获取多峰位置 vector>peaks; if(segments.size()==1){ peaks = segments; //单峰 } else{// 小线段删除 vector>big_segments; for(size_t i=0;i=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;jmin_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){ vectorcenter_r; for(size_t i=0;isort_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 candidate_idx; candidate_idx.push_back(sort_idx[0]); candidate_idx.push_back(sort_idx[1]); for(size_t id = 2;idvars; int vx0,vx1; for(size_t i=0;i::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=th){ // start_idx=i; // } // continue; // } // if(hist_h[i]>=th && hist_h[i-1]=th && hist_h[i-1]>=th){ // if((i-start_idx+1)>=min_segment_len){ // global_end_idx=i; // } // } // } // if(hist_h[i]=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&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>segments; get_hist_segment(hist_h,th,segments); // 2 获取多峰位置 vector>peaks; if(segments.size()==1){ peaks = segments; //单峰 } else{// 小线段删除 vector>big_segments; for(size_t i=0;i=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;jmin_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){ vectorcenter_r; for(size_t i=0;isort_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 candidate_idx; candidate_idx.push_back(sort_idx[0]); candidate_idx.push_back(sort_idx[1]); for(size_t id = 2;idvars; int vx0,vx1; for(size_t i=0;i::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=th){ // start_idx=i; // } // continue; // } // if(hist_h[i]>=th && hist_h[i-1]=th && hist_h[i-1]>=th){ // if((i-start_idx+1)>=min_segment_len){ // global_end_idx=i; // } // } // } // if(hist_h[i]=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&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& hist, int th,//threshold std::vector>& segments ) { segments.clear(); int start_idx=-1; int end_idx=-1; for(size_t i=0;i=th){ start_idx=i; } continue; } if(hist[i]>=th && hist[i-1]=th && hist[i-1]>=th){ segments.push_back(gcv_point(start_idx,i)); } } if(hist[i]=th){ segments.push_back(gcv_point(start_idx,i-1)); } } } void get_hist_mean_var_local( const std::vector& 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& 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::vectorreversed_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=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< 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 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& 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::vectorreversed_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::vectorreversed_mean; for(int ii=0; ii=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=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< 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=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< 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 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& 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::vectorreversed_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 index_of_diachange; vector index_of_diameter; for(; imax_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 && 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 tmp; for(size_t j=stem_root_y; 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=2.0){ local_max_y = j; break; } } max_y = local_max_y ; roi_max_y = hist.size() - max_y-1; ////////////////////////////////////////////////////////////////// // 计算vectorindex_of_diameter for(int idx = 0;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 index_yfork; for(int idx = 0;idx::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; vectorcent_xs; vectorcent_ys; for(int i=0;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(i,j)==0){continue;} if(j==x0 && bin_img.at(i,j)>0){start_x=j;continue;} pre_val = bin_img.at(i,j-1); cur_val = bin_img.at(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<x1){ roi_img.at(r,c) = 0; } } } } else{ for(int r=0;rcx1){ roi_img.at(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> contours; vector 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;imax_length_val){ max_length_val = contours[i].size(); max_length_idx = i; } } vectorinner_max_radius; vector nnindex; vector xs; /* ////////////////////////////////////////// //基于flann的方法 vector contours_serial; for(size_t i=0;i vecQuery(2); vector vecIndex(queryNum); vector vecDist(queryNum); flann::SearchParams params(32); //在茎中心线上计算最优点指标: // 1) inner_max_radius --- 中心点对应的最小内接圆半径 for(size_t r =0; r=roi_img.cols){ inner_max_radius.push_back(0.0); nnindex.push_back(-1); xs.push_back(-1); continue; } if(roi_img.at(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.cols){ inner_max_radius.push_back(0.0); nnindex.push_back(-1); xs.push_back(-1); continue; } if(roi_img.at(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::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); vectoropt_max; int opt_max_idx = 0; double optm=0; for(size_t r =0; r50){ 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 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_ymax_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& hist, int stem_dia_min, std::vector& 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=stem_dia_min){ if( low_y<0){low_y=i;} if(i>up_y){up_y = i;} } } for(int i=0;i=hist.size()){ var_ratio.push_back(0.0); continue; } if(iup_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;di3.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_fork_y,j)>0){ left_cnt+=1; } } for(size_t j=i+1; j(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(stem_fork_y,i)>0){ start_x =i; } continue; } if (stem_img.at(stem_fork_y,i-1)==0 && stem_img.at(stem_fork_y,i)>0){ start_x =i; continue; } if ((stem_img.at(stem_fork_y,i-1)>0 && stem_img.at(stem_fork_y,i)==0) || (stem_img.at(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(stem_fork_y,i)>0){ start_x =i; } continue; } if (stem_img.at(stem_fork_y,i-1)==0 && stem_img.at(stem_fork_y,i)>0) { start_x =i; continue; } if ((stem_img.at(stem_fork_y,i-1)>0 && stem_img.at(stem_fork_y,i)==0) || (stem_img.at(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(stem_fork_y,i)>0) { start_x =i; continue; } if (stem_img.at(stem_fork_y,i)==0 && stem_img.at(stem_fork_y,i+1)>0) { start_x =i; } if ((stem_img.at(stem_fork_y,i)>0 && stem_img.at(stem_fork_y,i+1)==0) || (stem_img.at(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&start_pt, gcv_point&pre_pt, gcv_point&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(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&xx, vector&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(i,0) = 1; A.at(i,1) = xx[i]; A.at(i,2) = xx[i] * xx[i]; } cv::Mat b = cv::Mat::zeros(cv::Size(1,yy.size()), CV_64FC1); for(int i = 0; i(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(0, 0); double a1 = result.at(1, 0); double a2 = result.at(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; } };