I have written the following script with which I aim to detect lines in Gazebo (a simulation environment):
JavaScript
x
137
137
1
#!/usr/bin/env python
2
# rospy for the subscriber
3
import rospy
4
# ROS Image message
5
from sensor_msgs.msg import Image
6
# ROS Image message -> OpenCV2 image converter
7
from cv_bridge import CvBridge, CvBridgeError
8
# OpenCV2 for saving an image
9
import cv2
10
import matplotlib.pyplot as plt
11
import numpy as np
12
13
def gradient(img):
14
# grayscale the image
15
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
16
# gaussian blur of image with a 5x5 kernel
17
gauss = cv2.GaussianBlur(gray,(5,5),0)
18
# Return the canny of the image
19
return cv2.Canny(gauss,20,30)
20
21
def region_of_interest(img):
22
# Height of image (number of rows)
23
height = img.shape[0]
24
# Width of the image (number of columns)
25
width = img.shape[1]
26
# Create an array of polygons to use for the masking of the canny image
27
polygons = np.array([
28
[(200,height), (200,500), (600,500), (600,height)]
29
])
30
# Create the mask image's background (black color)
31
mask_bg = np.zeros_like(img)
32
# Create the mask image (image with black background an white region of interest)
33
mask = cv2.fillPoly(mask_bg, polygons, 255)
34
# Isolate the area of interest using the bitwise operator of the mask and canny image
35
masked_image = cv2.bitwise_and(img,cv2.fillPoly(mask_bg, polygons, 255))
36
# Return the updated image
37
return masked_image
38
39
def make_coordinates(img, line_parameters):
40
# Extract the average slope and intercept of the line
41
slope, intercept = line_parameters
42
# Coordinate y(1) of the calculated line
43
y1 = img.shape[0]
44
# Coordinate y(2) of the calculated line
45
y2 = int(y1*0.5)
46
# Coordinate x(1) of the calculated line
47
x1 = int((y1-intercept)/slope)
48
# Coordinate x(2) of the calculated line
49
x2 = int((y2-intercept)/slope)
50
# Return the coordinates of the average line
51
return np.array([x1,y1,x2,y2])
52
53
def average_slope_intercep(img,lines):
54
# Create an empty list containing the coordinates of the detected line
55
line_fit = []
56
# Loop through all the detected lines
57
for line in lines:
58
# Store the coordinates of the detected lines into an 1D array of 4 elements
59
x1,y1,x2,y2 = line.reshape(4)
60
# Create a line y = mx+b based on the coordinates
61
parameters = np.polyfit((x1,x2),(y1,y2),1)
62
# Extract the slope m
63
slope = parameters[0]
64
# Extract the intercept b
65
intercept = parameters[1]
66
# Add elements on the list
67
line_fit.append((slope,intercept))
68
# Check slope of line
69
# if slope < 0:
70
# continue
71
# else:
72
# continue
73
# Calculate the average of the line fit parameters list
74
line_fit_average = np.average(line_fit,axis=0)
75
# Extract the coordinates of the calculated line
76
main_line = make_coordinates(img,line_fit_average)
77
78
return np.array([main_line])
79
80
81
def display_lines(img,lines):
82
# Create a mask image that will have the drawn lines
83
line_image = np.zeros_like(img)
84
# If no lines were detected
85
if lines is not None:
86
# Loop through all the lines
87
for line in lines:
88
# Store the coordinates of the first and last point of the lines into 1D arrays
89
x1, y1, x2, y2 = line.reshape(4)
90
# Draw the lines on the image with blue color and thicknes of 10
91
cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),10)
92
# Return the mask image with the drawn lines
93
return line_image
94
95
def image_callback(msg):
96
# print("Received an image!")
97
# Instantiate CvBridge
98
bridge = CvBridge()
99
try:
100
# Convert your ROS Image message to OpenCV2
101
frame = bridge.imgmsg_to_cv2(msg, "bgr8")
102
except CvBridgeError, e:
103
print(e)
104
else:
105
# Copy of the original frame
106
frame_copy = np.copy(frame)
107
# Canny of image
108
canny_frame = gradient(frame_copy)
109
# Apply mask in region of interest
110
cropped_image = region_of_interest(canny_frame)
111
# Apply Hough Transform on the region of interest
112
lines = cv2.HoughLinesP(cropped_image,1,np.pi/180,30,np.array([]),minLineLength=10,maxLineGap=2)
113
# Calculate the average slope of the detected lines
114
averaged_lines = average_slope_intercep(frame_copy,lines)
115
# Create a mask image with the drawn lines
116
line_image = display_lines(frame_copy,averaged_lines)
117
# Plot lines on the camera feed frame
118
combo_image = cv2.addWeighted(frame_copy,0.8,line_image,1,1)
119
#Show manipulated image feed
120
cv2.imshow("Result feed", frame_copy)
121
# plt.imshow(canny_frame)
122
cv2.waitKey(1)
123
# plt.show()
124
125
def main():
126
rospy.init_node('image_listener')
127
# Define your image topic
128
image_topic = "rover/camera1/image_raw"
129
# Set up your subscriber and define its callback
130
rospy.Subscriber(image_topic, Image, image_callback)
131
# Spin until ctrl + c
132
rospy.spin()
133
cv2.destroyAllWindows()
134
135
if __name__ == '__main__':
136
main()
137
The code is integrated in ROS, so please focus your attention at the image_callback
function. My issue is that the line that I want to detect is quite noisy and I cannot figure out how to detect it correctly.
To be more specific, from the following frame,
Original Frame
I get this image after gaussian blur and the canny algorithm,
Canny Frame
How could I filter the “noise” I see in the canny frame? I played a lot with the canny and gausian blur parameters but all that I have achieved is removing gradients instead of actually making it less “noisy”.
Advertisement
Answer
This method might help you to remove noise from the frame.
JavaScript
1
32
32
1
import cv2
2
import numpy as np
3
from skimage.morphology import skeletonize
4
5
6
def get_skeleton_iamge(threshold_image):
7
skeleton = skeletonize(threshold_image / 255)
8
skeleton = skeleton.astype(np.uint8)
9
skeleton *= 255
10
return skeleton
11
12
13
image = cv2.imread("road.png", 0)
14
image = cv2.resize(image, (300, 300))
15
16
bilateral = cv2.bilateralFilter(image, 15, 100, 100)
17
cv2.imshow("bilateral_image", bilateral)
18
19
canny_image = cv2.Canny(bilateral, 20, 30)
20
cv2.imshow("canny_image", canny_image)
21
22
kernel = np.ones((10, 10))
23
dilate_image = cv2.dilate(canny_image, kernel, iterations=1)
24
erode_image = cv2.erode(dilate_image, kernel, iterations=1)
25
cv2.imshow("erode_image", erode_image)
26
27
skeleton_iamge = get_skeleton_iamge(erode_image)
28
cv2.imshow("skeleton_iamge", skeleton_iamge)
29
30
cv2.waitKey(0)
31
32