aboutsummaryrefslogtreecommitdiffstats
path: root/AGENTS.md
blob: 65e10981e28e894cedcf7e408fed8ae89aa551c6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# trackcrop

Single-file Python script (`trackcrop.py`) that crops a video to a 1:1 square,
keeping a user-selected object centered. See README.md for usage.

## How it works

1. `cv2.selectROI` on the first frame, the user draws a box.
2. Pass 1: CSRT tracker (`cv2.TrackerCSRT_create`, needs opencv-contrib) records
   the object center per frame; when tracking is lost the last center is reused.
3. Centers are smoothed with a ~0.5s moving average (edge-padded, no lag).
4. Pass 2: re-read the video, crop an `s x s` square (`s = min(w, h)`) clamped
   to the frame, pipe raw BGR frames to ffmpeg.

Two passes keep memory flat, frames are never all held in RAM.

`--ak820 FPS` limits the output to the AK820 Pro screen's 255 frames at FPS:
if the clip is longer, an `input()` prompt picks the window (start, end or a
start second) before `selectROI`. Both passes skip to the window's first frame
with `grab()` (exact on any codec, unlike `CAP_PROP_POS_FRAMES` seeking) and
stop after `count` frames; audio is cut with `-ss`/`-t` on the second ffmpeg
input.

## Environment facts

- The local ffmpeg is built without `libx264` and without the native `aac`
  encoder. Encoding uses `h264_vaapi` on `/dev/dri/renderD128`. NVENC, AMF and
  Vulkan H.264 encoders are listed but do not work on this machine.
- Audio is stream-copied, so no audio encoder is needed. `--no-audio` drops it.
- For test clips use `libvpx-vp9` video and `libopus` audio.
- `drawbox` with a `t` expression did not animate in testing, use `overlay`
  with `y='...*t'` to make a moving test object.

## Testing

No test suite. Verify end to end headless by generating a synthetic clip with a
moving red square, stubbing the GUI, and measuring the square's offset from the
output frame center:

```python
import sys, runpy, cv2
cv2.namedWindow = lambda *a: None
cv2.selectROI = lambda *a: (310, 200, 100, 100)  # box around the test object
cv2.destroyAllWindows = lambda: None
sys.argv = ["trackcrop.py", "in.mp4", "out.mp4"]
runpy.run_path("trackcrop.py")
```

For `--ak820`, also stub the prompt (`builtins.input = lambda p: "e"`), use a
`testsrc2` background (it has a frame counter), and check the output's first
frame against the source with `cv2.matchTemplate`: the best match must be the
window's first frame.

Test clip:

```sh
ffmpeg -f lavfi -i "color=gray:s=720x1280:d=4:r=30" \
       -f lavfi -i "color=red:s=100x100:d=4:r=30" -f lavfi -i "sine=d=4" \
       -filter_complex "[0][1]overlay=x=310:y='200+200*t'" \
       -c:v libvpx-vp9 -c:a libopus -shortest in.mp4
```

A solid square is hard for CSRT (no texture), expect some drift late in the
clip; real footage tracks better.

## Conventions

- Keep it a single file, stdlib plus numpy/opencv only.
- Every source file carries the GPLv2-only header
  (`Copyright (C) <year> Danilo M. <danix@danix.xyz>`).
- Work directly on master.