|
11 | 11 | import json |
12 | 12 | import collections |
13 | 13 | from rclpy.node import Node |
14 | | -from python_qt_binding.QtCore import pyqtSignal, QObject |
| 14 | + |
15 | 15 | from std_msgs.msg import Float32MultiArray, String, Bool, Int32 |
16 | 16 | from sensor_msgs.msg import Image |
17 | 17 | from geometry_msgs.msg import Point |
|
22 | 22 | os.makedirs(MODELS_DIR, exist_ok=True) |
23 | 23 |
|
24 | 24 |
|
25 | | -class FrameGrabber(QObject): |
| 25 | +class FrameGrabber: |
26 | 26 | """Dedicated thread for frame capture to improve performance""" |
27 | | - frame_ready = pyqtSignal(np.ndarray) |
28 | | - error = pyqtSignal(str) |
29 | 27 |
|
30 | 28 | def __init__(self, node=None): |
31 | | - super().__init__() |
32 | 29 | self.node = node |
33 | 30 | # Camera properties |
34 | 31 | self.camera = None |
@@ -64,9 +61,8 @@ def __init__(self, node=None): |
64 | 61 | self.frame_lock = threading.Lock() |
65 | 62 | self.frame_timestamp = 0 # Track frame freshness |
66 | 63 |
|
67 | | - # UI frame rate control |
68 | | - self.last_ui_update = 0 |
69 | | - self.min_ui_interval = 1.0 / 30 # Target 30 FPS for UI |
| 64 | + # Frame processing control |
| 65 | + # (UI-related frame rate controls removed in headless mode) |
70 | 66 |
|
71 | 67 | # Face detection control |
72 | 68 | self.last_detection_time = 0 |
@@ -463,8 +459,7 @@ def set_quality(self, high_quality): |
463 | 459 |
|
464 | 460 | print(f"Quality changed to: {actual_width}x{actual_height} @ {actual_fps:.1f} FPS") |
465 | 461 |
|
466 | | - # Update UI with blank frame of new size |
467 | | - self.frame_ready.emit(np.zeros((self.frame_height, self.frame_width, 3), dtype=np.uint8)) |
| 462 | + # No UI update needed in headless mode |
468 | 463 |
|
469 | 464 | def toggle_face_detection(self, enable): |
470 | 465 | """Enable or disable face detection""" |
@@ -691,7 +686,8 @@ def _capture_loop(self): |
691 | 686 | continue |
692 | 687 |
|
693 | 688 | if not success: |
694 | | - self.error.emit(f"Failed to open camera: {error_msg}") |
| 689 | + if self.node: |
| 690 | + self.node.get_logger().error(f"Failed to open camera: {error_msg}") |
695 | 691 | return |
696 | 692 |
|
697 | 693 | # Configure camera with optimal settings |
@@ -736,7 +732,8 @@ def _capture_loop(self): |
736 | 732 | self.frame_timestamp = time.time() |
737 | 733 |
|
738 | 734 | except Exception as e: |
739 | | - self.error.emit(f"Error in capture thread: {str(e)}") |
| 735 | + if self.node: |
| 736 | + self.node.get_logger().error(f"Error in capture thread: {str(e)}") |
740 | 737 | finally: |
741 | 738 | if self.camera and self.camera.isOpened(): |
742 | 739 | self.camera.release() |
@@ -825,13 +822,10 @@ def _process_loop(self): |
825 | 822 | with self.frame_lock: |
826 | 823 | self.processed_frame = frame |
827 | 824 |
|
828 | | - # Emit frame for UI with rate limiting |
829 | | - current_time = time.time() |
830 | | - if current_time - self.last_ui_update >= self.min_ui_interval: |
831 | | - self.frame_ready.emit(frame) |
832 | | - self.last_ui_update = current_time |
| 825 | + # No UI frame emission needed in headless mode |
833 | 826 | except Exception as e: |
834 | | - self.error.emit(f"Error in process thread: {str(e)}") |
| 827 | + if self.node: |
| 828 | + self.node.get_logger().error(f"Error in process thread: {str(e)}") |
835 | 829 |
|
836 | 830 | def _publish_loop(self): |
837 | 831 | """Handle ROS publishing at controlled rate""" |
@@ -861,7 +855,8 @@ def _publish_loop(self): |
861 | 855 |
|
862 | 856 | self.last_publish_time = current_time |
863 | 857 | except Exception as e: |
864 | | - self.error.emit(f"Error in publish thread: {str(e)}") |
| 858 | + if self.node: |
| 859 | + self.node.get_logger().error(f"Error in publish thread: {str(e)}") |
865 | 860 |
|
866 | 861 |
|
867 | 862 | class CameraNode(Node): |
|
0 commit comments