Add new files
77
camera_kl520/Scan_kl520.py
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# ******************************************************************************
|
||||||
|
# Copyright (c) 2021-2022. Kneron Inc. All rights reserved. *
|
||||||
|
# ******************************************************************************
|
||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import kp
|
||||||
|
|
||||||
|
PWD = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
sys.path.insert(1, os.path.join(PWD, '..'))
|
||||||
|
|
||||||
|
class ScanForTesting(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
|
||||||
|
print('scanning kneron devices ...')
|
||||||
|
|
||||||
|
device_list = kp.core.scan_devices()
|
||||||
|
|
||||||
|
print('number of Kneron devices found: {}'.format(device_list.device_descriptor_number))
|
||||||
|
|
||||||
|
if device_list.device_descriptor_number == 0:
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
print('listing devices infomation as follows:')
|
||||||
|
|
||||||
|
for idx, device_descriptor in enumerate(device_list.device_descriptor_list):
|
||||||
|
print()
|
||||||
|
print('[{}] USB scan index: \'{}\''.format(idx, idx))
|
||||||
|
print('[{}] USB port ID: \'{}\''.format(idx, device_descriptor.usb_port_id))
|
||||||
|
print('[{}] Product ID: \'0x{:X} {}\''.format(idx,
|
||||||
|
device_descriptor.product_id,
|
||||||
|
self.get_product_name(product_id=device_descriptor.product_id)))
|
||||||
|
|
||||||
|
print('[{}] USB link speed: \'{}\''.format(idx, device_descriptor.link_speed))
|
||||||
|
print('[{}] USB port path: \'{}\''.format(idx, device_descriptor.usb_port_path))
|
||||||
|
print('[{}] KN number: \'0x{:X}\''.format(idx, device_descriptor.kn_number))
|
||||||
|
print('[{}] Connectable: \'{}\''.format(idx, device_descriptor.is_connectable))
|
||||||
|
print('[{}] Firmware: \'{}\''.format(idx, device_descriptor.firmware))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_product_name(self, product_id: int) -> str:
|
||||||
|
if product_id == kp.ProductId.KP_DEVICE_KL520.value:
|
||||||
|
return '(KL520)'
|
||||||
|
elif product_id == kp.ProductId.KP_DEVICE_KL720.value:
|
||||||
|
return '(KL720)'
|
||||||
|
elif product_id == 0x200:
|
||||||
|
return '(KL720) (Please update firmware by Kneron DFUT)'
|
||||||
|
elif product_id == kp.ProductId.KP_DEVICE_KL630.value:
|
||||||
|
return '(KL630)'
|
||||||
|
elif product_id == kp.ProductId.KP_DEVICE_KL730.value:
|
||||||
|
return '(KL730)'
|
||||||
|
elif product_id == kp.ProductId.KP_DEVICE_KL830.value:
|
||||||
|
return '(KL830)'
|
||||||
|
else:
|
||||||
|
return '(Unknown Device)'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = ScanForTesting("Testing_kl_520")
|
||||||
|
rclpy.spin_once(node)
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
0
camera_kl520/__init__.py
Executable file
133
camera_kl520/camera_yolo5l_pub.py
Executable file
@ -0,0 +1,133 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from vision_msgs.msg import Detection2D, Detection2DArray, BoundingBox2D, ObjectHypothesisWithPose
|
||||||
|
from geometry_msgs.msg import Pose2D
|
||||||
|
from cv_bridge import CvBridge
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import torch
|
||||||
|
|
||||||
|
class YoloInferenceNode_pub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
|
||||||
|
# Create publishers for the annotated image and detection results
|
||||||
|
self.image_pub = self.create_publisher(Image, 'yolo/annotated', 10)
|
||||||
|
self.detections_pub = self.create_publisher(Detection2DArray, 'yolo/detections', 10)
|
||||||
|
|
||||||
|
# Timer to call the callback every 0.2 seconds (approx. 5 FPS)
|
||||||
|
self.timer = self.create_timer(0.2, self.timer_callback)
|
||||||
|
|
||||||
|
# CvBridge for converting between ROS Image messages and OpenCV images
|
||||||
|
self.bridge = CvBridge()
|
||||||
|
|
||||||
|
# Open the camera using the V4L2 backend (suitable for WSL2)
|
||||||
|
self.cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
|
||||||
|
if not self.cap.isOpened():
|
||||||
|
self.get_logger().error("Unable to open camera")
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
# Set the camera format, resolution, and FPS
|
||||||
|
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
|
||||||
|
self.cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FPS, 30)
|
||||||
|
|
||||||
|
self.get_logger().info("Loading YOLOv5 model...")
|
||||||
|
self.model = torch.hub.load('ultralytics/yolov5', 'yolov5l', pretrained=True)
|
||||||
|
|
||||||
|
self.model.to('cuda')
|
||||||
|
self.model.eval()
|
||||||
|
self.get_logger().info("YOLOv5 model loaded.")
|
||||||
|
|
||||||
|
def timer_callback(self):
|
||||||
|
ret, frame = self.cap.read()
|
||||||
|
if not ret:
|
||||||
|
self.get_logger().error("Failed to read image")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Convert BGR image to RGB
|
||||||
|
img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
|
# Run inference
|
||||||
|
results = self.model(img_rgb)
|
||||||
|
|
||||||
|
#---------------------------------------------------------
|
||||||
|
|
||||||
|
# Render detections and get the annotated image (returned in RGB)
|
||||||
|
annotated_frame = results.render()[0]
|
||||||
|
# Convert annotated image back to BGR for cv2 display and ROS Image (bgr8)
|
||||||
|
annotated_frame = cv2.cvtColor(annotated_frame, cv2.COLOR_RGB2BGR)
|
||||||
|
|
||||||
|
# Display the image
|
||||||
|
cv2.imshow("YOLOv5 Inference", annotated_frame)
|
||||||
|
cv2.waitKey(1)
|
||||||
|
|
||||||
|
# Convert annotated_frame to a ROS Image message and publish it
|
||||||
|
img_msg = self.bridge.cv2_to_imgmsg(annotated_frame, encoding='bgr8')
|
||||||
|
header = img_msg.header
|
||||||
|
header.stamp = self.get_clock().now().to_msg()
|
||||||
|
header.frame_id = "C270_WEBCAM"
|
||||||
|
self.image_pub.publish(img_msg)
|
||||||
|
|
||||||
|
# Parse YOLOv5 detection results with the format: [x1, y1, x2, y2, confidence, class]
|
||||||
|
detections = results.xyxy[0]
|
||||||
|
|
||||||
|
# Create a Detection2DArray message
|
||||||
|
detection_array = Detection2DArray()
|
||||||
|
detection_array.header = header
|
||||||
|
|
||||||
|
# Process each detection bounding box
|
||||||
|
for det in detections:
|
||||||
|
x1, y1, x2, y2, conf, cls = det.tolist()
|
||||||
|
detection = Detection2D()
|
||||||
|
detection.header = header
|
||||||
|
|
||||||
|
# Create a BoundingBox2D and set its center and dimensions
|
||||||
|
bbox = BoundingBox2D()
|
||||||
|
bbox.center.position.x = (x1 + x2) / 2.0
|
||||||
|
bbox.center.position.y = (y1 + y2) / 2.0
|
||||||
|
bbox.center.theta = 0.0
|
||||||
|
|
||||||
|
bbox.size_x = x2 - x1
|
||||||
|
bbox.size_y = y2 - y1
|
||||||
|
detection.bbox = bbox
|
||||||
|
|
||||||
|
# Add class and confidence to ObjectHypothesisWithPose
|
||||||
|
hypothesis = ObjectHypothesisWithPose()
|
||||||
|
hypothesis.hypothesis.class_id = str(int(cls)) # Class represented as an integer string
|
||||||
|
hypothesis.hypothesis.score = conf
|
||||||
|
|
||||||
|
# Append the hypothesis to the detection results
|
||||||
|
detection.results.append(hypothesis)
|
||||||
|
|
||||||
|
# Append the detection to the detection array
|
||||||
|
detection_array.detections.append(detection)
|
||||||
|
|
||||||
|
# Publish the detection array so that other ROS2 nodes can subscribe to it
|
||||||
|
self.detections_pub.publish(detection_array)
|
||||||
|
detections = results.xyxy[0] # Each row represents one detection
|
||||||
|
|
||||||
|
for i, det in enumerate(detections):
|
||||||
|
x1, y1, x2, y2, conf, cls = det.tolist()
|
||||||
|
self.get_logger().info(
|
||||||
|
f"object class {cls}, Confidence: {conf:.2f}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = YoloInferenceNode_pub("yolo_inference_pub")
|
||||||
|
try:
|
||||||
|
rclpy.spin(node)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
node.get_logger().info("Shutting down...")
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
50
camera_kl520/camera_yolo5l_sub.py
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from vision_msgs.msg import Detection2D, Detection2DArray, BoundingBox2D, ObjectHypothesisWithPose
|
||||||
|
from geometry_msgs.msg import Pose2D
|
||||||
|
from cv_bridge import CvBridge
|
||||||
|
|
||||||
|
class YoloInferenceNode_pub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
# self.image_sub = self.create_subscription(Image, 'yolo/annotated',self.listener_callback, 10)
|
||||||
|
self.detections_sub = self.create_subscription(Detection2DArray, 'yolo/detections', self.listener_callback, 10)
|
||||||
|
|
||||||
|
def listener_callback(self, detection_array_msg):
|
||||||
|
# Check if there are any detections at all
|
||||||
|
if not detection_array_msg.detections:
|
||||||
|
self.get_logger().warn("No detections found in the received message.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Loop through each Detection2D message in the detection array
|
||||||
|
for det_index, detection in enumerate(detection_array_msg.detections):
|
||||||
|
# Check if the detection contains any results/hypotheses
|
||||||
|
if not detection.results:
|
||||||
|
self.get_logger().warn(f"Detection {det_index} has no results.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Process each hypothesis for this detection
|
||||||
|
for hyp_index, hypothesis_wrapper in enumerate(detection.results):
|
||||||
|
# Accessing the nested hypothesis fields
|
||||||
|
class_id = hypothesis_wrapper.hypothesis.class_id
|
||||||
|
score = hypothesis_wrapper.hypothesis.score
|
||||||
|
self.get_logger().info(
|
||||||
|
f"object class {class_id}, Confidence: {score:.2f}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = YoloInferenceNode_pub("yolo_inference_sub")
|
||||||
|
try:
|
||||||
|
rclpy.spin(node)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
node.get_logger().info("Shutting down...")
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
305
camera_kl520/camera_yolo5v_kl520_pub.py
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
# ******************************************************************************
|
||||||
|
# Copyright (c) 2021-2022. Kneron Inc. All rights reserved. *
|
||||||
|
# ******************************************************************************
|
||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
# from std_msgs.msg import String
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from vision_msgs.msg import Detection2D, Detection2DArray, BoundingBox2D, ObjectHypothesisWithPose
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from cv_bridge import CvBridge
|
||||||
|
|
||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
# current file directory pwd
|
||||||
|
# PWD = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
# sys.path.insert(1, os.path.join(PWD, '..'))
|
||||||
|
|
||||||
|
pkg_name = 'camera_kl520'
|
||||||
|
pkg_share = get_package_share_directory(pkg_name)
|
||||||
|
|
||||||
|
from utils.ExampleHelper import get_device_usb_speed_by_port_id
|
||||||
|
from utils.ExamplePostProcess import post_process_yolo_v5
|
||||||
|
import kp
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
# firmware sources
|
||||||
|
SCPU_FW_PATH = os.path.join(pkg_share, 'res', 'firmware', 'KL520', 'fw_scpu.bin')
|
||||||
|
NCPU_FW_PATH = os.path.join(pkg_share, 'res', 'firmware', 'KL520', 'fw_ncpu.bin')
|
||||||
|
|
||||||
|
# model and image file path
|
||||||
|
MODEL_FILE_PATH = os.path.join(pkg_share, 'res', 'models', 'KL520', 'yolov5-noupsample_w640h640_kn-model-zoo', 'kl520_20005_yolov5-noupsample_w640h640.nef')
|
||||||
|
IMAGE_FILE_PATH = os.path.join(pkg_share, 'res', 'images', 'testing_920x720.jpg')
|
||||||
|
|
||||||
|
class ImageClassifierNode_kl520_pub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
|
||||||
|
self.count = 0
|
||||||
|
self.declare_parameter('port_id', 0)
|
||||||
|
self.declare_parameter('model', MODEL_FILE_PATH)
|
||||||
|
self.declare_parameter('img', IMAGE_FILE_PATH)
|
||||||
|
|
||||||
|
self.usb_port_id = self.get_parameter('port_id').value
|
||||||
|
self.model_path = self.get_parameter('model').value
|
||||||
|
self.image_file_path = self.get_parameter('img').value
|
||||||
|
|
||||||
|
# Scan the device, get necessary parameters
|
||||||
|
self.ScanProduct()
|
||||||
|
|
||||||
|
self.image_pub = self.create_publisher(Image, 'camera_yolov5_kl520/annotated', 10)
|
||||||
|
self.detections_pub = self.create_publisher(Detection2DArray, 'camera_yolov5_kl520/detections', 10)
|
||||||
|
|
||||||
|
# Prepare Images
|
||||||
|
self.Prepare_image()
|
||||||
|
|
||||||
|
# Create directory for images
|
||||||
|
images_dir = os.path.join(pkg_share, "images")
|
||||||
|
os.makedirs(images_dir, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print('[Starting Inference Work]')
|
||||||
|
self.timer = self.create_timer(0.2, self.Inference) # 5fps
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def ScanProduct(self):
|
||||||
|
"""
|
||||||
|
check device USB speed (Recommend run KL520 at high speed)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if kp.UsbSpeed.KP_USB_SPEED_HIGH != get_device_usb_speed_by_port_id(usb_port_id=self.usb_port_id):
|
||||||
|
print('\033[91m' + '[Warning] Device is not run at high speed.' + '\033[0m')
|
||||||
|
except Exception as exception:
|
||||||
|
print('Error: check device USB speed fail, port ID = \'{}\', error msg: [{}]'.format(self.usb_port_id,
|
||||||
|
str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
connect the device
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print('[Connect Device]')
|
||||||
|
self.device_group = kp.core.connect_devices(usb_port_ids=[self.usb_port_id])
|
||||||
|
print(' - Success')
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print('Error: connect device fail, port ID = \'{}\', error msg: [{}]'.format(self.usb_port_id,
|
||||||
|
str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
setting timeout of the usb communication with the device
|
||||||
|
"""
|
||||||
|
print('[Set Device Timeout]')
|
||||||
|
kp.core.set_timeout(device_group=self.device_group, milliseconds=5000)
|
||||||
|
print(' - Success')
|
||||||
|
|
||||||
|
"""
|
||||||
|
upload firmware to device
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print('[Upload Firmware]')
|
||||||
|
kp.core.load_firmware_from_file(device_group=self.device_group,
|
||||||
|
scpu_fw_path=SCPU_FW_PATH,
|
||||||
|
ncpu_fw_path=NCPU_FW_PATH)
|
||||||
|
print(' - Success')
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print('Error: upload firmware failed, error = \'{}\''.format(str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
upload model to device
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print('[Upload Model]')
|
||||||
|
self.model_nef_descriptor = kp.core.load_model_from_file(device_group=self.device_group,
|
||||||
|
file_path=self.model_path)
|
||||||
|
print(' - Success')
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print('Error: upload model failed, error = \'{}\''.format(str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
def Prepare_image(self):
|
||||||
|
"""
|
||||||
|
Set the camera format, resolution, and FPS
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("[Configuring Image]")
|
||||||
|
|
||||||
|
self.bridge = CvBridge()
|
||||||
|
|
||||||
|
self.cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
|
||||||
|
if not self.cap.isOpened():
|
||||||
|
self.get_logger().error("Unable to open camera")
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
|
||||||
|
self.cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 640)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FPS, 5)
|
||||||
|
|
||||||
|
|
||||||
|
def Inference(self):
|
||||||
|
"""
|
||||||
|
starting inference work
|
||||||
|
"""
|
||||||
|
|
||||||
|
print(' - ', end='')
|
||||||
|
|
||||||
|
self.ret, self.frame = self.cap.read()
|
||||||
|
if not self.ret:
|
||||||
|
self.get_logger().error("Falied to read image")
|
||||||
|
return
|
||||||
|
|
||||||
|
# self.img = cv2.imread(filename=self.image_file_path)
|
||||||
|
# self.img = cv2.resize(self.frame, (640, 640)) # modify the size of the image to fit the model
|
||||||
|
self.img = cv2.resize(self.frame, (960, 720)) # modify the size of the image to fit the model
|
||||||
|
self.img_bgr565 = cv2.cvtColor(src=self.img, code=cv2.COLOR_BGR2BGR565)
|
||||||
|
|
||||||
|
"""
|
||||||
|
prepare generic image inference input descriptor
|
||||||
|
"""
|
||||||
|
self.generic_inference_input_descriptor = kp.GenericImageInferenceDescriptor(
|
||||||
|
model_id=self.model_nef_descriptor.models[0].id,
|
||||||
|
inference_number=0,
|
||||||
|
input_node_image_list=[
|
||||||
|
kp.GenericInputNodeImage(
|
||||||
|
image=self.img_bgr565,
|
||||||
|
image_format=kp.ImageFormat.KP_IMAGE_FORMAT_RGB565,
|
||||||
|
resize_mode=kp.ResizeMode.KP_RESIZE_ENABLE,
|
||||||
|
padding_mode=kp.PaddingMode.KP_PADDING_CORNER,
|
||||||
|
normalize_mode=kp.NormalizeMode.KP_NORMALIZE_KNERON
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
kp.inference.generic_image_inference_send(device_group=self.device_group,
|
||||||
|
generic_inference_input_descriptor=self.generic_inference_input_descriptor)
|
||||||
|
|
||||||
|
self.generic_raw_result = kp.inference.generic_image_inference_receive(device_group=self.device_group)
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print(' - Error: inference failed, error = {}'.format(exception))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
print('.', end='', flush=True)
|
||||||
|
print()
|
||||||
|
|
||||||
|
"""
|
||||||
|
retrieve inference node output
|
||||||
|
"""
|
||||||
|
print('[Retrieve Inference Node Output ]')
|
||||||
|
self.inf_node_output_list = []
|
||||||
|
for node_idx in range(self.generic_raw_result.header.num_output_node):
|
||||||
|
inference_float_node_output = kp.inference.generic_inference_retrieve_float_node(
|
||||||
|
node_idx=node_idx,
|
||||||
|
generic_raw_result=self.generic_raw_result,
|
||||||
|
channels_ordering=kp.ChannelOrdering.KP_CHANNEL_ORDERING_CHW
|
||||||
|
)
|
||||||
|
self.inf_node_output_list.append(inference_float_node_output)
|
||||||
|
|
||||||
|
print(' - Success')
|
||||||
|
|
||||||
|
self.yolo_result = post_process_yolo_v5(inference_float_node_output_list=self.inf_node_output_list,
|
||||||
|
hardware_preproc_info=self.generic_raw_result.header.hw_pre_proc_info_list[0],
|
||||||
|
thresh_value=0.2)
|
||||||
|
|
||||||
|
# # msg = String()
|
||||||
|
# coords = []
|
||||||
|
# print(" - Bounding boxes info (xmin,ymin,xmax,ymax, confidence, class):")
|
||||||
|
# print(" - Bounding boxes info (xmin,ymin,xmax,ymax, confidence, class):")
|
||||||
|
output_img_name = f'output_{self.count}_{os.path.basename(self.image_file_path)}'
|
||||||
|
self.count += 1
|
||||||
|
print(f"stored image {output_img_name}")
|
||||||
|
|
||||||
|
# for box in self.yolo_result.box_list:
|
||||||
|
# b = 100 + (25 * box.class_num) % 156
|
||||||
|
# g = 100 + (80 + 40 * box.class_num) % 156
|
||||||
|
# r = 100 + (120 + 60 * box.class_num) % 156
|
||||||
|
# color = (b, g, r)
|
||||||
|
|
||||||
|
# cv2.rectangle(img=self.img,
|
||||||
|
# pt1=(int(box.x1), int(box.y1)),
|
||||||
|
# pt2=(int(box.x2), int(box.y2)),
|
||||||
|
# color=color,
|
||||||
|
# thickness=2)
|
||||||
|
# coords.append(f"[{int(box.x1)}, {int(box.y1)}, {int(box.x2)}, {int(box.y2)}], confidence : {box.score}, class : {box.class_num}\n")
|
||||||
|
|
||||||
|
# msg.data = "".join(coords)
|
||||||
|
# self.image_pub.publish(msg)
|
||||||
|
# self.get_logger().info(f"Published: \n{msg.data}")
|
||||||
|
annotated = self.img.copy()
|
||||||
|
det_array = Detection2DArray()
|
||||||
|
det_array.header.stamp = self.get_clock().now().to_msg()
|
||||||
|
det_array.header.frame_id = "C270_WEBCAM"
|
||||||
|
|
||||||
|
for box in self.yolo_result.box_list:
|
||||||
|
b = 100 + (25 * box.class_num) % 156
|
||||||
|
g = 100 + (80 + 40 * box.class_num) % 156
|
||||||
|
r = 100 + (120 + 60 * box.class_num) % 156
|
||||||
|
color = (b, g, r)
|
||||||
|
cv2.rectangle(img=annotated,
|
||||||
|
pt1=(int(box.x1), int(box.y1)),
|
||||||
|
pt2=(int(box.x2), int(box.y2)),
|
||||||
|
color=color,
|
||||||
|
thickness=2)
|
||||||
|
|
||||||
|
detection = Detection2D()
|
||||||
|
detection.header = det_array.header
|
||||||
|
|
||||||
|
# Create a BoundingBox2D and set its center and dimensions
|
||||||
|
bbox = BoundingBox2D()
|
||||||
|
bbox.center.position.x = (box.x1 + box.x2) / 2.0
|
||||||
|
bbox.center.position.y = (box.y1 + box.y2) / 2.0
|
||||||
|
bbox.center.theta = 0.0
|
||||||
|
|
||||||
|
bbox.size_x = float(box.x2 - box.x1)
|
||||||
|
bbox.size_y = float(box.y2 - box.y1)
|
||||||
|
detection.bbox = bbox
|
||||||
|
|
||||||
|
# Add class and confidence to ObjectHypothesisWithPose
|
||||||
|
hypo = ObjectHypothesisWithPose()
|
||||||
|
hypo.hypothesis.class_id = str(int(box.class_num))
|
||||||
|
hypo.hypothesis.score = box.score
|
||||||
|
|
||||||
|
# Append the hypothesis to the detection results
|
||||||
|
detection.results.append(hypo)
|
||||||
|
|
||||||
|
# Append the detection to the detection array
|
||||||
|
det_array.detections.append(detection)
|
||||||
|
|
||||||
|
self.get_logger().info(
|
||||||
|
f"class: {box.class_num}, conf: {box.score:.2f}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Publish the detection array so that other ROS2 nodes can subscribe to it
|
||||||
|
img_msg = self.bridge.cv2_to_imgmsg(annotated, encoding='bgr8')
|
||||||
|
img_msg.header = det_array.header # under same time line
|
||||||
|
self.image_pub.publish(img_msg)
|
||||||
|
self.detections_pub.publish(det_array)
|
||||||
|
# Store the image for debugging.
|
||||||
|
|
||||||
|
cv2.imwrite(os.path.join(pkg_share, "images", './{}'.format(output_img_name)), img=annotated)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main(args = None):
|
||||||
|
rclpy.init(args = args)
|
||||||
|
node = ImageClassifierNode_kl520_pub("camera_yolov5_kl520_pub")
|
||||||
|
try:
|
||||||
|
rclpy.spin(node)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
node.get_logger().info("shutting down...")
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
91
camera_kl520/camera_yolo5v_kl520_sub.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
# from std_msgs.msg import String
|
||||||
|
# from sensor_msgs.msg import Image
|
||||||
|
import rclpy
|
||||||
|
import rclpy.executors
|
||||||
|
from rclpy.node import Node
|
||||||
|
from vision_msgs.msg import Detection2DArray
|
||||||
|
# We only need Detection2DArray since we packaged everything inside this 2D Array in the publisher Node.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
from ament_index_python.packages import get_package_prefix
|
||||||
|
|
||||||
|
prefix = get_package_prefix('ur_robot_driver')
|
||||||
|
scripts_dir = os.path.join(prefix, 'lib', 'ur_robot_driver') # where ur3e_actions.py located.
|
||||||
|
sys.path.append(scripts_dir)
|
||||||
|
|
||||||
|
from ur3e_actions import JTCClient
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ImageClassifierNode_sub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
self.arm_busy = False
|
||||||
|
self.arm = JTCClient("ur3e_traj_client")
|
||||||
|
|
||||||
|
def wrap_result_cb():
|
||||||
|
self.arm_busy = False # unlock
|
||||||
|
self.get_logger().info("arm done → resume detection")
|
||||||
|
|
||||||
|
self.arm.result_cb = wrap_result_cb
|
||||||
|
|
||||||
|
self.sub = self.create_subscription(Detection2DArray, 'camera_yolov5_kl520/detections',self.listener_callback, 10)
|
||||||
|
|
||||||
|
def listener_callback(self, detection_array_msg):
|
||||||
|
# msg = msg.data
|
||||||
|
# self.get_logger().info(f"I received msg \n{msg}")
|
||||||
|
|
||||||
|
if self.arm_busy:
|
||||||
|
print("Moving......")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not detection_array_msg.detections:
|
||||||
|
self.get_logger().info("No detections found in the received message.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Loop through each Detection2D message in the detection array
|
||||||
|
print("-"*20)
|
||||||
|
for det_index, detection in enumerate(detection_array_msg.detections):
|
||||||
|
# Check if the detection contains any results/hypotheses
|
||||||
|
if not detection.results:
|
||||||
|
self.get_logger().warn(f"Detection {det_index} has no results.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Process each hypothesis for this detection
|
||||||
|
for hyp_index, hypothesis_wrapper in enumerate(detection.results):
|
||||||
|
# Accessing the nested hypothesis fields
|
||||||
|
class_id = hypothesis_wrapper.hypothesis.class_id
|
||||||
|
score = hypothesis_wrapper.hypothesis.score
|
||||||
|
self.get_logger().info(
|
||||||
|
f"object class {class_id}, Confidence: {score:.2f}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if class_id == "0" and score > 0.5:
|
||||||
|
self.arm_busy = True # lock it
|
||||||
|
fut = self.arm.execute_trajectory('traj0') # received result_future
|
||||||
|
fut.add_done_callback(lambda _: setattr(self, "arm_busy", False))
|
||||||
|
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = ImageClassifierNode_sub("camera_yolov5_kl520_sub")
|
||||||
|
arm_node = node.arm
|
||||||
|
|
||||||
|
executor = rclpy.executors.MultiThreadedExecutor()
|
||||||
|
executor.add_node(node)
|
||||||
|
executor.add_node(arm_node)
|
||||||
|
|
||||||
|
try:
|
||||||
|
executor.spin()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
node.get_logger().info("Shutting down...")
|
||||||
|
node.destroy_node()
|
||||||
|
arm_node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
279
camera_kl520/camera_yolo5v_kl720_pub.py
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
# ******************************************************************************
|
||||||
|
# Copyright (c) 2021-2022. Kneron Inc. All rights reserved. *
|
||||||
|
# ******************************************************************************
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
# from std_msgs.msg import String
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
from vision_msgs.msg import Detection2D, Detection2DArray, BoundingBox2D, ObjectHypothesisWithPose
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from cv_bridge import CvBridge
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# PWD = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
# sys.path.insert(1, os.path.join(PWD, '..'))
|
||||||
|
# sys.path.insert(1, os.path.join(PWD, '../example/'))
|
||||||
|
|
||||||
|
pkg_name = 'camera_kl520'
|
||||||
|
pkg_share = get_package_share_directory(pkg_name)
|
||||||
|
|
||||||
|
from utils.ExampleHelper import get_device_usb_speed_by_port_id
|
||||||
|
from utils.ExamplePostProcess import post_process_yolo_v5
|
||||||
|
import kp
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
# model and image file path
|
||||||
|
MODEL_FILE_PATH = os.path.join(pkg_share, 'res', 'models', 'kl720', 'yolov5-noupsample_w640h640_kn-model-zoo/kl720_20005_yolov5-noupsample_w640h640.nef')
|
||||||
|
IMAGE_FILE_PATH = os.path.join(pkg_share, 'res', 'images', 'kl720', 'people_talk_in_street_1500x1500.bmp')
|
||||||
|
|
||||||
|
|
||||||
|
class ImageClassifierNode_kl720_pub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
|
||||||
|
self.count = 0
|
||||||
|
self.declare_parameter('port_id', 0)
|
||||||
|
self.declare_parameter('model', MODEL_FILE_PATH)
|
||||||
|
self.declare_parameter('img', IMAGE_FILE_PATH)
|
||||||
|
|
||||||
|
self.usb_port_id = self.get_parameter('port_id').value
|
||||||
|
self.model_path = self.get_parameter('model').value
|
||||||
|
self.image_file_path = self.get_parameter('img').value
|
||||||
|
|
||||||
|
# Scan the device, get necessary parameters
|
||||||
|
self.ScanProduct()
|
||||||
|
|
||||||
|
self.image_pub = self.create_publisher(Image, 'camera_yolov5_kl720/annotated', 10)
|
||||||
|
self.detections_pub = self.create_publisher(Detection2DArray, 'camera_yolov5_kl720/detections', 10)
|
||||||
|
|
||||||
|
# Prepare Images
|
||||||
|
self.Prepare_image()
|
||||||
|
|
||||||
|
# Create directory for images
|
||||||
|
images_dir = os.path.join(pkg_share, "images")
|
||||||
|
os.makedirs(images_dir, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
print('[Starting Inference Work]')
|
||||||
|
self.timer = self.create_timer(0.1, self.Inference) # 10fps
|
||||||
|
|
||||||
|
def ScanProduct(self):
|
||||||
|
"""
|
||||||
|
check device USB speed (Recommend run KL720 at super speed)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if kp.UsbSpeed.KP_USB_SPEED_SUPER != get_device_usb_speed_by_port_id(usb_port_id=self.usb_port_id):
|
||||||
|
print('\033[91m' + '[Warning] Device is not run at super speed.' + '\033[0m')
|
||||||
|
except Exception as exception:
|
||||||
|
print('Error: check device USB speed fail, port ID = \'{}\', error msg: [{}]'.format(self.usb_port_id,
|
||||||
|
str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
connect the device
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print('[Connect Device]')
|
||||||
|
device_group = kp.core.connect_devices(usb_port_ids=[self.usb_port_id])
|
||||||
|
print(' - Success')
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print('Error: connect device fail, port ID = \'{}\', error msg: [{}]'.format(self.usb_port_id,
|
||||||
|
str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
setting timeout of the usb communication with the device
|
||||||
|
"""
|
||||||
|
print('[Set Device Timeout]')
|
||||||
|
kp.core.set_timeout(device_group=device_group, milliseconds=5000)
|
||||||
|
print(' - Success')
|
||||||
|
|
||||||
|
"""
|
||||||
|
upload model to device
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print('[Upload Model]')
|
||||||
|
model_nef_descriptor = kp.core.load_model_from_file(device_group=device_group,
|
||||||
|
file_path=self.model_path)
|
||||||
|
print(' - Success')
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print('Error: upload model failed, error = \'{}\''.format(str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
def Prepare_image(self):
|
||||||
|
"""
|
||||||
|
Set the camera format, resolution, and FPS
|
||||||
|
"""
|
||||||
|
|
||||||
|
print("[Configuring Image]")
|
||||||
|
|
||||||
|
self.bridge = CvBridge()
|
||||||
|
|
||||||
|
self.cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
|
||||||
|
if not self.cap.isOpened():
|
||||||
|
self.get_logger().error("Unable to open camera")
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
|
||||||
|
self.cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 640)
|
||||||
|
self.cap.set(cv2.CAP_PROP_FPS, 5)
|
||||||
|
|
||||||
|
def Inference(self):
|
||||||
|
"""
|
||||||
|
starting inference work
|
||||||
|
"""
|
||||||
|
|
||||||
|
print(' - ', end='')
|
||||||
|
|
||||||
|
self.ret, self.frame = self.cap.read()
|
||||||
|
if not self.ret:
|
||||||
|
self.get_logger().error("Falied to read image")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.img = cv2.resize(self.frame, (960, 720)) # modify the size of the image to fit the model
|
||||||
|
self.img_bgr565 = cv2.cvtColor(src=self.img, code=cv2.COLOR_BGR2BGR565)
|
||||||
|
|
||||||
|
print(' - Success')
|
||||||
|
|
||||||
|
"""
|
||||||
|
prepare generic image inference input descriptor
|
||||||
|
"""
|
||||||
|
self.generic_inference_input_descriptor = kp.GenericImageInferenceDescriptor(
|
||||||
|
model_id=self.model_nef_descriptor.models[0].id,
|
||||||
|
inference_number=0,
|
||||||
|
input_node_image_list=[
|
||||||
|
kp.GenericInputNodeImage(
|
||||||
|
image=self.img_bgr565,
|
||||||
|
image_format=kp.ImageFormat.KP_IMAGE_FORMAT_RGB565,
|
||||||
|
resize_mode=kp.ResizeMode.KP_RESIZE_ENABLE,
|
||||||
|
padding_mode=kp.PaddingMode.KP_PADDING_CORNER,
|
||||||
|
normalize_mode=kp.NormalizeMode.KP_NORMALIZE_KNERON
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
kp.inference.generic_image_inference_send(device_group=self.device_group,
|
||||||
|
generic_inference_input_descriptor=self.generic_inference_input_descriptor)
|
||||||
|
|
||||||
|
self.generic_raw_result = kp.inference.generic_image_inference_receive(device_group=self.device_group)
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print(' - Error: inference failed, error = {}'.format(exception))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
print('.', end='', flush=True)
|
||||||
|
print()
|
||||||
|
|
||||||
|
"""
|
||||||
|
retrieve inference node output
|
||||||
|
"""
|
||||||
|
print('[Retrieve Inference Node Output ]')
|
||||||
|
|
||||||
|
self.inf_node_output_list = []
|
||||||
|
for node_idx in range(self.generic_raw_result.header.num_output_node):
|
||||||
|
inference_float_node_output = kp.inference.generic_inference_retrieve_float_node(
|
||||||
|
node_idx=node_idx,
|
||||||
|
generic_raw_result=self.generic_raw_result,
|
||||||
|
channels_ordering=kp.ChannelOrdering.KP_CHANNEL_ORDERING_CHW
|
||||||
|
)
|
||||||
|
self.inf_node_output_list.append(inference_float_node_output)
|
||||||
|
|
||||||
|
print(' - Success')
|
||||||
|
|
||||||
|
self.yolo_result = post_process_yolo_v5(inference_float_node_output_list=self.inf_node_output_list,
|
||||||
|
hardware_preproc_info=self.generic_raw_result.header.hw_pre_proc_info_list[0],
|
||||||
|
thresh_value=0.2)
|
||||||
|
|
||||||
|
output_img_name = f'output_{self.count}_{os.path.basename(self.image_file_path)}'
|
||||||
|
self.count += 1
|
||||||
|
print(f"stored image {output_img_name}")
|
||||||
|
|
||||||
|
# for box in self.yolo_result.box_list:
|
||||||
|
# b = 100 + (25 * box.class_num) % 156
|
||||||
|
# g = 100 + (80 + 40 * box.class_num) % 156
|
||||||
|
# r = 100 + (120 + 60 * box.class_num) % 156
|
||||||
|
# color = (b, g, r)
|
||||||
|
|
||||||
|
# cv2.rectangle(img=self.img,
|
||||||
|
# pt1=(int(box.x1), int(box.y1)),
|
||||||
|
# pt2=(int(box.x2), int(box.y2)),
|
||||||
|
# color=color,
|
||||||
|
# thickness=2)
|
||||||
|
# coords.append(f"[{int(box.x1)}, {int(box.y1)}, {int(box.x2)}, {int(box.y2)}], confidence : {box.score}, class : {box.class_num}\n")
|
||||||
|
|
||||||
|
# msg.data = "".join(coords)
|
||||||
|
# self.image_pub.publish(msg)
|
||||||
|
# self.get_logger().info(f"Published: \n{msg.data}")
|
||||||
|
annotated = self.img.copy()
|
||||||
|
det_array = Detection2DArray()
|
||||||
|
det_array.header.stamp = self.get_clock().now().to_msg()
|
||||||
|
det_array.header.frame_id = "C270_WEBCAM"
|
||||||
|
|
||||||
|
for box in self.yolo_result.box_list:
|
||||||
|
b = 100 + (25 * box.class_num) % 156
|
||||||
|
g = 100 + (80 + 40 * box.class_num) % 156
|
||||||
|
r = 100 + (120 + 60 * box.class_num) % 156
|
||||||
|
color = (b, g, r)
|
||||||
|
cv2.rectangle(img=annotated,
|
||||||
|
pt1=(int(box.x1), int(box.y1)),
|
||||||
|
pt2=(int(box.x2), int(box.y2)),
|
||||||
|
color=color,
|
||||||
|
thickness=2)
|
||||||
|
|
||||||
|
detection = Detection2D()
|
||||||
|
detection.header = det_array.header
|
||||||
|
|
||||||
|
# Create a BoundingBox2D and set its center and dimensions
|
||||||
|
bbox = BoundingBox2D()
|
||||||
|
bbox.center.position.x = (box.x1 + box.x2) / 2.0
|
||||||
|
bbox.center.position.y = (box.y1 + box.y2) / 2.0
|
||||||
|
bbox.center.theta = 0.0
|
||||||
|
|
||||||
|
bbox.size_x = float(box.x2 - box.x1)
|
||||||
|
bbox.size_y = float(box.y2 - box.y1)
|
||||||
|
detection.bbox = bbox
|
||||||
|
|
||||||
|
# Add class and confidence to ObjectHypothesisWithPose
|
||||||
|
hypo = ObjectHypothesisWithPose()
|
||||||
|
hypo.hypothesis.class_id = str(int(box.class_num))
|
||||||
|
hypo.hypothesis.score = box.score
|
||||||
|
|
||||||
|
# Append the hypothesis to the detection results
|
||||||
|
detection.results.append(hypo)
|
||||||
|
|
||||||
|
# Append the detection to the detection array
|
||||||
|
det_array.detections.append(detection)
|
||||||
|
|
||||||
|
self.get_logger().info(
|
||||||
|
f"class: {box.class_num}, conf: {box.score:.2f}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Publish the detection array so that other ROS2 nodes can subscribe to it
|
||||||
|
img_msg = self.bridge.cv2_to_imgmsg(annotated, encoding='bgr8')
|
||||||
|
img_msg.header = det_array.header # under same time line
|
||||||
|
self.image_pub.publish(img_msg)
|
||||||
|
self.detections_pub.publish(det_array)
|
||||||
|
# Store the image for debugging.
|
||||||
|
cv2.imwrite(os.path.join(pkg_share, "images", './{}'.format(output_img_name)), img=annotated)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main(args = None):
|
||||||
|
rclpy.init(args = args)
|
||||||
|
node = ImageClassifierNode_kl720_pub("camera_yolov5_kl720_pub")
|
||||||
|
try:
|
||||||
|
rclpy.spin(node)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
node.get_logger().info("shutting down...")
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
91
camera_kl520/camera_yolo5v_kl720_sub.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
# from std_msgs.msg import String
|
||||||
|
# from sensor_msgs.msg import Image
|
||||||
|
import rclpy
|
||||||
|
import rclpy.executors
|
||||||
|
from rclpy.node import Node
|
||||||
|
from vision_msgs.msg import Detection2DArray
|
||||||
|
# We only need Detection2DArray since we packaged everything inside this 2D Array in the publisher Node.
|
||||||
|
|
||||||
|
import sys, os
|
||||||
|
from ament_index_python.packages import get_package_prefix
|
||||||
|
|
||||||
|
prefix = get_package_prefix('ur_robot_driver')
|
||||||
|
scripts_dir = os.path.join(prefix, 'lib', 'ur_robot_driver') # where ur3e_actions.py located.
|
||||||
|
sys.path.append(scripts_dir)
|
||||||
|
|
||||||
|
from ur3e_actions import JTCClient
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ImageClassifierNode_sub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
self.arm_busy = False
|
||||||
|
self.arm = JTCClient("ur3e_traj_client")
|
||||||
|
|
||||||
|
def wrap_result_cb():
|
||||||
|
self.arm_busy = False # unlock
|
||||||
|
self.get_logger().info("arm done → resume detection")
|
||||||
|
|
||||||
|
self.arm.result_cb = wrap_result_cb
|
||||||
|
|
||||||
|
self.sub = self.create_subscription(Detection2DArray, 'camera_yolov5_kl520/detections',self.listener_callback, 10)
|
||||||
|
|
||||||
|
def listener_callback(self, detection_array_msg):
|
||||||
|
# msg = msg.data
|
||||||
|
# self.get_logger().info(f"I received msg \n{msg}")
|
||||||
|
|
||||||
|
if self.arm_busy:
|
||||||
|
print("Moving......")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not detection_array_msg.detections:
|
||||||
|
self.get_logger().info("No detections found in the received message.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Loop through each Detection2D message in the detection array
|
||||||
|
print("-"*20)
|
||||||
|
for det_index, detection in enumerate(detection_array_msg.detections):
|
||||||
|
# Check if the detection contains any results/hypotheses
|
||||||
|
if not detection.results:
|
||||||
|
self.get_logger().warn(f"Detection {det_index} has no results.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Process each hypothesis for this detection
|
||||||
|
for hyp_index, hypothesis_wrapper in enumerate(detection.results):
|
||||||
|
# Accessing the nested hypothesis fields
|
||||||
|
class_id = hypothesis_wrapper.hypothesis.class_id
|
||||||
|
score = hypothesis_wrapper.hypothesis.score
|
||||||
|
self.get_logger().info(
|
||||||
|
f"object class {class_id}, Confidence: {score:.2f}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if class_id == "0" and score > 0.5:
|
||||||
|
self.arm_busy = True # lock it
|
||||||
|
fut = self.arm.execute_trajectory('traj0') # received result_future
|
||||||
|
fut.add_done_callback(lambda _: setattr(self, "arm_busy", False))
|
||||||
|
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = ImageClassifierNode_sub("camera_yolov5_kl520_sub")
|
||||||
|
arm_node = node.arm
|
||||||
|
|
||||||
|
executor = rclpy.executors.MultiThreadedExecutor()
|
||||||
|
executor.add_node(node)
|
||||||
|
executor.add_node(arm_node)
|
||||||
|
|
||||||
|
try:
|
||||||
|
executor.spin()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
node.get_logger().info("Shutting down...")
|
||||||
|
node.destroy_node()
|
||||||
|
arm_node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
81
camera_kl520/infos/coco.txt
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
{0: u'__background__',
|
||||||
|
1: u'person',
|
||||||
|
2: u'bicycle',
|
||||||
|
3: u'car',
|
||||||
|
4: u'motorcycle',
|
||||||
|
5: u'airplane',
|
||||||
|
6: u'bus',
|
||||||
|
7: u'train',
|
||||||
|
8: u'truck',
|
||||||
|
9: u'boat',
|
||||||
|
10: u'traffic light',
|
||||||
|
11: u'fire hydrant',
|
||||||
|
12: u'stop sign',
|
||||||
|
13: u'parking meter',
|
||||||
|
14: u'bench',
|
||||||
|
15: u'bird',
|
||||||
|
16: u'cat',
|
||||||
|
17: u'dog',
|
||||||
|
18: u'horse',
|
||||||
|
19: u'sheep',
|
||||||
|
20: u'cow',
|
||||||
|
21: u'elephant',
|
||||||
|
22: u'bear',
|
||||||
|
23: u'zebra',
|
||||||
|
24: u'giraffe',
|
||||||
|
25: u'backpack',
|
||||||
|
26: u'umbrella',
|
||||||
|
27: u'handbag',
|
||||||
|
28: u'tie',
|
||||||
|
29: u'suitcase',
|
||||||
|
30: u'frisbee',
|
||||||
|
31: u'skis',
|
||||||
|
32: u'snowboard',
|
||||||
|
33: u'sports ball',
|
||||||
|
34: u'kite',
|
||||||
|
35: u'baseball bat',
|
||||||
|
36: u'baseball glove',
|
||||||
|
37: u'skateboard',
|
||||||
|
38: u'surfboard',
|
||||||
|
39: u'tennis racket',
|
||||||
|
40: u'bottle',
|
||||||
|
41: u'wine glass',
|
||||||
|
42: u'cup',
|
||||||
|
43: u'fork',
|
||||||
|
44: u'knife',
|
||||||
|
45: u'spoon',
|
||||||
|
46: u'bowl',
|
||||||
|
47: u'banana',
|
||||||
|
48: u'apple',
|
||||||
|
49: u'sandwich',
|
||||||
|
50: u'orange',
|
||||||
|
51: u'broccoli',
|
||||||
|
52: u'carrot',
|
||||||
|
53: u'hot dog',
|
||||||
|
54: u'pizza',
|
||||||
|
55: u'donut',
|
||||||
|
56: u'cake',
|
||||||
|
57: u'chair',
|
||||||
|
58: u'couch',
|
||||||
|
59: u'potted plant',
|
||||||
|
60: u'bed',
|
||||||
|
61: u'dining table',
|
||||||
|
62: u'toilet',
|
||||||
|
63: u'tv',
|
||||||
|
64: u'laptop',
|
||||||
|
65: u'mouse',
|
||||||
|
66: u'remote',
|
||||||
|
67: u'keyboard',
|
||||||
|
68: u'cell phone',
|
||||||
|
69: u'microwave',
|
||||||
|
70: u'oven',
|
||||||
|
71: u'toaster',
|
||||||
|
72: u'sink',
|
||||||
|
73: u'refrigerator',
|
||||||
|
74: u'book',
|
||||||
|
75: u'clock',
|
||||||
|
76: u'vase',
|
||||||
|
77: u'scissors',
|
||||||
|
78: u'teddy bear',
|
||||||
|
79: u'hair drier',
|
||||||
|
80: u'toothbrush'}
|
||||||
72
camera_kl520/object_detection.py
Executable file
@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from vision_msgs.msg import Detection2D, ObjectHypothesisWithPose, BoundingBox2D
|
||||||
|
from cv_bridge import CvBridge, CvBridgeError
|
||||||
|
from geometry_msgs.msg import Pose2D
|
||||||
|
import cv2
|
||||||
|
import torch ## we have it in our jack env, but not root, so it doesn't matter.
|
||||||
|
import torchvision
|
||||||
|
from torchvision.models.detection import FasterRCNN_ResNet50_FPN_Weights
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectDetectionNode(Node):
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
self.subscriptions = self.create_subscription(Image, 'image_raw', self.listener_callback, 10)
|
||||||
|
self.publishers = self.create_publisher(Image, 'detection_iamge', 10)
|
||||||
|
# Initializes the CvBridge for converting ROS images to OpenCV images.
|
||||||
|
self.bridge = CvBridge()
|
||||||
|
self.model = torchvision.models.detection.fasterrcnn_resnet50_fpn(FasterRCNN_ResNet50_FPN_Weights.DEFAULT)
|
||||||
|
self.model.eval()
|
||||||
|
self.coco_labels = {v: k for k, v in self.model.coco_labels.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def listener_callback(self, msg):
|
||||||
|
'''
|
||||||
|
Callback function triggered when a new image is received.
|
||||||
|
'''
|
||||||
|
try:
|
||||||
|
# Converts the ROS image message to an OpenCV image in BGR format.
|
||||||
|
cv_image = self.bridge.imgmsg_to_cv2(msg, 'bgr8')
|
||||||
|
# convert the image to a tensor.
|
||||||
|
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])
|
||||||
|
image_tensor = transform(cv_image)
|
||||||
|
outputs = self.model([image_tensor])[0]
|
||||||
|
for i, (box, score, label) in enumerate(zip(outputs['boxes'], outputs['scores'], outputs['labels'])):
|
||||||
|
if score >= 0.5:
|
||||||
|
x1, y1, x2, y2 = box.int().tolist()
|
||||||
|
label_name = self.coco_labels[label.item()]
|
||||||
|
cv2.rectangle(cv_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
|
||||||
|
overlay = cv_image.copy()
|
||||||
|
cv2.rectangle(overlay, (x1, y1), (x2, y2), (0, 255, 0), -1)
|
||||||
|
alpha = 0.4
|
||||||
|
cv_image = cv2.addWeighted(overlay, alpha, cv_image, 1 - alpha, 0)
|
||||||
|
cv2.putText(cv_image, label_name, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)
|
||||||
|
try:
|
||||||
|
detection_image = self.bridge.cv2_to_imgmsg(cv_image, 'bgr8')
|
||||||
|
self.publisher_.publish(detection_image)
|
||||||
|
except CvBridgeError as e:
|
||||||
|
self.get_logger().error('Failed to convert image: %s' % str(e))
|
||||||
|
|
||||||
|
except CvBridgeError as e:
|
||||||
|
self.get_logger().error('Failed to convert image: %s' % str(e))
|
||||||
|
return
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args = args)
|
||||||
|
detect = ObjectDetectionNode("nice")
|
||||||
|
rclpy.spin(detect)
|
||||||
|
detect.destroy_node()
|
||||||
|
detect.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
214
camera_kl520/object_detection_kl520_pub.py
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
# ******************************************************************************
|
||||||
|
# Copyright (c) 2021-2022. Kneron Inc. All rights reserved. *
|
||||||
|
# ******************************************************************************
|
||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
from std_msgs.msg import String
|
||||||
|
from ament_index_python.packages import get_package_share_directory
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# current file directory pwd
|
||||||
|
# PWD = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
# sys.path.insert(1, os.path.join(PWD, '..'))
|
||||||
|
|
||||||
|
pkg_name = 'camera_kl520'
|
||||||
|
pkg_share = get_package_share_directory(pkg_name)
|
||||||
|
|
||||||
|
from utils.ExampleHelper import get_device_usb_speed_by_port_id
|
||||||
|
from utils.ExamplePostProcess import post_process_yolo_v5
|
||||||
|
import kp
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
# firmware sources
|
||||||
|
SCPU_FW_PATH = os.path.join(pkg_share, 'res', 'firmware', 'KL520', 'fw_scpu.bin')
|
||||||
|
NCPU_FW_PATH = os.path.join(pkg_share, 'res', 'firmware', 'KL520', 'fw_ncpu.bin')
|
||||||
|
|
||||||
|
# model and image file path
|
||||||
|
MODEL_FILE_PATH = os.path.join(pkg_share, 'res', 'models', 'KL520', 'yolov5-noupsample_w640h640_kn-model-zoo', 'kl520_20005_yolov5-noupsample_w640h640.nef')
|
||||||
|
IMAGE_FILE_PATH = os.path.join(pkg_share, 'res', 'images', 'mason_640x640.jpg')
|
||||||
|
|
||||||
|
class ImageClassifierNode_kl520_pub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
|
||||||
|
self.declare_parameter('port_id', 0)
|
||||||
|
self.declare_parameter('model', MODEL_FILE_PATH)
|
||||||
|
self.declare_parameter('img', IMAGE_FILE_PATH)
|
||||||
|
|
||||||
|
self.usb_port_id = self.get_parameter('port_id').value
|
||||||
|
self.model_path = self.get_parameter('model').value
|
||||||
|
self.image_file_path = self.get_parameter('img').value
|
||||||
|
|
||||||
|
# Scan the device, get necessary parameters
|
||||||
|
self.ScanProduct()
|
||||||
|
# Prepare Images
|
||||||
|
self.prepareImage()
|
||||||
|
|
||||||
|
|
||||||
|
self.pub = self.create_publisher(String, 'kl520_objection_detection/result', 10)
|
||||||
|
self.timer = self.create_timer(1.0, self.Inference)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def ScanProduct(self):
|
||||||
|
"""
|
||||||
|
check device USB speed (Recommend run KL520 at high speed)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if kp.UsbSpeed.KP_USB_SPEED_HIGH != get_device_usb_speed_by_port_id(usb_port_id=self.usb_port_id):
|
||||||
|
print('\033[91m' + '[Warning] Device is not run at high speed.' + '\033[0m')
|
||||||
|
except Exception as exception:
|
||||||
|
print('Error: check device USB speed fail, port ID = \'{}\', error msg: [{}]'.format(self.usb_port_id,
|
||||||
|
str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
connect the device
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print('[Connect Device]')
|
||||||
|
self.device_group = kp.core.connect_devices(usb_port_ids=[self.usb_port_id])
|
||||||
|
print(' - Success')
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print('Error: connect device fail, port ID = \'{}\', error msg: [{}]'.format(self.usb_port_id,
|
||||||
|
str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
setting timeout of the usb communication with the device
|
||||||
|
"""
|
||||||
|
print('[Set Device Timeout]')
|
||||||
|
kp.core.set_timeout(device_group=self.device_group, milliseconds=5000)
|
||||||
|
print(' - Success')
|
||||||
|
|
||||||
|
"""
|
||||||
|
upload firmware to device
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print('[Upload Firmware]')
|
||||||
|
kp.core.load_firmware_from_file(device_group=self.device_group,
|
||||||
|
scpu_fw_path=SCPU_FW_PATH,
|
||||||
|
ncpu_fw_path=NCPU_FW_PATH)
|
||||||
|
print(' - Success')
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print('Error: upload firmware failed, error = \'{}\''.format(str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
"""
|
||||||
|
upload model to device
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
print('[Upload Model]')
|
||||||
|
self.model_nef_descriptor = kp.core.load_model_from_file(device_group=self.device_group,
|
||||||
|
file_path=self.model_path)
|
||||||
|
print(' - Success')
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print('Error: upload model failed, error = \'{}\''.format(str(exception)))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
def prepareImage(self):
|
||||||
|
"""
|
||||||
|
prepare the image
|
||||||
|
"""
|
||||||
|
|
||||||
|
print('[Read Image]')
|
||||||
|
self.img = cv2.imread(filename=self.image_file_path)
|
||||||
|
self.img_bgr565 = cv2.cvtColor(src=self.img, code=cv2.COLOR_BGR2BGR565)
|
||||||
|
print(' - Success')
|
||||||
|
|
||||||
|
"""
|
||||||
|
prepare generic image inference input descriptor
|
||||||
|
"""
|
||||||
|
self.generic_inference_input_descriptor = kp.GenericImageInferenceDescriptor(
|
||||||
|
model_id=self.model_nef_descriptor.models[0].id,
|
||||||
|
inference_number=0,
|
||||||
|
input_node_image_list=[
|
||||||
|
kp.GenericInputNodeImage(
|
||||||
|
image=self.img_bgr565,
|
||||||
|
image_format=kp.ImageFormat.KP_IMAGE_FORMAT_RGB565,
|
||||||
|
resize_mode=kp.ResizeMode.KP_RESIZE_ENABLE,
|
||||||
|
padding_mode=kp.PaddingMode.KP_PADDING_CORNER,
|
||||||
|
normalize_mode=kp.NormalizeMode.KP_NORMALIZE_KNERON
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def Inference(self):
|
||||||
|
"""
|
||||||
|
starting inference work
|
||||||
|
"""
|
||||||
|
print('[Starting Inference Work]')
|
||||||
|
print(' - ', end='')
|
||||||
|
|
||||||
|
try:
|
||||||
|
kp.inference.generic_image_inference_send(device_group=self.device_group,
|
||||||
|
generic_inference_input_descriptor=self.generic_inference_input_descriptor)
|
||||||
|
|
||||||
|
self.generic_raw_result = kp.inference.generic_image_inference_receive(device_group=self.device_group)
|
||||||
|
except kp.ApiKPException as exception:
|
||||||
|
print(' - Error: inference failed, error = {}'.format(exception))
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
print('.', end='', flush=True)
|
||||||
|
print()
|
||||||
|
|
||||||
|
"""
|
||||||
|
retrieve inference node output
|
||||||
|
"""
|
||||||
|
print('[Retrieve Inference Node Output ]')
|
||||||
|
self.inf_node_output_list = []
|
||||||
|
for node_idx in range(self.generic_raw_result.header.num_output_node):
|
||||||
|
inference_float_node_output = kp.inference.generic_inference_retrieve_float_node(
|
||||||
|
node_idx=node_idx,
|
||||||
|
generic_raw_result=self.generic_raw_result,
|
||||||
|
channels_ordering=kp.ChannelOrdering.KP_CHANNEL_ORDERING_CHW
|
||||||
|
)
|
||||||
|
self.inf_node_output_list.append(inference_float_node_output)
|
||||||
|
|
||||||
|
print(' - Success')
|
||||||
|
|
||||||
|
self.yolo_result = post_process_yolo_v5(inference_float_node_output_list=self.inf_node_output_list,
|
||||||
|
hardware_preproc_info=self.generic_raw_result.header.hw_pre_proc_info_list[0],
|
||||||
|
thresh_value=0.2)
|
||||||
|
|
||||||
|
msg = String()
|
||||||
|
coords = []
|
||||||
|
print(" - Bounding boxes info (xmin,ymin,xmax,ymax):")
|
||||||
|
# output_img_name = 'output_{}'.format(os.path.basename(self.image_file_path))
|
||||||
|
|
||||||
|
for box in self.yolo_result.box_list:
|
||||||
|
# b = 100 + (25 * box.class_num) % 156
|
||||||
|
# g = 100 + (80 + 40 * box.class_num) % 156
|
||||||
|
# r = 100 + (120 + 60 * box.class_num) % 156
|
||||||
|
# color = (b, g, r)
|
||||||
|
|
||||||
|
# cv2.rectangle(img=self.img,
|
||||||
|
# pt1=(int(box.x1), int(box.y1)),
|
||||||
|
# pt2=(int(box.x2), int(box.y2)),
|
||||||
|
# color=color,
|
||||||
|
# thickness=2)
|
||||||
|
coords.append(f"[{int(box.x1)}, {int(box.y1)}, {int(box.x2)}, {int(box.y2)}]\n")
|
||||||
|
|
||||||
|
msg.data = "".join(coords)
|
||||||
|
self.pub.publish(msg)
|
||||||
|
self.get_logger().info(f"Published: \n{msg.data}")
|
||||||
|
# cv2.imwrite(os.path.join(pkg_share, './{}'.format(output_img_name)), img=self.img)
|
||||||
|
|
||||||
|
def main(args = None):
|
||||||
|
rclpy.init(args = args)
|
||||||
|
node = ImageClassifierNode_kl520_pub("image_classifier_kl520_pub")
|
||||||
|
try:
|
||||||
|
rclpy.spin(node)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
node.get_logger().info("shutting down...")
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
24
camera_kl520/object_detection_kl520_sub.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
import rclpy
|
||||||
|
from std_msgs.msg import String
|
||||||
|
from rclpy.node import Node
|
||||||
|
|
||||||
|
class ImageClassifierNode_sub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
self.sub = self.create_subscription(String, 'kl520_objection_detection/result',self.listener_callback, 10)
|
||||||
|
|
||||||
|
def listener_callback(self, msg):
|
||||||
|
msg = msg.data
|
||||||
|
self.get_logger().info(f"I received msg \n{msg}")
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = ImageClassifierNode_sub("image_classifier_kl520_sub")
|
||||||
|
if rclpy.ok:
|
||||||
|
rclpy.spin(node)
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
105
camera_kl520/object_detection_pub.py
Executable file
@ -0,0 +1,105 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
import rclpy
|
||||||
|
from rclpy.node import Node
|
||||||
|
from sensor_msgs.msg import Image
|
||||||
|
from vision_msgs.msg import Detection2D, ObjectHypothesisWithPose, BoundingBox2D
|
||||||
|
import cv2
|
||||||
|
import torch
|
||||||
|
import torchvision.transforms as transforms
|
||||||
|
from torchvision.models import ResNet18_Weights, resnet18
|
||||||
|
|
||||||
|
class ImageClassifierNode_pub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
# Create a ROS 2 Publisher to publish Detection2D messages
|
||||||
|
self.publisher = self.create_publisher(Detection2D, 'object_detection/result', 10)
|
||||||
|
# Retrieve the static image path from parameters (use absolute path)
|
||||||
|
self.declare_parameter('image_path', '/home/jack/workspace/ros_ur_driver/src/camera_kl520/camera_kl520/image1.jpg')
|
||||||
|
image_path = self.get_parameter('image_path').get_parameter_value().string_value
|
||||||
|
|
||||||
|
# Load pretrained ResNet18 model and set to evaluation mode
|
||||||
|
weights = ResNet18_Weights.DEFAULT
|
||||||
|
self.model = resnet18(weights=weights)
|
||||||
|
self.model.eval() # Evaluation mode disables training-specific layers
|
||||||
|
# Get ImageNet class names
|
||||||
|
self.class_names = weights.meta["categories"]
|
||||||
|
|
||||||
|
# Read local image (for display purposes)
|
||||||
|
image = cv2.imread(image_path)
|
||||||
|
if image is None:
|
||||||
|
self.get_logger().error(f"Failed to read image: {image_path}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Preprocess image to match model input requirements
|
||||||
|
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||||
|
image_rgb = cv2.resize(image_rgb, (224, 224))
|
||||||
|
transform = transforms.Compose([
|
||||||
|
transforms.ToTensor(), # Convert to tensor and scale values to [0,1]
|
||||||
|
transforms.Normalize(mean=[0.485, 0.456, 0.406],
|
||||||
|
std=[0.229, 0.224, 0.225])
|
||||||
|
])
|
||||||
|
self.input_tensor = transform(image_rgb).unsqueeze(0) # Add batch dimension
|
||||||
|
self.timer = self.create_timer(1, self.timer_callback)
|
||||||
|
|
||||||
|
def timer_callback(self):
|
||||||
|
# Run model inference
|
||||||
|
with torch.no_grad():
|
||||||
|
output = self.model(self.input_tensor) # Output logits
|
||||||
|
probabilities = torch.nn.functional.softmax(output, dim=1)
|
||||||
|
score, predicted_idx = torch.max(probabilities, 1)
|
||||||
|
score = float(score.cpu().item())
|
||||||
|
predicted_idx = int(predicted_idx.cpu().item())
|
||||||
|
|
||||||
|
# Get predicted class name (fallback to index if out of range)
|
||||||
|
if 0 <= predicted_idx < len(self.class_names):
|
||||||
|
class_name = self.class_names[predicted_idx]
|
||||||
|
else:
|
||||||
|
class_name = str(predicted_idx)
|
||||||
|
|
||||||
|
# Create Detection2D message
|
||||||
|
detection = Detection2D()
|
||||||
|
detection.header.stamp = self.get_clock().now().to_msg()
|
||||||
|
detection.header.frame_id = 'camera_frame'
|
||||||
|
|
||||||
|
# Fill in object classification result
|
||||||
|
hypothesis = ObjectHypothesisWithPose()
|
||||||
|
hypothesis.hypothesis.class_id = class_name
|
||||||
|
hypothesis.hypothesis.score = score
|
||||||
|
detection.results.append(hypothesis)
|
||||||
|
|
||||||
|
# Publish the Detection2D message
|
||||||
|
self.publisher.publish(detection)
|
||||||
|
self.get_logger().info(f"Result class: {class_name}, Confidence: {score:.2f}")
|
||||||
|
|
||||||
|
# Example bounding box covering the entire image (commented out)
|
||||||
|
# bbox = BoundingBox2D()
|
||||||
|
# height, width, _ = image.shape
|
||||||
|
# # Set center x, y, theta of bbox (Pose2D fields)
|
||||||
|
# bbox.center.position.x = width / 2.0
|
||||||
|
# bbox.center.position.y = height / 2.0
|
||||||
|
# bbox.center.theta = 0.0 # No rotation
|
||||||
|
# bbox.size_x = float(width)
|
||||||
|
# bbox.size_y = float(height)
|
||||||
|
# detection.bbox = bbox
|
||||||
|
|
||||||
|
# Draw bounding box and label on image and display (commented out)
|
||||||
|
# top_left = (int(bbox.center.position.x - bbox.size_x / 2), int(bbox.center.position.y - bbox.size_y / 2))
|
||||||
|
# bottom_right = (int(bbox.center.position.x + bbox.size_x / 2), int(bbox.center.position.y + bbox.size_y / 2))
|
||||||
|
# cv2.rectangle(image, top_left, bottom_right, (0, 255, 0), 2)
|
||||||
|
# label = f"{class_name}: {score:.2f}"
|
||||||
|
# cv2.putText(image, label, (top_left[0], top_left[1] - 10), cv2.FONT_HERSHEY_SIMPLEX,
|
||||||
|
# 0.9, (0, 255, 0), 2)
|
||||||
|
# cv2.imshow("Detection Result", image)
|
||||||
|
# cv2.waitKey(0)
|
||||||
|
# cv2.destroyAllWindows()
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = ImageClassifierNode_pub("image_classifier_pub")
|
||||||
|
if rclpy.ok():
|
||||||
|
rclpy.spin(node)
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
24
camera_kl520/object_detection_sub.py
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
import rclpy
|
||||||
|
from vision_msgs.msg import Detection2D, ObjectHypothesisWithPose
|
||||||
|
from rclpy.node import Node
|
||||||
|
|
||||||
|
class ImageClassifierNode_sub(Node):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
self.sub = self.create_subscription(Detection2D, 'object_detection/result',self.listener_callback, 10)
|
||||||
|
|
||||||
|
def listener_callback(self, detection):
|
||||||
|
hypothesis = detection.results[0].hypothesis
|
||||||
|
self.get_logger().info(f"Result class: {hypothesis.class_id}, Confidence: {hypothesis.score:.2f}")
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
rclpy.init(args=args)
|
||||||
|
node = ImageClassifierNode_sub("image_classifier_sub")
|
||||||
|
if rclpy.ok:
|
||||||
|
rclpy.spin(node)
|
||||||
|
node.destroy_node()
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
||||||
30
camera_kl520/testing.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import cv2
|
||||||
|
|
||||||
|
# Open webcam device 0 using the V4L2 backend
|
||||||
|
cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
|
||||||
|
|
||||||
|
if not cap.isOpened():
|
||||||
|
print("Failed to open camera.")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
# Force MJPG pixel format for higher throughput
|
||||||
|
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
|
||||||
|
cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
||||||
|
|
||||||
|
# Configure resolution and FPS – adjust as needed
|
||||||
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
|
||||||
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) # can change to 640 or other resolution
|
||||||
|
cap.set(cv2.CAP_PROP_FPS, 30)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
ret, frame = cap.read()
|
||||||
|
if not ret:
|
||||||
|
print("Failed to grab frame.")
|
||||||
|
break
|
||||||
|
|
||||||
|
# cv2.imshow('Camera', frame)
|
||||||
|
# if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
# break
|
||||||
|
|
||||||
|
cap.release()
|
||||||
|
cv2.destroyAllWindows()
|
||||||
58
camera_kl520/testing_camera.py
Executable file
@ -0,0 +1,58 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
import cv2
|
||||||
|
import torch
|
||||||
|
|
||||||
|
# Load the YOLOv5‑L model from the Ultralytics hub and move it to GPU
|
||||||
|
model = torch.hub.load("ultralytics/yolov5", "yolov5l", pretrained=True)
|
||||||
|
model.to("cuda")
|
||||||
|
model.eval()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Open the default webcam, perform inference, and display annotated frames."""
|
||||||
|
|
||||||
|
# Open webcam device 0 using the V4L2 backend
|
||||||
|
cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
|
||||||
|
|
||||||
|
if not cap.isOpened():
|
||||||
|
print("Failed to open camera.")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
# Force MJPG pixel format for higher throughput
|
||||||
|
fourcc = cv2.VideoWriter_fourcc(*"MJPG")
|
||||||
|
cap.set(cv2.CAP_PROP_FOURCC, fourcc)
|
||||||
|
|
||||||
|
# Configure resolution and FPS – adjust as needed
|
||||||
|
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
|
||||||
|
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)
|
||||||
|
cap.set(cv2.CAP_PROP_FPS, 30)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
ret, frame = cap.read()
|
||||||
|
if not ret:
|
||||||
|
print("Failed to grab frame.")
|
||||||
|
break
|
||||||
|
|
||||||
|
# Convert BGR (OpenCV default) to RGB for the model
|
||||||
|
img_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
|
# Run inference (returns a Results object)
|
||||||
|
results = model(img_rgb)
|
||||||
|
|
||||||
|
# Render detections on the frame
|
||||||
|
annotated_frame = results.render()[0]
|
||||||
|
annotated_frame = cv2.cvtColor(annotated_frame, cv2.COLOR_RGB2BGR)
|
||||||
|
|
||||||
|
# Display the annotated frame
|
||||||
|
cv2.imshow("YOLOv5 Inference", annotated_frame)
|
||||||
|
|
||||||
|
# Press 'q' to quit
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord("q"):
|
||||||
|
break
|
||||||
|
|
||||||
|
cap.release()
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
20
package.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||||
|
<package format="3">
|
||||||
|
<name>camera_kl520</name>
|
||||||
|
<version>0.0.0</version>
|
||||||
|
<description>TODO: Package description</description>
|
||||||
|
<maintainer email="jack23162329@gmail.com">jack</maintainer>
|
||||||
|
<license>TODO: License declaration</license>
|
||||||
|
|
||||||
|
<test_depend>ament_copyright</test_depend>
|
||||||
|
<test_depend>ament_flake8</test_depend>
|
||||||
|
<test_depend>ament_pep257</test_depend>
|
||||||
|
<test_depend>python3-pytest</test_depend>
|
||||||
|
|
||||||
|
<depend>rclpy</depend>
|
||||||
|
|
||||||
|
<export>
|
||||||
|
<build_type>ament_python</build_type>
|
||||||
|
</export>
|
||||||
|
</package>
|
||||||
1
res/firmware/KL520/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
2.2.0
|
||||||
BIN
res/firmware/KL520/dfw/minions.bin
Normal file
BIN
res/firmware/KL520/fw_loader.bin
Normal file
BIN
res/firmware/KL520/fw_ncpu.bin
Normal file
BIN
res/firmware/KL520/fw_scpu.bin
Normal file
1
res/firmware/KL630/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
SDK-v2.5.7
|
||||||
BIN
res/firmware/KL630/kp_firmware.tar
Normal file
BIN
res/firmware/KL630/kp_loader.tar
Normal file
1
res/firmware/KL720/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
2.2.0
|
||||||
BIN
res/firmware/KL720/fw_ncpu.bin
Normal file
BIN
res/firmware/KL720/fw_scpu.bin
Normal file
1
res/firmware/KL730/VERSION
Normal file
@ -0,0 +1 @@
|
|||||||
|
SDK-v1.2.1
|
||||||
BIN
res/firmware/KL730/kp_firmware.tar
Normal file
BIN
res/firmware/KL730/kp_loader.tar
Normal file
BIN
res/images/58_people.bmp
Normal file
|
After Width: | Height: | Size: 5.9 MiB |
BIN
res/images/ArchVizInterior03Data_Night_l_1_left.bmp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
res/images/ArchVizInterior03Data_Night_l_1_right.bmp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
res/images/MOT16-03_trim.mp4
Normal file
BIN
res/images/a_man_640x480.bmp
Normal file
|
After Width: | Height: | Size: 900 KiB |
BIN
res/images/a_woman_640x480.bmp
Normal file
|
After Width: | Height: | Size: 900 KiB |
BIN
res/images/bike_cars_street_224x224.bmp
Normal file
|
After Width: | Height: | Size: 147 KiB |
166
res/images/bike_cars_street_224x224_cby0cry1.bin
Normal file
166
res/images/bike_cars_street_224x224_cby1cry0.bin
Normal file
166
res/images/bike_cars_street_224x224_cry0cby1.bin
Normal file
166
res/images/bike_cars_street_224x224_cry1cby0.bin
Normal file
129
res/images/bike_cars_street_224x224_raw8.bin
Normal file
202
res/images/bike_cars_street_224x224_rgb565.bin
Normal file
424
res/images/bike_cars_street_224x224_rgba8888.bin
Normal file
BIN
res/images/bike_cars_street_224x224_rgba8888_normalized.bin
Normal file
166
res/images/bike_cars_street_224x224_y0cby1cr.bin
Normal file
166
res/images/bike_cars_street_224x224_y0cry1cb.bin
Normal file
166
res/images/bike_cars_street_224x224_y1cby0cr.bin
Normal file
166
res/images/bike_cars_street_224x224_y1cry0cb.bin
Normal file
1
res/images/bike_cars_street_224x224_yuv420p.yuv
Normal file
166
res/images/bike_cars_street_224x224_yuyv.bin
Normal file
1
res/images/bike_cars_street_224x224_yuyv422.yuv
Normal file
BIN
res/images/bike_cars_street_416x416.bmp
Normal file
|
After Width: | Height: | Size: 507 KiB |
BIN
res/images/bypass_preproc_172x224x3_8W1C16B_231168B.bin
Normal file
BIN
res/images/car_park_barrier_608x608.bmp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
res/images/car_plate_1280x720.bmp
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
res/images/car_plate_1920x1080.bmp
Normal file
|
After Width: | Height: | Size: 5.9 MiB |
BIN
res/images/car_plate_AAE-8772_1280x720.bmp
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
res/images/car_plate_AAE-8772_1920x1080.bmp
Normal file
|
After Width: | Height: | Size: 5.9 MiB |
BIN
res/images/city_scape_640x428.jpg
Normal file
|
After Width: | Height: | Size: 204 KiB |
BIN
res/images/desert_ant_812x642.bmp
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
res/images/dms_640x360.bmp
Normal file
|
After Width: | Height: | Size: 675 KiB |
BIN
res/images/hand_224x224.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
res/images/headpose_gesture_640x480.bmp
Normal file
|
After Width: | Height: | Size: 900 KiB |
BIN
res/images/horse_detection_kpts_2560x1440_24kpts.bmp
Normal file
|
After Width: | Height: | Size: 10 MiB |
BIN
res/images/one_bike_many_cars_224x224.bmp
Normal file
|
After Width: | Height: | Size: 147 KiB |
36
res/images/one_bike_many_cars_224x224_rgb565.bin
Normal file
1
res/images/one_bike_many_cars_224x224_rgba8888.bin
Normal file
1
res/images/one_bike_many_cars_224x224_yuyv.bin
Normal file
BIN
res/images/one_bike_many_cars_416x224.bmp
Normal file
|
After Width: | Height: | Size: 273 KiB |
BIN
res/images/one_bike_many_cars_416x416.bmp
Normal file
|
After Width: | Height: | Size: 507 KiB |
BIN
res/images/one_bike_many_cars_608x608.bmp
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
res/images/one_bike_many_cars_800x800.bmp
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
489
res/images/one_bike_many_cars_800x800_rgb565.bin
Normal file
18
res/images/one_bike_many_cars_800x800_rgba8888.bin
Normal file
3
res/images/one_bike_many_cars_800x800_yuyv.bin
Normal file
BIN
res/images/one_car_1500x1500.bmp
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
BIN
res/images/one_car_900x900.bmp
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
res/images/pd_forklift.bmp
Normal file
|
After Width: | Height: | Size: 3.5 MiB |
BIN
res/images/people_cars_street_1500x1500.bmp
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
BIN
res/images/people_talk_in_street_1500x1500.bmp
Normal file
|
After Width: | Height: | Size: 6.4 MiB |
BIN
res/images/people_talk_in_street_640x320.bmp
Normal file
|
After Width: | Height: | Size: 600 KiB |
BIN
res/images/people_talk_in_street_640x640.bmp
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
res/images/people_talk_in_street_640x640_rgba8888_normalized.bin
Normal file
BIN
res/images/raft_optical_flow_0_1024x436.bmp
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
res/images/raft_optical_flow_1_1024x436.bmp
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
res/images/shark_1000x667.jpg
Normal file
|
After Width: | Height: | Size: 486 KiB |
BIN
res/images/skeleton_960x540.bmp
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
res/images/street_1280x720.bmp
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
res/images/street_traffic_cone.bmp
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
res/images/tinyVD075_golden.bmp
Normal file
|
After Width: | Height: | Size: 4.1 MiB |
BIN
res/images/tiny_dms_800x540.bmp
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
res/images/travel_walk_480x256.bmp
Normal file
|
After Width: | Height: | Size: 360 KiB |
289
res/images/travel_walk_480x256_rgb565.bin
Normal file
694
res/images/travel_walk_480x256_rgba8888.bin
Normal file
239
res/images/travel_walk_480x256_yuyv.bin
Normal file
BIN
res/images/us_car_plate_BR9-T294_1920x1080.bmp
Normal file
|
After Width: | Height: | Size: 5.9 MiB |
BIN
res/images/video_conference_928x192.bmp
Normal file
|
After Width: | Height: | Size: 522 KiB |