ソースを参照

v0.6.10 重写砧木、穗苗切后重识别算法,现在用切后侧面图,输出夹爪基面y值、切后3点y与基面y值的距离

chenhongjiang 1 年間 前
コミット
7540c239d2
6 ファイル変更163 行追加37 行削除
  1. 2 1
      ReadMe.txt
  2. 128 25
      cut_point_rs_reid.cpp
  3. 6 2
      cut_point_rs_reid.h
  4. 5 5
      graft_cv_api.cpp
  5. 16 1
      graft_cv_api.h
  6. 6 3
      utils.h

+ 2 - 1
ReadMe.txt

@@ -77,4 +77,5 @@ v0.6.5 
 v0.6.6 增加剪裁后没有点的异常处理
 v0.6.7 修改点云显示代码,显示抓取点xy方向,及z平面上的xoy坐标系
 v0.6.8 增加穗苗的inbox定义,增加最优茎高的定义;修改vector随机读取[]为at方法;增加接口参数用于定义抓取苗是砧木还是穗苗;修改了接口函数名称
-v0.6.9 修改抓取砧木、穗苗的定义0--穗苗,1--砧木;穗苗和砧木抓取顺序相反
+v0.6.9 修改抓取砧木、穗苗的定义0--穗苗,1--砧木;穗苗和砧木抓取顺序相反
+v0.6.10 重写砧木、穗苗切后重识别算法,现在用切后侧面图,输出夹爪基面y值、切后3点y与基面y值的距离

+ 128 - 25
cut_point_rs_reid.cpp

