Skip to content

Commit 5e6a75a

Browse files
committed
initial commit
1 parent b333287 commit 5e6a75a

File tree

12 files changed

+758
-0
lines changed

12 files changed

+758
-0
lines changed

README.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Image Filtering Node
2+
3+
The `image_filtering_node` is a ROS 2 node developed in the `vortex::image_filters` namespace. It is designed to subscribe to image topics, apply various image filters using OpenCV, and publish the filtered images back to ROS.
4+
5+
## Features
6+
7+
- **Multiple Filters**: Supports sharpening, unsharpening, eroding, dilating, white balancing, and a custom "ebus" filter.
8+
- **Dynamic Reconfiguration**: Allows for runtime changes to filter parameters and subscribed image topics via ROS 2 parameters.
9+
- **Parameter-Driven Configuration**: Configures filter types and specific attributes through ROS 2 parameters.
10+
11+
## Configuration
12+
13+
Configure the node using ROS 2 parameters:
14+
15+
- **`image_topic`**: Topic from which the node will subscribe to image messages.
16+
- **`filter_params.filter_type`**: Specifies the filter to apply. Current options include:
17+
- `nofilter`
18+
- `sharpening`
19+
- `unsharpening`
20+
- `eroding`
21+
- `dilating`
22+
- `white_balancing`
23+
- `ebus`
24+
- **Other filter-specific parameters** such as `blur_size`, `size`, `contrast_percentage`, can be configured through parameter `filter_params`.{filter_name}.{param_name}
25+
26+
Parameters can be set through a YAML file or dynamically adjusted at runtime.
27+
28+
## Additional filters
29+
30+
## Implementing New Filters
31+
32+
To extend the functionality of the `image_filtering_node` by adding new filters, follow these steps to ensure compatibility and integration with the existing codebase:
33+
34+
### Step 1: Define Filter Parameters
35+
36+
Each filter should have its own set of parameters encapsulated in a structure. Define this structure within the `vortex::image_filters` namespace.
37+
38+
```cpp
39+
struct YourFilterParams {
40+
// Add necessary parameters here
41+
int example_param;
42+
};
43+
```
44+
45+
### Step 2: Add to FilterParams Structure
46+
47+
Integrate your new filter parameters structure into the existing `FilterParams` structure. This allows the `apply_filter` function to access the parameters specific to your filter.
48+
49+
```cpp
50+
struct FilterParams {
51+
std::string filter_type;
52+
UnsharpeningFilterParams unsharpening;
53+
ErodingFilterParams eroding;
54+
DilatingFilterParams dilating;
55+
WhiteBalancingFilterParams white_balancing;
56+
EbusFilterParams ebus;
57+
YourFilterParams your_filter; // Add your filter params here
58+
};
59+
```
60+
61+
### Step 3: Create the Filter Function
62+
63+
Implement your filter function. This function should take the `cv::Mat` objects for the input and output images and a `const FilterParams&` which includes your specific filter parameters. Make sure to use your parameter structure within this function.
64+
65+
```cpp
66+
void your_filter_function(const cv::Mat &original, cv::Mat &filtered, const FilterParams& filter_params) {
67+
// Access your filter-specific parameters like this:
68+
int example_param = filter_params.your_filter.example_param;
69+
70+
// Implement your filtering logic here
71+
}
72+
```
73+
74+
### Step 4: Register the Filter Function
75+
76+
Add an entry to the `filter_functions` map for your new filter. This step is crucial as it links the filter name (as a string) to the corresponding filter function pointer.
77+
78+
```cpp
79+
std::map<std::string, FilterFunction> filter_functions = {
80+
{"no_filter", no_filter},
81+
{"sharpening", sharpening_filter},
82+
{"unsharpening", unsharpening_filter},
83+
{"eroding", eroding_filter},
84+
{"dilating", dilating_filter},
85+
{"white_balancing", white_balance_filter},
86+
{"ebus", ebus_filter},
87+
{"your_filter", your_filter_function} // Add your filter here
88+
};
89+
```
90+
91+
### Step 5: Declare and Assign Parameters
92+
93+
Declare the new filter parameters in the ROS 2 node constructor and assign these parameters to the `FilterParams` structure within the `set_filter_params` function.
94+
95+
#### In the Node Constructor
96+
97+
In the constructor of your ROS 2 node, declare each of the new filter parameters using the `declare_parameter` function. This sets the default values and prepares the node to accept these parameters at runtime through command line or a YAML configuration file.
98+
99+
```cpp
100+
ImageFilteringNode::ImageFilteringNode() : Node("image_filtering_node")
101+
{
102+
this->declare_parameter<std::string>("filter_params.your_filter.example_param", "default_value");
103+
...
104+
// Other parameters declarations
105+
}
106+
```
107+
108+
109+
#### In the set_filter_params Function
110+
111+
In the set_filter_params function, retrieve and assign the parameters to the corresponding fields in the FilterParams structure. Ensure to handle cases where the parameter might not be set or provided.
112+
113+
```cpp
114+
115+
void ImageFilteringNode::set_filter_params(){
116+
FilterParams params = filter_params_; // assuming filter_params_ is already defined in your class
117+
118+
params.your_filter.example_param = this->get_parameter("filter_params.your_filter.example_param").as_string();
119+
...
120+
// Retrieve other parameters and handle cases where parameters might not be provided
121+
filter_params_ = params; // Update the filter parameters structure
122+
RCLCPP_INFO(this->get_logger(), "Filter parameters updated for your_filter.");
123+
}
124+
```

