A.R.G.U.S 2.1.0
Adaptive Real-Time Guardian for Unsafe Situations
Loading...
Searching...
No Matches
VisionProcessor.cpp
Go to the documentation of this file.
1
14#include "VisionProcessor.hpp"
15#include <opencv2/imgproc.hpp>
16
17// Constructor
18
30 : config_(config),
31 // Reserved marker resources are still constructed for compatibility
32 // with marker-based configurations.
33 dictionary_(cv::aruco::getPredefinedDictionary(
34 static_cast<cv::aruco::PredefinedDictionaryType>(config.dictionaryId))),
35 detector_(dictionary_, detectorParams_)
36{}
37
38// updateConfig()
39
50 config_ = newConfig;
51}
52
53// process()
54
76 const cv::Mat& frame,
77 std::chrono::steady_clock::time_point captureTimestamp)
78{
79 (void)captureTimestamp;
80
81 // Record entry time so processing_time can be measured at each return point.
82 const auto startTime = std::chrono::steady_clock::now();
83
88 auto makeResult = [&](SafetyState state) -> SafetyResult {
89 const auto now = std::chrono::steady_clock::now();
90 return SafetyResult{
91 state,
92 now,
93 std::chrono::duration_cast<std::chrono::microseconds>(now - startTime)
94 };
95 };
96
97 // Marker-based stages are intentionally disabled on this branch.
98 // Safety decisions currently come from forbidden-layer colour detection.
99
100 // Depth layer colour detection
101 //
102 // Detects whether the forbidden playdough layer colour is visible anywhere
103 // inside the configured safe zone ROI. Exposure of this colour indicates
104 // the tool has cut beyond the permitted depth and the robot must retract.
105 //
106 // Pipeline:
107 // 1. Crop frame to safe zone ROI — limits work to the relevant area
108 // and keeps per-frame latency bounded.
109 // 2. Convert ROI from BGR to HSV — separates colour from brightness
110 // for lighting-robust detection.
111 // 3. Threshold for target hue using two cv::inRange calls combined
112 // with cv::bitwise_or. Two ranges are required for red because red
113 // wraps around 0°/180° in OpenCV HSV (H range 0–179).
114 // 4. Count non-zero pixels in the combined mask. A pixel count
115 // above depthPixelThreshold confirms the layer is exposed.
116 //
117 // Returning DEPTH_EXCEEDED here triggers FREEZE_NOW in GuardianFSM
118 // followed by a retract command via RobotInterlock — distinct from
119 // a standard freeze in that the robot actively retracts rather than
120 // simply halting in place.
121
122 if (config_.depthCheckEnabled) {
123
124 // Step 1: Crop to safe zone ROI.
125 //
126 // Clamp all coordinates defensively to frame bounds.
127 // Guards against runtime-adjusted ROI values (e.g. dragged via UI)
128 // that transiently extend past the image edges between frames.
129 const int roiX = std::max(0, static_cast<int>(config_.safeZoneXMin));
130 const int roiY = std::max(0, static_cast<int>(config_.safeZoneYMin));
131 const int roiW = std::min(
132 frame.cols - roiX,
133 static_cast<int>(config_.safeZoneXMax - config_.safeZoneXMin));
134 const int roiH = std::min(
135 frame.rows - roiY,
136 static_cast<int>(config_.safeZoneYMax - config_.safeZoneYMin));
137
138 // Guard: skip stage if ROI is degenerate (zero or negative dimensions).
139 // Prevents cv::Rect from throwing on a misconfigured or transitional
140 // ROI. The absence of a DEPTH_EXCEEDED result implicitly signals that
141 // the check was bypassed this frame.
142 if (roiW > 0 && roiH > 0) {
143
144 // cv::Mat::operator() with a Rect is a zero-copy view — no pixel
145 // data is duplicated. Cheap enough to be safe on every frame.
146 const cv::Mat roi = frame(cv::Rect(roiX, roiY, roiW, roiH));
147
148 // Step 2: Convert ROI from BGR to HSV.
149 //
150 // HSV isolates hue from brightness — under variable lab lighting
151 // the same playdough colour can appear significantly darker or
152 // lighter in BGR but its hue remains stable. This makes HSV
153 // thresholding substantially more reliable than BGR thresholding
154 // for a physical demo.
155 cv::Mat hsvRoi;
156 cv::cvtColor(roi, hsvRoi, cv::COLOR_BGR2HSV);
157
158 // Step 3: Threshold for target hue range.
159 //
160 // Two inRange calls are combined with bitwise_or for a generic
161 // dual-band hue model:
162 //
163 // mask1 - primary hue band: H in [depthHueLower1, depthHueUpper1]
164 // mask2 - optional second band: H in [depthHueLower2, depthHueUpper2]
165 //
166 // For current green-layer detection, hue band 1 targets the green
167 // interval and mask2 is disabled by setting depthHueLower2 >
168 // depthHueUpper2 in VisionConfig, so bitwise_or reduces to mask1
169 // with no code change required.
170 cv::Mat mask1, mask2, combinedMask;
171
172 cv::inRange(
173 hsvRoi,
174 cv::Scalar(config_.depthHueLower1,
175 config_.depthSatMin,
176 config_.depthValMin),
177 cv::Scalar(config_.depthHueUpper1,
178 config_.depthSatMax,
179 config_.depthValMax),
180 mask1);
181
182 cv::inRange(
183 hsvRoi,
184 cv::Scalar(config_.depthHueLower2,
185 config_.depthSatMin,
186 config_.depthValMin),
187 cv::Scalar(config_.depthHueUpper2,
188 config_.depthSatMax,
189 config_.depthValMax),
190 mask2);
191
192 // Combine both masks — any pixel matching either hue range is
193 // considered a match. bitwise_or is O(pixels), no allocation.
194 cv::bitwise_or(mask1, mask2, combinedMask);
195
196 // Step 4: Count matching pixels.
197 //
198 // countNonZero is O(pixels) with no allocation — safe for
199 // real-time use on every frame.
200 // Compared against depthPixelThreshold to filter noise:
201 // isolated reflections or stray colour patches produce far
202 // fewer matching pixels than genuine layer exposure.
203 const int matchingPixels = cv::countNonZero(combinedMask);
204
205 if (matchingPixels >= config_.depthPixelThreshold) {
206 // Forbidden layer confirmed exposed.
207 return makeResult(SafetyState::DEPTH_EXCEEDED);
208 }
209 }
210 }
211
212 // All checks passed.
213 return makeResult(SafetyState::SAFE);
214}
SafetyState
Outcome of a single vision safety evaluation.
Definition Types.hpp:22
Vision safety evaluator for the ARGUS pipeline.
SafetyResult process(const cv::Mat &frame, std::chrono::steady_clock::time_point captureTimestamp)
Processes a single camera frame and evaluates tool safety.
void updateConfig(const VisionConfig &newConfig)
Updates the safety configuration at runtime.
VisionProcessor(const VisionConfig &config)
Constructs a VisionProcessor with the given safety configuration.
Output of VisionProcessor::process() for each camera frame.
Definition Types.hpp:51
All tunable safety thresholds and zone boundaries for VisionProcessor.
int safeZoneXMax
Right boundary of the safe zone in pixels.
int depthValMax
Maximum value threshold.
int depthHueLower1
Lower hue bound for pink/magenta.
int safeZoneYMax
Bottom boundary of the safe zone in pixels.
int depthPixelThreshold
Minimum number of HSV-matching pixels required to confirm the forbidden layer is exposed.
int depthHueUpper2
Disabled - see depthHueLower2 above.
int safeZoneYMin
Top boundary of the safe zone in pixels.
int depthHueLower2
Disabled - lower > upper produces empty mask2.
int safeZoneXMin
Left boundary of the safe zone in pixels.
int depthSatMax
Maximum saturation threshold.
int depthHueUpper1
Upper hue bound for pink/magenta.
bool depthCheckEnabled
Runtime enable/disable flag for Stage 8 colour detection.