-
Notifications
You must be signed in to change notification settings - Fork 0
Aruco grid filter #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5af770a
dc8e520
9cc2cf3
04ce549
bb11065
abb087f
ed1e516
5f6fa43
c76c93d
556b003
93a3bdb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,12 @@ | ||
| /**: | ||
| 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: "temporal_noise" | ||
| filter_type: "remove_grid" | ||
| flip: | ||
| flip_code: 1 | ||
| unsharpening: | ||
|
|
@@ -30,6 +30,13 @@ | |
| otsu_segmentation: true | ||
| erosion_size: 2 | ||
| dilation_size: 2 | ||
| remove_grid: | ||
| threshold_green: 0.5 | ||
| threshold_binary: 30 | ||
| inpaint_radius: 1.0 | ||
| rotation: 0 | ||
| height: 400 | ||
| width: 400 | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add units |
||
| 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 | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not a problem but you use a lot of redundant variables in this file, a lot of them are not necessary and you would shorten the code |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| #ifndef IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ | ||
| #define IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ | ||
|
|
||
| #include <spdlog/spdlog.h> | ||
| #include <algorithm> | ||
| #include <opencv2/imgproc.hpp> | ||
| #include <opencv2/photo.hpp> | ||
| #include <vector> | ||
|
|
||
| #include "abstract_filter_class.hpp" | ||
|
|
||
| namespace vortex::image_filtering { | ||
|
|
||
| struct RemoveGridParams { | ||
| double threshold_green; | ||
| int threshold_binary; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. threshold_binary was double in config. why dis int |
||
| double inpaint_radius; | ||
| int rotation; | ||
| int height; | ||
| int width; | ||
| }; | ||
|
|
||
| class RemoveGrid : public Filter { | ||
| public: | ||
| explicit RemoveGrid(RemoveGridParams params) : params_(params) {} | ||
|
|
||
| void apply_filter(const cv::Mat& original, | ||
| cv::Mat& filtered) const override; | ||
|
|
||
| private: | ||
| RemoveGridParams params_; | ||
|
||
| }; | ||
|
|
||
| inline void RemoveGrid::apply_filter(const cv::Mat& original, | ||
| cv::Mat& filtered) const { | ||
| if (original.empty()) { | ||
| spdlog::error("RemoveGrid: input image is empty"); | ||
| return; | ||
| } | ||
|
Comment on lines
+36
to
+39
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add unit test for this |
||
|
|
||
| if (original.type() != CV_8UC3) { | ||
| spdlog::error("RemoveGrid: expected CV_8UC3 image"); | ||
| original.copyTo(filtered); | ||
| return; | ||
|
Comment on lines
+36
to
+44
|
||
| } | ||
|
|
||
| // 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) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| spdlog::warn( | ||
| "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, | ||
| 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 | ||
|
|
||
| cv::Mat M = cv::getRotationMatrix2D(center_src, params_.rotation, | ||
| 1.0); // affine matrix | ||
| // Ensure type for at<double> | ||
| if (M.type() != CV_64F) { | ||
| M.convertTo(M, CV_64F); | ||
| } | ||
|
|
||
| // Shift translation so original center maps to cropped center | ||
| M.at<double>(0, 2) += (center_dst.x - center_src.x); | ||
| M.at<double>(1, 2) += (center_dst.y - center_src.y); | ||
|
|
||
| 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 | ||
| cv::Mat cropped_f; | ||
| cropped.convertTo(cropped_f, CV_32F, 1.0 / 255.0); | ||
|
|
||
| std::vector<cv::Mat> 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 | ||
| for (auto& c : ch) | ||
| 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); | ||
|
|
||
| // prevent border leak | ||
| dilated.row(0).setTo(0); | ||
| dilated.row(dilated.rows - 1).setTo(0); | ||
| dilated.col(0).setTo(0); | ||
| dilated.col(dilated.cols - 1).setTo(0); | ||
|
|
||
| if (cv::countNonZero(dilated) == 0) { | ||
| // 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); | ||
|
Comment on lines
+119
to
+121
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this necessary for the ArUco detector to work? This should be optional (converting is a slow operation, and this is mainly for debugging) |
||
|
|
||
| // Undo rotation & merge (using M) | ||
| cv::Mat invM; | ||
| cv::invertAffineTransform(M, invM); | ||
|
|
||
| // 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)); | ||
|
|
||
| // 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(); | ||
| overlay_full.copyTo(filtered, mask_full); | ||
| } | ||
|
|
||
| } // namespace vortex::image_filtering | ||
|
|
||
| #endif // IMAGE_FILTERING__LIB__FILTERS__REMOVE_GRID_HPP_ | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default configuration has been changed to use the new 'remove_grid' filter and different topics ('/cam/image_color' instead of '/fls_publisher/display_mono', 'rgb8' encoding instead of 'mono8'). This changes the default behavior of the image filtering node. Consider whether these changes should be committed as defaults, or if they should be reverted to maintain the original default behavior, with the new filter being opt-in via configuration override.