@@ -280,6 +280,7 @@ CSolaCutPointReid::CSolaCutPointReid(ConfigParam&cp, int stemType, CGcvLogger*pL
 	m_pImginfoBin(0),
 	m_pImgCutPoint(0)
 {
+	//m_stem_type -- 0-接穗, 1-砧木
 
 }
 
@@ -302,10 +303,10 @@ int CSolaCutPointReid::cut_point_reid(
 )
 {
 	if (m_stem_type == 0) {
-		m_imgId = getImgId(img_type::sola_rs_reid);
+		m_imgId = getImgId(img_type::sola_sc_reid);
 	}
 	else {
-		m_imgId = getImgId(img_type::sola_sc_reid);
+		m_imgId = getImgId(img_type::sola_rs_reid);
 	}
 	
 	//1 image segment	
@@ -382,7 +383,7 @@ int CSolaCutPointReid::cut_point_reid(
 		t = clock();
 		if (1000.0*((float)(t - t0)) / CLOCKS_PER_SEC>(float)m_cparam.timeout_proc) {
 			if (m_pLogger) {
-				m_pLogger->ERRORINFO(m_imgId + " rootstock reid timeout.");
+				m_pLogger->ERRORINFO(m_imgId + " sola cut points reid timeout.");
 			}
 			throw_msg(m_imgId + " time out");
 		}
@@ -413,27 +414,48 @@ int CSolaCutPointReid::cut_point_reid(
 		throw_msg(m_imgId + " no valid object");
 	}
 
-	vector<cv::Point> cut_curve;
-	cv::RotatedRect retval;
-	find_cut_curve(contours[max_idx], cut_curve, retval);
+	// 找到切口处y值
+	int minx, maxx, miny, maxy;
+	int cut_miny, cut_midy, cut_maxy;
+
+	find_cut_ys(contours[max_idx],
+			minx,  maxx, miny, maxy,
+			cut_miny, cut_midy, cut_maxy);
+
+    // 夹爪基面的y
+	int base_y = maxy;	//	砧木情况
+	if (m_stem_type == 0) {//	接穗情况
+		base_y = miny;
+	}
 	if (m_cparam.image_show) { 		
 		cv::Mat tmp = m_grayImg.clone();
-		cv::ellipse(tmp, retval, cv::Scalar(200));
-		imshow_wait("gray_ellipse", tmp);
+		cv::line(tmp, cv::Point(minx, cut_miny), cv::Point(maxx, cut_miny), cv::Scalar(200));
+		cv::line(tmp, cv::Point(minx, cut_midy), cv::Point(maxx, cut_midy), cv::Scalar(200));
+		cv::line(tmp, cv::Point(minx, cut_maxy), cv::Point(maxx, cut_maxy), cv::Scalar(200));	
+		cv::line(tmp, cv::Point(minx, base_y), cv::Point(maxx, base_y), cv::Scalar(255));
+		imshow_wait("gray_lines", tmp);
+	}
+	if (m_stem_type == 0) {//接穗,坐标左上角位0点
+		posinfo.sc_reid_sola_upoint_x = base_y;  //夹爪基面的y, 砧木在下方(夹爪的上沿),接穗在上方(夹爪的下沿)
+		posinfo.sc_reid_sola_upoint_y = fabs(cut_miny - base_y) + 1 ;
+		posinfo.sc_reid_sola_cpoint_x = minx;
+		posinfo.sc_reid_sola_cpoint_y = fabs(cut_midy - base_y) + 1;
+		posinfo.sc_reid_sola_lpoint_x = minx;
+		posinfo.sc_reid_sola_lpoint_y = fabs(cut_maxy - base_y) + 1;
+	}
+	else {//砧木,坐标左下角位0点
+		posinfo.rs_reid_sola_upoint_x = m_grayImg.rows - base_y - 1;  //夹爪基面的y, 砧木在下方(夹爪的上沿),接穗在上方(夹爪的下沿)
+		posinfo.rs_reid_sola_upoint_y = fabs(cut_miny - base_y) + 1;
+		posinfo.rs_reid_sola_cpoint_x = minx;
+		posinfo.rs_reid_sola_cpoint_y = fabs(cut_midy - base_y) + 1;
+		posinfo.rs_reid_sola_lpoint_x = minx;
+		posinfo.rs_reid_sola_lpoint_y = fabs(cut_maxy - base_y) + 1;
 	}
-
-	posinfo.rs_reid_sola_upoint_x = retval.center.x;
-	posinfo.rs_reid_sola_upoint_y = retval.center.y - retval.size.height / 2;
-	posinfo.rs_reid_sola_cpoint_x = retval.center.x;
-	posinfo.rs_reid_sola_cpoint_y = retval.center.y;
-	posinfo.rs_reid_sola_lpoint_x = retval.center.x;
-	posinfo.rs_reid_sola_lpoint_y = retval.center.y + retval.size.height/2;
-
 	
 
 	if(m_pLogger){		
 		stringstream buff;
-		buff<<m_imgId<<" rootstock image, rs_cut_upoint("<< posinfo.rs_reid_sola_upoint_x
+		buff<<m_imgId<<" sola rootstock(scion) image, rs_cut_upoint("<< posinfo.rs_reid_sola_upoint_x
 			<<","<< posinfo.rs_reid_sola_upoint_y <<")"
 			<<", rs_cut_cpoint=("<< posinfo.rs_reid_sola_cpoint_x
 			<< "," << posinfo.rs_reid_sola_cpoint_y << ")"
@@ -453,7 +475,9 @@ int CSolaCutPointReid::cut_point_reid(
 
 		//2 cut point int gray image
 		cv::Mat tmp = m_grayImg.clone();
-		cv::ellipse(tmp, retval, cv::Scalar(200));
+		cv::line(tmp, cv::Point(minx, cut_miny), cv::Point(maxx, cut_miny), cv::Scalar(200));
+		cv::line(tmp, cv::Point(minx, cut_midy), cv::Point(maxx, cut_midy), cv::Scalar(200));
+		cv::line(tmp, cv::Point(minx, cut_maxy), cv::Point(maxx, cut_maxy), cv::Scalar(200));
 	
 		m_pImgCutPoint = mat2imginfo(tmp);
 
@@ -466,10 +490,82 @@ int CSolaCutPointReid::cut_point_reid(
 		}		
 	}
 	if (m_pLogger) {
-		m_pLogger->INFO(m_imgId + " rootstock cut reid detect finished.");
+		if (m_stem_type == 0) {
+			m_pLogger->INFO(m_imgId + " sola scion cut reid detect finished.");
+		}
+		else {
+			m_pLogger->INFO(m_imgId + " sola rootstock cut reid detect finished.");
+		}
+		
 	}
 	return 0;
 };
+void CSolaCutPointReid::find_cut_ys(
+	vector<cv::Point>&points,
+	int&minx,int& maxx,
+	int&miny,int&maxy,
+	int&cut_miny, int&cut_midy, int&cut_maxy
+)
+{
+	//找到切口的y值
+
+	// 找到边框	
+	minx = miny = 1000;
+	maxx = maxy = -1;
+	for (auto &pt : points) {
+		if (pt.x > maxx) { maxx = pt.x; }
+		if (pt.x < minx) { minx = pt.x; }
+		if (pt.y > maxy) { maxy = pt.y; }
+		if (pt.y < miny) { miny = pt.y; }
+	}
+   // 找到水平的边
+	std::vector<int> hist(maxy-miny+1,0);
+	std::vector<int> hist_x0(maxy - miny + 1, 1000);
+	std::vector<int> hist_x1(maxy - miny + 1, 0);
+	std::vector<int> hist_width(maxy - miny + 1, 0);
+	
+	for (auto&pt : points) {
+		int idx = pt.y - miny;
+		hist.at(idx) += 1;
+		if (pt.x > hist_x1.at(idx)) {
+			hist_x1.at(idx) = pt.x;
+		}
+		if (pt.x < hist_x0.at(idx)) {
+			hist_x0.at(idx) = pt.x;
+		}
+	}
+	for (int i = 0; i < hist.size(); ++i) {
+		hist_width.at(i) = hist_x1.at(i) - hist_x0.at(i);
+	}
+
+    //计算茎的直径
+	std::vector<int> hist_width_copy(hist_width);
+	sort(hist_width_copy.begin(), hist_width_copy.end());
+	int pert_idx = int(0.98*hist_width_copy.size());
+	int stem_width = hist_width_copy.at(pert_idx);
+
+	int max_idx = max_element(hist.begin(), hist.end()) - hist.begin();
+	if (max_idx<int(hist.size() / 2.0)) {
+		//下锥,
+		std::vector<int> hist_width_rev;
+		for (int i = hist_width.size()-1; i >=0 ; i--) {
+			hist_width_rev.push_back(hist_width.at(i));
+		}
+		//在茎的指导下进行查找
+		int pos = trend_detect_pos(hist_width_rev, stem_width);
+		cut_miny = maxy - pos;
+		cut_maxy = maxy;
+		cut_midy = int((cut_miny + cut_maxy) / 2);
+	}
+	else {
+		//上锥,找到上锥点
+		int pos = trend_detect_pos(hist_width, stem_width);
+		cut_miny = miny;
+		cut_maxy = miny + pos;
+		cut_midy = int((cut_miny + cut_maxy) / 2);
+	}
+
+}
 void CSolaCutPointReid::find_cut_curve(vector<cv::Point>&curve_points, 
 	vector<cv::Point>&cut_curve, cv::RotatedRect& retval)
 {
@@ -503,11 +599,18 @@ void CSolaCutPointReid::find_cut_curve(vector<cv::Point>&curve_points,
 		}
 		stem_width.push_back(line_max_x - line_min_x + 1);		
 	}
+
+	//计算茎的直径
+	std::vector<int> hist_width_copy(stem_width);
+	sort(hist_width_copy.begin(), hist_width_copy.end());
+	int max_idx = int(0.98*hist_width_copy.size());
+	int max_stem_width = hist_width_copy.at(max_idx);
+
 	// find y range curve
 	int min_yc = min_y;
 	int max_yc = max_y;
-	if (m_stem_type == 0) {
-		int pos = trend_detect_pos(stem_width, 15);
+	if (m_stem_type != 0) {//砧木
+		int pos = trend_detect_pos(stem_width, max_stem_width,15);
 		if (pos > 0) {
 			max_yc = min_yc + pos;
 		}
@@ -515,13 +618,13 @@ void CSolaCutPointReid::find_cut_curve(vector<cv::Point>&curve_points,
 			throw(m_imgId + " not found cut curve range");
 		}
 	}
-	else {
+	else { //接穗
 		vector<int> stem_width_r;
 		vector<int>::reverse_iterator rit = stem_width.rbegin();
 		for (; rit != stem_width.rend(); ++rit) {
 			stem_width_r.push_back(*rit);
 		}
-		int pos = trend_detect_pos(stem_width_r, 15);
+		int pos = trend_detect_pos(stem_width_r, max_stem_width, 15);
 		if (pos > 0) {
 			min_yc = max_yc - pos;
 		}
@@ -554,10 +657,10 @@ void CSolaCutPointReid::find_cut_curve(vector<cv::Point>&curve_points,
 }
 string CSolaCutPointReid::get_stem_type_name() {
 	if (m_stem_type == 0) {
-		return string("solanaceae rootstock");
+		return string("solanaceae scion");
 	}
 	else {
-		return string("solanaceae scion");
+		return string("solanaceae rootstock");
 	}
 }
 void CSolaCutPointReid::img_preprocess(cv::Mat&img)

+ 6 - 2
cut_point_rs_reid.h

@@ -57,7 +57,7 @@ public:
 		PositionInfo& posinfo
 	);
 private:
-	int m_stem_type; //植株类别:0--砧木;1--穗苗
+	int m_stem_type; //植株类别:0--穗苗;1--砧木
 					 //global configure parameters
 	ConfigParam& m_cparam;
 	CGcvLogger * m_pLogger;
@@ -76,6 +76,10 @@ private:
 	void find_cut_curve(vector<cv::Point>&points,
 		vector<cv::Point>&cut_curve,
 		cv::RotatedRect& retval);
-	
+	void find_cut_ys(vector<cv::Point>&points,
+		int&minx, int& maxx,
+		int&miny, int&maxy,
+		int&cut_miny, int&cut_midy, int&cut_maxy
+	);
 };
 };

+ 5 - 5
graft_cv_api.cpp

@@ -19,7 +19,7 @@ extern CRITICAL_SECTION g_cs;
 namespace graft_cv
 {
 
-	char *g_version_str = "0.6.9";
+	char *g_version_str = "0.6.10";
 
 	//configure
 	string g_conf_file = "./gcv_conf.yml";	
@@ -46,8 +46,8 @@ namespace graft_cv
 
 	CRootStockGrabPoint g_rs_gp(g_cp, &g_logger);
 
-	CSolaCutPointReid g_sola_rs = CSolaCutPointReid(g_cp, 0, &g_logger);
-	CSolaCutPointReid g_sola_sc = CSolaCutPointReid(g_cp, 1, &g_logger);
+	CSolaCutPointReid g_sola_rs = CSolaCutPointReid(g_cp, 1, &g_logger);
+	CSolaCutPointReid g_sola_sc = CSolaCutPointReid(g_cp, 0, &g_logger);
 
 	//
 	map<string, cv::Mat> g_img_cache;
@@ -424,10 +424,10 @@ namespace graft_cv
 		memset(&posinfo, 0, sizeof(PositionInfo));
 		try {
 			if (sola_type == 0) {
-				g_sola_rs.cut_point_reid(imginfo, cv::Mat(), posinfo);
+				g_sola_sc.cut_point_reid(imginfo, cv::Mat(), posinfo);
 			}
 			else {
-				g_sola_sc.cut_point_reid(imginfo, cv::Mat(), posinfo);
+				g_sola_rs.cut_point_reid(imginfo, cv::Mat(), posinfo);
 			}			
 		}
 		catch (std::exception &err) {

+ 16 - 1
graft_cv_api.h

@@ -126,11 +126,26 @@ GCV_API int rs_cut_point_reid(ImgInfo*, const char* pre_img_id, PositionInfo& po
 //16 茄科切割点切后识别
 //  input:
 //       ImgInfo*  --- 输入图像
-//       sola_type --- 0--砧木; 1---穗苗
+//       sola_type --- 0---穗苗; 1--砧木
 //       posinfo---- 返回信息
 //
 // 返回
 //		上切点,中切点,下切点
+//      //砧木,坐标左下角位0点
+//      posinfo.rs_reid_sola_upoint_x;			//base_y, 夹爪基面的y像素坐标, 砧木在下方(夹爪的上沿),接穗在上方(夹爪的下沿)
+//      posinfo.rs_reid_sola_upoint_y;			//上切割点y像素坐标与base_y的像素距离
+//      posinfo.rs_reid_sola_cpoint_x;			//--------------------无意义(目标框x)
+//      posinfo.rs_reid_sola_cpoint_y;			//中切割点y像素坐标与base_y的像素距离
+//      posinfo.rs_reid_sola_lpoint_x;			//--------------------无意义(目标框x)
+//      posinfo.rs_reid_sola_lpoint_y;			//下切割点y像素坐标与base_y的像素距离
+//   	
+//      //接穗,坐标左上角位0点
+//      posinfo.sc_reid_sola_upoint_x;			//base_y, 夹爪基面的y像素坐标, 砧木在下方(夹爪的上沿),接穗在上方(夹爪的下沿)
+//      posinfo.sc_reid_sola_upoint_y;			//上切割点y像素坐标与base_y的像素距离
+//      posinfo.sc_reid_sola_cpoint_x;			//--------------------无意义(目标框x)
+//      posinfo.sc_reid_sola_cpoint_y;			//中切割点y像素坐标与base_y的像素距离
+//      posinfo.sc_reid_sola_lpoint_x;			//--------------------无意义(目标框x)
+//      posinfo.sc_reid_sola_lpoint_y;			//下切割点y像素坐标与base_y的像素距离
 //   	
 //  	 posinfo.pp_images   (return image 为 true时)
 //               返回2张图片:图0:二值图像

+ 6 - 3
utils.h

@@ -435,7 +435,7 @@ namespace graft_cv{
 	}
 
 	template<typename T>
-	int trend_detect_pos(const vector<T>&data, int step=15) {
+	int trend_detect_pos(const vector<T>&data, int data_th, int step=15) {
 		int pos = -1;
 		if (data.size() < 2 * step) {
 			return pos;
@@ -460,8 +460,11 @@ namespace graft_cv{
 			double p = 2.0 * boost::math::cdf(binomal, k);
 			std::cout <<"idx="<<i<< ", posn=" << pos_n << ", negn=" << neg_n << ", pvalue=" << p << endl;
 			if (p > 0.05) {
-				pos = i;
-				break;
+				if(fabs(data.at(i) - data_th)<3)
+				{
+					pos = i;
+					break;
+				}
 			}
 		}
 		return pos;