chore: remove duplicate v4-best (1).pt model file
This commit is contained in:
1 parent
16135c1b86
commit
1bc5e0634c
10 files changed
+588
-40
No files matched your search
@@ -1,40 +0,0 @@
|
||||
## Task 6: Jobs Page Redesign — Report
|
||||
|
||||
### Changes Made
|
||||
|
||||
**`templates/jobs.html`** — Complete rewrite from table layout to responsive card grid.
|
||||
|
||||
- Page heading changed from "All Jobs" to "Processing Jobs"
|
||||
- Card grid layout with responsive columns: 1 col (mobile), 2 col (640px+), 3 col (1024px+)
|
||||
- Each job card contains:
|
||||
- Thumbnail image from `/preview/thumb/<job_id>` with SVG placeholder fallback on error
|
||||
- Status badge (top-right corner) using `.badge-*` classes: success (COMPLETED), info (RUNNING), warning (PENDING), error (FAILED), neutral (CANCELLED)
|
||||
- Truncated job ID (12 chars + ellipsis) with copy-to-clipboard button (green checkmark feedback)
|
||||
- Video filename extracted from `job.video_path`
|
||||
- Thin 4px progress bar with model count and percentage
|
||||
- Relative timestamp (e.g. "2 minutes ago") with auto-update every 60s
|
||||
- "View Details" link to `/status/<job_id>`
|
||||
- Empty state with film-strip SVG icon, "No jobs yet" heading, and "Upload a video" CTA button
|
||||
- Scoped `<style>` block with design system tokens (no new CSS in `style.css`)
|
||||
- Inline `<script>` for relative time formatting and clipboard copy functionality
|
||||
|
||||
### Verification
|
||||
|
||||
- Template loads without Jinja2 errors
|
||||
- Flask `/jobs` endpoint returns 200 OK
|
||||
- Empty state renders correctly (no jobs by default)
|
||||
- All required elements present in rendered HTML: page header, grid, card structure, thumbnails, badges, copy button, progress bars, relative times, empty state, CTA button
|
||||
|
||||
### Commit
|
||||
|
||||
`b1170f4` — `feat(ui): jobs page — card grid with badges and thumbnails`
|
||||
|
||||
---
|
||||
|
||||
### Review Fixes (3 findings)
|
||||
|
||||
1. **Non-semantic card container** — Changed `<div class="card job-card">` → `<article class="card job-card">` (and closing tag) for better screen reader navigation.
|
||||
|
||||
2. **`onclick` on copy button** — Removed inline `onclick="copyJobId(this, '{{ job.job_id }}')"` attribute; added `data-job-id="{{ job.job_id }}"` for identification; moved copy logic to `addEventListener('click', ...)` in the `<script>` block using `document.querySelectorAll('.copy-btn')`.
|
||||
|
||||
3. **Gradient on progress bar** — Replaced `linear-gradient(90deg, var(--color-primary-500), var(--color-primary-400))` with solid `var(--color-primary-500)` to match flat design system.
|
||||
@@ -0,0 +1,260 @@
|
||||
# Feedmill Recounter — Web UI Redesign
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Redesign the feedmill_recounter web UI from a basic dark theme to a clean, modern, user-friendly interface with live preview of processing, using a Flat Design system with teal primary (#0D9488), orange accent (#EA580C), Plus Jakarta Sans font, and dark-mode-ready tokens.
|
||||
|
||||
**Current State:** 4 templates (base.html, index.html, status.html, jobs.html), 1 CSS file (34 lines), no JS file. App.py serves on port 9000.
|
||||
|
||||
**Tech Stack:** HTML, CSS, vanilla JS (no new Python dependencies), Flask templates
|
||||
|
||||
---
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Python >= 3.10 (uses `X | Y` union syntax)
|
||||
- No new Python dependencies for UI redesign (Flask + OpenCV already installed)
|
||||
- Web UI port 9000 via `WEB_PORT` env variable
|
||||
- All model weights in `models/` directory; `.engine` files are gitignored
|
||||
- Class filtering by name string (`"sack"`, `"box"`, `"truck"`), not numeric ID
|
||||
- CSS uses Flat Design with teal primary (#0D9488), orange accent (#EA580C)
|
||||
- Font: Plus Jakarta Sans (Google Fonts CDN)
|
||||
- Dark-mode-ready CSS custom properties (light mode default, easy dark swap)
|
||||
- Accessible: skip-link, focus rings, semantic HTML, ARIA where needed
|
||||
- Responsive: mobile-first, works on 320px to 1920px+
|
||||
- No JS frameworks — vanilla JS only
|
||||
- Live preview: pipeline emits JPEG frame every Nth frame → Job.latest_frame → served as base64 in API → JS polls and displays
|
||||
|
||||
---
|
||||
|
||||
## File Structure (changes only)
|
||||
|
||||
```
|
||||
feedmill_recounter/
|
||||
├── static/
|
||||
│ ├── style.css # REWRITE — full Flat Design system
|
||||
│ └── app.js # CREATE — JS for drag-drop, polling, preview
|
||||
├── templates/
|
||||
│ ├── base.html # REWRITE — semantic HTML, skip-link, font, tokens
|
||||
│ ├── index.html # REWRITE — drag-drop upload, model cards
|
||||
│ ├── status.html # REWRITE — progress bar, live preview, result cards
|
||||
│ └── jobs.html # REWRITE — card grid with badges + thumbnails
|
||||
├── src/
|
||||
│ ├── preview.py # CREATE — extract_thumbnail, probe_video, extract_sample_frames
|
||||
│ ├── pipeline.py # MODIFY — emit frame JPEG every Nth frame to job.latest_frame
|
||||
│ └── job.py # MODIFY — add latest_frame: bytes | None field
|
||||
└── app.py # MODIFY — add /preview/thumb, /api/jobs/<id>/samples, /api/jobs/<id>/frame routes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Design System Foundation
|
||||
|
||||
**Files:**
|
||||
- Rewrite: `feedmill_recounter/static/style.css`
|
||||
- Rewrite: `feedmill_recounter/templates/base.html`
|
||||
|
||||
**Requirements:**
|
||||
1. `style.css` — complete rewrite with:
|
||||
- CSS custom properties (design tokens) for colors, spacing, typography, radii, shadows
|
||||
- Teal primary: `--color-primary: #0D9488` (with 50-900 scale shades)
|
||||
- Orange accent: `--color-accent: #EA580C` (with 50-900 scale shades)
|
||||
- Neutrals: `--color-neutral-50` through `--color-neutral-900`
|
||||
- Status colors: success (#16A34A), warning (#F59E0B), error (#DC2626), info (#0EA5E9)
|
||||
- Typography: `--font-sans: 'Plus Jakarta Sans', system-ui, sans-serif`
|
||||
- Spacing scale: `--space-1` (4px) through `--space-16` (64px)
|
||||
- Border radius: `--radius-sm` (6px), `--radius-md` (8px), `--radius-lg` (12px), `--radius-xl` (16px)
|
||||
- Shadows: `--shadow-sm`, `--shadow-md`, `--shadow-lg`
|
||||
- Transitions: `--transition-fast` (150ms), `--transition-normal` (250ms)
|
||||
- Base reset/normalize
|
||||
- `.container` max-width 1200px centered
|
||||
- Skip-link (visually hidden, visible on focus)
|
||||
- Focus ring: 2px solid primary, offset 2px
|
||||
- Button styles: primary (teal), secondary (outline), accent (orange), ghost
|
||||
- Card component: `.card` with padding, border-radius, shadow, border
|
||||
- Badge component: `.badge` with status variants
|
||||
- Form elements: inputs, selects, labels with consistent styling
|
||||
- Responsive breakpoints: 640px, 768px, 1024px, 1280px
|
||||
|
||||
2. `base.html` — rewrite with:
|
||||
- `<!DOCTYPE html>`, `<html lang="en">`
|
||||
- `<meta charset="UTF-8">`, `<meta name="viewport" content="width=device-width, initial-scale=1.0">`
|
||||
- Google Fonts link for Plus Jakarta Sans (weights 400, 500, 600, 700)
|
||||
- Skip-link: `<a href="#main" class="skip-link">Skip to content</a>`
|
||||
- Semantic `<header>` with `<nav>` containing Upload and Jobs links
|
||||
- `<main id="main">` with `{% block content %}{% endblock %}`
|
||||
- `<footer>` with project credit
|
||||
- Link to `style.css`
|
||||
- `{% block head %}{% endblock %}` for page-specific head content (scripts, meta)
|
||||
|
||||
**Commit:** `feat(ui): design system foundation — CSS tokens, base template`
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Upload Page Redesign
|
||||
|
||||
**Files:**
|
||||
- Rewrite: `feedmill_recounter/templates/index.html`
|
||||
- Create: `feedmill_recounter/static/app.js` (drag-drop + video preview logic)
|
||||
|
||||
**Requirements:**
|
||||
1. `index.html` — rewrite with:
|
||||
- Page heading: "Upload & Analyze"
|
||||
- Drag-and-drop zone: `.upload-zone` with dashed border, icon, text "Drag video here or click to browse"
|
||||
- Hidden file input triggered by zone click
|
||||
- Video preview: `<video>` element showing selected file (controls, muted)
|
||||
- File info display: name, size, duration (if probed)
|
||||
- Model selection section: grid of `.model-card` elements
|
||||
- Each model card: checkbox, model name, known classes as badges, class-filter dropdown
|
||||
- "Select All" / "Deselect All" toggle for models
|
||||
- Submit button: "Start Analysis" (primary, disabled until video + model selected)
|
||||
- Loading state on submit: button text changes to "Starting..."
|
||||
|
||||
2. `app.js` — create with:
|
||||
- `initUploadZone()`: click-to-browse, drag-drop handlers, file validation
|
||||
- `initVideoPreview()`: FileReader → `<video>` src, display file info
|
||||
- `initModelCards()`: select-all toggle, enable/disable submit based on selections
|
||||
- `initFormSubmit()`: disable button on submit, show loading state
|
||||
- Progressive enhancement: works without JS (basic file input still functions)
|
||||
|
||||
**Commit:** `feat(ui): upload page — drag-drop, video preview, model cards`
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Preview Backend
|
||||
|
||||
**Files:**
|
||||
- Create: `feedmill_recounter/src/preview.py`
|
||||
- Modify: `feedmill_recounter/app.py` — add preview routes
|
||||
|
||||
**Requirements:**
|
||||
1. `preview.py` — create with:
|
||||
- `probe_video(video_path: str) -> dict`: returns {width, height, fps, duration, frame_count} using cv2
|
||||
- `extract_thumbnail(video_path: str, output_path: str, time_sec: float = 1.0) -> str`: extracts a single frame as JPEG, returns path
|
||||
- `extract_sample_frames(video_path: str, output_dir: str, count: int = 6) -> list[str]`: extracts evenly-spaced frames as JPEGs, returns paths
|
||||
|
||||
2. `app.py` — add routes:
|
||||
- `GET /preview/thumb/<job_id>` — serves thumbnail JPEG for a job's video (extract on first request, cache in job output_dir)
|
||||
- `GET /api/jobs/<job_id>/samples` — returns JSON array of sample frame paths (extract if not cached)
|
||||
- `GET /api/jobs/<job_id>/frame` — returns current latest_frame as JPEG (for live preview during processing)
|
||||
|
||||
**Commit:** `feat(api): video preview endpoints — thumbnail, sample frames, live frame`
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Status Page Redesign
|
||||
|
||||
**Files:**
|
||||
- Rewrite: `feedmill_recounter/templates/status.html`
|
||||
- Modify: `feedmill_recounter/static/app.js` — add polling + live preview logic
|
||||
|
||||
**Requirements:**
|
||||
1. `status.html` — rewrite with:
|
||||
- Job header: job ID, status badge (color-coded), created time
|
||||
- Progress section: animated progress bar with percentage label, current model name
|
||||
- Live preview section: `<img>` element that polls `/api/jobs/<id>/frame` every 2s, shows annotated frame during processing
|
||||
- Video thumbnail: extracted from original video
|
||||
- Results section: grid of result cards (not table)
|
||||
- Each result card: model name, loading/unloading/net counts as big numbers, batch count, duration, download button
|
||||
- Error display: alert-style error message if job failed
|
||||
- Auto-refresh: JS polls `/api/jobs/<id>` every 2s, updates progress bar + status badge + live preview + results
|
||||
|
||||
2. `app.js` — add:
|
||||
- `initStatusPage()`: starts polling loop
|
||||
- `updateProgressBar(progress)`: animates width
|
||||
- `updateLivePreview(jobId)`: fetches `/api/jobs/<id>/frame`, updates `<img>` src
|
||||
- `updateResults(results)`: renders result cards
|
||||
- Polling stops when status is COMPLETED, FAILED, or CANCELLED
|
||||
|
||||
**Commit:** `feat(ui): status page — progress bar, live preview, result cards, polling`
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Live Annotated Frame Emission
|
||||
|
||||
**Files:**
|
||||
- Modify: `feedmill_recounter/src/job.py` — add `latest_frame: bytes | None` field to Job
|
||||
- Modify: `feedmill_recounter/src/pipeline.py` — emit JPEG frame every 10th frame to callback
|
||||
|
||||
**Requirements:**
|
||||
1. `job.py` — add to `Job` dataclass:
|
||||
- `latest_frame: bytes | None = None` field
|
||||
- Thread-safe update: worker sets `job.latest_frame = jpeg_bytes` under `self._lock`
|
||||
|
||||
2. `pipeline.py` — modify `run_pipeline()`:
|
||||
- Accept optional `frame_callback: Callable[[bytes], None] | None = None` parameter
|
||||
- Every 10th frame (frame_idx % 10 == 0), encode frame as JPEG bytes via `cv2.imencode('.jpg', viz)`
|
||||
- Call `frame_callback(jpeg_bytes)` if provided
|
||||
- In `job.py` `_run_job()`, pass callback that sets `job.latest_frame`
|
||||
|
||||
**Commit:** `feat(pipeline): emit live annotated frames for preview`
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Jobs Page Redesign
|
||||
|
||||
**Files:**
|
||||
- Rewrite: `feedmill_recounter/templates/jobs.html`
|
||||
|
||||
**Requirements:**
|
||||
1. `jobs.html` — rewrite with:
|
||||
- Page heading: "Processing Jobs"
|
||||
- Empty state: illustration + "No jobs yet" + "Upload a video" CTA
|
||||
- Card grid layout (responsive: 1 col mobile, 2 col tablet, 3 col desktop)
|
||||
- Each job card:
|
||||
- Thumbnail (extracted from video, or placeholder)
|
||||
- Status badge (top-right corner)
|
||||
- Job ID (truncated, with copy button)
|
||||
- Video filename
|
||||
- Model count + progress bar (thin)
|
||||
- Created time (relative: "2 minutes ago")
|
||||
- "View Details" link
|
||||
- Sort: newest first (already default from job_queue.list_jobs)
|
||||
|
||||
**Commit:** `feat(ui): jobs page — card grid with badges and thumbnails`
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Accessibility, Responsive, Polish
|
||||
|
||||
**Files:**
|
||||
- Modify: `feedmill_recounter/static/style.css` — add responsive utilities, a11y enhancements
|
||||
- Modify: `feedmill_recounter/static/app.js` — add keyboard handlers, ARIA updates
|
||||
- Modify: `feedmill_recounter/templates/*.html` — add ARIA attributes where needed
|
||||
|
||||
**Requirements:**
|
||||
1. CSS additions:
|
||||
- `@media (prefers-reduced-motion: reduce)` — disable animations
|
||||
- `@media (prefers-color-scheme: dark)` — dark mode token overrides
|
||||
- Print styles: `@media print` — hide nav, buttons, show content
|
||||
- `.sr-only` utility for screen-reader-only text
|
||||
- Focus-visible styles for keyboard navigation
|
||||
|
||||
2. JS additions:
|
||||
- Keyboard: Enter/Space on upload zone triggers file input
|
||||
- ARIA: `aria-live="polite"` on progress region, `aria-busy` during processing
|
||||
- Announce status changes to screen readers
|
||||
|
||||
3. HTML fixes:
|
||||
- All images have `alt` text
|
||||
- All interactive elements have accessible names
|
||||
- Form inputs have associated labels
|
||||
- Status changes announced via `aria-live`
|
||||
|
||||
**Commit:** `fix(ui): accessibility, responsive, polish pass`
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Final Verification
|
||||
|
||||
**Files:** None (verification only)
|
||||
|
||||
**Requirements:**
|
||||
1. Run existing tests: `cd /home/jetson/feedmill_semarang_project/feedmill_recounter && python -m pytest tests/ -v`
|
||||
2. Verify Flask app starts: `python -c "from app import app; print('OK')"`
|
||||
3. Verify templates render: manual check or curl localhost:9000
|
||||
4. Verify CSS loads: check static/style.css served correctly
|
||||
5. Verify JS loads: check static/app.js served correctly
|
||||
6. Verify all routes work: /, /jobs, /status/<id>, /preview/thumb/<id>, /api/jobs, /api/jobs/<id>, /api/jobs/<id>/frame
|
||||
|
||||
**Commit:** none (verification only)
|
||||
@@ -0,0 +1,291 @@
|
||||
# Models Cleanup & Truck Filter Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Clean up the models directory, create TensorRT .engine files for all .pt models, add a "truck only" filter option, and fix the onnxruntime dependency issue.
|
||||
|
||||
**Architecture:** One script to export all .pt models to .engine, one cleanup step to remove duplicates, one template change to add truck filter, one fix to handle missing onnxruntime gracefully.
|
||||
|
||||
**Tech Stack:** Python, ultralytics YOLO, TensorRT (via ultralytics export), Flask templates
|
||||
|
||||
**Spec:** User request — April 2026
|
||||
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
### Current State
|
||||
- **Models directory:** 16 files (7 `.pt`, 7 `.onnx`, 0 `.engine`)
|
||||
- **Duplicate file:** `v4-best (1).pt` (same size as `v4-best.pt`) — needs removal
|
||||
- **No .engine files:** All models run as .pt or .onnx; .engine (TensorRT) would be faster on Jetson
|
||||
- **Recent job failure:** `job-e4e73c85` failed with `No module named 'onnxruntime'` when trying to run the second .onnx model
|
||||
|
||||
### Filtering Explained
|
||||
The class filter dropdown in the upload page controls which object classes the YOLO model detects:
|
||||
|
||||
| Filter Option | Value | Behavior |
|
||||
|---------------|-------|----------|
|
||||
| Use model defaults | `"default"` | Uses `model_config.known_classes` from `KNOWN_MODEL_CLASSES` map (e.g., `["person", "sack"]` for karung model) |
|
||||
| sack only | `"sack"` | Only detects sack objects |
|
||||
| box only | `"box"` | Only detects box objects |
|
||||
| sack + box | `"sack,box"` | Detects both sack and box |
|
||||
| **truck only** | (missing) | Should detect only truck objects |
|
||||
| all classes | `"all"` | No filtering — model detects everything it was trained on |
|
||||
|
||||
**Key difference:** "Use model defaults" applies the known class filter from the registry. "All classes" passes `None` as the filter, letting the model detect all its trained classes. The pipeline code at `src/pipeline.py:92-94` shows:
|
||||
```python
|
||||
effective_filter = class_filter or (
|
||||
model_config.known_classes if model_config.known_classes else None
|
||||
)
|
||||
```
|
||||
|
||||
### Onnxruntime Issue
|
||||
The error `No module named 'onnxruntime'` occurs when the pipeline tries to load `.onnx` models. Two options:
|
||||
1. Install onnxruntime (`pip install onnxruntime`)
|
||||
2. Convert .onnx models to .engine (TensorRT) which doesn't need onnxruntime
|
||||
|
||||
Since we're creating .engine files anyway, option 2 is preferred for Jetson.
|
||||
|
||||
---
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Python >= 3.10
|
||||
- Platform: Jetson (ARM64) with CUDA
|
||||
- ultralytics already installed
|
||||
- TensorRT available on Jetson
|
||||
- .engine files are gitignored
|
||||
- Models directory: `./models/`
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Remove Duplicate Model File
|
||||
|
||||
**Files:**
|
||||
- Delete: `models/v4-best (1).pt`
|
||||
|
||||
**Requirements:**
|
||||
- Remove the duplicate `v4-best (1).pt` file (same content as `v4-best.pt`)
|
||||
- Verify `v4-best.pt` still exists after deletion
|
||||
|
||||
- [ ] **Step 1: Remove duplicate file**
|
||||
|
||||
```bash
|
||||
rm "/home/jetson/feedmill_semarang_project/feedmill_recounter/models/v4-best (1).pt"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify v4-best.pt still exists**
|
||||
|
||||
```bash
|
||||
ls -la /home/jetson/feedmill_semarang_project/feedmill_recounter/models/v4-best.pt
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add -A && git commit -m "chore: remove duplicate v4-best (1).pt model file"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Create TensorRT .engine Files for All .pt Models
|
||||
|
||||
**Files:**
|
||||
- Create: `scripts/export_engines.py`
|
||||
|
||||
**Requirements:**
|
||||
- Script iterates over all `.pt` files in `models/`
|
||||
- For each .pt file, export to .engine using `model.export(format='engine', device=0, half=True, imgsz=640)`
|
||||
- Skip if .engine already exists
|
||||
- Handle export failures gracefully (log warning, continue)
|
||||
- Print summary of successful/failed exports
|
||||
|
||||
- [ ] **Step 1: Create export script**
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""Export all .pt models to TensorRT .engine format for Jetson."""
|
||||
|
||||
from pathlib import Path
|
||||
from ultralytics import YOLO
|
||||
|
||||
MODELS_DIR = Path(__file__).resolve().parent.parent / "models"
|
||||
|
||||
def main():
|
||||
pt_files = sorted(MODELS_DIR.glob("*.pt"))
|
||||
if not pt_files:
|
||||
print("No .pt files found in", MODELS_DIR)
|
||||
return
|
||||
|
||||
success = 0
|
||||
failed = 0
|
||||
skipped = 0
|
||||
|
||||
for pt_path in pt_files:
|
||||
engine_path = pt_path.with_suffix(".engine")
|
||||
if engine_path.exists():
|
||||
print(f"[SKIP] {pt_path.name} — .engine already exists")
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
print(f"[INFO] Exporting {pt_path.name} to TensorRT engine...")
|
||||
try:
|
||||
model = YOLO(str(pt_path))
|
||||
engine_path_str = model.export(format="engine", device=0, half=True, imgsz=640)
|
||||
print(f"[OK] Exported: {engine_path_str}")
|
||||
success += 1
|
||||
except Exception as e:
|
||||
print(f"[FAIL] {pt_path.name}: {e}")
|
||||
failed += 1
|
||||
|
||||
print(f"\nSummary: {success} exported, {skipped} skipped, {failed} failed")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the export script**
|
||||
|
||||
```bash
|
||||
cd /home/jetson/feedmill_semarang_project/feedmill_recounter
|
||||
python scripts/export_engines.py
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Verify .engine files created**
|
||||
|
||||
```bash
|
||||
ls -la models/*.engine
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add scripts/export_engines.py && git commit -m "feat: add TensorRT engine export script"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Add "truck only" Filter Option
|
||||
|
||||
**Files:**
|
||||
- Modify: `templates/index.html:255` — add truck only option
|
||||
|
||||
**Requirements:**
|
||||
- Add `<option value="truck">truck only</option>` after the "sack + box" option
|
||||
- The value `"truck"` will be split into `["truck"]` by the existing filter logic in `app.py:72`
|
||||
|
||||
- [ ] **Step 1: Add truck only option to template**
|
||||
|
||||
In `templates/index.html`, after line 254 (`<option value="sack,box">sack + box</option>`), add:
|
||||
|
||||
```html
|
||||
<option value="truck">truck only</option>
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify template renders**
|
||||
|
||||
```bash
|
||||
curl -s http://192.168.192.93:9000/ | grep "truck only"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add templates/index.html && git commit -m "feat: add truck only filter option to upload page"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Handle Missing onnxruntime Gracefully
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/pipeline.py:95` — catch import error and suggest .engine
|
||||
|
||||
**Requirements:**
|
||||
- When loading a .onnx model, if onnxruntime is not installed, raise a clear error message
|
||||
- Suggest the user either install onnxruntime or use the .engine version of the model
|
||||
- This prevents cryptic `ModuleNotFoundError` deep in the stack
|
||||
|
||||
- [ ] **Step 1: Add onnxruntime check in pipeline.py**
|
||||
|
||||
In `src/pipeline.py`, before line 95 (`shared_model = YOLO(model_config.path)`), add:
|
||||
|
||||
```python
|
||||
# Check onnxruntime for .onnx models
|
||||
if model_config.path.endswith(".onnx"):
|
||||
try:
|
||||
import onnxruntime # noqa: F401
|
||||
except ImportError:
|
||||
raise RuntimeError(
|
||||
f"onnxruntime is not installed. Cannot load .onnx model '{model_config.filename}'. "
|
||||
f"Either install it (pip install onnxruntime) or use the .engine version of this model."
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests**
|
||||
|
||||
```bash
|
||||
cd /home/jetson/feedmill_semarang_project/feedmill_recounter
|
||||
python -m pytest tests/test_pipeline.py -v
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add src/pipeline.py && git commit -m "fix: graceful error when onnxruntime missing for .onnx models"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Update Model Registry for .engine Files
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/model_registry.py:9-16` — add .engine entries to KNOWN_MODEL_CLASSES
|
||||
|
||||
**Requirements:**
|
||||
- Add entries for .engine files so they get proper known_classes
|
||||
- Since .engine files have the same stem as .pt files, the existing mapping should work
|
||||
- But verify that .engine files are picked up by `scan_models()` (they should be, since `MODEL_EXTENSIONS` includes `.engine`)
|
||||
|
||||
- [ ] **Step 1: Verify .engine files are scanned**
|
||||
|
||||
```python
|
||||
cd /home/jetson/feedmill_semarang_project/feedmill_recounter
|
||||
python3 -c "from src.model_registry import scan_models; models = scan_models('./models'); print([m.filename for m in models if m.filename.endswith('.engine')])"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: If not scanned, check MODEL_EXTENSIONS includes .engine**
|
||||
|
||||
The current code already has `MODEL_EXTENSIONS = {".pt", ".onnx", ".engine"}` — no change needed.
|
||||
|
||||
- [ ] **Step 3: Verify known_classes are assigned to .engine models**
|
||||
|
||||
```python
|
||||
python3 -c "from src.model_registry import scan_models; models = scan_models('./models'); [(print(m.filename, m.known_classes)) for m in models if m.filename.endswith('.engine')]"
|
||||
```
|
||||
|
||||
- [ ] **Step 4: No commit needed if everything works — this is verification only**
|
||||
|
||||
---
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
| Task | File | Change |
|
||||
|------|------|--------|
|
||||
| 1 | `models/v4-best (1).pt` | DELETE |
|
||||
| 2 | `scripts/export_engines.py` | CREATE — export script |
|
||||
| 3 | `templates/index.html` | ADD — truck only filter option |
|
||||
| 4 | `src/pipeline.py` | ADD — onnxruntime check |
|
||||
| 5 | `src/model_registry.py` | VERIFY only — .engine already supported |
|
||||
|
||||
---
|
||||
|
||||
## Execution Order
|
||||
|
||||
1. Task 1 (quick cleanup)
|
||||
2. Task 3 (quick template fix)
|
||||
3. Task 4 (quick pipeline fix)
|
||||
4. Task 5 (verification)
|
||||
5. Task 2 (export engines — longest, can run in background)
|
||||
|
||||
Tasks 1, 3, 4 can be done in parallel. Task 2 should be last since it takes time.
|
||||
@@ -0,0 +1,13 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: feedmill-recounter
|
||||
Version: 0.1.0
|
||||
Summary: AI video analysis tool for counting objects in feedmill videos
|
||||
Requires-Python: >=3.10
|
||||
Requires-Dist: ultralytics
|
||||
Requires-Dist: opencv-python
|
||||
Requires-Dist: numpy
|
||||
Requires-Dist: shapely
|
||||
Requires-Dist: flask
|
||||
Requires-Dist: python-dotenv
|
||||
Provides-Extra: dev
|
||||
Requires-Dist: pytest; extra == "dev"
|
||||
@@ -0,0 +1,9 @@
|
||||
README.md
|
||||
pyproject.toml
|
||||
src/__init__.py
|
||||
src/feedmill_recounter.egg-info/PKG-INFO
|
||||
src/feedmill_recounter.egg-info/SOURCES.txt
|
||||
src/feedmill_recounter.egg-info/dependency_links.txt
|
||||
src/feedmill_recounter.egg-info/entry_points.txt
|
||||
src/feedmill_recounter.egg-info/requires.txt
|
||||
src/feedmill_recounter.egg-info/top_level.txt
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[console_scripts]
|
||||
recounter = cli:main
|
||||
recounter-web = app:main
|
||||
@@ -0,0 +1,9 @@
|
||||
ultralytics
|
||||
opencv-python
|
||||
numpy
|
||||
shapely
|
||||
flask
|
||||
python-dotenv
|
||||
|
||||
[dev]
|
||||
pytest
|
||||
@@ -0,0 +1 @@
|
||||
__init__
|
||||
@@ -252,6 +252,7 @@
|
||||
<option value="sack">sack only</option>
|
||||
<option value="box">box only</option>
|
||||
<option value="sack,box">sack + box</option>
|
||||
<option value="truck">truck only</option>
|
||||
<option value="all">all classes</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user