Image Quality Checks
A usable Series 3/4 sample needs an image that exists and decodes, a parseable logical steering label, and a parseable absolute-throttle field. The current tools check these properties in two stages: a read-only image decoder and the trainer's label scan. Neither stage automatically proves that the command is the correct human target for the scene; that still requires review.
Capture Format
The runtime queues photo_<timestamp>.jpg into a dated run folder and appends the sampled command to <run>_labels.csv. When a capture run ends, finalize_photo_run() builds <run>.json. A finalized entry has this form:
{
"photo_20260702_141530_123456.jpg": {
"steering": 128,
"throttle": 0.35
}
}
steering is the logical 0..180 command (0 left, 90 center, 180 right). throttle is absolute forward physical PWM in 0.0..1.0; physical 55% is 0.55, not zero on the reference-throttle scale.
Image Scan
code/test_files/data/check_dataset_frames.py is read-only. It:
- Verifies each JPEG header and full decode;
- Flags truncation, unexpected dimensions, near-black/near-white frames, and files smaller than 1 KiB; and
- Compares disk filenames with
labels.jsonkeys.
Run it against the frozen dataset folder:
python3 code/test_files/data/check_dataset_frames.py \
code/ai_models_datasets/series_3_and_4/sidewalkpilot_dataset
The script does not calculate perceptual near-duplicates and does not judge whether a visually valid label is behaviorally correct.
Trainer Label Scan
The Series 3 trainer's SteeringDataset reports:
skipped_missing: no image path could be resolved;skipped_bad: steering or throttle could not be converted to a number;clipped_labels: steering was converted from normalized form or clamped into range; andskipped_overridden: a correction entry replaces the base row.
Run the dataset-building path without starting optimization:
cd code/ai_models_datasets/series_3_and_4
python3 series_3_sidewalkpilot_trainer.py \
--roots sidewalkpilot_dataset \
--dry-run
--dry-run validates paths, labels, split construction, balancing, and class weights. It does not decode every image; use check_dataset_frames.py for that.
Examples
Valid structure:
{
"photo_20260702_141530_123456.jpg": {
"steering": 62,
"throttle": 0.40
}
}
Examples that need review:
{
"photo_missing.jpg": {
"steering": 90,
"throttle": 0.30
},
"photo_bad_label.jpg": {
"steering": "n/a",
"throttle": 0.30
},
"photo_out_of_range.jpg": {
"steering": 240,
"throttle": 0.30
}
}
The first becomes missing if the file is absent, the second is skipped as bad, and the third is clamped and counted in clipped_labels; it is not automatically dropped.
Recovery
- For missing files, first verify whether the run was partially synced or renamed. Recopy the scoped source run without reverse
--delete, then repeat both scans. - For bad or out-of-range labels, inspect the original run CSV and field context before changing the JSON.
- For corrupt images, preserve the report and source path. Do not delete images, labels, logs, or checkpoints without Ram's explicit approval.
Evidence
- Complete
check_dataset_frames.pysummary - Trainer
--dry-runroot summaries and class counts - Dataset snapshot name, image count, label count, command, and code revision
- Any reviewed rows before and after correction
Coverage and Leakage Review
Image integrity is only the first gate. Before training, compare steering-class counts, left and right balance, ordinary turns, turns in shadow, lighting periods, surfaces, routes, and source runs. A collection countdown may guide field work, but filling numeric buckets does not prove visual diversity.
Consecutive frames create leakage risk. Series 3/4 window splitting reduces adjacency across train/validation, while Series 1/2's historical random split can place near-neighbors on both sides. Neither result should be described as capture-run-independent unless the split is actually grouped by run.
After any computer-to-computer sync, verify image and label counts, representative hashes, missing files, and unexpected deletions before accepting the destination as a new source of truth. Reverse sync with --delete is especially dangerous when large datasets are excluded on one side.