mi-task/utils/image_raw.py

221 lines
7.0 KiB
Python
Raw Normal View History

2025-05-11 15:44:54 +00:00
#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from cv_bridge import CvBridge
import cv2
# from qreader import QReader
from threading import Thread, Lock
import time
import queue
from utils.log_helper import get_logger
2025-08-18 11:32:16 +08:00
# 导入AI相机服务
from protocol.srv import CameraService
2025-05-11 15:44:54 +00:00
class ImageSubscriber(Node):
def __init__(self):
super().__init__('image_subscriber')
2025-08-18 11:32:16 +08:00
# 创建AI相机服务客户端
self.camera_client = self.create_client(
CameraService,
'/camera_service'
2025-05-11 15:44:54 +00:00
)
2025-08-18 11:32:16 +08:00
# 等待服务可用
while not self.camera_client.wait_for_service(timeout_sec=1.0):
self.get_logger().info('AI相机服务不可用等待中...')
# 订阅AI相机图像话题
self.image_subscription = self.create_subscription(
2025-05-11 15:44:54 +00:00
Image,
2025-08-18 11:32:16 +08:00
'/image',
2025-05-11 15:44:54 +00:00
self.image_callback,
2025-08-18 11:32:16 +08:00
10
2025-05-11 15:44:54 +00:00
)
2025-08-18 11:32:16 +08:00
2025-05-11 15:44:54 +00:00
self.bridge = CvBridge()
self.cv_image = None # Store latest image
2025-08-18 11:32:16 +08:00
self.camera_started = False
# 启动AI相机
self.start_camera()
def start_camera(self):
"""启动AI相机"""
req = CameraService.Request()
req.command = 9 # START_IMAGE_PUBLISH
req.width = 640 # 图像宽度
req.height = 480 # 图像高度
req.fps = 30 # 帧率
future = self.camera_client.call_async(req)
rclpy.spin_until_future_complete(self, future)
if future.result().result == 0: # RESULT_SUCCESS
self.get_logger().info('AI相机已启动')
self.camera_started = True
return True
else:
self.get_logger().error(f'启动AI相机失败: {future.result().msg}')
return False
def stop_camera(self):
"""停止AI相机"""
if not self.camera_started:
return True
req = CameraService.Request()
req.command = 10 # STOP_IMAGE_PUBLISH
future = self.camera_client.call_async(req)
rclpy.spin_until_future_complete(self, future)
if future.result().result == 0: # RESULT_SUCCESS
self.get_logger().info('AI相机已停止')
self.camera_started = False
return True
else:
self.get_logger().error(f'停止AI相机失败: {future.result().msg}')
return False
2025-05-11 15:44:54 +00:00
def image_callback(self, msg):
try:
# 将ROS图像消息转换为OpenCV格式
self.cv_image = self.bridge.imgmsg_to_cv2(msg, desired_encoding='bgr8')
except Exception as e:
self.get_logger().error('Failed to convert image: %s' % str(e))
2025-05-11 15:44:54 +00:00
class ImageProcessor:
def __init__(self):
self.image_subscriber = ImageSubscriber()
# self.qreader = QReader()
self.spin_thread = None
self.running = True
self.log = get_logger("图像处理器")
# 异步 QR 码扫描相关
self.scan_thread = None
self.image_queue = queue.Queue(maxsize=3) # 限制队列大小,只保留最新的图像
self.scan_lock = Lock()
self.last_qr_result = None
self.last_qr_time = 0
self.is_scanning = False
self.enable_async_scan = False
2025-05-11 15:44:54 +00:00
def run(self):
self.spin_thread = Thread(target=self._spin)
self.spin_thread.start()
def _spin(self):
2025-05-11 15:44:54 +00:00
try:
while self.running:
rclpy.spin_once(self.image_subscriber, timeout_sec=0.1)
except Exception as e:
self.log.error(f"Spin线程错误: {e}", "错误")
def destroy(self):
self.running = False
self.stop_async_scan()
if self.spin_thread:
self.spin_thread.join()
2025-08-18 11:32:16 +08:00
# 停止AI相机
self.image_subscriber.stop_camera()
self.image_subscriber.destroy_node()
2025-05-11 15:44:54 +00:00
def get_current_image(self):
return self.image_subscriber.cv_image
def decode_qrcode(self, img = None):
if img is None:
img = self.get_current_image()
# decoded_info = self.qreader.detect_and_decode(image=img)
decoded_info = None
if decoded_info and len(decoded_info) > 0:
return decoded_info[0]
return None
def start_async_scan(self, interval=0.3):
"""
启动异步 QR 码扫描
参数:
interval: 扫描间隔单位秒
"""
if self.scan_thread is not None and self.scan_thread.is_alive():
self.log.warning("异步扫描已经在运行中", "警告")
return
self.enable_async_scan = True
self.is_scanning = False
self.scan_thread = Thread(target=self._async_scan_worker, args=(interval,))
self.scan_thread.daemon = True # 设为守护线程,主线程结束时自动结束
self.scan_thread.start()
self.log.info("启动异步 QR 码扫描线程", "启动")
def stop_async_scan(self):
"""停止异步 QR 码扫描"""
self.enable_async_scan = False
if self.scan_thread and self.scan_thread.is_alive():
self.scan_thread.join(timeout=1.0)
self.log.info("异步 QR 码扫描线程已停止", "停止")
def _async_scan_worker(self, interval):
"""异步扫描工作线程"""
last_scan_time = 0
while self.enable_async_scan and self.running:
current_time = time.time()
# 按指定间隔扫描
if current_time - last_scan_time >= interval:
img = self.get_current_image()
if img is not None:
try:
self.is_scanning = True
qr_data = self.decode_qrcode(img)
self.is_scanning = False
with self.scan_lock:
if qr_data:
self.last_qr_result = qr_data
self.last_qr_time = current_time
self.log.success(f"异步扫描到 QR 码: {qr_data}", "扫描")
except Exception as e:
self.is_scanning = False
self.log.error(f"异步 QR 码扫描出错: {e}", "错误")
last_scan_time = current_time
# 短暂休眠避免占用过多 CPU
time.sleep(0.05)
def get_last_qr_result(self):
"""获取最后一次成功扫描的 QR 码结果"""
with self.scan_lock:
return self.last_qr_result, self.last_qr_time
2025-05-11 15:44:54 +00:00
""" DEBUG """
def main(args=None):
rclpy.init(args=args)
image_subscriber = ImageSubscriber()
try:
rclpy.spin(image_subscriber)
except KeyboardInterrupt:
pass
# 清理
2025-08-18 11:32:16 +08:00
image_subscriber.stop_camera()
2025-05-11 15:44:54 +00:00
image_subscriber.destroy_node()
if __name__ == '__main__':
main()