From 5af770ae74001a6b8e1ad37a77b8d8abea32954d Mon Sep 17 00:00:00 2001 From: Sophia-Mina Date: Mon, 19 Jan 2026 20:50:41 +0100 Subject: [PATCH 01/10] filter to remove grid (without box) --- .../config/image_filtering_params.yaml | 8 +++- .../image_filters/image_processing.hpp | 18 +++++++- image-filtering/src/image_filtering_ros.cpp | 22 ++++++++- image-filtering/src/image_processing.cpp | 45 +++++++++++++++++++ 4 files changed, 89 insertions(+), 4 deletions(-) diff --git a/image-filtering/config/image_filtering_params.yaml b/image-filtering/config/image_filtering_params.yaml index d5272f7..2f1ed64 100644 --- a/image-filtering/config/image_filtering_params.yaml +++ b/image-filtering/config/image_filtering_params.yaml @@ -1,10 +1,10 @@ /**: ros__parameters: - sub_topic: "/downwards_camera/image_raw" + sub_topic: "/cam_down/image_color" pub_topic: "/filtered_image" output_encoding: "mono8" filter_params: - filter_type: "otsu" + filter_type: "remove_grid" flip: flip_code: 1 unsharpening: @@ -28,6 +28,10 @@ otsu_segmentation: true erosion_size: 10 dilation_size: 10 + remove_grid: + threshold_green: 0.5 + threshold_binary: 30.0 + inpaint_radius: 1.0 # Filter params should reflect the FilterParams struct # defined in /include/image_filters/image_processing.hpp diff --git a/image-filtering/include/image_filters/image_processing.hpp b/image-filtering/include/image_filters/image_processing.hpp index 59e9bf5..d40d549 100644 --- a/image-filtering/include/image_filters/image_processing.hpp +++ b/image-filtering/include/image_filters/image_processing.hpp @@ -9,6 +9,8 @@ #include #include #include +#include // dilate +#include // inpaint #include struct FlipParams { @@ -47,6 +49,12 @@ struct OtsuParams { int dilation_size; }; +struct GridRemovalParams { + double threshold_green; + double threshold_binary; + double inpaint_radius; +}; + struct FilterParams { FlipParams flip; UnsharpeningParams unsharpening; @@ -55,6 +63,7 @@ struct FilterParams { WhiteBalancingParams white_balancing; EbusParams ebus; OtsuParams otsu; + GridRemovalParams remove_grid; }; typedef void (*FilterFunction)(const FilterParams&, const cv::Mat&, cv::Mat&); @@ -132,6 +141,12 @@ void otsu_segmentation_filter(const FilterParams& params, const cv::Mat& original, cv::Mat& output); +/** +* A filter to remove yellow/green-ish grid lines from images +**/ + +void remove_grid_filter(const FilterParams& params, const cv::Mat& original, cv::Mat& filtered); + static const std::map filter_functions = { {"no_filter", no_filter}, {"flip", flip_filter}, @@ -141,6 +156,7 @@ static const std::map filter_functions = { {"dilation", dilation_filter}, {"white_balancing", white_balance_filter}, {"ebus", ebus_filter}, - {"otsu", otsu_segmentation_filter}}; + {"otsu", otsu_segmentation_filter}, + {"remove_grid", remove_grid_filter}}; #endif // IMAGE_FILTERS__IMAGE_PROCESSING_HPP_ diff --git a/image-filtering/src/image_filtering_ros.cpp b/image-filtering/src/image_filtering_ros.cpp index 4dd53bf..c8c2514 100644 --- a/image-filtering/src/image_filtering_ros.cpp +++ b/image-filtering/src/image_filtering_ros.cpp @@ -42,6 +42,10 @@ void ImageFilteringNode::declare_parameters() { this->declare_parameter("filter_params.otsu.gsc_weight_b"); this->declare_parameter("filter_params.otsu.erosion_size"); this->declare_parameter("filter_params.otsu.dilation_size"); + this->declare_parameter( + "filter_params.remove_grid.threshold_green"); + this->declare_parameter("filter_params.remove_grid.threshold_binary"); + this->declare_parameter("filter_params.remove_grid.inpaint_radius"); } void ImageFilteringNode::set_filter_params() { @@ -91,6 +95,12 @@ void ImageFilteringNode::set_filter_params() { this->get_parameter("filter_params.otsu.erosion_size").as_int(); params.otsu.dilation_size = this->get_parameter("filter_params.otsu.dilation_size").as_int(); + params.remove_grid.threshold_green = + this->get_parameter("filter_params.remove_grid.threshold_green").as_double(); + params.remove_grid.threshold_binary = + this->get_parameter("filter_params.remove_grid.threshold_binary").as_double(); + params.remove_grid.inpaint_radius = + this->get_parameter("filter_params.remove_grid.inpaint_radius").as_double(); filter_params_ = params; spdlog::info("Filter parameters set: {}", filter); } @@ -141,7 +151,10 @@ void ImageFilteringNode::on_parameter_event( void ImageFilteringNode::image_callback( const sensor_msgs::msg::Image::SharedPtr msg) { cv_bridge::CvImagePtr cv_ptr; - + std::cout << "Received image with height: " << msg->height << " width: " << msg->width << std::endl; + //get time before processing + auto start_time = std::chrono::high_resolution_clock::now(); + std::cout << "time start: " << start_time.time_since_epoch().count() << std::endl; try { cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8); @@ -164,6 +177,13 @@ void ImageFilteringNode::image_callback( cv_bridge::CvImage(msg->header, output_encoding, filtered_image) .toImageMsg(*message); + // get time after processing + auto end_time = std::chrono::high_resolution_clock::now(); + std::cout << "time end: " << end_time.time_since_epoch().count() << std::endl; + auto processing_duration = std::chrono::duration_cast( + end_time - start_time); + std::cout << "Processing time (ms): " << processing_duration.count() << std::endl; + std::cout << "Publishing filtered image with height: " << message->height << " width: " << message->width << std::endl; image_pub_->publish(std::move(message)); } diff --git a/image-filtering/src/image_processing.cpp b/image-filtering/src/image_processing.cpp index 00b4aec..a9b2f2b 100644 --- a/image-filtering/src/image_processing.cpp +++ b/image-filtering/src/image_processing.cpp @@ -236,6 +236,51 @@ void otsu_segmentation_filter(const FilterParams& params, } } +void remove_grid_filter(const FilterParams& params, const cv::Mat& original, cv::Mat& filtered) { + CV_Assert(!original.empty()); + CV_Assert(original.type() == CV_8UC3); + + cv::Mat img_u8 = original.clone(); + cv::Mat img_f; + + // extract grid lines + img_u8.convertTo(img_f, CV_32F, 1.0 / 255.0); + std::vector color_channels(3); + cv::split(img_f, color_channels); // BGR + + cv::Mat B = color_channels[0]; + cv::Mat G = color_channels[1]; + cv::Mat R = color_channels[2]; + + cv::Mat sum_rgb = B + G + R + 1e-6f; // avoid division by zero + + for (int i = 0; i < 3; ++i) { + color_channels[i] /= sum_rgb; + } + + /* mask the green channel (grid) */ + cv::Mat grid_mask = (color_channels[1] > params.remove_grid.threshold_green); + cv::Mat kernel = cv::Mat::ones(3, 3, CV_8U); + cv::Mat dilated_mask; + cv::dilate(grid_mask, dilated_mask, kernel, cv::Point(-1,-1), 1); + + /* prevent "leak" at image borders */ + dilated_mask.row(0).setTo(0); + dilated_mask.row(dilated_mask.rows - 1).setTo(0); + dilated_mask.col(0).setTo(0); + dilated_mask.col(dilated_mask.cols - 1).setTo(0); + + // inpaint the masked areas + cv::Mat inpainted_img; + cv::inpaint(img_u8, dilated_mask, inpainted_img, params.remove_grid.inpaint_radius, cv::INPAINT_TELEA); + + // binary thresholding + cv::Mat gray; + cv::cvtColor(inpainted_img, gray, cv::COLOR_BGR2GRAY); + cv::threshold(gray, filtered, params.remove_grid.threshold_binary, 255, cv::THRESH_BINARY); + +} + void apply_filter(const std::string& filter, const FilterParams& params, const cv::Mat& original, From dc8e520d33d7bfa33adb1e8906936f1329a0f939 Mon Sep 17 00:00:00 2001 From: AntTra Date: Wed, 11 Feb 2026 21:03:13 +0100 Subject: [PATCH 02/10] feat: implemented bounded filtering box in image_processing and added necessary variables --- .../config/image_filtering_params.yaml | 7 ++- .../image_filters/image_processing.hpp | 3 ++ image-filtering/src/image_filtering_ros.cpp | 9 ++++ image-filtering/src/image_processing.cpp | 48 ++++++++++++++++--- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/image-filtering/config/image_filtering_params.yaml b/image-filtering/config/image_filtering_params.yaml index 2f1ed64..a567076 100644 --- a/image-filtering/config/image_filtering_params.yaml +++ b/image-filtering/config/image_filtering_params.yaml @@ -1,8 +1,8 @@ /**: ros__parameters: - sub_topic: "/cam_down/image_color" + sub_topic: "/cam/image_color" pub_topic: "/filtered_image" - output_encoding: "mono8" + output_encoding: "bgr8" filter_params: filter_type: "remove_grid" flip: @@ -32,6 +32,9 @@ threshold_green: 0.5 threshold_binary: 30.0 inpaint_radius: 1.0 + rotation: 0 + height: 50 + width: 50 # Filter params should reflect the FilterParams struct # defined in /include/image_filters/image_processing.hpp diff --git a/image-filtering/include/image_filters/image_processing.hpp b/image-filtering/include/image_filters/image_processing.hpp index d40d549..99135e3 100644 --- a/image-filtering/include/image_filters/image_processing.hpp +++ b/image-filtering/include/image_filters/image_processing.hpp @@ -53,6 +53,9 @@ struct GridRemovalParams { double threshold_green; double threshold_binary; double inpaint_radius; + int rotation; // degrees + int height; + int width; }; struct FilterParams { diff --git a/image-filtering/src/image_filtering_ros.cpp b/image-filtering/src/image_filtering_ros.cpp index c8c2514..0a6853e 100644 --- a/image-filtering/src/image_filtering_ros.cpp +++ b/image-filtering/src/image_filtering_ros.cpp @@ -46,6 +46,9 @@ void ImageFilteringNode::declare_parameters() { "filter_params.remove_grid.threshold_green"); this->declare_parameter("filter_params.remove_grid.threshold_binary"); this->declare_parameter("filter_params.remove_grid.inpaint_radius"); + this->declare_parameter("filter_params.remove_grid.rotation"); + this->declare_parameter("filter_params.remove_grid.height"); + this->declare_parameter("filter_params.remove_grid.width"); } void ImageFilteringNode::set_filter_params() { @@ -101,6 +104,12 @@ void ImageFilteringNode::set_filter_params() { this->get_parameter("filter_params.remove_grid.threshold_binary").as_double(); params.remove_grid.inpaint_radius = this->get_parameter("filter_params.remove_grid.inpaint_radius").as_double(); + params.remove_grid.rotation = + this->get_parameter("filter_params.remove_grid.rotation").as_int(); + params.remove_grid.height = + this->get_parameter("filter_params.remove_grid.height").as_int(); + params.remove_grid.width = + this->get_parameter("filter_params.remove_grid.width").as_int(); filter_params_ = params; spdlog::info("Filter parameters set: {}", filter); } diff --git a/image-filtering/src/image_processing.cpp b/image-filtering/src/image_processing.cpp index a9b2f2b..8451892 100644 --- a/image-filtering/src/image_processing.cpp +++ b/image-filtering/src/image_processing.cpp @@ -240,13 +240,33 @@ void remove_grid_filter(const FilterParams& params, const cv::Mat& original, cv: CV_Assert(!original.empty()); CV_Assert(original.type() == CV_8UC3); - cv::Mat img_u8 = original.clone(); - cv::Mat img_f; + // rotation and cropping of the image + cv::Point2f center(original.cols * 0.5f, img_u8.rows * 0.5f); + cv::Mat rotation_matrix = + cv::getRotationMatrix2D(center, params.remove_grid.rotation, 1.0); + + cv::Mat rotated_img_u8; + cv::warpAffine(original, rotated_img_u8, rotation_matrix, original.size(), + cv::INTER_NEAREST, + cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); + + + /* centered crop */ + int crop_w = std::min(params.remove_grid.width, rotated_img_u8.cols); + int crop_h = std::min(params.remove_grid.height, rotated_img_u8.rows); + + int x_corner = (rotated_img_u8.cols - crop_w) / 2; // x of top left corner + int y_corner = (rotated_img_u8.rows - crop_h) / 2; // y of top left corner + + cv::Rect roi(x_corner, y_corner, crop_w, crop_h); + cv::Mat cropped_img_u8 = rotated_img_u8(roi); + + cv::Mat cropped_img_f; // extract grid lines - img_u8.convertTo(img_f, CV_32F, 1.0 / 255.0); + cropped_img_u8.convertTo(cropped_img_f, CV_32F, 1.0 / 255.0); std::vector color_channels(3); - cv::split(img_f, color_channels); // BGR + cv::split(cropped_img_f, color_channels); // BGR cv::Mat B = color_channels[0]; cv::Mat G = color_channels[1]; @@ -272,12 +292,28 @@ void remove_grid_filter(const FilterParams& params, const cv::Mat& original, cv: // inpaint the masked areas cv::Mat inpainted_img; - cv::inpaint(img_u8, dilated_mask, inpainted_img, params.remove_grid.inpaint_radius, cv::INPAINT_TELEA); + cv::inpaint(cropped_img_u8, dilated_mask, inpainted_img, params.remove_grid.inpaint_radius, cv::INPAINT_TELEA); // binary thresholding cv::Mat gray; + cv::Mat thresholded_img; cv::cvtColor(inpainted_img, gray, cv::COLOR_BGR2GRAY); - cv::threshold(gray, filtered, params.remove_grid.threshold_binary, 255, cv::THRESH_BINARY); + //cv::threshold(gray, filtered, params.remove_grid.threshold_binary, 255, cv::THRESH_BINARY); + + cv::threshold(gray, thresholded_img, params.remove_grid.threshold_binary, 255, cv::THRESH_BINARY); + + // paste the aruco-marker back to the published image + cv::Mat thresholded_bgr; + cv::cvtColor(thresholded_img, thresholded_bgr, cv::COLOR_GRAY2BGR); + + cv::Mat rotated_thresholded_img = rotated_img_u8; + thresholded_bgr.copyTo(rotated_thresholded_img(roi)); + + cv::Mat inverse_rotation_matrix; + cv::invertAffineTransform(rotation_matrix, inverse_rotation_matrix); + + cv::warpAffine(rotated_thresholded_img, filtered, inverse_rotation_matrix, original.size(), + cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); } From 9cc2cf38bdf9c5c5bbdc93dc76819b441d233e10 Mon Sep 17 00:00:00 2001 From: AntTra Date: Wed, 11 Feb 2026 21:35:02 +0100 Subject: [PATCH 03/10] renamed a variable --- image-filtering/src/image_processing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image-filtering/src/image_processing.cpp b/image-filtering/src/image_processing.cpp index 8451892..7079349 100644 --- a/image-filtering/src/image_processing.cpp +++ b/image-filtering/src/image_processing.cpp @@ -241,7 +241,7 @@ void remove_grid_filter(const FilterParams& params, const cv::Mat& original, cv: CV_Assert(original.type() == CV_8UC3); // rotation and cropping of the image - cv::Point2f center(original.cols * 0.5f, img_u8.rows * 0.5f); + cv::Point2f center(original.cols * 0.5f, original.rows * 0.5f); cv::Mat rotation_matrix = cv::getRotationMatrix2D(center, params.remove_grid.rotation, 1.0); From bb1106583c47724d17f7012fbc1ada604ded62ac Mon Sep 17 00:00:00 2001 From: Andreas Kluge Svendsrud <89779148+kluge7@users.noreply.github.com> Date: Mon, 16 Feb 2026 19:40:00 +0100 Subject: [PATCH 04/10] chore: add configs values for remove_grid --- image-filtering/config/image_filtering_params.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/image-filtering/config/image_filtering_params.yaml b/image-filtering/config/image_filtering_params.yaml index 66a7634..514de77 100644 --- a/image-filtering/config/image_filtering_params.yaml +++ b/image-filtering/config/image_filtering_params.yaml @@ -1,9 +1,9 @@ /**: ros__parameters: - sub_topic: "/fls_publisher/display_mono" + sub_topic: "/cam/image_color" pub_topic: "/filtered_image" - input_encoding: "mono8" - output_encoding: "mono8" + input_encoding: "rgb8" + output_encoding: "rgb8" filter_params: filter_type: "remove_grid" @@ -35,8 +35,8 @@ threshold_binary: 30.0 inpaint_radius: 1.0 rotation: 0 - height: 50 - width: 50 + height: 400 + width: 400 overlap: percentage_threshold: 20.0 # Percentage (0-100) to cap the pixel intensity difference median_binary: # finds the median of each n x n square around each pixel From abb087f32c44a359e1c598db897045a904ba21f7 Mon Sep 17 00:00:00 2001 From: AntTra Date: Mon, 16 Feb 2026 19:56:46 +0100 Subject: [PATCH 05/10] added pre-commit changes --- .../lib/filters/all_filters.hpp | 2 +- .../lib/filters/remove_grid.hpp | 28 +++++++++---------- .../src/ros/image_filtering_ros.cpp | 12 ++++---- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/image-filtering/include/image_filtering/lib/filters/all_filters.hpp b/image-filtering/include/image_filtering/lib/filters/all_filters.hpp index 46751c7..af462e8 100644 --- a/image-filtering/include/image_filtering/lib/filters/all_filters.hpp +++ b/image-filtering/include/image_filtering/lib/filters/all_filters.hpp @@ -10,9 +10,9 @@ #include "image_filtering/lib/filters/no_filter.hpp" #include "image_filtering/lib/filters/otsu.hpp" #include "image_filtering/lib/filters/overlap.hpp" +#include "image_filtering/lib/filters/remove_grid.hpp" #include "image_filtering/lib/filters/sharpening.hpp" #include "image_filtering/lib/filters/unsharpening.hpp" #include "image_filtering/lib/filters/white_balancing.hpp" -#include "image_filtering/lib/filters/remove_grid.hpp" #endif // IMAGE_FILTERING__LIB__FILTERS__ALL_FILTERS_HPP_ diff --git a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp index fce6909..e724153 100644 --- a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp +++ b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp @@ -1,9 +1,11 @@ #ifndef IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ #define IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ +#include +#include #include #include -#include +#include #include "abstract_filter_class.hpp" @@ -23,8 +25,7 @@ struct RemoveGridParams { class RemoveGrid : public Filter { public: - explicit RemoveGrid(RemoveGridParams params) - : params_(params) {} + explicit RemoveGrid(RemoveGridParams params) : params_(params) {} void apply_filter(const cv::Mat& original, cv::Mat& filtered) const override; @@ -55,13 +56,12 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cv::Mat rotated; cv::warpAffine(original, rotated, rotation_matrix, original.size(), - cv::INTER_NEAREST, - cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); + cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); // ---------------------------- // Center crop // ---------------------------- - int crop_w = std::min(params_.width, rotated.cols); + int crop_w = std::min(params_.width, rotated.cols); int crop_h = std::min(params_.height, rotated.rows); int x = (rotated.cols - crop_w) / 2; @@ -77,10 +77,11 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cropped.convertTo(cropped_f, CV_32F, 1.0 / 255.0); std::vector ch(3); - cv::split(cropped_f, ch); // BGR + cv::split(cropped_f, ch); // BGR cv::Mat sum = ch[0] + ch[1] + ch[2] + 1e-6f; - for (auto& c : ch) c /= sum; + for (auto& c : ch) + c /= sum; cv::Mat grid_mask = (ch[1] > params_.threshold_green); @@ -98,8 +99,8 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, // Inpaint grid // ---------------------------- cv::Mat inpainted; - cv::inpaint(cropped, dilated, inpainted, - params_.inpaint_radius, cv::INPAINT_TELEA); + cv::inpaint(cropped, dilated, inpainted, params_.inpaint_radius, + cv::INPAINT_TELEA); // ---------------------------- // Binary threshold @@ -107,9 +108,7 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cv::Mat gray, thresh; cv::cvtColor(inpainted, gray, cv::COLOR_BGR2GRAY); - cv::threshold(gray, thresh, - params_.threshold_binary, - 255, + cv::threshold(gray, thresh, params_.threshold_binary, 255, cv::THRESH_BINARY); cv::Mat thresh_bgr; @@ -125,8 +124,7 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cv::invertAffineTransform(rotation_matrix, inv_rot); cv::warpAffine(rotated_out, filtered, inv_rot, original.size(), - cv::INTER_LINEAR, - cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); + cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); } } // namespace vortex::image_filtering diff --git a/image-filtering/src/ros/image_filtering_ros.cpp b/image-filtering/src/ros/image_filtering_ros.cpp index 8cb1177..82fe4e3 100644 --- a/image-filtering/src/ros/image_filtering_ros.cpp +++ b/image-filtering/src/ros/image_filtering_ros.cpp @@ -157,14 +157,14 @@ void ImageFilteringNode::set_filter_params() { params.inpaint_radius = declare_and_get( "filter_params.remove_grid.inpaint_radius"); - params.rotation = declare_and_get( - "filter_params.remove_grid.rotation"); + params.rotation = + declare_and_get("filter_params.remove_grid.rotation"); - params.width = declare_and_get( - "filter_params.remove_grid.width"); + params.width = + declare_and_get("filter_params.remove_grid.width"); - params.height = declare_and_get( - "filter_params.remove_grid.height"); + params.height = + declare_and_get("filter_params.remove_grid.height"); filter_ptr_ = std::make_unique(params); break; From ed1e516f5ab56a9e6770a372b109efede38f0ed5 Mon Sep 17 00:00:00 2001 From: AntTra Date: Mon, 2 Mar 2026 17:32:59 +0100 Subject: [PATCH 06/10] fixed the rotation of the image replaced the binary thresholding with apply_fixed_threshold to avoid duplicating code --- .../lib/filters/remove_grid.hpp | 42 +++++++++++++------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp index e724153..2690765 100644 --- a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp +++ b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp @@ -16,7 +16,7 @@ namespace vortex::image_filtering { struct RemoveGridParams { double threshold_green; - double threshold_binary; + int threshold_binary; double inpaint_radius; int rotation; int height; @@ -63,6 +63,18 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, // ---------------------------- int crop_w = std::min(params_.width, rotated.cols); int crop_h = std::min(params_.height, rotated.rows); + if (crop_w != params_.width || crop_h != params_.height) { + spdlog::warn( + "RemoveGrid: requested crop size (width={}, height={}) exceeds " + "rotated image size (width={}, height={}; clamping to ) " + "(width={}, height={})", + params_.width, + params_.height, + rotated.cols, + rotated.rows, + crop_w, + crop_h); + } int x = (rotated.cols - crop_w) / 2; int y = (rotated.rows - crop_h) / 2; @@ -105,26 +117,30 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, // ---------------------------- // Binary threshold // ---------------------------- - cv::Mat gray, thresh; - cv::cvtColor(inpainted, gray, cv::COLOR_BGR2GRAY); - - cv::threshold(gray, thresh, params_.threshold_binary, 255, - cv::THRESH_BINARY); - - cv::Mat thresh_bgr; + cv::Mat thresh, thresh_bgr; + apply_fixed_threshold(inpainted, thresh, this->params_.threshold_binary, false); cv::cvtColor(thresh, thresh_bgr, cv::COLOR_GRAY2BGR); - cv::Mat rotated_out = rotated.clone(); - thresh_bgr.copyTo(rotated_out(roi)); - // ---------------------------- // Undo rotation // ---------------------------- + cv::Mat roi_thresh_bgr = cv::Mat::zeros(original.size(), original.type()); + thresh_bgr.copyTo(roi_thresh_bgr(roi)); + + cv::Mat roi_mask = cv::Mat::zeros(original.size(), CV_8U); + roi_mask(roi).setTo(255); + cv::Mat inv_rot; cv::invertAffineTransform(rotation_matrix, inv_rot); - cv::warpAffine(rotated_out, filtered, inv_rot, original.size(), - cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); + cv::Mat rotated_back, rotated_mask; + cv::warpAffine(roi_thresh_bgr, rotated_back, inv_rot, original.size(), + cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); + cv::warpAffine(roi_mask, rotated_mask, inv_rot, original.size(), + cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0)); + + filtered = original.clone(); + rotated_back.copyTo(filtered, rotated_mask); } } // namespace vortex::image_filtering From c76c93d3bf09380df3b63c370b844d3d1dc156cf Mon Sep 17 00:00:00 2001 From: AntTra Date: Mon, 9 Mar 2026 17:35:55 +0100 Subject: [PATCH 07/10] optimizing the filter (still not finished) --- .../lib/filters/remove_grid.hpp | 91 ++++++++++--------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp index 2690765..2e861af 100644 --- a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp +++ b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp @@ -27,8 +27,7 @@ class RemoveGrid : public Filter { public: explicit RemoveGrid(RemoveGridParams params) : params_(params) {} - void apply_filter(const cv::Mat& original, - cv::Mat& filtered) const override; + void apply_filter(const cv::Mat& original, cv::Mat& filtered) const override; private: RemoveGridParams params_; @@ -48,39 +47,40 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, } // ---------------------------- - // Rotation + // Rotate directly into cropped output // ---------------------------- - cv::Point2f center(original.cols * 0.5f, original.rows * 0.5f); - cv::Mat rotation_matrix = - cv::getRotationMatrix2D(center, params_.rotation, 1.0); + int crop_w = std::min(params_.width, original.cols); + int crop_h = std::min(params_.height, original.rows); - cv::Mat rotated; - cv::warpAffine(original, rotated, rotation_matrix, original.size(), - cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); - - // ---------------------------- - // Center crop - // ---------------------------- - int crop_w = std::min(params_.width, rotated.cols); - int crop_h = std::min(params_.height, rotated.rows); if (crop_w != params_.width || crop_h != params_.height) { spdlog::warn( "RemoveGrid: requested crop size (width={}, height={}) exceeds " - "rotated image size (width={}, height={}; clamping to ) " + "original image size (width={}, height={}); clamping to " "(width={}, height={})", params_.width, params_.height, - rotated.cols, - rotated.rows, + original.cols, + original.rows, crop_w, crop_h); } - int x = (rotated.cols - crop_w) / 2; - int y = (rotated.rows - crop_h) / 2; + const cv::Point2f center_src(original.cols * 0.5f, original.rows * 0.5f); // center of source image + const cv::Point2f center_dst(crop_w * 0.5f, crop_h * 0.5f); // center of destination image + + cv::Mat M = cv::getRotationMatrix2D(center_src, params_.rotation, 1.0); // affine matrix + // Ensure type for at + if (M.type() != CV_64F) { + M.convertTo(M, CV_64F); + } + + // Shift translation so original center maps to cropped center + M.at(0, 2) += (center_dst.x - center_src.x); + M.at(1, 2) += (center_dst.y - center_src.y); - cv::Rect roi(x, y, crop_w, crop_h); - cv::Mat cropped = rotated(roi); + cv::Mat cropped; + cv::warpAffine(original, cropped, M, cv::Size(crop_w, crop_h), + cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); // ---------------------------- // Extract green grid mask @@ -97,7 +97,7 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cv::Mat grid_mask = (ch[1] > params_.threshold_green); - cv::Mat kernel = cv::Mat::ones(3, 3, CV_8U); + static const cv::Mat kernel = cv::Mat::ones(3, 3, CV_8U); cv::Mat dilated; cv::dilate(grid_mask, dilated, kernel); @@ -107,6 +107,11 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, dilated.col(0).setTo(0); dilated.col(dilated.cols - 1).setTo(0); + if (cv::countNonZero(dilated) == 0) { + original.copyTo(filtered); // or original.copyTo(filtered); + return; + } + // ---------------------------- // Inpaint grid // ---------------------------- @@ -115,34 +120,36 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cv::INPAINT_TELEA); // ---------------------------- - // Binary threshold + // Binary threshold (on cropped ROI) // ---------------------------- - cv::Mat thresh, thresh_bgr; - apply_fixed_threshold(inpainted, thresh, this->params_.threshold_binary, false); - cv::cvtColor(thresh, thresh_bgr, cv::COLOR_GRAY2BGR); + cv::Mat thresh_gray; + apply_fixed_threshold(inpainted, thresh_gray, params_.threshold_binary, false); + + cv::Mat thresh_bgr; + cv::cvtColor(thresh_gray, thresh_bgr, cv::COLOR_GRAY2BGR); // ---------------------------- - // Undo rotation + // Undo rotation & merge (using M) // ---------------------------- - cv::Mat roi_thresh_bgr = cv::Mat::zeros(original.size(), original.type()); - thresh_bgr.copyTo(roi_thresh_bgr(roi)); - - cv::Mat roi_mask = cv::Mat::zeros(original.size(), CV_8U); - roi_mask(roi).setTo(255); + cv::Mat invM; + cv::invertAffineTransform(M, invM); - cv::Mat inv_rot; - cv::invertAffineTransform(rotation_matrix, inv_rot); + // Warp ROI result back into full-size overlay + cv::Mat overlay_full; + cv::warpAffine(thresh_bgr, overlay_full, invM, original.size(), + cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); - cv::Mat rotated_back, rotated_mask; - cv::warpAffine(roi_thresh_bgr, rotated_back, inv_rot, original.size(), - cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); - cv::warpAffine(roi_mask, rotated_mask, inv_rot, original.size(), - cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0)); + // Warp a mask the same way (so black pixels are copied too) + cv::Mat local_mask(thresh_bgr.rows, thresh_bgr.cols, CV_8U, cv::Scalar(255)); + cv::Mat mask_full; + cv::warpAffine(local_mask, mask_full, invM, original.size(), + cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0)); + // Merge into the original image filtered = original.clone(); - rotated_back.copyTo(filtered, rotated_mask); + overlay_full.copyTo(filtered, mask_full); } } // namespace vortex::image_filtering -#endif // IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ +#endif // IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ \ No newline at end of file From 556b003aac7e4bdc39124ab6407281a1ecc8b680 Mon Sep 17 00:00:00 2001 From: sophia-mina Date: Thu, 26 Mar 2026 11:54:35 +0100 Subject: [PATCH 08/10] fixed comments --- .../config/image_filtering_params.yaml | 2 +- .../lib/filters/remove_grid.hpp | 33 +++++++------------ 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/image-filtering/config/image_filtering_params.yaml b/image-filtering/config/image_filtering_params.yaml index e506ed9..6e0ac28 100644 --- a/image-filtering/config/image_filtering_params.yaml +++ b/image-filtering/config/image_filtering_params.yaml @@ -32,7 +32,7 @@ dilation_size: 2 remove_grid: threshold_green: 0.5 - threshold_binary: 30.0 + threshold_binary: 30 inpaint_radius: 1.0 rotation: 0 height: 400 diff --git a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp index 2e861af..23dd5c7 100644 --- a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp +++ b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp @@ -9,9 +9,9 @@ #include "abstract_filter_class.hpp" -///////////////////////////// -// Remove grid filter -///////////////////////////// + + + namespace vortex::image_filtering { struct RemoveGridParams { @@ -46,16 +46,14 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, return; } - // ---------------------------- // Rotate directly into cropped output - // ---------------------------- int crop_w = std::min(params_.width, original.cols); int crop_h = std::min(params_.height, original.rows); if (crop_w != params_.width || crop_h != params_.height) { spdlog::warn( - "RemoveGrid: requested crop size (width={}, height={}) exceeds " - "original image size (width={}, height={}); clamping to " + "RemoveGrid: requested crop size (width={}, height={}) does not fit " + "within original image size (width={}, height={}); clamping to " "(width={}, height={})", params_.width, params_.height, @@ -82,21 +80,19 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cv::warpAffine(original, cropped, M, cv::Size(crop_w, crop_h), cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); - // ---------------------------- // Extract green grid mask - // ---------------------------- cv::Mat cropped_f; cropped.convertTo(cropped_f, CV_32F, 1.0 / 255.0); - std::vector ch(3); + std::vector ch(3); // make a vector for BGR cv::split(cropped_f, ch); // BGR - cv::Mat sum = ch[0] + ch[1] + ch[2] + 1e-6f; + cv::Mat sum = ch[0] + ch[1] + ch[2] + 1e-6f; // avoid division by zero for (auto& c : ch) - c /= sum; - - cv::Mat grid_mask = (ch[1] > params_.threshold_green); + c /= sum; // normalized color values + // mask the green channel + cv::Mat grid_mask = (ch[1] > params_.threshold_green); static const cv::Mat kernel = cv::Mat::ones(3, 3, CV_8U); cv::Mat dilated; cv::dilate(grid_mask, dilated, kernel); @@ -108,29 +104,24 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, dilated.col(dilated.cols - 1).setTo(0); if (cv::countNonZero(dilated) == 0) { - original.copyTo(filtered); // or original.copyTo(filtered); + // If no grid detected, leave image unchanged + original.copyTo(filtered); return; } - // ---------------------------- // Inpaint grid - // ---------------------------- cv::Mat inpainted; cv::inpaint(cropped, dilated, inpainted, params_.inpaint_radius, cv::INPAINT_TELEA); - // ---------------------------- // Binary threshold (on cropped ROI) - // ---------------------------- cv::Mat thresh_gray; apply_fixed_threshold(inpainted, thresh_gray, params_.threshold_binary, false); cv::Mat thresh_bgr; cv::cvtColor(thresh_gray, thresh_bgr, cv::COLOR_GRAY2BGR); - // ---------------------------- // Undo rotation & merge (using M) - // ---------------------------- cv::Mat invM; cv::invertAffineTransform(M, invM); From 93a3bdba4d839769c07126ebb7ce6eaf8fb33846 Mon Sep 17 00:00:00 2001 From: sophia-mina Date: Thu, 26 Mar 2026 11:56:21 +0100 Subject: [PATCH 09/10] chore: apply pre-commit changes --- .../lib/filters/remove_grid.hpp | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp index 23dd5c7..3bf1ba4 100644 --- a/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp +++ b/image-filtering/include/image_filtering/lib/filters/remove_grid.hpp @@ -9,9 +9,6 @@ #include "abstract_filter_class.hpp" - - - namespace vortex::image_filtering { struct RemoveGridParams { @@ -27,7 +24,8 @@ class RemoveGrid : public Filter { public: explicit RemoveGrid(RemoveGridParams params) : params_(params) {} - void apply_filter(const cv::Mat& original, cv::Mat& filtered) const override; + void apply_filter(const cv::Mat& original, + cv::Mat& filtered) const override; private: RemoveGridParams params_; @@ -46,27 +44,27 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, return; } - // Rotate directly into cropped output + // Rotate directly into cropped output int crop_w = std::min(params_.width, original.cols); int crop_h = std::min(params_.height, original.rows); if (crop_w != params_.width || crop_h != params_.height) { spdlog::warn( - "RemoveGrid: requested crop size (width={}, height={}) does not fit " + "RemoveGrid: requested crop size (width={}, height={}) does not " + "fit " "within original image size (width={}, height={}); clamping to " "(width={}, height={})", - params_.width, - params_.height, - original.cols, - original.rows, - crop_w, + params_.width, params_.height, original.cols, original.rows, crop_w, crop_h); } - const cv::Point2f center_src(original.cols * 0.5f, original.rows * 0.5f); // center of source image - const cv::Point2f center_dst(crop_w * 0.5f, crop_h * 0.5f); // center of destination image + const cv::Point2f center_src( + original.cols * 0.5f, original.rows * 0.5f); // center of source image + const cv::Point2f center_dst(crop_w * 0.5f, + crop_h * 0.5f); // center of destination image - cv::Mat M = cv::getRotationMatrix2D(center_src, params_.rotation, 1.0); // affine matrix + cv::Mat M = cv::getRotationMatrix2D(center_src, params_.rotation, + 1.0); // affine matrix // Ensure type for at if (M.type() != CV_64F) { M.convertTo(M, CV_64F); @@ -84,15 +82,15 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cv::Mat cropped_f; cropped.convertTo(cropped_f, CV_32F, 1.0 / 255.0); - std::vector ch(3); // make a vector for BGR - cv::split(cropped_f, ch); // BGR + std::vector ch(3); // make a vector for BGR + cv::split(cropped_f, ch); // BGR - cv::Mat sum = ch[0] + ch[1] + ch[2] + 1e-6f; // avoid division by zero + cv::Mat sum = ch[0] + ch[1] + ch[2] + 1e-6f; // avoid division by zero for (auto& c : ch) - c /= sum; // normalized color values + c /= sum; // normalized color values // mask the green channel - cv::Mat grid_mask = (ch[1] > params_.threshold_green); + cv::Mat grid_mask = (ch[1] > params_.threshold_green); static const cv::Mat kernel = cv::Mat::ones(3, 3, CV_8U); cv::Mat dilated; cv::dilate(grid_mask, dilated, kernel); @@ -116,7 +114,8 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, // Binary threshold (on cropped ROI) cv::Mat thresh_gray; - apply_fixed_threshold(inpainted, thresh_gray, params_.threshold_binary, false); + apply_fixed_threshold(inpainted, thresh_gray, params_.threshold_binary, + false); cv::Mat thresh_bgr; cv::cvtColor(thresh_gray, thresh_bgr, cv::COLOR_GRAY2BGR); @@ -131,7 +130,8 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0)); // Warp a mask the same way (so black pixels are copied too) - cv::Mat local_mask(thresh_bgr.rows, thresh_bgr.cols, CV_8U, cv::Scalar(255)); + cv::Mat local_mask(thresh_bgr.rows, thresh_bgr.cols, CV_8U, + cv::Scalar(255)); cv::Mat mask_full; cv::warpAffine(local_mask, mask_full, invM, original.size(), cv::INTER_NEAREST, cv::BORDER_CONSTANT, cv::Scalar(0)); @@ -143,4 +143,4 @@ inline void RemoveGrid::apply_filter(const cv::Mat& original, } // namespace vortex::image_filtering -#endif // IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ \ No newline at end of file +#endif // IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ From bf39031618ec90b3ab4da55e6d43f9378c771192 Mon Sep 17 00:00:00 2001 From: sophia-mina Date: Thu, 2 Apr 2026 16:08:58 +0200 Subject: [PATCH 10/10] changed threshold_binary from double to int --- image-filtering/src/ros/image_filtering_ros.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/image-filtering/src/ros/image_filtering_ros.cpp b/image-filtering/src/ros/image_filtering_ros.cpp index 9c91d27..46fa286 100644 --- a/image-filtering/src/ros/image_filtering_ros.cpp +++ b/image-filtering/src/ros/image_filtering_ros.cpp @@ -171,7 +171,7 @@ void ImageFilteringNode::set_filter_params() { params.threshold_green = declare_and_get( "filter_params.remove_grid.threshold_green"); - params.threshold_binary = declare_and_get( + params.threshold_binary = declare_and_get( "filter_params.remove_grid.threshold_binary"); params.inpaint_radius = declare_and_get(