1+ import rclpy
2+ from rclpy .node import Node
3+ from rclpy import qos
4+
5+ import cv2 as cv
6+
7+ from sensor_msgs .msg import Image
8+ from geometry_msgs .msg import Polygon , PolygonStamped , Point32
9+ from cv_bridge import CvBridge
10+
11+ class DetectorBasic (Node ):
12+ visualisation = True
13+ data_logging = True
14+ log_path = 'evaluation/data/'
15+ seq = 0
16+
17+ def __init__ (self ):
18+ super ().__init__ ('detector_basic' )
19+ self .bridge = CvBridge ()
20+
21+ self .min_area_size = 100.0
22+ self .countour_color = (255 , 255 , 0 ) # cyan
23+ self .countour_width = 1 # in pixels
24+
25+ self .object_pub = self .create_publisher (PolygonStamped , '/object_polygon' , 10 )
26+ self .image_sub = self .create_subscription (Image , '/limo/depth_camera_link/image_raw' ,
27+ self .image_color_callback , qos_profile = qos .qos_profile_sensor_data )
28+
29+ def image_color_callback (self , data ):
30+ bgr_image = self .bridge .imgmsg_to_cv2 (data , "bgr8" ) # convert ROS Image message to OpenCV format
31+
32+ # detect a color blob in the color image
33+ # provide the right range values for each BGR channel (set to red bright objects)
34+ bgr_thresh = cv .inRange (bgr_image , (0 , 0 , 80 ), (50 , 50 , 255 ))
35+
36+ # finding all separate image regions in the binary image, using connected components algorithm
37+ bgr_contours , _ = cv .findContours ( bgr_thresh ,
38+ cv .RETR_TREE , cv .CHAIN_APPROX_SIMPLE )
39+
40+ detected_objects = []
41+ for contour in bgr_contours :
42+ area = cv .contourArea (contour )
43+ # detect only large objects
44+ if area > self .min_area_size :
45+ # get the bounding box of the region
46+ bbx , bby , bbw , bbh = cv .boundingRect (contour )
47+ # append the bounding box of the region into a list
48+ detected_objects .append (Polygon (points = [Point32 (x = float (bbx ), y = float (bby )), Point32 (x = float (bbw ), y = float (bbh ))]))
49+ if self .visualisation :
50+ cv .rectangle (bgr_image , (bbx , bby ), (bbx + bbw , bby + bbh ), self .countour_color , self .countour_width )
51+
52+ # publish individual objects from the list
53+ # the header information is taken from the Image message
54+ for polygon in detected_objects :
55+ self .object_pub .publish (PolygonStamped (polygon = polygon , header = data .header ))
56+
57+ # log the processed images to files
58+ if self .data_logging :
59+ cv .imwrite (self .log_path + f'colour_{ self .seq :06d} .png' , bgr_image )
60+ cv .imwrite (self .log_path + f'mask_{ self .seq :06d} .png' , bgr_thresh )
61+
62+ # visualise the image processing results
63+ if self .visualisation :
64+ cv .imshow ("colour image" , bgr_image )
65+ cv .imshow ("detection mask" , bgr_thresh )
66+ cv .waitKey (1 )
67+
68+ self .seq += 1
69+
70+ def main (args = None ):
71+ rclpy .init (args = args )
72+ detector_basic = DetectorBasic ()
73+
74+ rclpy .spin (detector_basic )
75+
76+ detector_basic .destroy_node ()
77+ rclpy .shutdown ()
78+
79+ if __name__ == '__main__' :
80+ main ()
0 commit comments