Skip to content

Commit 41e78fe

Browse files
authored
Merge pull request #3 from Blenders-FC/package_structure
[PKG] Package structure
2 parents d6e42bf + 7f47fd5 commit 41e78fe

File tree

5 files changed

+90
-34
lines changed

5 files changed

+90
-34
lines changed

launch/vision.launch

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" ?>
2+
<launch>
3+
4+
<!-- Define robot_id with a default value (used if not inherited) -->
5+
<arg name="robot_id" default="1"/>
6+
<param name="robot_id" value="$(arg robot_id)"/>
7+
8+
<!-- Camera Node -->
9+
<node pkg="usb_cam" type="usb_cam_node" name="usb_cam_node" output="screen"/>
10+
11+
<!-- Vision Node -->
12+
<node pkg="vision_pkg" type="main_vision_node.py" name="main_vision_node" output="screen"/>
13+
14+
</launch>
File renamed without changes.

src/lines_detector.py

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,21 @@ def calculate_fps(prev_frame_time, new_frame_time, frame, position=(7, 70), font
2121
cv2.putText(frame, fps_text, position, font, font_scale, color, thickness, cv2.LINE_AA)
2222
return prev_frame_time
2323

24+
# Function to calculate lines angles
25+
def calculate_angles(lines):
26+
try:
27+
for line in lines:
28+
x1, y1, x2, y2 = line[0]
29+
# Calculate the angle of the line
30+
angle = np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi # Convert to degrees
31+
print(f"Line angle: {angle} degrees")
32+
except TypeError:
33+
pass
34+
35+
36+
2437
# Open the video file
25-
video_path = Path(__file__).parent.parent / "media" / "video1.mp4" # Get path relative to script location
38+
video_path = Path(__file__).resolve().parent.parent / "media" / "video1.mp4" # Get path relative to script location
2639
cap = cv2.VideoCapture(str(video_path))
2740

2841
# Check if the video opened successfully
@@ -38,12 +51,18 @@ def calculate_fps(prev_frame_time, new_frame_time, frame, position=(7, 70), font
3851
# used to record the time when we processed last frame
3952
prev_frame_time = 0
4053

41-
# Pre-calculate zones to exclude from drawing lines
42-
def calculate_nozone(shape, offset=50):
43-
y, x = shape[:2]
44-
y_nozone = list(range(y - offset, y + 1)) + list(range(0, offset + 1))
45-
x_nozone = list(range(x - offset, x + 1)) + list(range(0, offset + 1))
46-
return y_nozone, x_nozone
54+
# Calculate zones for the current frame
55+
ret, orig_frame = cap.read()
56+
57+
# Define the region of interest (ROI)
58+
image_height, image_width = orig_frame.shape[:2]
59+
roi_height = int(image_height * 0.5) # Crop the top 50% of the image
60+
roi_width = int(image_width * 0.8) # Crop the middle 80% of the image
61+
x_offset = int(image_width * 0.1) # Offset to focus on the center
62+
y_offset = int(image_height * 0.25) # Vertical offset
63+
64+
orig_frame = orig_frame[y_offset:y_offset+roi_height, x_offset:x_offset+roi_width]
65+
yimg_nozone, ximg_nozone = calculate_nozone(orig_frame.shape)
4766

4867
# Loop to read frames
4968
while True:
@@ -52,12 +71,12 @@ def calculate_nozone(shape, offset=50):
5271
if not ret:
5372
break
5473

74+
# Crop the image
75+
orig_frame = orig_frame[y_offset:y_offset+roi_height, x_offset:x_offset+roi_width]
76+
5577
# Start measuring time for each frame
5678
new_frame_time = time.time()
5779

58-
# Calculate zones for the current frame
59-
yimg_nozone, ximg_nozone = calculate_nozone(orig_frame.shape)
60-
6180
# Blur and convert to grayscale
6281
blurred = cv2.GaussianBlur(orig_frame, (9, 9), 0)
6382
gray = cv2.cvtColor(blurred, cv2.COLOR_BGR2GRAY)
@@ -85,6 +104,7 @@ def calculate_nozone(shape, offset=50):
85104

86105
# Find contours of the field markings
87106
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
107+
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:10] # Keep top 10 largest contours
88108

89109
# Draw only large contours
90110
dummy_mask = np.zeros_like(mask_green)
@@ -95,20 +115,27 @@ def calculate_nozone(shape, offset=50):
95115
#adap_thrs_morph = cv2.morphologyEx(dummy_mask, cv2.MORPH_CLOSE, kernel2, iterations=1)
96116

97117
# Detect lines using Hough transform
98-
lines = cv2.HoughLinesP(dummy_mask, rho=2, theta=np.pi / 180, threshold=250,
99-
minLineLength=25, maxLineGap=50)
118+
lines = cv2.HoughLinesP(dummy_mask, rho=10, theta=np.pi / 180, threshold=250,
119+
minLineLength=10, maxLineGap=50) # minLineLength=25
120+
121+
filtered_lines = []
122+
if lines is not None:
123+
for line in lines:
124+
x1, y1, x2, y2 = line[0]
125+
if (y1 not in yimg_nozone or y2 not in yimg_nozone) and (x1 not in ximg_nozone or x2 not in ximg_nozone):
126+
filtered_lines.append(line)
127+
128+
calculate_angles(filtered_lines)
100129

101130
# Create a copy to draw lines on
102131
image_lines_thrs = orig_frame.copy()
103132
lines_thrs = np.zeros_like(dummy_mask)
104133

105134
# Draw detected lines while avoiding no-line zones
106-
if lines is not None:
107-
for line in lines:
108-
x1, y1, x2, y2 = line[0]
109-
if (y1 not in yimg_nozone or y2 not in yimg_nozone) and (x1 not in ximg_nozone or x2 not in ximg_nozone):
110-
cv2.line(image_lines_thrs, (x1, y1), (x2, y2), (255, 0, 0), 10) # Blue lines
111-
cv2.line(lines_thrs, (x1, y1), (x2, y2), (255, 0, 0), 10)
135+
for line in filtered_lines:
136+
x1, y1, x2, y2 = line[0]
137+
cv2.line(image_lines_thrs, (x1, y1), (x2, y2), (255, 0, 0), 1) # Blue lines
138+
cv2.line(lines_thrs, (x1, y1), (x2, y2), (255, 0, 0), 10)
112139

113140
# Mask lines with white areas
114141
lines_thrs = cv2.bitwise_and(lines_thrs, white_msk)
@@ -125,6 +152,7 @@ def calculate_nozone(shape, offset=50):
125152

126153
# Display the combined frame
127154
cv2.imshow('Combined Video', overlay_image)
155+
# cv2.imshow("Region of Interest", roi)
128156

129157
# Break loop if 'q' is pressed
130158
if cv2.waitKey(30) & 0xFF == ord('q'):
Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
import cv2
44
import numpy as np
5-
from utils import *
6-
from openvino.runtime import Core
75
import rospy
86
import math
7+
from yolo_utils import *
8+
from openvino.runtime import Core
99
from sensor_msgs.msg import Image
1010
from sensor_msgs.msg import JointState
1111
from cv_bridge import CvBridge, CvBridgeError
1212
from geometry_msgs.msg import Point
1313
from std_msgs.msg import Float64
1414

15+
1516
def detect(session, image_src, namesfile):
1617
global center
1718

@@ -34,15 +35,15 @@ def detect(session, image_src, namesfile):
3435

3536
boxes = post_processing(img=img_in, conf_thresh=0.8, nms_thresh=0.6, output=outputs)
3637

37-
print(boxes)
38+
# print(boxes)
3839

3940
if len(boxes) > 0 and len(boxes[0]) > 0 and len(boxes[0][0]) > 0:
40-
print(boxes[0])
41+
# print(boxes[0])
4142
# Update center coordinates
4243
center.x = int(boxes[0][0][7] * IN_IMAGE_W)
4344
center.y = int(boxes[0][0][8] * IN_IMAGE_H)
4445
center.z = int((boxes[0][0][2]*IN_IMAGE_W - boxes[0][0][0]*IN_IMAGE_W ) * (boxes[0][0][3]*IN_IMAGE_H- boxes[0][0][1]*IN_IMAGE_H))
45-
print(center)
46+
# print(center)
4647
pub_center.publish(center)
4748
else:
4849
center.x = 999
@@ -54,18 +55,19 @@ def detect(session, image_src, namesfile):
5455

5556
return final_img, center
5657

58+
5759
def compileModel():
5860
# Function for compiling model with OpenVino
5961
global compiled_model
6062

6163
ie = Core()
62-
model = ie.read_model(model="/home/robotis/blenders_ws/src/soccer_pkg/src/yolov4soccer.onnx")
63-
compiled_model = ie.compile_model(model=model, device_name="MYRIAD")
64+
model = ie.read_model(model=find_file(parent_folder="models", filename="yolov4soccer.onnx"))
65+
compiled_model = ie.compile_model(model=model, device_name="CPU") # "MYRIAD")
6466

6567

6668
def imageCallback(img_msg):
6769
global center, compiled_model
68-
print("Llega image callback")
70+
# print("Llega image callback")
6971
try:
7072
frame = bridge.imgmsg_to_cv2(img_msg, "passthrough")
7173
except CvBridgeError as e:
@@ -74,7 +76,7 @@ def imageCallback(img_msg):
7476

7577
# Process image with model and publish
7678

77-
namesfile = '/home/robotis/blenders_ws/src/soccer_pkg/src/_classes.txt'
79+
namesfile = find_file(parent_folder="models", filename="_classes.txt")
7880

7981
img, center = detect(session=compiled_model, image_src=frame, namesfile=namesfile)
8082
final_img = bridge.cv2_to_imgmsg(img, "rgb8")
@@ -93,18 +95,15 @@ def imageCallback(img_msg):
9395

9496
compileModel()
9597

96-
97-
98-
rospy.init_node('yolo_vision')
98+
rospy.init_node('vision_node')
9999
robot_id = rospy.get_param('robot_id', 1)
100100

101101

102102
pub_img = rospy.Publisher(f'/robotis_{robot_id}/ImgFinal', Image, queue_size=1)
103103
pub_center = rospy.Publisher(f'/robotis_{robot_id}/ball_center', Point, queue_size=1)
104104

105-
rospy.loginfo("Hello ROS")
105+
rospy.loginfo("Vision node initialized")
106106

107107
subimg = rospy.Subscriber("/usb_cam_node/image_raw", Image, imageCallback)
108108

109109
rospy.spin()
110-

src/yolo_utils.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import numpy as np
55
import cv2 as cv
66
from collections import deque
7+
from pathlib import Path
78

89

910
def sigmoid(x):
@@ -116,7 +117,7 @@ def get_color(c, x, max_val):
116117
if len(box) >= 7 and class_names:
117118
cls_conf = box[5]
118119
cls_id = box[6]
119-
print('%s: %f' % (class_names[cls_id], cls_conf))
120+
# print('%s: %f' % (class_names[cls_id], cls_conf))
120121
classes = len(class_names)
121122
offset = cls_id * 123457 % classes
122123
red = get_color(2, offset, classes)
@@ -140,11 +141,12 @@ def get_color(c, x, max_val):
140141
cv2.imwrite(savename, img)
141142
return img
142143

144+
143145
def plot_boxes_cv2_video(img, boxes, class_names=None, color=None):
144146
import cv2
145147
img = np.copy(img)
146148
colors = np.array([[1, 0, 1], [0, 0, 1], [0, 1, 1], [0, 1, 0], [1, 1, 0], [1, 0, 0]], dtype=np.float32)
147-
print(boxes)
149+
# print(boxes)
148150
def get_color(c, x, max_val):
149151
ratio = float(x) / max_val * 5
150152
i = int(math.floor(ratio))
@@ -263,6 +265,19 @@ def post_processing(img, conf_thresh, nms_thresh, output):
263265
t3 = time.time()
264266

265267
return bboxes_batch
268+
269+
270+
def find_file(parent_folder, filename):
271+
current_dir = Path(__file__).resolve().parent
272+
soccer_pkg_dir = current_dir.parent
273+
274+
dir_path = soccer_pkg_dir / parent_folder
275+
file_path = dir_path / filename
276+
277+
if file_path.exists():
278+
return file_path
279+
else:
280+
return None
266281

267282

268283
class CvFpsCalc(object):

0 commit comments

Comments
 (0)