|
@@ -85,10 +85,23 @@ int CTeaSort::detect(
|
|
|
imshow_wait("regions_img", rects_img);
|
|
|
}
|
|
|
|
|
|
- //5 detect
|
|
|
+ //5 empty feeder dection
|
|
|
+ if (m_dtype == img_type::tea_grab) {
|
|
|
+ bool is_empty = is_empty_feeder(m_raw_gray_img);
|
|
|
+ if (is_empty) {
|
|
|
+ stringstream bufftmp;
|
|
|
+ bufftmp << m_imgId << m_dtype_str << "empty feeder" ;
|
|
|
+ m_pLogger->INFO(bufftmp.str());
|
|
|
+ //拍照无苗, 返回识别结果-1
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //6 detect
|
|
|
vector<Bbox> droplets_raw;
|
|
|
int dn = detect_impl(m_raw_img, drop_regions, droplets_raw);
|
|
|
- if (dn < 2 && m_dtype == img_type::tea_grab) {
|
|
|
+ if (m_dtype == img_type::tea_grab) {
|
|
|
//up-down flip
|
|
|
cv::Mat flip_img;
|
|
|
cv::flip(m_raw_img, flip_img, 0);
|
|
@@ -115,43 +128,21 @@ int CTeaSort::detect(
|
|
|
droplets_flip.end());
|
|
|
}
|
|
|
}
|
|
|
- /*for (auto rect : drop_regions) {
|
|
|
- Mat roi = m_raw_img(rect);
|
|
|
- vector<Bbox> head_droplets = m_drop_detector.RunModel(roi, m_pLogger);
|
|
|
- if (m_pLogger) {
|
|
|
- stringstream buff_;
|
|
|
- buff_ << m_imgId << m_dtype_str << "-------crop_rect["<< rect.x<<","<<rect.y<<","<<rect.width
|
|
|
- <<","<<rect.height<<"],"
|
|
|
- <<" roi image detect over. tea number is " << head_droplets.size();
|
|
|
- m_pLogger->INFO(buff_.str());
|
|
|
- }
|
|
|
- for (Bbox& b : head_droplets) {
|
|
|
- b.x1 += rect.x;
|
|
|
- b.x2 += rect.x;
|
|
|
- b.y1 += rect.y;
|
|
|
- b.y2 += rect.y;
|
|
|
- for (int i = 0; i < 5; ++i) {
|
|
|
- b.ppoint[2 * i] += rect.x;
|
|
|
- b.ppoint[2 * i + 1] += rect.y;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (head_droplets.size()) {
|
|
|
- droplets_raw.insert(
|
|
|
- droplets_raw.end(),
|
|
|
- head_droplets.begin(),
|
|
|
- head_droplets.end());
|
|
|
- }
|
|
|
- }*/
|
|
|
+
|
|
|
if (m_pLogger) {
|
|
|
stringstream buff_;
|
|
|
buff_ << m_imgId<<m_dtype_str << "image detect over. tea number is " << droplets_raw.size();
|
|
|
m_pLogger->INFO(buff_.str());
|
|
|
}
|
|
|
- //6 nms, width(height) filt and area calculation
|
|
|
+ //7 nms, width(height) filt and area calculation
|
|
|
vector<Bbox> droplets;
|
|
|
vector<int> keep;
|
|
|
nms_bbox(droplets_raw, m_drop_detector.GetNmsThreshold(), keep);
|
|
|
+ if (m_pLogger) {
|
|
|
+ stringstream buff_;
|
|
|
+ buff_ << m_imgId << m_dtype_str << "after nms_bbox, keep size is " << keep.size();
|
|
|
+ m_pLogger->INFO(buff_.str());
|
|
|
+ }
|
|
|
//nms keep and area filter
|
|
|
double min_area_th = m_cp.min_area_ratio_grab;
|
|
|
double max_area_th = m_cp.max_area_ratio_grab;
|
|
@@ -166,7 +157,14 @@ int CTeaSort::detect(
|
|
|
area_ratio /= static_cast<double>(m_raw_img.rows);
|
|
|
area_ratio /= static_cast<double>(m_raw_img.cols);
|
|
|
tbox.area = area_ratio;
|
|
|
- if (area_ratio < min_area_th || area_ratio > max_area_th) { continue; }
|
|
|
+ if (m_pLogger) {
|
|
|
+ stringstream buff_;
|
|
|
+ buff_ << m_imgId << m_dtype_str << "object's area ratio is " << area_ratio<<", range is ["<< min_area_th<<", "<< max_area_th <<"]";
|
|
|
+ m_pLogger->INFO(buff_.str());
|
|
|
+ }
|
|
|
+ if (area_ratio < min_area_th || area_ratio > max_area_th) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
//检查box边界是否在图像内,如果没有,修改之
|
|
|
if (tbox.x1 < 0) { tbox.x1 = 0; }
|
|
|
if (tbox.y1 < 0) { tbox.y1 = 0; }
|
|
@@ -188,6 +186,7 @@ int CTeaSort::detect(
|
|
|
int valid_cnt = 0;
|
|
|
if (m_dtype == img_type::tea_grab) {
|
|
|
//grab
|
|
|
+ calculate_overall_score_grab(droplets);//通过综合得分排序
|
|
|
double pre_cx, pre_cy;
|
|
|
double min_dist_grab = m_cp.min_distance_grab;
|
|
|
pre_cx = -min_dist_grab;
|
|
@@ -256,7 +255,7 @@ int CTeaSort::detect(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- //6 draw
|
|
|
+ //8 draw
|
|
|
if (m_cp.image_return) {
|
|
|
this->clear_imginfo();
|
|
|
cv::Mat img_rst = m_raw_img.clone();
|
|
@@ -273,7 +272,7 @@ int CTeaSort::detect(
|
|
|
char name[256];
|
|
|
cv::Scalar color(120, 120, 0);//bgr
|
|
|
|
|
|
- sprintf_s(name, "%.2f", b.score);
|
|
|
+ sprintf_s(name, "%.2f - %.2f", b.score, b.score_overall);
|
|
|
cv::putText(img_rst, name,
|
|
|
cv::Point(b.x1, b.y1),
|
|
|
cv::FONT_HERSHEY_COMPLEX, 0.7, color, 2);
|
|
@@ -350,8 +349,8 @@ int CTeaSort::detect(
|
|
|
(*m_ppImgSaver)->saveImage(img_rst, m_imgId + "_rst_0");
|
|
|
}
|
|
|
}
|
|
|
- //拍照无苗, 返回识别结果-1
|
|
|
- if (valid_cnt == 0) { return -1; }
|
|
|
+ //结果为1无: 算法结果(相机范围内有苗,但是算法没能识别到可以抓取的苗,告诉嵌入式需要抖动)
|
|
|
+ if (valid_cnt == 0) { return 1; }
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -522,6 +521,12 @@ int CTeaSort::load_data(
|
|
|
m_pLogger->INFO(buff.str());
|
|
|
}
|
|
|
}
|
|
|
+ //to gray
|
|
|
+ if (m_raw_img.channels() == 1) { m_raw_gray_img = m_raw_img; }
|
|
|
+ else {
|
|
|
+ cv::cvtColor(m_raw_img, m_raw_gray_img, cv::COLOR_BGR2GRAY);
|
|
|
+ }
|
|
|
+
|
|
|
return rst;
|
|
|
}
|
|
|
|
|
@@ -1600,4 +1605,145 @@ void CTeaSort::calculate_stem_cut_position_opt(
|
|
|
grab_y += y1;
|
|
|
}
|
|
|
|
|
|
+bool CTeaSort::is_empty_feeder(
|
|
|
+cv::Mat& raw_img,
|
|
|
+double th/*=50.0*/
|
|
|
+)
|
|
|
+{
|
|
|
+ vector<Rect> drop_regions;
|
|
|
+ //生成grid
|
|
|
+
|
|
|
+ int grid_row = 16;
|
|
|
+ int grid_col = 16;
|
|
|
+ int block_height = int(raw_img.rows / (float)grid_row + 0.5);
|
|
|
+ int block_width = int(raw_img.cols / (float)grid_col + 0.5);
|
|
|
+ for (int r = 0; r < grid_row; ++r) {
|
|
|
+ for (int c = 0; c < grid_col; ++c) {
|
|
|
+ int x0 = c*block_width;
|
|
|
+ int y0 = r*block_height;
|
|
|
+ int x1 = (c + 1)*block_width;
|
|
|
+ int y1 = (r + 1)*block_height;
|
|
|
+
|
|
|
+ if (x0 < 0) { x0 = 0; }
|
|
|
+ if (y0 < 0) { y0 = 0; }
|
|
|
+ if (x1 > raw_img.cols) { x1 = raw_img.cols; }
|
|
|
+ if (y1 > raw_img.rows) { y1 = raw_img.rows; }
|
|
|
+ Rect r(x0, y0, x1 - x0, y1 - y0);
|
|
|
+ drop_regions.push_back(r);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ //对原始灰度图进行分析
|
|
|
+ std::vector<double> gray_values;
|
|
|
+ for (auto rect : drop_regions) {
|
|
|
+ Mat roi = raw_img(rect);
|
|
|
+ cv::Scalar mu = cv::mean(roi);
|
|
|
+ gray_values.push_back(mu[0]);
|
|
|
+ }
|
|
|
+ bool is_empty = true;
|
|
|
+ double maxv = *max_element(gray_values.begin(), gray_values.end());
|
|
|
+ double minv = *min_element(gray_values.begin(), gray_values.end());
|
|
|
+ if((maxv-minv)>th){
|
|
|
+ is_empty = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_empty) {
|
|
|
+ return is_empty;
|
|
|
+ }
|
|
|
+
|
|
|
+ //计算前景的百分比
|
|
|
+ cv::Mat bin_img;
|
|
|
+ double th_bin = cv::threshold(raw_img, bin_img, 255, 255, cv::THRESH_OTSU);
|
|
|
+ //统计bin_img中0个数
|
|
|
+ double fg_area = 0;
|
|
|
+ cv::Mat_<uchar>::iterator it = bin_img.begin<uchar>();
|
|
|
+ cv::Mat_<uchar>::iterator it_end = bin_img.end<uchar>();
|
|
|
+ for (; it != it_end; ++it) {
|
|
|
+ if ((*it)==0) {
|
|
|
+ fg_area += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (m_cp.image_show) {
|
|
|
+ imshow_wait("overall bin", bin_img);
|
|
|
+ }
|
|
|
+ double objects_ratio = fg_area / static_cast<double>(bin_img.cols * bin_img.rows);
|
|
|
+ if (objects_ratio <= 0.005) {
|
|
|
+ is_empty = true;
|
|
|
+ }
|
|
|
+ return is_empty;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+double CTeaSort::singleten_ratio(
|
|
|
+Bbox& box
|
|
|
+)
|
|
|
+{
|
|
|
+ //计算图片中背景的占有率
|
|
|
+ //padding
|
|
|
+ //扩展box的范围,4个方向全部扩展
|
|
|
+ int x1 = box.x1;
|
|
|
+ int y1 = box.y1;
|
|
|
+ int x2 = box.x2;
|
|
|
+ int y2 = box.y2;
|
|
|
+ int padding_border = m_cp.offset_grab;
|
|
|
+ x1 -= padding_border;
|
|
|
+ x1 = x1 < 0 ? 0 : x1;
|
|
|
+ y1 -= padding_border;
|
|
|
+ y1 = y1 < 0 ? 0 : y1;
|
|
|
+
|
|
|
+ x2 += padding_border;
|
|
|
+ x2 = x2 < m_raw_img.cols ? x2 : m_raw_img.cols - 1;
|
|
|
+
|
|
|
+ y2 += padding_border;
|
|
|
+ y2 = y2 < m_raw_img.rows ? y2 : m_raw_img.rows - 1;
|
|
|
+
|
|
|
+ cv::Rect r(x1,y1,x2-x1,y2-y1);
|
|
|
+
|
|
|
+ cv::Mat roi = m_raw_gray_img(r).clone();
|
|
|
+ cv::Mat bin_img;
|
|
|
+ double th = cv::threshold(roi, bin_img, 255, 255, cv::THRESH_OTSU);
|
|
|
+
|
|
|
+ //统计bin_img中非0个数
|
|
|
+ double bg_area = 0;
|
|
|
+ cv::Mat_<uchar>::iterator it = bin_img.begin<uchar>();
|
|
|
+ cv::Mat_<uchar>::iterator it_end = bin_img.end<uchar>();
|
|
|
+ for(;it!=it_end;++it){
|
|
|
+ if((*it)>0){
|
|
|
+ bg_area+=1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ double singleten_ratio = bg_area / static_cast<double>(roi.cols * roi.rows);
|
|
|
+ return singleten_ratio;
|
|
|
+}
|
|
|
+double CTeaSort::direction_ratio(
|
|
|
+ Bbox& box
|
|
|
+)
|
|
|
+{
|
|
|
+ float x3 = box.ppoint[4];
|
|
|
+ float y3 = box.ppoint[5];
|
|
|
+ float x5 = box.ppoint[8];
|
|
|
+ float y5 = box.ppoint[9];
|
|
|
+ double angle = atan2(x5 - x3, y5 - y3);
|
|
|
+ double ratio = cos(angle);
|
|
|
+ if(ratio < 0) {
|
|
|
+ ratio *= -0.75;
|
|
|
+ }
|
|
|
+ return ratio;
|
|
|
+}
|
|
|
+void CTeaSort::calculate_overall_score_grab(
|
|
|
+ std::vector<Bbox> &boxes
|
|
|
+)
|
|
|
+{
|
|
|
+ for (auto&b : boxes) {
|
|
|
+ double single_ratio = singleten_ratio(b);
|
|
|
+ double dir_score = direction_ratio(b);
|
|
|
+ b.score_overall = single_ratio * dir_score;
|
|
|
+ }
|
|
|
+ sort(boxes.begin(), boxes.end(),
|
|
|
+ [=](const Bbox& left, const Bbox& right) {
|
|
|
+ return left.score_overall > right.score_overall;
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
}
|