image-filtering/CMakeLists.txt

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
cmake_minimum_required(VERSION 3.8)
2+
project(image_filtering)
3+
4+
# === C++ standard ===
5+
if(NOT CMAKE_CXX_STANDARD)
6+
set(CMAKE_CXX_STANDARD 20)
7+
endif()
8+
9+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
10+
add_compile_options(-Wall -Wextra -Wpedantic)
11+
endif()
12+
13+
# find dependencies
14+
find_package(ament_cmake REQUIRED)
15+
find_package(rclcpp REQUIRED)
16+
find_package(rclcpp_components REQUIRED)
17+
find_package(OpenCV 4.5.4 REQUIRED)
18+
find_package(sensor_msgs REQUIRED)
19+
find_package(cv_bridge REQUIRED)
20+
21+
include_directories(include)
22+
23+
set(LIB_NAME "${PROJECT_NAME}_component")
24+
25+
add_library(${LIB_NAME} SHARED
26+
src/image_processing.cpp
27+
src/image_filtering_ros.cpp
28+
)
29+
30+
target_link_libraries(${LIB_NAME} PUBLIC
31+
${OpenCV_LIBS}
32+
)
33+
34+
target_include_directories(${LIB_NAME} PUBLIC
35+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
36+
$<INSTALL_INTERFACE:include>
37+
${OpenCV_INCLUDE_DIRS}
38+
)
39+
40+
ament_target_dependencies(${LIB_NAME} PUBLIC
41+
rclcpp
42+
rclcpp_components
43+
cv_bridge
44+
sensor_msgs
45+
)
46+
47+
rclcpp_components_register_node(
48+
${LIB_NAME}
49+
PLUGIN "vortex::image_processing::ImageFilteringNode"
50+
EXECUTABLE ${PROJECT_NAME}_node
51+
)
52+
53+
# Export the target for other packages to use
54+
ament_export_targets(export_${LIB_NAME})
55+
56+
install(TARGETS ${LIB_NAME}
57+
EXPORT export_${LIB_NAME}
58+
ARCHIVE DESTINATION lib
59+
LIBRARY DESTINATION lib
60+
RUNTIME DESTINATION bin
61+
)
62+
63+
install(
64+
DIRECTORY include/
65+
DESTINATION include
66+
)
67+
68+
install(DIRECTORY
69+
launch
70+
params
71+
DESTINATION share/${PROJECT_NAME}/
72+
)
73+
74+
if(BUILD_TESTING)
75+
add_subdirectory(test)
76+
endif()
77+
78+
ament_package()
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#ifndef IMAGE_FILTERING_ROS_HPP
2+
#define IMAGE_FILTERING_ROS_HPP
3+
4+
#include <rclcpp/rclcpp.hpp>
5+
#include <cv_bridge/cv_bridge.h>
6+
#include <sensor_msgs/msg/image.hpp>
7+
#include <rclcpp/qos.hpp>
8+
#include "image_processing.hpp"
9+
#include <rclcpp/parameter_event_handler.hpp>
10+
11+
12+
13+
namespace vortex::image_processing
14+
{
15+
class ImageFilteringNode : public rclcpp::Node{
16+
17+
18+
public:
19+
explicit ImageFilteringNode(const rclcpp::NodeOptions & options);
20+
~ImageFilteringNode(){};
21+
22+
23+
private:
24+
/**
25+
* @brief Subscribes to image topic
26+
*/
27+
rclcpp::Subscription<sensor_msgs::msg::Image>::SharedPtr image_sub_;
28+
/**
29+
* @brief Publishes the filtered image
30+
*/
31+
rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr image_pub_;
32+
33+
/**
34+
* @brief Check and subscribe to image if not yet subscribed. Allows for dynaminc reconfiguration of image topic.
35+
* If a new topic is set, the old subscription is cancelled and a new one is bound to the callback function.
36+
*
37+
*/
38+
void check_and_subscribe_to_image_topic();
39+
40+
/**
41+
* @brief Set the filter parameters for the FilterParams struct.
42+
*
43+
*/
44+
void set_filter_params();
45+
46+
/**
47+
* @brief Initialize the parameter handler and a parameter event callback.
48+
*
49+
*/
50+
void initialize_parameter_handler();
51+
/**
52+
* @brief Callback function for parameter events.
53+
* Checks for parameter changes that matches the nodes' namespace and invokes the relevant initializer functions to update member variables.
54+
*
55+
* @param event The parameter event.
56+
*/
57+
void on_parameter_event(const rcl_interfaces::msg::ParameterEvent &event);
58+
59+
/**
60+
* @brief Manages parameter events for the node.
61+
*
62+
* This handle is used to set up a mechanism to listen for and react to changes in parameters.
63+
* Parameters can be used to configure the node's operational behavior dynamically,
64+
* allowing adjustments without altering the code. The `param_handler_` is responsible for
65+
* registering callbacks that are triggered on parameter changes, providing a centralized
66+
* management system within the node for such events.
67+
*/
68+
std::shared_ptr<rclcpp::ParameterEventHandler> param_handler_;
69+
70+
/**
71+
* @brief Handle to the registration of the parameter event callback.
72+
*
73+
* Represents a token or reference to the specific callback registration made with
74+
* the parameter event handler (`param_handler_`). This handle allows for management
75+
* of the lifecycle of the callback, such as removing the callback if it's no longer needed.
76+
* It ensures that the node can respond to parameter changes with the registered callback
77+
* in an efficient and controlled manner.
78+
*/
79+
rclcpp::ParameterEventCallbackHandle::SharedPtr param_cb_handle_;
80+
81+
/**
82+
* @brief Callback function for image topic
83+
*
84+
* @param msg The image message
85+
*/
86+
void image_callback(const sensor_msgs::msg::Image::SharedPtr msg);
87+
88+
/**
89+
* @brief The image topic to subscribe to
90+
*
91+
*/
92+
std::string image_topic_;
93+
94+
/**
95+
* @brief The filter parameters
96+
*
97+
*/
98+
FilterParams filter_params_;
99+
100+
/**
101+
* @brief filter to apply
102+
*
103+
*/
104+
std::string filter_;
105+
106+
};
107+
108+
} // namespace vortex::image_processing
109+
110+
111+
#endif // IMAGE_FILTERING_ROS_HPP

0 commit comments

Comments
 (0)