mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-12 07:23:45 +08:00
Avoid numeric reads that cannot change sign results
This commit is contained in:
committed by
Dominick Thompson
parent
d574b959c1
commit
7f2273aabf
@@ -805,7 +805,8 @@ class VisionModelCore:
|
||||
cached_bytes += crop.nbytes
|
||||
return result
|
||||
|
||||
for proposal_confidence, class_id, (x1, y1, x2, y2) in self._collect_detector_classifier_proposals(frame_bgr):
|
||||
proposals = self._collect_detector_classifier_proposals(frame_bgr)
|
||||
for proposal_index, (proposal_confidence, class_id, (x1, y1, x2, y2)) in enumerate(proposals):
|
||||
if class_id == 1:
|
||||
continue
|
||||
if class_id == 0:
|
||||
@@ -945,6 +946,16 @@ class VisionModelCore:
|
||||
if face_filter is not None and face_filter.reject_once((proposal_confidence, class_id, (x1, y1, x2, y2))):
|
||||
self.latest_detector_proposal = None
|
||||
return None
|
||||
if (type(proposals) is list and proposal_index == len(proposals) - 1 and
|
||||
expansions is DETECTOR_CLASSIFIER_EXPANSIONS and
|
||||
(expand_left, expand_top, expand_right, expand_bottom, expansion_weight) == expansions[-1] and
|
||||
getattr(self, 'classifier_net', None) is not None and class_id == 0 and not speed_scores and not raw_is_regulatory and
|
||||
proposal_confidence < DETECTOR_CLASSIFIER_STRONG_MODEL_MIN_PROPOSAL_CONFIDENCE and
|
||||
not (x1 >= frame_width * DETECTOR_CLASSIFIER_TRUSTED_MODEL_MIN_X_RATIO and
|
||||
box_height <= DETECTOR_CLASSIFIER_TRUSTED_MODEL_MAX_HEIGHT and
|
||||
proposal_area_ratio <= DETECTOR_CLASSIFIER_TRUSTED_MODEL_MAX_AREA_RATIO and
|
||||
proposal_confidence >= DETECTOR_CLASSIFIER_TRUSTED_MODEL_MIN_PROPOSAL_CONFIDENCE)):
|
||||
break
|
||||
model_read = classify_crop(sign_crop)
|
||||
if not any((expand_left, expand_top, expand_right, expand_bottom)):
|
||||
primary_reject_dominant = bool(
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
import numpy as np
|
||||
|
||||
from openpilot.starpilot.speed_limits.vision import model
|
||||
|
||||
|
||||
class TestFinalUnusableCrop(unittest.TestCase):
|
||||
def run_case(self, *, proposals=None, regulatory=(False, False, False), reads=None,
|
||||
control=False, custom=False, net=True, primary_reject=False, veto_at=None):
|
||||
core = object.__new__(model.VisionModelCore)
|
||||
core.latest_detector_proposal = None
|
||||
core.temporal_tracking_enabled = True
|
||||
core.last_classifier_advisory_rejected = False
|
||||
core.last_classifier_reject_dominant = False
|
||||
if net:
|
||||
core.classifier_net = object()
|
||||
proposals = proposals if proposals is not None else [(.4, 0, (1400, 100, 1490, 237))]
|
||||
core._collect_detector_classifier_proposals = lambda frame: proposals
|
||||
if control or custom:
|
||||
core.detector_classifier_expansions = list(model.DETECTOR_CLASSIFIER_EXPANSIONS)
|
||||
core._iter_school_zone_read_crops = lambda crop: [(crop, 1.)]
|
||||
regions = iter(regulatory)
|
||||
region_count = [0]
|
||||
|
||||
def color(crop):
|
||||
region_count[0] += 1
|
||||
return next(regions)
|
||||
|
||||
core._is_regulatory_speed_sign = color
|
||||
calls, identity_calls = [], []
|
||||
values = iter(reads) if reads is not None else None
|
||||
|
||||
def classify(crop):
|
||||
index = len(calls)
|
||||
calls.append(region_count[0])
|
||||
result = next(values) if values is not None else (40, .8)
|
||||
core.last_classifier_advisory_rejected = result is None and not primary_reject
|
||||
core.last_classifier_reject_dominant = primary_reject and index == 0
|
||||
return result
|
||||
|
||||
def reject(candidate):
|
||||
identity_calls.append(candidate)
|
||||
return len(identity_calls) == veto_at
|
||||
|
||||
core._classify_speed_limit_from_model = classify
|
||||
core._speed_face_filter = SimpleNamespace(reject_once=reject)
|
||||
result = core._detect_sign_from_detector_classifier(np.zeros((1100, 2000, 3), dtype=np.uint8))
|
||||
return result, core.latest_detector_proposal, calls, identity_calls
|
||||
|
||||
def test_only_final_unusable_read_is_skipped(self):
|
||||
actual = self.run_case()
|
||||
control = self.run_case(control=True)
|
||||
self.assertEqual(actual[:2], control[:2])
|
||||
self.assertEqual(actual[2], [1, 2])
|
||||
self.assertEqual(control[2], [1, 2, 3])
|
||||
self.assertEqual(actual[3], control[3])
|
||||
|
||||
def test_eligibility_counterexamples_keep_all_reads(self):
|
||||
cases = (
|
||||
('strong_boundary', {'proposals': [(.60, 0, (1400, 100, 1490, 237))]}),
|
||||
('trusted_shape_boundary', {'proposals': [(.18, 0, (1040, 100, 1120, 155))]}),
|
||||
('tiny_trusted', {'proposals': [(.18, 0, (1040, 100, 1060, 130))]}),
|
||||
('accumulated_scores', {'regulatory': (True, False, False)}),
|
||||
('regulatory', {'regulatory': (True, True, True)}),
|
||||
('custom_expansions', {'custom': True}),
|
||||
('missing_net', {'net': False}),
|
||||
)
|
||||
for name, kwargs in cases:
|
||||
with self.subTest(name=name):
|
||||
actual = self.run_case(**kwargs)
|
||||
control = self.run_case(control=True, **kwargs)
|
||||
self.assertEqual(actual[:2], control[:2])
|
||||
self.assertEqual(actual[2], [1, 2, 3])
|
||||
self.assertEqual(actual[3], control[3])
|
||||
|
||||
def test_nonfinal_proposal_keeps_its_final_read(self):
|
||||
proposals = [(.4, 0, (1400, 100, 1490, 237)), (.3, 0, (1550, 150, 1650, 300))]
|
||||
actual = self.run_case(proposals=proposals, regulatory=(False,) * 6)
|
||||
control = self.run_case(proposals=proposals, regulatory=(False,) * 6, control=True)
|
||||
self.assertEqual(actual[:2], control[:2])
|
||||
self.assertEqual(actual[2], [1, 2, 3, 4, 5])
|
||||
self.assertEqual(control[2], [1, 2, 3, 4, 5, 6])
|
||||
|
||||
def test_prior_best_and_retry_conflict_are_preserved(self):
|
||||
proposals = [(.4, 0, (1400, 100, 1490, 237)), (.3, 0, (1550, 150, 1650, 300))]
|
||||
cases = (
|
||||
((True, True, True, False, False, False), ((40, .5),) * 3 + ((50, .8),) * 3, False, 40),
|
||||
((False, True, True, False, False, False), (None, (40, .5), None) + ((50, .8),) * 3, True, None),
|
||||
)
|
||||
for regulatory, reads, primary_reject, expected in cases:
|
||||
with self.subTest(retry_conflict=primary_reject):
|
||||
kwargs = {'proposals': proposals, 'regulatory': regulatory, 'reads': reads, 'primary_reject': primary_reject}
|
||||
actual = self.run_case(**kwargs)
|
||||
control = self.run_case(control=True, **kwargs)
|
||||
self.assertEqual(actual[:2], control[:2])
|
||||
self.assertEqual(None if actual[0] is None else actual[0].speed_limit_mph, expected)
|
||||
self.assertEqual(len(actual[2]), 5)
|
||||
self.assertEqual(len(control[2]), 6)
|
||||
|
||||
def test_school_class_is_unchanged(self):
|
||||
reads = (None,) * len(model.SCHOOL_ZONE_DIRECT_EXPANSIONS) + ((40, .8),) * 3
|
||||
kwargs = {'proposals': [(.4, 2, (1400, 100, 1490, 237))], 'reads': reads}
|
||||
actual = self.run_case(**kwargs)
|
||||
control = self.run_case(control=True, **kwargs)
|
||||
self.assertEqual(actual, control)
|
||||
self.assertEqual(len(actual[2]), 4)
|
||||
|
||||
def test_identity_veto_runs_before_skip(self):
|
||||
actual = self.run_case(veto_at=3)
|
||||
self.assertIsNone(actual[0])
|
||||
self.assertIsNone(actual[1])
|
||||
self.assertEqual(actual[2], [1, 2])
|
||||
self.assertEqual(len(actual[3]), 3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"protected_inventory_sha256": "7e0769655012151a24d010b6fe2498b7f002d08a1821c5285825fcea7037fbe5", "schema_version": 1}
|
||||
{"protected_inventory_sha256": "2d361bacbecdd746f4a72467a899523697cc45c88d70a3ccb8500c8ef6e15f63", "schema_version": 1}
|
||||
|
||||
Reference in New Issue
Block a user