diff --git a/.fish/activate.fish b/.fish/activate.fish
new file mode 100755
index 0000000..aadfcdf
--- /dev/null
+++ b/.fish/activate.fish
@@ -0,0 +1,75 @@
+# Self-contained environment: ros2-pass-2
+# Exported on: Thu Nov 13 06:58:18 PM CET 2025
+# Original environment from: /home/wessel/.config/fish/environments/configs/ros2-pass-2
+
+# ROS2 development environment (requires distrobox)
+# Environment: ros2-pass-2
+
+# First check if running inside distrobox
+if not test -f /run/.containerenv; and test -z "$CONTAINER_ID"
+ echo (set_color red)"This ROS2 environment should only be run inside a distrobox container"(set_color normal)
+ return 1
+end
+
+# Check if a previous initialization has occurred
+if test -n "$__ENV_INITIALIZED"
+ echo (set_color yellow)"Environment already initialized"(set_color normal)
+ return 0
+end
+
+# Mark as initialized (only after distrobox check passes)
+set -gx __ENV_INITIALIZED "1"
+set -gx CURRENT_ENV "ros2-pass-2"
+# Source ROS2 setup files using bass
+if type -q bass
+ bass source /opt/ros/jazzy/setup.bash
+ if test -f ./install/setup.bash
+ bass source ./install/setup.bash
+ end
+else
+ echo (set_color red)"Error: bass is required for ROS2 environment. Install with: fisher install edc/bass"(set_color normal)
+ return 1
+end
+
+# Set environment variable for the prompt prefix
+set -gx ROS2_ACTIVE 1
+
+# Save the original prompt function if it exists
+# Only save if we don't already have a backup or if current prompt is not from an environment
+if not functions -q __env_orig_prompt
+ if functions -q fish_prompt
+ functions -c fish_prompt __env_orig_prompt
+ else
+ function __env_orig_prompt
+ echo -n (whoami)'@'(prompt_hostname)' '(set_color $fish_color_cwd)(prompt_pwd)(set_color normal)'> '
+ end
+ end
+else
+ # If we already have a backup, we're switching environments
+ # No need to create a new backup
+end
+
+# Define new prompt with ROS2 prefix
+function fish_prompt
+ echo -n (set_color magenta)'(ros2-pass-2)'(set_color normal)' '
+ __env_orig_prompt
+end
+
+# ROS2 aliases and functions
+alias cb="colcon build"
+alias cbe="colcon build && env deactivate && cd ."
+alias cbs="colcon build --symlink-install"
+alias cbt="colcon build --packages-select"
+alias ct="colcon test"
+alias ctr="colcon test-result"
+
+echo (set_color green)"Activated ROS2 environment: ros2-pass-2"(set_color normal)
+
+# Custom deactivation function
+function __env_custom_deactivate
+ # Remove ROS2-specific variables and aliases
+ set -e ROS2_ACTIVE
+ functions -e cb cbs cbt ct ctr 2>/dev/null
+
+ echo (set_color blue)"ROS2 environment cleanup completed"(set_color normal)
+end
diff --git a/.fish/readme.norg b/.fish/readme.norg
new file mode 100644
index 0000000..059556d
--- /dev/null
+++ b/.fish/readme.norg
@@ -0,0 +1,60 @@
+* Exported Fish Environment: ros2-pass-2
+This directory contains a self-contained fish environment.
+
+** Files Structure
+ @code
+ .fish/
+ |-- activate.fish
+ |-- readme.norg
+ |-- bin/
+ @end
+
+** Usage
+
+*** Automatic Activation (Recommended)
+ The environment will automatically activate when you `cd` into this directory
+ if your Fish shell is configured with the auto-activation script.
+
+ @code fish
+ function check_and_source_activate
+ if test -f (pwd)/.fish/activate.fish
+ source (pwd)/.fish/activate.fish
+ elif test -f (pwd)/activate.fish
+ source (pwd)/activate.fish
+ end
+ end
+
+ function cd
+ builtin cd $argv && check_and_source_activate
+ end
+ @end
+
+*** Manual Activation
+ To manually activate the environment, run from the project root:
+ @code bash
+ source ./.fish/activate.fish
+ @end
+
+*** Deactivation
+ To deactivate the environment, run:
+ @code bash
+ env deactivate
+ @end
+
+ Or simply `cd` to a different directory if using auto-activation.
+
+** What This Environment Provides
+ - Prompt showing the environment name
+ - Environment-specific aliases and functions
+ - Custom environment variables
+ - Automatic cleanup when deactivated
+
+** Requirements
+ - Fish shell
+ - `bass` plugin (`fisher install edc/bass`) for compatibility with bash scripts
+
+** Sharing
+ This environment is completely self-contained. You can:
+ - Copy this directory to another machine
+ - Share it with others
+ - Version control it with your project (add .fish/ to your repo)
\ No newline at end of file
diff --git a/Assignement - image reader.pdf b/Assignement - image reader.pdf
new file mode 100644
index 0000000..24b9a62
Binary files /dev/null and b/Assignement - image reader.pdf differ
diff --git a/src/image_reader_wessel_pkg/CMakeLists.txt b/src/image_reader_wessel_pkg/CMakeLists.txt
new file mode 100644
index 0000000..45ac9ca
--- /dev/null
+++ b/src/image_reader_wessel_pkg/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.8)
+project(image_reader_wessel_pkg)
+
+if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+ add_compile_options(-Wall -Wextra -Wpedantic)
+endif()
+
+# find dependencies
+find_package(ament_cmake REQUIRED)
+find_package(rclcpp REQUIRED)
+find_package(sensor_msgs REQUIRED)
+find_package(cv_bridge REQUIRED)
+find_package(OpenCV REQUIRED)
+
+# Add executable
+add_executable(image_reader_node
+ src/Main.cpp
+ src/nodes/ImageReader.cpp
+)
+
+# Link dependencies
+ament_target_dependencies(image_reader_node
+ rclcpp
+ sensor_msgs
+ cv_bridge
+ OpenCV
+)
+
+# Install targets
+install(TARGETS
+ image_reader_node
+ DESTINATION lib/${PROJECT_NAME}
+)
+
+ament_package()
diff --git a/src/image_reader_wessel_pkg/package.xml b/src/image_reader_wessel_pkg/package.xml
new file mode 100644
index 0000000..bb3a4f9
--- /dev/null
+++ b/src/image_reader_wessel_pkg/package.xml
@@ -0,0 +1,23 @@
+
+
+
+ image_reader_wessel_pkg
+ 0.0.0
+ Image reader package with OpenCV processing
+ wessel
+ TODO: License declaration
+
+ ament_cmake
+
+ rclcpp
+ sensor_msgs
+ cv_bridge
+ libopencv-dev
+
+ ament_lint_auto
+ ament_lint_common
+
+
+ ament_cmake
+
+
diff --git a/src/image_reader_wessel_pkg/src/Main.cpp b/src/image_reader_wessel_pkg/src/Main.cpp
new file mode 100644
index 0000000..0a7394f
--- /dev/null
+++ b/src/image_reader_wessel_pkg/src/Main.cpp
@@ -0,0 +1,15 @@
+#include
+
+#include "rclcpp/rclcpp.hpp"
+#include "nodes/ImageReader.hpp"
+
+int main(int argc,char *argv[]) {
+ rclcpp::init(argc,argv);
+
+ auto node = std::make_shared();
+
+ rclcpp::spin(node);
+ rclcpp::shutdown();
+
+ return 0;
+}
\ No newline at end of file
diff --git a/src/image_reader_wessel_pkg/src/nodes/ImageReader.cpp b/src/image_reader_wessel_pkg/src/nodes/ImageReader.cpp
new file mode 100644
index 0000000..494d4e9
--- /dev/null
+++ b/src/image_reader_wessel_pkg/src/nodes/ImageReader.cpp
@@ -0,0 +1,145 @@
+#include "ImageReader.hpp"
+
+#include
+#include
+
+namespace assignments::two::image_reader {
+
+ImageReaderNode::ImageReaderNode() : Node("image_reader_node") {
+ circle_color_.r = this->declare_parameter("circle_color_r", 255);
+ circle_color_.g = this->declare_parameter("circle_color_g", 0);
+ circle_color_.b = this->declare_parameter("circle_color_b", 0);
+
+ RCLCPP_INFO(this->get_logger(), "circle_color_=(r%d, g%d, b%d)",
+ circle_color_.r, circle_color_.g, circle_color_.b);
+
+ // Subscribe to image topic
+ image_subscriber_ = this->create_subscription(
+ "/image_raw",
+ 10,
+ std::bind(
+ &ImageReaderNode::image_callback,
+ this,
+ std::placeholders::_1
+ )
+ );
+
+ // Publisher for processed image
+ image_publisher_ = this->create_publisher("/image_processed", 10);
+
+ RCLCPP_INFO(this->get_logger(), "initialized");
+}
+
+void ImageReaderNode::image_callback(const sensor_msgs::msg::Image::SharedPtr msg) {
+ RCLCPP_INFO_ONCE(this->get_logger(), "'/image_raw': received image, processing");
+
+ try {
+ // handle both RGB and BGR
+ cv_bridge::CvImagePtr cv_ptr;
+ if (msg->encoding == sensor_msgs::image_encodings::RGB8) {
+ cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::RGB8);
+ cv::cvtColor(cv_ptr->image, cv_ptr->image, cv::COLOR_RGB2BGR);
+ } else {
+ cv_ptr = cv_bridge::toCvCopy(msg, sensor_msgs::image_encodings::BGR8);
+ }
+
+ ImageMetadata img;
+ img.canvas = cv_ptr->image;
+ img.width = img.canvas.cols;
+ img.height = img.canvas.rows;
+
+ process_top_left_quadrant_white(img);
+ process_top_right_quadrant_circle(img);
+ process_bottom_left_quadrant_remove_blue(img);
+ process_bottom_right_quadrant_edge_detection(img);
+
+ draw_division_lines(img);
+
+ cv_ptr->image = img.canvas;
+ image_publisher_->publish(*cv_ptr->toImageMsg());
+ } catch (cv_bridge::Exception& e) {
+ RCLCPP_ERROR(this->get_logger(), "cv_bridge exception: %s", e.what());
+ }
+}
+
+void ImageReaderNode::draw_division_lines(ImageMetadata& img) {
+ int half_width = img.width / 2;
+ int half_height = img.height / 2;
+
+ cv::line(
+ img.canvas,
+ cv::Point(half_width, 0),
+ cv::Point(half_width, img.height),
+ cv::Scalar(255, 255, 255),
+ 2
+ );
+
+ cv::line(
+ img.canvas,
+ cv::Point(0, half_height),
+ cv::Point(img.width, half_height),
+ cv::Scalar(255, 255, 255),
+ 2
+ );
+}
+
+void ImageReaderNode::process_top_left_quadrant_white(ImageMetadata& img) {
+ int half_width = img.width / 2;
+ int half_height = img.height / 2;
+
+ cv::Mat top_left = img.canvas(cv::Rect(0, 0, half_width, half_height));
+ top_left.setTo(cv::Scalar(255, 255, 255));
+}
+
+void ImageReaderNode::process_top_right_quadrant_circle(ImageMetadata& img) {
+ int half_width = img.width / 2;
+ int half_height = img.height / 2;
+
+ cv::Mat top_right = img.canvas(cv::Rect(half_width, 0, img.width - half_width, half_height));
+ int radius = std::min(top_right.cols, top_right.rows) / 4;
+ cv::Point center(top_right.cols / 2, top_right.rows / 2);
+
+ cv::circle(
+ top_right,
+ center,
+ radius,
+ cv::Scalar(circle_color_.b, circle_color_.g, circle_color_.r),
+ -1
+ );
+}
+
+void ImageReaderNode::process_bottom_left_quadrant_remove_blue(ImageMetadata& img) {
+ int half_width = img.width / 2;
+ int half_height = img.height / 2;
+ cv::Mat bottom_left =
+ img.canvas(cv::Rect(0, half_height, half_width, img.height - half_height));
+
+ for (int y = 0; y < bottom_left.rows; y++) {
+ for (int x = 0; x < bottom_left.cols; x++) {
+ bottom_left.at(y, x)[0] = 0; // Blue channel
+ }
+ }
+}
+
+void ImageReaderNode::process_bottom_right_quadrant_edge_detection(ImageMetadata& img) {
+ int half_width = img.width / 2;
+ int half_height = img.height / 2;
+
+ cv::Mat bottom_right =
+ img.canvas(
+ cv::Rect(
+ half_width,
+ half_height,
+ img.width - half_width,
+ img.height - half_height
+ )
+ );
+
+ cv::Mat gray, edges;
+
+ cv::cvtColor(bottom_right, gray, cv::COLOR_BGR2GRAY);
+ cv::Canny(gray, edges, 50, 150);
+ cv::cvtColor(edges, bottom_right, cv::COLOR_GRAY2BGR);
+}
+
+} // namespace assignments::two::image_reader
diff --git a/src/image_reader_wessel_pkg/src/nodes/ImageReader.hpp b/src/image_reader_wessel_pkg/src/nodes/ImageReader.hpp
new file mode 100644
index 0000000..edb1565
--- /dev/null
+++ b/src/image_reader_wessel_pkg/src/nodes/ImageReader.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "rclcpp/rclcpp.hpp"
+#include "sensor_msgs/msg/image.hpp"
+#include
+
+namespace assignments::two::image_reader {
+
+struct RGBColors {
+ int r;
+ int g;
+ int b;
+};
+
+struct ImageMetadata {
+ cv::Mat canvas;
+ int width;
+ int height;
+};
+
+class ImageReaderNode : public rclcpp::Node {
+public:
+ ImageReaderNode();
+
+private:
+ rclcpp::Subscription::SharedPtr image_subscriber_;
+ rclcpp::Publisher::SharedPtr image_publisher_;
+
+ RGBColors circle_color_;
+
+ void image_callback(const sensor_msgs::msg::Image::SharedPtr msg);
+
+ void draw_division_lines(ImageMetadata& img);
+ void process_top_left_quadrant_white(ImageMetadata& img);
+ void process_top_right_quadrant_circle(ImageMetadata& img);
+ void process_bottom_left_quadrant_remove_blue(ImageMetadata& img);
+ void process_bottom_right_quadrant_edge_detection(ImageMetadata& img);
+};
+
+} // namespace assignments::two::image_reader
\ No newline at end of file
diff --git a/src/launch_wessel_pkg/CMakeLists.txt b/src/launch_wessel_pkg/CMakeLists.txt
new file mode 100644
index 0000000..91e76ed
--- /dev/null
+++ b/src/launch_wessel_pkg/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.8)
+project(launch_wessel_pkg)
+
+find_package(ament_cmake REQUIRED)
+
+install(
+ DIRECTORY launch rviz
+ DESTINATION share/${PROJECT_NAME}
+)
+
+ament_package()
diff --git a/src/launch_wessel_pkg/launch/image_reader.launch.xml b/src/launch_wessel_pkg/launch/image_reader.launch.xml
new file mode 100644
index 0000000..39daf5b
--- /dev/null
+++ b/src/launch_wessel_pkg/launch/image_reader.launch.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/launch_wessel_pkg/package.xml b/src/launch_wessel_pkg/package.xml
new file mode 100644
index 0000000..61923fe
--- /dev/null
+++ b/src/launch_wessel_pkg/package.xml
@@ -0,0 +1,15 @@
+
+
+
+ launch_wessel_pkg
+ 0.0.0
+ TODO: Package description
+ Wessel T
+ TODO: License declaration
+
+ ament_cmake
+
+
+ ament_cmake
+
+
diff --git a/src/launch_wessel_pkg/rviz/image_processor_config.rviz b/src/launch_wessel_pkg/rviz/image_processor_config.rviz
new file mode 100644
index 0000000..65a172d
--- /dev/null
+++ b/src/launch_wessel_pkg/rviz/image_processor_config.rviz
@@ -0,0 +1,131 @@
+Panels:
+ - Class: rviz_common/Displays
+ Help Height: 78
+ Name: Displays
+ Property Tree Widget:
+ Expanded:
+ - /Global Options1
+ - /Status1
+ Splitter Ratio: 0.5
+ Tree Height: 1255
+ - Class: rviz_common/Selection
+ Name: Selection
+ - Class: rviz_common/Tool Properties
+ Expanded:
+ - /2D Goal Pose1
+ - /Publish Point1
+ Name: Tool Properties
+ Splitter Ratio: 0.5886790156364441
+ - Class: rviz_common/Views
+ Expanded:
+ - /Current View1
+ Name: Views
+ Splitter Ratio: 0.5
+ - Class: rviz_common/Time
+ Experimental: false
+ Name: Time
+ SyncMode: 0
+ SyncSource: ""
+Visualization Manager:
+ Class: ""
+ Displays:
+ - Class: rviz_default_plugins/Image
+ Enabled: true
+ Max Value: 1
+ Median window: 5
+ Min Value: 0
+ Name: Image
+ Normalize Range: true
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /image_processed
+ Value: true
+ Enabled: true
+ Global Options:
+ Background Color: 48; 48; 48
+ Fixed Frame: map
+ Frame Rate: 30
+ Name: root
+ Tools:
+ - Class: rviz_default_plugins/Interact
+ Hide Inactive Objects: true
+ - Class: rviz_default_plugins/MoveCamera
+ - Class: rviz_default_plugins/Select
+ - Class: rviz_default_plugins/FocusCamera
+ - Class: rviz_default_plugins/Measure
+ Line color: 128; 128; 0
+ - Class: rviz_default_plugins/SetInitialPose
+ Covariance x: 0.25
+ Covariance y: 0.25
+ Covariance yaw: 0.06853891909122467
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /initialpose
+ - Class: rviz_default_plugins/SetGoal
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /goal_pose
+ - Class: rviz_default_plugins/PublishPoint
+ Single click: true
+ Topic:
+ Depth: 5
+ Durability Policy: Volatile
+ History Policy: Keep Last
+ Reliability Policy: Reliable
+ Value: /clicked_point
+ Transformation:
+ Current:
+ Class: rviz_default_plugins/TF
+ Value: true
+ Views:
+ Current:
+ Class: rviz_default_plugins/Orbit
+ Distance: 10
+ Enable Stereo Rendering:
+ Stereo Eye Separation: 0.05999999865889549
+ Stereo Focal Distance: 1
+ Swap Stereo Eyes: false
+ Value: false
+ Focal Point:
+ X: 0
+ Y: 0
+ Z: 0
+ Focal Shape Fixed Size: true
+ Focal Shape Size: 0.05000000074505806
+ Invert Z Axis: false
+ Name: Current View
+ Near Clip Distance: 0.009999999776482582
+ Pitch: 0.785398006439209
+ Target Frame:
+ Value: Orbit (rviz)
+ Yaw: 0.785398006439209
+ Saved: ~
+Window Geometry:
+ Displays:
+ collapsed: false
+ Height: 1436
+ Hide Left Dock: false
+ Hide Right Dock: false
+ Image:
+ collapsed: false
+ QMainWindow State: 000000ff00000000fd0000000400000000000001560000056ffc0200000008fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c00610079007301000000130000056f000000c600fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261000000010000032c0000056ffc0200000004fb0000000a0049006d00610067006501000000130000056f0000001600fffffffb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003a000005040000009e00fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000004ae0000003efc0100000002fb0000000800540069006d00650000000000000004ae000002e800fffffffb0000000800540069006d00650100000000000004500000000000000000000000200000056f00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730000000000ffffffff0000000000000000
+ Selection:
+ collapsed: false
+ Time:
+ collapsed: false
+ Tool Properties:
+ collapsed: false
+ Views:
+ collapsed: false
+ Width: 1198
+ X: 30
+ Y: 54
diff --git a/src/usb_cam_wessel_pkg/CMakeLists.txt b/src/usb_cam_wessel_pkg/CMakeLists.txt
new file mode 100644
index 0000000..1e05b48
--- /dev/null
+++ b/src/usb_cam_wessel_pkg/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.8)
+project(usb_cam_wessel_pkg)
+
+find_package(ament_cmake REQUIRED)
+
+install(
+ DIRECTORY config/
+ DESTINATION share/${PROJECT_NAME}/config
+)
+
+ament_package()
diff --git a/src/usb_cam_wessel_pkg/config/params.yaml b/src/usb_cam_wessel_pkg/config/params.yaml
new file mode 100644
index 0000000..31331a8
--- /dev/null
+++ b/src/usb_cam_wessel_pkg/config/params.yaml
@@ -0,0 +1,23 @@
+/usb_cam:
+ ros__parameters:
+ video_device: "/dev/video0"
+ framerate: 30.0
+ io_method: "mmap"
+ frame_id: "camera"
+ pixel_format: "mjpeg2rgb"
+ color_format: "yuv422p"
+ image_width: 640
+ image_height: 480
+ camera_name: "usb_cam"
+ camera_info_url: ""
+ brightness: -1
+ contrast: -1
+ saturation: -1
+ sharpness: -1
+ gain: -1
+ auto_white_balance: true
+ white_balance: 4000
+ autoexposure: true
+ exposure: 100
+ autofocus: false
+ focus: -1
diff --git a/src/usb_cam_wessel_pkg/package.xml b/src/usb_cam_wessel_pkg/package.xml
new file mode 100644
index 0000000..00e0de5
--- /dev/null
+++ b/src/usb_cam_wessel_pkg/package.xml
@@ -0,0 +1,15 @@
+
+
+
+ usb_cam_wessel_pkg
+ 0.0.0
+ USB camera package for ROS2
+ wessel
+ TODO: License declaration
+
+ ament_cmake
+
+
+ ament_cmake
+
+