diff --git a/ROLLINGSUM_GUIDE.md b/ROLLINGSUM_GUIDE.md index e78ab81..d576b40 100644 --- a/ROLLINGSUM_GUIDE.md +++ b/ROLLINGSUM_GUIDE.md @@ -307,7 +307,7 @@ frame,column_mean,rolling_mean,deviation,normalized_deviation,dropped Use the included analysis script: ```powershell -uv run analyze_sma.py output.csv +uv run scripts/analyze_sma.py output.csv ``` **Output includes:** @@ -376,7 +376,7 @@ window-size=50 threshold=0.0002 **Solution**: 1. Run with CSV logging -2. Analyze with `uv run analyze_sma.py output.csv` +2. Analyze with `uv run scripts/analyze_sma.py output.csv` 3. Use recommended threshold from 90th-99th percentile ### Too many frames dropped (threshold too low) @@ -464,7 +464,7 @@ subprocess.run([ ]) # Analyze results -subprocess.run(['uv', 'run', 'analyze_sma.py', 'output.csv']) +subprocess.run(['uv', 'run', 'scripts/analyze_sma.py', 'output.csv']) ``` ### Adaptive Threshold @@ -601,7 +601,7 @@ Update [`README.md`](README.md): - Original algorithm: `cli.py` lines 64-79 (column extraction and mean comparison) - Template element: [`gst/select/gstselect.c`](gst/select/gstselect.c) - GStreamer base transform: [GstBaseTransform documentation](https://gstreamer.freedesktop.org/documentation/base/gstbasetransform.html) -- [analyze_sma.py](analyze_sma.py) - Analysis tool +- [scripts/analyze_sma.py](scripts/analyze_sma.py) - Analysis tool - GStreamer documentation: https://gstreamer.freedesktop.org/documentation/ ## Support diff --git a/network_guide.md b/network_guide.md new file mode 100644 index 0000000..1850401 --- /dev/null +++ b/network_guide.md @@ -0,0 +1,29 @@ +# how to send a line + +## Sender (crop to first column, send raw over UDP) +```pwsh +gst-launch-1.0 -v ` + videotestsrc pattern=smpte ! ` + videocrop left=0 right=639 top=0 bottom=0 ! ` + video/x-raw,format=RGB,width=1,height=640,framerate=30/1 ! ` + udpsink host=127.0.0.1 port=5000 +``` + +**Note:** Default `videotestsrc` height is 640, not 480. Adjust caps to match your actual frame size. + +## Receiver Options + +### GStreamer Receiver (raw UDP → display) +```pwsh +gst-launch-1.0 -v ` + udpsrc port=5000 caps="video/x-raw,format=RGB,width=1,height=640,framerate=30/1" ! ` + videoconvert ! ` + autovideosink +``` + +### Python/OpenCV Receiver +```pwsh +uv run scripts/recv_raw_column.py +``` + +See [`scripts/recv_raw_column.py`](scripts/recv_raw_column.py) for the Python implementation with debug options. \ No newline at end of file diff --git a/analyze_sma.py b/scripts/analyze_sma.py similarity index 100% rename from analyze_sma.py rename to scripts/analyze_sma.py diff --git a/scripts/recv_raw_column.py b/scripts/recv_raw_column.py new file mode 100644 index 0000000..8229229 --- /dev/null +++ b/scripts/recv_raw_column.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = "<=3.10" +# dependencies = [ +# "opencv-python", +# "numpy", +# ] +# /// + +import socket +import numpy as np +import cv2 + +# Debug flag - set to True to see frame reception details +DEBUG = False + +# Stream parameters (match your GStreamer sender) +WIDTH = 1 +HEIGHT = 640 # Default videotestsrc height +CHANNELS = 3 +FRAME_SIZE = WIDTH * HEIGHT * CHANNELS # bytes + +UDP_IP = "0.0.0.0" +UDP_PORT = 5000 + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +sock.bind((UDP_IP, UDP_PORT)) + +print(f"Receiving raw {WIDTH}x{HEIGHT} RGB frames on UDP port {UDP_PORT}") +if DEBUG: + print(f"Expected frame size: {FRAME_SIZE} bytes") + +cv2.namedWindow("Raw Column Stream", cv2.WINDOW_NORMAL) + +frame_count = 0 +while True: + data, addr = sock.recvfrom(65536) + + if len(data) != FRAME_SIZE: + if DEBUG: + print(f"Received {len(data)} bytes (expected {FRAME_SIZE}), skipping...") + continue + + if DEBUG: + frame_count += 1 + if frame_count % 30 == 0: + print(f"Received {frame_count} frames") + + frame = np.frombuffer(data, dtype=np.uint8).reshape((HEIGHT, WIDTH, CHANNELS)) + + # scale for visibility + frame_large = cv2.resize( + frame, (200, HEIGHT), interpolation=cv2.INTER_NEAREST + ) + + cv2.imshow("Raw Column Stream", frame_large) + + if cv2.waitKey(1) == 27: # ESC to quit + break + +cv2.destroyAllWindows()