Переглянути джерело

v0.8.19 增加茎高占比阈值、遮挡高度占比阈值,用户指定

chenhongjiang 1 рік тому
батько
коміт
a55d8e3bec
6 змінених файлів з 82 додано та 33 видалено
  1. 2 1
      ReadMe.txt
  2. 17 0
      config.cpp
  3. 4 0
      data_def_api.h
  4. 56 30
      grab_point_rs.cpp
  5. 2 1
      grab_point_rs.h
  6. 1 1
      graft_cv_api.cpp

+ 2 - 1
ReadMe.txt

@@ -127,4 +127,5 @@ v0.8.14 优化茎粗检测功能,增加xx_grab_offset_cut, 在ybt、yup中
 v0.8.15 优化茎节检测功能,增加xx_grab_fork_height,控制识别较长的茎节
 v0.8.16 修改茎节检测中while循环死循环的bug
 v0.8.17 修改内存报错的bug
-v0.8.18 优化茎识别(叶子误判成茎);优化遮挡识别(位置误判)
+v0.8.18 优化茎识别(叶子误判成茎);优化遮挡识别(位置误判)
+v0.8.19 增加茎高占比阈值、遮挡高度占比阈值,用户指定

+ 17 - 0
config.cpp

@@ -45,6 +45,9 @@ namespace graft_cv{
 			<< "rs_grab_fork_height" << m_cparam->rs_grab_fork_height
 			<< "rs_grab_holes_number" << m_cparam->rs_grab_holes_number
 
+            << "rs_grab_valid_stem_ratio" << m_cparam->rs_grab_valid_stem_ratio
+			<< "rs_grab_valid_occlusion_ratio" << m_cparam->rs_grab_valid_occlusion_ratio
+
 			<< "sc_grab_xmin" << m_cparam->sc_grab_xmin
 			<< "sc_grab_xmax" << m_cparam->sc_grab_xmax
 			<< "sc_grab_ymin" << m_cparam->sc_grab_ymin
@@ -67,6 +70,9 @@ namespace graft_cv{
 			<< "sc_grab_fork_height" << m_cparam->sc_grab_fork_height
 			<< "sc_grab_holes_number" << m_cparam->sc_grab_holes_number
 
+			<< "sc_grab_valid_stem_ratio" << m_cparam->sc_grab_valid_stem_ratio
+			<< "sc_grab_valid_occlusion_ratio" << m_cparam->sc_grab_valid_occlusion_ratio
+
 			<< "}"; 	
 	};
 	void CGCvConfig::read(const cv::FileNode& node){ //Read serialization for this class
@@ -96,6 +102,9 @@ namespace graft_cv{
 		m_cparam->rs_grab_offset_cut = (double)node["rs_grab_offset_cut"];
 		m_cparam->rs_grab_fork_height = (double)node["rs_grab_fork_height"];
 		m_cparam->rs_grab_holes_number = (int)node["rs_grab_holes_number"];
+		m_cparam->rs_grab_valid_stem_ratio = (double)node["rs_grab_valid_stem_ratio"];
+		m_cparam->rs_grab_valid_occlusion_ratio = (double)node["rs_grab_valid_occlusion_ratio"];
+
 
 		m_cparam->sc_grab_xmin = (double)node["sc_grab_xmin"];
 		m_cparam->sc_grab_xmax = (double)node["sc_grab_xmax"];
@@ -118,6 +127,10 @@ namespace graft_cv{
 		m_cparam->sc_grab_offset_cut = (double)node["sc_grab_offset_cut"];
 		m_cparam->sc_grab_fork_height = (double)node["sc_grab_fork_height"];
 		m_cparam->sc_grab_holes_number = (int)node["sc_grab_holes_number"];
+		m_cparam->sc_grab_valid_stem_ratio = (double)node["sc_grab_valid_stem_ratio"];
+		m_cparam->sc_grab_valid_occlusion_ratio = (double)node["sc_grab_valid_occlusion_ratio"];
+
+
   }
 	string get_cparam_info(ConfigParam*m_cparam)
 	{
@@ -150,6 +163,8 @@ namespace graft_cv{
 			<< "rs_grab_offset_cut:\t" << m_cparam->rs_grab_offset_cut<< endl
 			<< "rs_grab_fork_height:\t" << m_cparam->rs_grab_fork_height << endl
 			<< "rs_grab_holes_number:\t" << m_cparam->rs_grab_holes_number << endl
+			<< "rs_grab_valid_stem_ratio:\t" << m_cparam->rs_grab_valid_stem_ratio << endl
+			<< "rs_grab_valid_occlusion_ratio:\t" << m_cparam->rs_grab_valid_occlusion_ratio << endl
 
 			<< "sc_grab_xmin:\t" << m_cparam->sc_grab_xmin << endl
 			<< "sc_grab_xmax:\t" << m_cparam->sc_grab_xmax << endl
@@ -172,6 +187,8 @@ namespace graft_cv{
 			<< "sc_grab_offset_cut:\t" << m_cparam->sc_grab_offset_cut << endl
 			<< "sc_grab_fork_height:\t" << m_cparam->sc_grab_fork_height << endl
 			<< "sc_grab_holes_number:\t" << m_cparam->sc_grab_holes_number << endl
+			<< "sc_grab_valid_stem_ratio:\t" << m_cparam->sc_grab_valid_stem_ratio << endl
+			<< "sc_grab_valid_occlusion_ratio:\t" << m_cparam->sc_grab_valid_occlusion_ratio << endl
 
 			<< "}" << endl; 	
 		return buff.str();

+ 4 - 0
data_def_api.h

@@ -40,6 +40,8 @@ typedef struct{
 	double rs_grab_offset_cut; // 在ybt、yup中间点进行偏移,在这个位置计算茎粗
 	double rs_grab_fork_height; //茎节高度(最小值),毫米,小于此值不被识别
 	int rs_grab_holes_number; //单排穴孔数量
+	double rs_grab_valid_stem_ratio; //茎长度在视野高度上的占比阈值,小于此阈值,不被识别成茎
+	double rs_grab_valid_occlusion_ratio;//遮挡物在视野高度上的占比阈值,小于此阈值,不被识别成遮挡物
 
 	// scion grab based points cloud
 	double sc_grab_xmin;//单排穴孔外沿
@@ -66,6 +68,8 @@ typedef struct{
 	double sc_grab_offset_cut; // 在ybt、yup中间点进行偏移,在这个位置计算茎粗
 	double sc_grab_fork_height; //茎节高度(最小值),毫米,小于此值不被识别
 	int sc_grab_holes_number; //单排穴孔数量
+	double sc_grab_valid_stem_ratio;
+	double sc_grab_valid_occlusion_ratio;
 } ConfigParam;
 
 typedef struct 

+ 56 - 30
grab_point_rs.cpp

@@ -366,7 +366,7 @@ namespace graft_cv {
 		occluded_seedling_detect_by_leaf(cloud_ror, leaf_indices);
 		if (m_pLogger) {
 			stringstream buff;
-			buff << m_pcdId << ": leafoccl status: ";
+			buff << m_pcdId << ": leaf occlusion status: ";
 			for (auto&st : m_root_center_with_occlusion) {
 					buff <<st<< "\t";
 			}
@@ -724,12 +724,14 @@ namespace graft_cv {
 		double stem_diameter = m_cparam.rs_grab_stem_diameter;
 		double y_min = m_cparam.rs_grab_ymin;
 		double y_max = m_cparam.rs_grab_ymax;
+		double occlusion_height_ratio = m_cparam.rs_grab_valid_occlusion_ratio;
 
 		if (m_dtype == 0) {
 			//th_pcd_size = m_cparam.sc_grab_seedling_min_pts;
 			stem_diameter = m_cparam.sc_grab_stem_diameter;
 			y_min = m_cparam.sc_grab_ymin;
 			y_max = m_cparam.sc_grab_ymax;
+			occlusion_height_ratio = m_cparam.sc_grab_valid_occlusion_ratio;
 		}
 		int th_pcd_size = int(0.333 * stem_diameter * (y_max - y_min) / m_cloud_mean_dist / m_cloud_mean_dist);
 		std::vector<pcl::PointXYZ> aabb_mins_maxs;	
@@ -738,11 +740,12 @@ namespace graft_cv {
 		for (int i = 0; i < m_root_centers.size(); ++i) {
 			CStemResult& rc = m_root_centers.at(i);
 			pcl::PointXYZ aabb_min;
-			pcl::PointXYZ aabb_max;			
+			pcl::PointXYZ aabb_max;	
+			double valid_y_ratio = 0.0;
 			get_leaf_point_count_inbox(rc, in_cloud, leaf_idx, 
 				aabb_min, aabb_max,
 				total_cnt, leaf_cnt,
-				total_cx, leaf_cx);
+				total_cx, leaf_cx, valid_y_ratio);
 
 			double leaf_ratio = 0.0;
 			if (total_cnt == 0) {
@@ -759,7 +762,7 @@ namespace graft_cv {
 			}
 			pcd_cnt.push_back(total_cnt);
 			bool has_seedling = total_cnt > th_pcd_size;
-			if (has_seedling) {
+			if (has_seedling && valid_y_ratio>occlusion_height_ratio) {
 				m_root_center_with_occlusion.at(i) = 2;
 			}
 			
@@ -957,17 +960,20 @@ namespace graft_cv {
 		int& total_cnt,		//output
 		int& leaf_cnt,		//output
 		double& total_cx,	//output
-		double& leaf_cx		//output
+		double& leaf_cx,		//output
+		double& valid_y_ratio//output
 		)
 	{
 		//计算每一个穴位叶子遮挡的点云数量(以及整体点云数量),并计算x方向的中心
 		double seedling_distance = m_cparam.rs_grab_seedling_dist;
 		double min_y = m_cparam.rs_grab_ymin;
 		double max_y = m_cparam.rs_grab_ymax;
+		double stem_diameter = m_cparam.rs_grab_stem_diameter;
 		if (m_dtype == 0) {
 			seedling_distance = m_cparam.sc_grab_seedling_dist;
 			min_y = m_cparam.sc_grab_ymin;
 			max_y = m_cparam.sc_grab_ymax;
+			stem_diameter = m_cparam.sc_grab_stem_diameter;
 		}
 		double min_x = sr.root_x - 0.5 * seedling_distance;
 		double max_x = sr.root_x + 0.5 * seedling_distance;
@@ -986,6 +992,12 @@ namespace graft_cv {
 		leaf_cx = 1.0e6;
 		std::vector<double>xs;
 
+		std::vector<int> y_counts;
+		int y_length = int(max_y - min_y);
+		y_counts.assign(y_length, 0);
+		int y_idx = 0;
+		valid_y_ratio = 0.0;
+
 		for (auto&pt : in_cloud->points) {			
 			if (pt.y >= min_y && pt.y <= max_y &&
 				pt.x >= min_x && pt.x <= max_x &&
@@ -993,12 +1005,22 @@ namespace graft_cv {
 			{
 				total_cnt++;
 				xs.push_back(pt.x);
+				y_idx = int(pt.y - min_y);
+				if (y_idx >= 0 && y_idx < y_length) {
+					y_counts.at(y_idx) += 1;
+				}
 			}
 		}
 		if (total_cnt == 0) { return; }
 		int med_idx = int(0.5 * xs.size());
 		std::sort(xs.begin(), xs.end());
 		total_cx = xs.at(med_idx);
+		
+		double th_width = stem_diameter / m_cloud_mean_dist;
+		for (auto&c : y_counts) {
+			if (c >= th_width) { valid_y_ratio += 1; }
+		}
+		valid_y_ratio /= (max_y - min_y);
 
 		//leaf center
 		xs.clear();
@@ -1431,6 +1453,15 @@ void CRootStockGrabPoint::line_filter(
 	target_filtered_root.clear();
 	if (target_root.size() == 0) { return; }
 
+	double stem_height_ratio = m_cparam.rs_grab_valid_stem_ratio;	
+	float stem_radius = m_cparam.rs_grab_stem_diameter / 2.0;
+	int min_stem_pts = m_cparam.rs_grab_stem_min_pts;
+	if (m_dtype == 0) {
+		stem_radius = m_cparam.sc_grab_stem_diameter / 2.0;
+		min_stem_pts = m_cparam.sc_grab_stem_min_pts;
+		stem_height_ratio = m_cparam.sc_grab_valid_stem_ratio;
+	}
+
 	for (auto&p : target_root) {
 		// 构建box,获取植株点云
 		pcl::PointCloud<pcl::PointXYZ>::Ptr seedling_inbox(new pcl::PointCloud<pcl::PointXYZ>);
@@ -1455,11 +1486,7 @@ void CRootStockGrabPoint::line_filter(
 		pcl::copyPointCloud(*in_cloud, inbox_idx, *seedling_inbox);
 
 		//植株点云直线查找
-		//找到inbox点云中的直线
-		float stem_radius = m_cparam.rs_grab_stem_diameter / 2.0;
-		if (m_dtype == 0) {
-			stem_radius = m_cparam.sc_grab_stem_diameter / 2.0;
-		}
+		//找到inbox点云中的直线		
 		pcl::ModelCoefficients::Ptr coefficients(new pcl::ModelCoefficients);
 		pcl::PointIndices::Ptr inliers(new pcl::PointIndices);
 		pcl::SACSegmentation<pcl::PointXYZ> seg;
@@ -1489,9 +1516,7 @@ void CRootStockGrabPoint::line_filter(
 			continue;
 		}
 
-		//点数过滤
-		int min_stem_pts = m_cparam.rs_grab_stem_min_pts;
-		if (m_dtype == 0) { min_stem_pts = m_cparam.sc_grab_stem_min_pts; }
+		//点数过滤		
 		if (inliers->indices.size() < int(min_stem_pts / 2)) { continue; }
 		//y方向分布范围滤波
 		float y_length_th = 10.0;
@@ -1499,7 +1524,7 @@ void CRootStockGrabPoint::line_filter(
 		pcl::PointXYZ max_v;
 		pcl::getMinMax3D(*stem_cloud, min_v, max_v);
 		float dy = max_v.y - min_v.y;
-		if (dy<y_length_th && dy / (ymax - ymin) < 0.25) { continue; }
+		if (dy<y_length_th || dy / (ymax - ymin) < stem_height_ratio) { continue; }
 		//y方向分布中心滤波
 		float dy_c = 0.5*(max_v.y + min_v.y);
 		if ((dy_c - ymin) / (ymax - ymin) > 0.75) { continue; }
@@ -1574,7 +1599,7 @@ void CRootStockGrabPoint::line_filter(
 			
 		}
 
-		if (longest_segment<10.0 && valid_length / (ymax - ymin) < 0.35) { continue; }
+		if (longest_segment<10.0 || valid_length / (ymax - ymin) < stem_height_ratio) { continue; }
 
 
 		float min_y = 10000.0;
@@ -1649,11 +1674,21 @@ void CRootStockGrabPoint::line_filter(
 	)
 	{	
 		first_row_size = 0;
+
 		// 确定植株inbox范围
 		float hole_step = m_cparam.rs_grab_seedling_dist - 5.0; //穴盘中穴间距
+		float ymin = m_cparam.rs_grab_ymin;
+		float ymax = m_cparam.rs_grab_ymax;
+		double stem_height_ratio = m_cparam.rs_grab_valid_stem_ratio;
+		double radius = m_cparam.rs_grab_stem_diameter;
 		if (m_dtype == 0) {
-			hole_step = m_cparam.sc_grab_seedling_dist - 5.0;
+			hole_step = m_cparam.sc_grab_seedling_dist - 5.0;		
+			ymin = m_cparam.sc_grab_ymin;
+			ymax = m_cparam.sc_grab_ymax;
+			stem_height_ratio = m_cparam.sc_grab_valid_stem_ratio;		
+			radius = m_cparam.sc_grab_stem_diameter;
 		}
+
 		float hole_step_radius = 2.0 * hole_step / 3.0;
 
 		// 点云降维到xz平面,y=0
@@ -1663,11 +1698,7 @@ void CRootStockGrabPoint::line_filter(
 			pt.y = 0.0;
 		}	
 
-		// 在xz平面内统计点的密度
-		double radius = m_cparam.rs_grab_stem_diameter;
-		if (m_dtype == 0) {
-			radius = m_cparam.sc_grab_stem_diameter;
-		}
+		// 在xz平面内统计点的密度		
 		std::vector<int> counter;
 		pcl::KdTreeFLANN<pcl::PointXYZ> kdtree;
 		kdtree.setInputCloud(cloud2d);
@@ -1695,13 +1726,7 @@ void CRootStockGrabPoint::line_filter(
 		}
 		//  生成植株的中心及box
 		std::vector<pcl::PointXYZ>clt_root;
-		std::vector<pcl::PointXYZ> clt_box;
-		float ymin = m_cparam.rs_grab_ymin;
-		float ymax = m_cparam.rs_grab_ymax;
-		if (m_dtype == 0) {			
-			ymin = m_cparam.sc_grab_ymin;
-			ymax = m_cparam.sc_grab_ymax;			
-		}
+		std::vector<pcl::PointXYZ> clt_box;		
 		gen_all_seedling_positions(in_cloud->points.at(max_density_idx), clt_root);	
 		// 显示生成的每一个候选框				
 		/*if (m_cparam.image_show) {
@@ -1728,7 +1753,8 @@ void CRootStockGrabPoint::line_filter(
 		// 每个位置点云情况,过滤		
 		std::vector<int> valid_index;		//初步有效的矩形index
 		std::vector<int> valid_box_pts;		//立方体内点云数量
-		std::vector<float>valid_box_cc_dist;//重心和矩形中心距离
+		std::vector<float>valid_box_cc_dist;//重心和矩形中心距离		
+		
 		for (size_t i = 0; i < clt_root.size();++i) {
 			pcl::PointXYZ &p = clt_root.at(i);
 			pcl::PointCloud<pcl::PointXYZ>::Ptr seedling_inbox(new pcl::PointCloud<pcl::PointXYZ>);
@@ -1758,7 +1784,7 @@ void CRootStockGrabPoint::line_filter(
 			pcl::PointXYZ max_v;
 			pcl::getMinMax3D(*seedling_inbox, min_v, max_v);
 			float dy = max_v.y - min_v.y;
-			if (dy / (ymax - ymin) < 0.35) { continue; }
+			if (dy / (ymax - ymin) < stem_height_ratio) { continue; }
 			//y方向分布中心滤波
 			float dy_c = 0.5*(max_v.y + min_v.y);
 			if ((dy_c-ymin) / (ymax - ymin) > 0.75) { continue; }

+ 2 - 1
grab_point_rs.h

@@ -232,7 +232,8 @@ namespace graft_cv {
 			int& total_cnt,		//output
 			int& leaf_cnt,		//output
 			double& total_cx,	//output
-			double& leaf_cx		//output
+			double& leaf_cx,		//output
+			double& valid_y_ratio//output
 		);
 		//没有检测到苗的情况,后处理
 		void no_seedling_detected_post_process(

+ 1 - 1
graft_cv_api.cpp

@@ -15,7 +15,7 @@ extern CRITICAL_SECTION g_cs;
 namespace graft_cv
 {
 
-	char *g_version_str = "0.8.18";
+	char *g_version_str = "0.8.19";
 
 	//configure
 	string g_conf_file = "./gcv_conf.yml";