|
@@ -269,4 +269,325 @@ void CRootStockCutPointReid::img_preprocess(cv::Mat&img)
|
|
|
|
|
|
}
|
|
|
|
|
|
+///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+CSolaCutPointReid::CSolaCutPointReid(ConfigParam&cp, int stemType, CGcvLogger*pLog)
|
|
|
+ :m_cparam(cp),
|
|
|
+ m_pLogger(pLog),
|
|
|
+ m_stem_type(stemType),
|
|
|
+ m_imgId(""),
|
|
|
+ m_ppImgSaver(0),
|
|
|
+ m_pImginfoBin(0),
|
|
|
+ m_pImgCutPoint(0)
|
|
|
+{
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+CSolaCutPointReid::~CSolaCutPointReid()
|
|
|
+{}
|
|
|
+void CSolaCutPointReid::clear_imginfo() {
|
|
|
+ if (m_pImginfoBin) {
|
|
|
+ imginfo_release(&m_pImginfoBin);
|
|
|
+ m_pImginfoBin = 0;
|
|
|
+ }
|
|
|
+ if (m_pImgCutPoint) {
|
|
|
+ imginfo_release(&m_pImgCutPoint);
|
|
|
+ m_pImgCutPoint = 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+int CSolaCutPointReid::cut_point_reid(
|
|
|
+ ImgInfo* imginfo,
|
|
|
+ cv::Mat&cimg,
|
|
|
+ PositionInfo& posinfo
|
|
|
+)
|
|
|
+{
|
|
|
+ if (m_stem_type == 0) {
|
|
|
+ m_imgId = getImgId(img_type::sola_rs_reid);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ m_imgId = getImgId(img_type::sola_sc_reid);
|
|
|
+ }
|
|
|
+
|
|
|
+ //1 image segment
|
|
|
+ clock_t t;
|
|
|
+ clock_t t0 = clock();
|
|
|
+
|
|
|
+ cv::Mat img;
|
|
|
+ if (imginfo) {
|
|
|
+ if (m_pLogger) {
|
|
|
+ stringstream buff;
|
|
|
+ buff << m_imgId<<" "<<get_stem_type_name() << " image, width=" << imginfo->width
|
|
|
+ << "\theight=" << imginfo->height;
|
|
|
+ m_pLogger->INFO(buff.str());
|
|
|
+ }
|
|
|
+ if (!isvalid(imginfo)) {
|
|
|
+ if (m_pLogger) {
|
|
|
+ m_pLogger->ERRORINFO(m_imgId +string(" ")+get_stem_type_name() + " input image invalid.");
|
|
|
+ }
|
|
|
+ throw_msg(m_imgId + string(" ") + get_stem_type_name() + " invalid input image");
|
|
|
+
|
|
|
+ }
|
|
|
+ img = imginfo2mat(imginfo);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (m_pLogger) {
|
|
|
+ stringstream buff;
|
|
|
+ buff << m_imgId << " " << get_stem_type_name() << " image, width=" << cimg.cols
|
|
|
+ << "\theight=" << cimg.rows;
|
|
|
+ m_pLogger->INFO(buff.str());
|
|
|
+ }
|
|
|
+ if (cimg.empty()) {
|
|
|
+ if (m_pLogger) {
|
|
|
+ m_pLogger->ERRORINFO(m_imgId +string(" ")+ get_stem_type_name() + " input image invalid.");
|
|
|
+ }
|
|
|
+ throw_msg(m_imgId + string(" ") +get_stem_type_name() + " invalid input image");
|
|
|
+
|
|
|
+ }
|
|
|
+ img = cimg;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*if (m_cparam.self_camera) {
|
|
|
+ image_set_bottom(img, 20, 8);
|
|
|
+ if (m_pLogger) {
|
|
|
+ m_pLogger->DEBUG(m_imgId + " image set bottom with pixel value 20.");
|
|
|
+ }
|
|
|
+ }*/
|
|
|
+ if (m_cparam.rs_y_flip) {
|
|
|
+ flip(img, img, 0);
|
|
|
+ if (m_pLogger) {
|
|
|
+ m_pLogger->DEBUG(m_imgId + string(" ") + get_stem_type_name() + " image y fliped.");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //image saver
|
|
|
+ if (m_ppImgSaver && *m_ppImgSaver) {
|
|
|
+ (*m_ppImgSaver)->saveImage(img, m_imgId);
|
|
|
+ }
|
|
|
+ if (m_pLogger) {
|
|
|
+ m_pLogger->DEBUG(m_imgId + string(" ") + get_stem_type_name() + " before image segment.");
|
|
|
+ }
|
|
|
+ ///////////////////////////////////////////////////////
|
|
|
+ // image segment
|
|
|
+ this->img_preprocess(img);
|
|
|
+ if (m_pLogger) {
|
|
|
+ m_pLogger->DEBUG(m_imgId + " after image gray.");
|
|
|
+ }
|
|
|
+
|
|
|
+ if (m_cparam.image_show) {
|
|
|
+ cv::destroyAllWindows();
|
|
|
+ imshow_wait("gray", m_grayImg);
|
|
|
+ imshow_wait("binary", m_binImg);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ 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.");
|
|
|
+ }
|
|
|
+ throw_msg(m_imgId + " time out");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (m_pLogger) {
|
|
|
+ m_pLogger->DEBUG(m_imgId + " after gray image show.");
|
|
|
+ }
|
|
|
+
|
|
|
+ // find object
|
|
|
+ vector<vector<cv::Point>> contours;
|
|
|
+ vector<cv::Vec4i> hierarchy;
|
|
|
+ contours.clear();
|
|
|
+ hierarchy.clear();
|
|
|
+ int obj_area_th = 100;//m_cparam
|
|
|
+ double max_area = 0;
|
|
|
+ int max_idx = -1;
|
|
|
+
|
|
|
+ findContours(m_binImg, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
|
|
+ for (int i = 0; i<contours.size(); ++i) {
|
|
|
+ double area = contourArea(contours[i]);
|
|
|
+ if (area < obj_area_th) { continue; }
|
|
|
+ if(area<max_area) { continue; }
|
|
|
+ max_area = area;
|
|
|
+ max_idx = i;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (max_idx < 0 || contours[max_idx].size()==0) {
|
|
|
+ throw_msg(m_imgId + " no valid object");
|
|
|
+ }
|
|
|
+
|
|
|
+ vector<cv::Point> cut_curve;
|
|
|
+ cv::RotatedRect retval;
|
|
|
+ find_cut_curve(contours[max_idx], cut_curve, retval);
|
|
|
+ if (m_cparam.image_show) {
|
|
|
+ cv::Mat tmp = m_grayImg.clone();
|
|
|
+ cv::ellipse(tmp, retval, cv::Scalar(200));
|
|
|
+ imshow_wait("gray_ellipse", tmp);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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
|
|
|
+ <<","<< posinfo.rs_reid_sola_upoint_y <<")"
|
|
|
+ <<", rs_cut_cpoint=("<< posinfo.rs_reid_sola_cpoint_x
|
|
|
+ << "," << posinfo.rs_reid_sola_cpoint_y << ")"
|
|
|
+ <<", rs_cut_lpoint(mm)("<< posinfo.rs_reid_sola_lpoint_x
|
|
|
+ <<","<< posinfo.rs_reid_sola_lpoint_y <<")";
|
|
|
+ m_pLogger->INFO(buff.str());
|
|
|
+ }
|
|
|
+
|
|
|
+ // return images: posinfo.pp_images
|
|
|
+ if(m_cparam.image_return){
|
|
|
+ this->clear_imginfo();
|
|
|
+ //0) image id
|
|
|
+ strcpy(posinfo.rs_img_id,m_imgId.c_str());
|
|
|
+
|
|
|
+ //1) bin image
|
|
|
+ m_pImginfoBin=mat2imginfo(m_binImg);
|
|
|
+
|
|
|
+ //2 cut point int gray image
|
|
|
+ cv::Mat tmp = m_grayImg.clone();
|
|
|
+ cv::ellipse(tmp, retval, cv::Scalar(200));
|
|
|
+
|
|
|
+ m_pImgCutPoint = mat2imginfo(tmp);
|
|
|
+
|
|
|
+ posinfo.pp_images[0] = m_pImginfoBin;
|
|
|
+ posinfo.pp_images[1] = m_pImgCutPoint;
|
|
|
+
|
|
|
+ if(m_ppImgSaver && *m_ppImgSaver){
|
|
|
+ (*m_ppImgSaver)->saveImage(m_binImg, m_imgId+"_rst_0");
|
|
|
+ (*m_ppImgSaver)->saveImage(tmp, m_imgId+"_rst_1");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (m_pLogger) {
|
|
|
+ m_pLogger->INFO(m_imgId + " rootstock cut reid detect finished.");
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+};
|
|
|
+void CSolaCutPointReid::find_cut_curve(vector<cv::Point>&curve_points,
|
|
|
+ vector<cv::Point>&cut_curve, cv::RotatedRect& retval)
|
|
|
+{
|
|
|
+ int max_y = curve_points[0].y;
|
|
|
+ int min_y = curve_points[0].y;
|
|
|
+ int max_x = curve_points[0].x;
|
|
|
+ int min_x = curve_points[0].x;
|
|
|
+ for (auto&pt : curve_points) {
|
|
|
+ if (pt.y > max_y) {
|
|
|
+ max_y = pt.y;
|
|
|
+ }
|
|
|
+ if (pt.y < min_y) {
|
|
|
+ min_y = pt.y;
|
|
|
+ }
|
|
|
+ if (pt.x > max_x) {
|
|
|
+ max_x = pt.x;
|
|
|
+ }
|
|
|
+ if (pt.x < min_x) {
|
|
|
+ min_x = pt.x;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vector<int>stem_width;
|
|
|
+ int line_min_x, line_max_x;
|
|
|
+ for (int y = min_y; y <= max_y;++y) {
|
|
|
+ line_min_x = max_x;
|
|
|
+ line_max_x = min_x;
|
|
|
+ for (auto&pt : curve_points) {
|
|
|
+ if (pt.y != y) { continue; }
|
|
|
+ if (pt.x < line_min_x) { line_min_x = pt.x; }
|
|
|
+ if (pt.x > line_max_x) { line_max_x = pt.x; }
|
|
|
+ }
|
|
|
+ stem_width.push_back(line_max_x - line_min_x + 1);
|
|
|
+ }
|
|
|
+ // 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 (pos > 0) {
|
|
|
+ max_yc = min_yc + pos;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ throw(m_imgId + " not found cut curve range");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ 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);
|
|
|
+ if (pos > 0) {
|
|
|
+ min_yc = max_yc - pos;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ throw(m_imgId + " not found cut curve range");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // copy cut curve points
|
|
|
+ vector<cv::Point>cut_curve_points;
|
|
|
+ for (auto&pt : curve_points) {
|
|
|
+ if (pt.y >= min_yc && pt.y <= max_yc) {
|
|
|
+ cut_curve_points.push_back(cv::Point(pt));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ecllipse fit
|
|
|
+ retval = cv::fitEllipse(cut_curve_points);
|
|
|
+ //if (m_cparam.image_show) {
|
|
|
+ // //cv::destroyAllWindows();
|
|
|
+ // cv::Mat tmp = m_grayImg.clone();
|
|
|
+ // for (auto&pt : cut_curve_points) {
|
|
|
+ // tmp.at<cv::uint8_t>(pt.y, pt.x+500) = 200;
|
|
|
+ // }
|
|
|
+
|
|
|
+ // //cv::ellipse(tmp, retval, cv::Scalar(200));
|
|
|
+ // imshow_wait("cut_curve", tmp);
|
|
|
+ //}
|
|
|
+
|
|
|
+}
|
|
|
+string CSolaCutPointReid::get_stem_type_name() {
|
|
|
+ if (m_stem_type == 0) {
|
|
|
+ return string("solanaceae rootstock");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return string("solanaceae scion");
|
|
|
+ }
|
|
|
+}
|
|
|
+void CSolaCutPointReid::img_preprocess(cv::Mat&img)
|
|
|
+{
|
|
|
+ //»Ò¶È»¯
|
|
|
+ cv::Mat b_img;
|
|
|
+ if (img.channels() != 1) {
|
|
|
+ //color image ,bgr, for testing
|
|
|
+ cvtColor(img, m_grayImg, cv::COLOR_BGR2GRAY);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ m_grayImg = img.clone();
|
|
|
+ }
|
|
|
+
|
|
|
+ cv::Mat kernel = cv::getStructuringElement(
|
|
|
+ cv::MORPH_ELLIPSE,
|
|
|
+ cv::Size(5, 5),
|
|
|
+ cv::Point(2,2)
|
|
|
+ );
|
|
|
+
|
|
|
+ double th = threshold(m_grayImg, b_img, 255, 255, cv::THRESH_OTSU);
|
|
|
+
|
|
|
+ morphologyEx(
|
|
|
+ b_img,
|
|
|
+ m_binImg,
|
|
|
+ cv::MORPH_CLOSE,
|
|
|
+ kernel,
|
|
|
+ cv::Point(-1, -1),
|
|
|
+ 2
|
|
|
+ );
|
|
|
+
|
|
|
+}
|
|
|
}
|