Skip to content

Commit bb1b928

Browse files
AntTrakluge7sophia-mina
committed
refactor: remove grid now uses different thresholding (#21)
* three different thresholding methods added * implemented HSV tuning * added tuning comments in params and const in some values * Update comments in image_filtering_params.yaml Clarified comments for hsv saturation and value adjustments. * Update comment for threshold calculation in BinaryThreshold Clarify the calculation of threshold value in the apply_filter method. * chore: apply pre-commit formatting --------- Co-authored-by: Andreas Kluge Svendsrud <89779148+kluge7@users.noreply.github.com> Co-authored-by: Sophia Mina Friedensburg <pianist.sophia.friedensburg@gmail.com>
1 parent 071d513 commit bb1b928

4 files changed

Lines changed: 73 additions & 55 deletions

File tree

image-filtering/config/image_filtering_params.yaml

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**:
22
ros__parameters:
3-
sub_topic: "/cam/image_color"
3+
sub_topic: "/realsense_d555/color/image_rect"
44
pub_topic: "/filtered_image"
55
input_encoding: "rgb8"
66
output_encoding: "rgb8"
@@ -51,12 +51,18 @@
5151
erosion_size: 2
5252
dilation_size: 2
5353
remove_grid:
54-
threshold_green: 0.5
55-
threshold_binary: 30
56-
inpaint_radius: 1.0
54+
inpaint_radius: 1.0 # Increase when it get blurry close
55+
threshold_binary: 90 # Adjust depending on how many black patches obstructs around the marker
56+
use_binary_threshold: true
5757
rotation: 0
5858
height: 400
5959
width: 400
60+
hsv_hue_low: 9 # This value is calibrated for yellow-green grids. Increase this value to ~40 if the grid is more orange
61+
hsv_hue_high: 40 # Increase this value to ~80 if more orange
62+
hsv_sat_low: 50 # Adjust together with hsv_val_high depending on brightness
63+
hsv_sat_high: 255
64+
hsv_val_low: 50 # Adjust together with hsv_sat_low depending on brightness
65+
hsv_val_high: 255
6066
overlap:
6167
percentage_threshold: 20.0 # Percentage (0-100) to cap the pixel intensity difference
6268
median_binary: # finds the median of each n x n square around each pixel

image-filtering/include/image_filtering/lib/filters/binary_threshold.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ inline void BinaryThreshold::apply_filter(const cv::Mat& original,
3030
cv::Mat& filtered) const {
3131
CV_Assert(!original.empty());
3232

33-
const double thresh = this->filter_params_.threshold;
33+
const double thresh = this->filter_params_.threshold * 255.0 /
34+
100.0; // Params is in percent while cv::threshold
35+
// takes in values from 0 to 255
3436
const double maxval = this->filter_params_.maxval;
3537
const bool invert = this->filter_params_.invert;
3638

image-filtering/include/image_filtering/lib/filters/remove_grid.hpp

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@
1212
namespace vortex::image_filtering {
1313

1414
struct RemoveGridParams {
15-
double threshold_green;
16-
int threshold_binary;
1715
double inpaint_radius;
16+
int threshold_binary;
17+
bool use_binary_threshold;
1818
int rotation;
1919
int height;
2020
int width;
21+
int hsv_hue_low;
22+
int hsv_hue_high;
23+
int hsv_sat_low;
24+
int hsv_sat_high;
25+
int hsv_val_low;
26+
int hsv_val_high;
2127
};
2228

2329
class RemoveGrid : public Filter {
@@ -44,9 +50,9 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original,
4450
return;
4551
}
4652

47-
// Rotate directly into cropped output
48-
int crop_w = std::min(params_.width, original.cols);
49-
int crop_h = std::min(params_.height, original.rows);
53+
// Rotate/crop to ROI
54+
const int crop_w = std::min(params_.width, original.cols);
55+
const int crop_h = std::min(params_.height, original.rows);
5056

5157
if (crop_w != params_.width || crop_h != params_.height) {
5258
spdlog::warn(
@@ -58,85 +64,76 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original,
5864
crop_h);
5965
}
6066

61-
const cv::Point2f center_src(
62-
original.cols * 0.5f, original.rows * 0.5f); // center of source image
63-
const cv::Point2f center_dst(crop_w * 0.5f,
64-
crop_h * 0.5f); // center of destination image
67+
const cv::Point2f center_src(original.cols * 0.5f, original.rows * 0.5f);
68+
const cv::Point2f center_dst(crop_w * 0.5f, crop_h * 0.5f);
6569

66-
cv::Mat M = cv::getRotationMatrix2D(center_src, params_.rotation,
67-
1.0); // affine matrix
68-
// Ensure type for at<double>
69-
if (M.type() != CV_64F) {
70+
cv::Mat M = cv::getRotationMatrix2D(center_src, params_.rotation, 1.0);
71+
if (M.type() != CV_64F)
7072
M.convertTo(M, CV_64F);
71-
}
7273

73-
// Shift translation so original center maps to cropped center
7474
M.at<double>(0, 2) += (center_dst.x - center_src.x);
7575
M.at<double>(1, 2) += (center_dst.y - center_src.y);
7676

7777
cv::Mat cropped;
7878
cv::warpAffine(original, cropped, M, cv::Size(crop_w, crop_h),
7979
cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
8080

81-
// Extract green grid mask
82-
cv::Mat cropped_f;
83-
cropped.convertTo(cropped_f, CV_32F, 1.0 / 255.0);
84-
85-
std::vector<cv::Mat> ch(3); // make a vector for BGR
86-
cv::split(cropped_f, ch); // BGR
87-
88-
cv::Mat sum = ch[0] + ch[1] + ch[2] + 1e-6f; // avoid division by zero
89-
for (auto& c : ch)
90-
c /= sum; // normalized color values
91-
92-
// mask the green channel
93-
cv::Mat grid_mask = (ch[1] > params_.threshold_green);
81+
// Detect yellow grid bars via HSV hue range (input is rgb8)
82+
cv::Mat hsv;
83+
cv::cvtColor(cropped, hsv, cv::COLOR_RGB2HSV);
84+
cv::Mat grid_mask;
85+
cv::inRange(hsv,
86+
cv::Scalar(params_.hsv_hue_low, params_.hsv_sat_low,
87+
params_.hsv_val_low),
88+
cv::Scalar(params_.hsv_hue_high, params_.hsv_sat_high,
89+
params_.hsv_val_high),
90+
grid_mask);
91+
92+
// Dilate mask to fully cover grid bar edges
9493
static const cv::Mat kernel = cv::Mat::ones(3, 3, CV_8U);
9594
cv::Mat dilated;
9695
cv::dilate(grid_mask, dilated, kernel);
9796

98-
// prevent border leak
97+
// Prevent border leak
9998
dilated.row(0).setTo(0);
10099
dilated.row(dilated.rows - 1).setTo(0);
101100
dilated.col(0).setTo(0);
102101
dilated.col(dilated.cols - 1).setTo(0);
103102

104103
if (cv::countNonZero(dilated) == 0) {
105-
// If no grid detected, leave image unchanged
106104
original.copyTo(filtered);
107105
return;
108106
}
109107

108+
// Optionally apply binary threshold before inpainting
109+
cv::Mat inpaint_src;
110+
if (params_.use_binary_threshold) {
111+
cv::Mat thresh_gray;
112+
apply_fixed_threshold(cropped, thresh_gray, params_.threshold_binary,
113+
false);
114+
cv::cvtColor(thresh_gray, inpaint_src, cv::COLOR_GRAY2BGR);
115+
} else {
116+
inpaint_src = cropped;
117+
}
118+
110119
// Inpaint grid
111120
cv::Mat inpainted;
112-
cv::inpaint(cropped, dilated, inpainted, params_.inpaint_radius,
121+
cv::inpaint(inpaint_src, dilated, inpainted, params_.inpaint_radius,
113122
cv::INPAINT_TELEA);
114123

115-
// Binary threshold (on cropped ROI)
116-
cv::Mat thresh_gray;
117-
apply_fixed_threshold(inpainted, thresh_gray, params_.threshold_binary,
118-
false);
119-
120-
cv::Mat thresh_bgr;
121-
cv::cvtColor(thresh_gray, thresh_bgr, cv::COLOR_GRAY2BGR);
122-
123-
// Undo rotation & merge (using M)
124+
// Warp inpainted ROI back into full-size image
124125
cv::Mat invM;
125126
cv::invertAffineTransform(M, invM);
126127

127-
// Warp ROI result back into full-size overlay
128128
cv::Mat overlay_full;
129-
cv::warpAffine(thresh_bgr, overlay_full, invM, original.size(),
129+
cv::warpAffine(inpainted, overlay_full, invM, original.size(),
130130
cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
131131

132-
// Warp a mask the same way (so black pixels are copied too)
133-
cv::Mat local_mask(thresh_bgr.rows, thresh_bgr.cols, CV_8U,
134-
cv::Scalar(255));
132+
cv::Mat local_mask(inpainted.rows, inpainted.cols, CV_8U, cv::Scalar(255));
135133
cv::Mat mask_full;
136134
cv::warpAffine(local_mask, mask_full, invM, original.size(),
137135
cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0));
138136

139-
// Merge into the original image
140137
filtered = original.clone();
141138
overlay_full.copyTo(filtered, mask_full);
142139
}

image-filtering/src/ros/image_filtering_ros.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,14 +238,14 @@ void ImageFilteringNode::set_filter_params() {
238238
case FilterType::RemoveGrid: {
239239
RemoveGridParams params;
240240

241-
params.threshold_green = declare_and_get<double>(
242-
"filter_params.remove_grid.threshold_green");
241+
params.inpaint_radius = declare_and_get<double>(
242+
"filter_params.remove_grid.inpaint_radius");
243243

244244
params.threshold_binary = declare_and_get<int>(
245245
"filter_params.remove_grid.threshold_binary");
246246

247-
params.inpaint_radius = declare_and_get<double>(
248-
"filter_params.remove_grid.inpaint_radius");
247+
params.use_binary_threshold = declare_and_get<bool>(
248+
"filter_params.remove_grid.use_binary_threshold");
249249

250250
params.rotation =
251251
declare_and_get<int>("filter_params.remove_grid.rotation");
@@ -256,6 +256,19 @@ void ImageFilteringNode::set_filter_params() {
256256
params.height =
257257
declare_and_get<int>("filter_params.remove_grid.height");
258258

259+
params.hsv_hue_low =
260+
declare_and_get<int>("filter_params.remove_grid.hsv_hue_low");
261+
params.hsv_hue_high =
262+
declare_and_get<int>("filter_params.remove_grid.hsv_hue_high");
263+
params.hsv_sat_low =
264+
declare_and_get<int>("filter_params.remove_grid.hsv_sat_low");
265+
params.hsv_sat_high =
266+
declare_and_get<int>("filter_params.remove_grid.hsv_sat_high");
267+
params.hsv_val_low =
268+
declare_and_get<int>("filter_params.remove_grid.hsv_val_low");
269+
params.hsv_val_high =
270+
declare_and_get<int>("filter_params.remove_grid.hsv_val_high");
271+
259272
filter_ptr_ = std::make_unique<RemoveGrid>(params);
260273
break;
261274
}

0 commit comments

Comments
 (0)