Galaxis Codebase  1.0.0
Team Galaxis software stack for the Carolo Cup 2023 - ROS2 based software
stoplinedetection.cpp
Go to the documentation of this file.
1 #include "stoplinedetection.hpp"
2 
4 
5 #include <utils/logger.hpp>
6 
7 #include <sensor_msgs/image_encodings.hpp>
8 #include <std_msgs/msg/header.hpp>
9 
10 #include <opencv2/imgproc/imgproc.hpp>
11 #include <vector>
12 
13 #include <iostream>
14 #include <ostream>
15 #include <string>
16 
17 #include <cmath>
18 
19 #include "utils/clock.hpp"
20 
21 #include <opencv2/opencv.hpp>
22 #include <opencv2/imgproc.hpp>
23 #include <opencv2/core/types.hpp>
24 
25 using namespace ::std::literals;
26 
27 using namespace galaxis::ros;
28 using namespace galaxis::utils;
29 
30 using namespace std;
31 
32 using std::placeholders::_1;
33 
35  log("StopLineDetectorNode: Initializing...");
36 
37  std::string configPath = "settings/onstartup_startstopline.json";
38  auto settingsJsonSPtr = read_json_file(configPath);
39  json& settingsJson = (*settingsJsonSPtr)["settings"];
40  m_debugMode = settingsJson["debug_mode"];
41  m_startlineDetectionMode = settingsJson["startline_detection"];
42  m_stopLineDetectionMode = settingsJson["stopline_detection"];
43 
44  // Publisher
45  if(m_debugMode == true) {
46  m_stopLineImagePublisher = image_transport::create_publisher(this, galaxis::ros::TOPIC_STOP_LINE_IMAGE);
47  m_startLineImagePublisher = image_transport::create_publisher(this, galaxis::ros::TOPIC_START_LINE_IMAGE);
48  }
49 
50  galaxis::ros::create_publisher(this, m_stopLinePublisherSPtr);
51 
52 
53  // get gloabl settings
54  m_simulationMode = this->get_global_settings()["simulation_mode"];
55 
56  log("Configuration:\n - SimulationMode: ", m_simulationMode ? "ENABLED" : "DISABLED");
57 
58  if(m_simulationMode) { // Initializing Simulation
59  rclcpp::QoS qos_params = get_qos_profile(QOS_BEST_EFFORT);
60  m_StopLineImageSubscriber = this->create_subscription<sensor_msgs::msg::Image>(
61  "/camera/image_raw",
62  qos_params,
64  } else { // Initializing Normal Mode
65  rclcpp::QoS qos_params = get_qos_profile(QOS_BEST_EFFORT);
66  m_StopLineImageSubscriber = this->create_subscription<sensor_msgs::msg::Image>(
67  TOPIC_CAMERA_RAW,
68  qos_params,
70  }
71 
72 
73  // get camera matrix and top view transform
76 
78 }
79 
81 
82 void StopLineDetection::on_stop_line_detection_callback(sensor_msgs::msg::Image::SharedPtr msg) {
84 
85  // CvImage
86  // class CvImage
87  // {
88  // public:
89  // std_msgs::Header header;
90  // std::string encoding;
91  // cv::Mat image;
92  // };
93  std::shared_ptr<cv_bridge::CvImage const> cvImgSPtr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::BGR8); //BGR8 //RGB8
94 
95  assert(cvImgSPtr->image.cols == 512 && cvImgSPtr->image.rows == 256);
96 
97  cv::Mat undistortedImage;
98  undistortedImage.create(256, 512, cvImgSPtr->image.type());
99  cv::undistort(cvImgSPtr->image, undistortedImage, galaxis::camera::matrixD_mtx, galaxis::camera::matrixK_dist, newCameraMatrix);
100 
101  cv::Mat undistortedTopViewImage;
102  undistortedImage.create(256, 512, cvImgSPtr->image.type());
103  cv::warpPerspective(undistortedImage, undistortedTopViewImage, perspectiveTransformMatrix, cv::Size(512, 256));
104 
105  // StopLineDetection
106  bool stopLineDetected = false;
107  int height = 0;
108  int yOffset = 0;
110  height = undistortedTopViewImage.rows * 0.3f;
111  yOffset = undistortedTopViewImage.rows * 0.55f;
112  cv::Rect roiForCropStopLine = Rect(undistortedTopViewImage.cols * 0.4f, yOffset, undistortedTopViewImage.cols * 0.25f, height);
113  cv::Mat croppedImageForStartStopLineDetection = undistortedTopViewImage(roiForCropStopLine);
114  cv::Mat copiedImageForStopLineDetec = croppedImageForStartStopLineDetection.clone(); // copy
115  stopLineDetected = this->detect_stopline(copiedImageForStopLineDetec, msg->header);
116  }
117 
118  // StartLineDetection
119  bool startLineDetected = false;
121  height = undistortedTopViewImage.rows * 0.2f;
122  yOffset = undistortedTopViewImage.rows * 0.6f;
123  cv::Rect roiForCrop = Rect(undistortedTopViewImage.cols * 0.2f, yOffset, undistortedTopViewImage.cols * 0.4f, height);
124  cv::Mat croppedImageForStartLineDetection = undistortedTopViewImage(roiForCrop);
125  startLineDetected = this->detect_startline(croppedImageForStartLineDetection, msg->header);
126  }
127 
128  if(startLineDetected || stopLineDetected) {
129  log("Start or Stopline detected: Sending message...");
130  std::unique_ptr<StopLineType> lineMessageUPtr = std::make_unique<StopLineType>();
131 
132  if(stopLineDetected && startLineDetected) {
133  stopLineDetected = false;
134  }
135 
136  lineMessageUPtr->header.stamp = msg->header.stamp;
137  lineMessageUPtr->stopline_flag = stopLineDetected;
138  lineMessageUPtr->stopline_distance = stopLineDetected ? 0.5f : -1.f; // TODO
139 
140  lineMessageUPtr->startline_flag = startLineDetected;
141  lineMessageUPtr->startline_distance = startLineDetected ? 0.5f : -1.f; // TODO
142 
143  m_stopLinePublisherSPtr->publish(std::move(lineMessageUPtr));
144  log("Total Calculation and Publish Time [ms]: ", stopwatch.get_milliseconds());
145  }
146 }
147 
148 bool StopLineDetection::detect_stopline(cv::Mat &image, std_msgs::msg::Header const& header) {
150  bool stopLineDetected = false;
151 
152  cv::Mat greyImage;
153  cv::Mat temp;
154  assert(image.channels() > 1);
155  cv::cvtColor(image, temp, cv::COLOR_BGR2GRAY);
156  threshold(temp, greyImage, 200, 255, CV_THRESH_BINARY);
157 
159  cv::Mat inputImage;
160  Canny(greyImage, inputImage, 60, 200, 3);
161 
162  cv::Mat debugImage;
163  cv::cvtColor(inputImage, debugImage, CV_GRAY2BGR);
164 
165  // Do the work
166  std::vector<cv::Vec4i> p_lines;
168  cv::HoughLinesP( inputImage, p_lines,
169  1, CV_PI/180, m_stopLineHoughThreshold,
171 
172  // std::cout << "Raw detections from hough: " << std::to_string(p_lines.size()) << std::endl;
173 
174  std::vector<cv::Vec4i> p_linesPostprocessed;
175  p_linesPostprocessed.reserve(p_lines.size());
176 
177  int yTolerance = 18;
178  int xMinLength = 50;
179  for(auto const& line : p_lines) {
180  if(std::fabs(line[1] - line[3]) < yTolerance && std::fabs(line[0] - line[2]) > xMinLength) {
181  p_linesPostprocessed.push_back(line);
182  }
183  }
184 
185  if(p_linesPostprocessed.size() > 0) {
186  stopLineDetected = true;
187  }
188 
189  if(m_debugMode && m_stopLineImagePublisher.getNumSubscribers() > 0) {
190  if(p_linesPostprocessed.size() > 0) log_debug("Detected StopLines: ", p_linesPostprocessed.size());
191  // log_debug("StartStopLineDetection Time [us]: ", stopwatch.get());
192 
194  for (const cv::Vec4i& l : p_linesPostprocessed) {
195  #ifndef CV_VERSION_EPOCH
196  cv::line(debugImage, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(255, 0, 0), 3, cv::LINE_AA);
197  #else
198  cv::line(debugImage, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(255, 0, 0), 3, CV_AA);
199  #endif
200  }
201 
202  // send debug image
203  std::shared_ptr<sensor_msgs::msg::Image> msgImgSPtr = std::make_shared<sensor_msgs::msg::Image>();
204  cv_bridge::CvImage m_bridgeImg = cv_bridge::CvImage(header, sensor_msgs::image_encodings::BGR8, debugImage); // RGB8
205  m_bridgeImg.toImageMsg(*msgImgSPtr); // cv_bridge to sensor_msgs::Image
206  m_stopLineImagePublisher.publish(msgImgSPtr);
207  }
208 
209  return stopLineDetected;
210 }
211 
212 bool StopLineDetection::detect_startline(cv::Mat &image, std_msgs::msg::Header const& header) {
214 
215  cv::Mat& frame = image;
216  bool startLineDetected = false;
217 
218 
219  cv::Mat greyImage;
220  cv::Mat temp;
221  cv::cvtColor(frame, temp, cv::COLOR_BGR2GRAY);
222  threshold(temp, greyImage, m_startLineGreynessLowerThreshold, 255, CV_THRESH_BINARY);
223 
225  std::vector<cv::Point2f> corners;
226  bool use_harris_detector = false;
227 
228 
229  cv::RNG rng(12345);
230 
232  cv::goodFeaturesToTrack(greyImage, corners, 0, m_startLineQualityLevel, m_startLineMinDistance, cv::Mat(), m_startLineBlockSize,
233  use_harris_detector, m_startLineK);
234 
235 
236  double yAverage = 0.0;
237  for(auto const& corner : corners) {
238  yAverage += corner.y;
239  }
240  yAverage /= corners.size();
241 
242 
243  int pointsInAverage = 0;
244  std::vector<cv::Point2f> cornersPostprocessed;
245  cornersPostprocessed.reserve(corners.size());
246  for(auto const& corner : corners) {
247  if(std::fabs(corner.y - yAverage) < m_startLineClusterEps) {
248  pointsInAverage++;
249  cornersPostprocessed.push_back(corner);
250  }
251  }
252 
254  if(pointsInAverage > m_startLinePointsCountThreshold) {
255  log_debug("StartLine with Detected Corners: ", cornersPostprocessed.size());
256  log_debug("Corner Detection Time [ms]: ", stopwatch.get_milliseconds());
257  startLineDetected = true;
258  }
259 
260  if(m_debugMode && m_startLineImagePublisher.getNumSubscribers() > 0) {
261  int r = 4;
262  for (const cv::Point2f& corner : cornersPostprocessed)
263  {
264  cv::circle(frame, corner, r, cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), -1, 8,
265  0);
266  }
267 
268  // send debug image
269  std::shared_ptr<sensor_msgs::msg::Image> msgImgSPtr = std::make_shared<sensor_msgs::msg::Image>();
270  cv_bridge::CvImage m_bridgeImg = cv_bridge::CvImage(header, sensor_msgs::image_encodings::BGR8, frame); // RGB8
271  m_bridgeImg.toImageMsg(*msgImgSPtr); // cv_bridge to sensor_msgs::Image
272  m_startLineImagePublisher.publish(msgImgSPtr);
273  }
274 
275  return startLineDetected;
276 }
277 
278 rcl_interfaces::msg::SetParametersResult StopLineDetection::new_parameters_callback(const std::vector<rclcpp::Parameter>& parameters) {
279  log("[StopLineDetection] NEW PARAMETERS");
280 
281  rcl_interfaces::msg::SetParametersResult result;
282  result.successful = true;
283  result.reason = "";
284 
285  for (const auto &param: parameters) {
286  if (param.get_name() == "stopline.hough_lines_minLineLength") {
287  if(check_parameter<int>(param, result, 1, 1000)) {
288  m_stopLineHoughMinLineLength = param.as_int();
289  }
290  }
291  else if (param.get_name() == "stopline.hough_lines_maxLineGap") {
292  if(check_parameter<int>(param, result, 1, 1000)) {
293  m_stopLineHoughMaxLineGap = param.as_int();
294  }
295  }
296  else if (param.get_name() == "startline.whiteness_lower_threshold") {
297  if(check_parameter<int>(param, result, 1, 1000)) {
298  m_startLineGreynessLowerThreshold = param.as_int();
299  }
300  }
301  else if (param.get_name() == "startline.feature_quality_level") {
302  if(check_parameter<double>(param, result, 0, 1)) {
303  m_startLineQualityLevel = param.as_double();
304  }
305  }
306  else if (param.get_name() == "startline.feature_min_distance") {
307  if(check_parameter<int>(param, result, 0, 1000)) {
308  m_startLineMinDistance = param.as_int();
309  }
310  }
311  else if (param.get_name() == "startline.feature_block_size") {
312  if(check_parameter<int>(param, result, 0, 1000)) {
313  m_startLineBlockSize = param.as_int();
314  }
315  }
316  else if (param.get_name() == "startline.feature_k") {
317  if(check_parameter<double>(param, result, 0, 5)) {
318  m_startLineK = param.as_double();
319  }
320  }
321  else if (param.get_name() == "startline.cluster_eps") {
322  if(check_parameter<int>(param, result, 0, 50)) {
323  m_startLineClusterEps = param.as_int();
324  }
325  }
326  else if (param.get_name() == "startline.points_count_threshold") {
327  if(check_parameter<int>(param, result, 0, 500)) {
328  m_startLinePointsCountThreshold = param.as_int();
329  }
330  }
331  else if (param.get_name() == "stopline.hough_lines_threshold") {
332  if(check_parameter<int>(param, result, 1, 1000)) {
333  m_stopLineHoughThreshold = param.as_int();
334  }
335  } else {
336  result.successful = false;
337  result.reason += "[" + param.get_name() + "] UNKNOWN param or NOT IMPLEMENTED; ";
338  }
339  }
340 
341  if(result.successful == false) {
342  log_warning("[AutopilotNode] Failed to set some parameters. Reasons: ", result.reason);
343  }
344 
345  return result;
346 }
StopLineDetection::m_startlineDetectionMode
bool m_startlineDetectionMode
Definition: stoplinedetection.hpp:28
StopLineDetection::m_startLineBlockSize
int m_startLineBlockSize
Definition: stoplinedetection.hpp:56
stoplinedetection.hpp
StopLineDetection::perspectiveTransformMatrix
cv::Mat perspectiveTransformMatrix
Definition: stoplinedetection.hpp:62
StopLineDetection::StopLineDetection
StopLineDetection()
Definition: stoplinedetection.cpp:34
GalaxisNode::enable_parameters
void enable_parameters(std::function< rcl_interfaces::msg::SetParametersResult(const std::vector< rclcpp::Parameter > &)> callbackFunc)
Definition: GalaxisNode.cpp:69
StopLineDetection::new_parameters_callback
rcl_interfaces::msg::SetParametersResult new_parameters_callback(const std::vector< rclcpp::Parameter > &parameters)
Definition: stoplinedetection.cpp:278
node_ros_pubsub_wrapper.hpp
StopLineDetection::m_startLineK
double m_startLineK
Definition: stoplinedetection.hpp:57
StopLineDetection::m_startLineImagePublisher
image_transport::Publisher m_startLineImagePublisher
Definition: stoplinedetection.hpp:40
StopLineDetection::m_debugMode
bool m_debugMode
Definition: stoplinedetection.hpp:27
json
nlohmann::json json
Definition: AutoPilotNode.hpp:21
StopLineDetection::detect_stopline
bool detect_stopline(cv::Mat &image, std_msgs::msg::Header const &header)
Definition: stoplinedetection.cpp:148
galaxis::ros
Definition: node_ros_pubsub_wrapper.hpp:15
galaxis::utils::log_warning
void log_warning(const Args &... args)
Definition: logger.hpp:41
clock.hpp
StopLineDetection::m_startLineGreynessLowerThreshold
int m_startLineGreynessLowerThreshold
Definition: stoplinedetection.hpp:53
galaxis::camera::getPerspectiveTransform
cv::Mat getPerspectiveTransform()
Definition: CameraCalibration.cpp:11
galaxis::camera::matrixK_dist
const cv::Mat matrixK_dist
Definition: CameraCalibration.hpp:30
StopLineDetection::m_startLineQualityLevel
double m_startLineQualityLevel
Definition: stoplinedetection.hpp:54
galaxis::utils::read_json_file
shared_ptr< json > read_json_file(std::string path)
Definition: settings.cpp:5
GalaxisNode::get_global_settings
json const & get_global_settings()
Definition: GalaxisNode.cpp:119
StopLineDetection::m_stopLineImagePublisher
image_transport::Publisher m_stopLineImagePublisher
Definition: stoplinedetection.hpp:39
StopLineDetection::m_startLinePointsCountThreshold
int m_startLinePointsCountThreshold
Definition: stoplinedetection.hpp:59
StopLineDetection::m_startLineMinDistance
double m_startLineMinDistance
Definition: stoplinedetection.hpp:55
galaxis::lina::Point2f
Eigen::Vector2f Point2f
Definition: linatypes.hpp:18
StopLineDetection::m_stopLinePublisherSPtr
rclcpp::Publisher< StopLineType >::SharedPtr m_stopLinePublisherSPtr
Definition: stoplinedetection.hpp:42
StopLineDetection::newCameraMatrix
cv::Mat newCameraMatrix
Definition: stoplinedetection.hpp:61
StopLineDetection::m_stopLineHoughMinLineLength
int m_stopLineHoughMinLineLength
Definition: stoplinedetection.hpp:49
galaxis::camera::matrixD_mtx
const cv::Mat matrixD_mtx
Definition: CameraCalibration.hpp:28
galaxis::utils::MicroStopwatch::get_milliseconds
long long get_milliseconds() const
Definition: clock.cpp:37
GalaxisNode
Definition: GalaxisNode.hpp:21
StopLineDetection::detect_startline
bool detect_startline(cv::Mat &image, std_msgs::msg::Header const &header)
Definition: stoplinedetection.cpp:212
StopLineDetection::m_startLineClusterEps
int m_startLineClusterEps
Definition: stoplinedetection.hpp:58
StopLineDetection::m_stopLineDetectionMode
bool m_stopLineDetectionMode
Definition: stoplinedetection.hpp:29
StopLineDetection::m_StopLineImageSubscriber
rclcpp::Subscription< sensor_msgs::msg::Image >::SharedPtr m_StopLineImageSubscriber
Definition: stoplinedetection.hpp:45
logger.hpp
galaxis::utils
Definition: clock.hpp:5
StopLineDetection::m_simulationMode
bool m_simulationMode
Definition: stoplinedetection.hpp:26
StopLineDetection::m_stopLineHoughMaxLineGap
int m_stopLineHoughMaxLineGap
Definition: stoplinedetection.hpp:50
StopLineDetection::on_stop_line_detection_callback
void on_stop_line_detection_callback(sensor_msgs::msg::Image::SharedPtr msg)
Definition: stoplinedetection.cpp:82
galaxis::utils::log
void log(const Args &... args)
Definition: logger.hpp:33
galaxis::camera::getOptimalCameraMatrix
cv::Mat getOptimalCameraMatrix()
Definition: CameraCalibration.cpp:6
StopLineDetection::~StopLineDetection
~StopLineDetection()
Definition: stoplinedetection.cpp:80
galaxis::utils::MicroStopwatch
Definition: clock.hpp:10
galaxis::utils::log_debug
void log_debug(const Args &... args)
Definition: logger.hpp:62
StopLineDetection::m_stopLineHoughThreshold
int m_stopLineHoughThreshold
Definition: stoplinedetection.hpp:51