diff --git a/BUILD_IDSUEYE.md b/BUILD_IDSUEYE.md new file mode 100644 index 0000000..aeee77b --- /dev/null +++ b/BUILD_IDSUEYE.md @@ -0,0 +1,118 @@ +# Building IDS uEye Plugin Only + +This guide explains how to build only the IDS uEye GStreamer plugin from the gst-plugins-vision project. + +## Prerequisites + +1. **IDS uEye SDK** must be installed on your system + - Default location (Windows): `C:\Program Files\IDS\uEye\Develop` + - If installed elsewhere, set the `IDSUEYE_DIR` CMake variable + +2. **Build tools**: + - CMake (version 2.8.0 or higher) + - Visual Studio 2019 or newer (on Windows) + - GStreamer development files (1.2.0 or higher) + +3. **Required GStreamer components**: + - GStreamer base library + - GStreamer video library + - GLib2 + - GObject + +## Building + +### Quick Build (Windows) + +Simply run the provided batch script: + +```batch +build_idsueye_only.bat +``` + +Or provide a custom GStreamer path: + +```batch +build_idsueye_only.bat "C:\path\to\gstreamer" +``` + +This will: +1. Create a `build_idsueye` directory +2. Use the standalone IDS uEye CMakeLists configuration +3. Configure the project with CMake +4. Build only the IDS uEye plugin +5. Output: `build_idsueye\sys\idsueye\Release\libgstidsueye.dll` + +**Default GStreamer location:** `C:\bin\gstreamer\1.0\msvc_x86_64` + +### Manual Build + +If you prefer manual control or need to customize the build: + +```batch +# Create build directory +mkdir build_idsueye +cd build_idsueye + +# Configure (adjust generator as needed) +cmake .. -G "Visual Studio 16 2019" -A x64 + +# Build only IDS uEye target +cmake --build . --config Release --target gstidsueye + +# Or for Debug build +cmake --build . --config Debug --target gstidsueye +``` + +### Custom IDS uEye SDK Location + +If your IDS uEye SDK is installed in a non-standard location: + +```batch +cmake .. -G "Visual Studio 16 2019" -A x64 -DIDSUEYE_DIR="C:\Path\To\IDS\uEye\Develop" +``` + +### Linux Build + +```bash +mkdir build_idsueye +cd build_idsueye +cmake .. +make gstidsueye +``` + +## Output + +The built plugin will be located at: +- **Windows Release**: `build_idsueye\sys\idsueye\Release\libgstidsueye.dll` +- **Windows Debug**: `build_idsueye\sys\idsueye\Debug\libgstidsueye.dll` +- **Linux**: `build_idsueye/sys/idsueye/libgstidsueye.so` + +## Installation + +To install the plugin to the GStreamer plugin directory: + +```batch +cmake --build . --config Release --target install +``` + +Or manually copy the DLL to your GStreamer plugins directory (typically `C:\gstreamer\1.0\msvc_x86_64\lib\gstreamer-1.0\`). + +## Troubleshooting + +### IDS uEye SDK Not Found + +If CMake cannot find the IDS uEye SDK: +1. Verify the SDK is installed +2. Check the installation path +3. Set `IDSUEYE_DIR` explicitly in the CMake command + +### Missing Dependencies + +If GStreamer dependencies are not found: +1. Ensure GStreamer and development headers are installed +2. Add GStreamer to your system PATH +3. Set `GSTREAMER_ROOT` environment variable if needed + +### Link Errors + +Make sure you're building for the correct architecture (x64 vs x86) matching your IDS uEye SDK installation. \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e86a36..3fd7a3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION "2.8.0") +cmake_minimum_required(VERSION "3.5.0") project(gst-plugins-vision) diff --git a/CMakeLists_idsueye_only.txt b/CMakeLists_idsueye_only.txt new file mode 100644 index 0000000..638a733 --- /dev/null +++ b/CMakeLists_idsueye_only.txt @@ -0,0 +1,86 @@ +cmake_minimum_required(VERSION 3.5.0) + +project(gst-idsueye-plugin) + +# Values used when registering plugins +add_definitions(-DGST_PACKAGE_VERSION="idsueye-standalone") +add_definitions(-DGST_PACKAGE_LICENSE="LGPL") +add_definitions(-DGST_PACKAGE_ORIGIN="Unknown package origin") +add_definitions(-DGST_PACKAGE_NAME="${CMAKE_PROJECT_NAME}") +add_definitions(-DPACKAGE="${CMAKE_PROJECT_NAME} package") + +set(CMAKE_SHARED_MODULE_PREFIX "lib") +set(CMAKE_SHARED_LIBRARY_PREFIX "lib") + +# Add local cmake modules +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) + +# Find required packages +find_package(GStreamer REQUIRED COMPONENTS base) +find_package(GStreamerPluginsBase REQUIRED COMPONENTS video) +find_package(GLIB2 REQUIRED) +find_package(GObject REQUIRED) +find_package(IDSuEye REQUIRED) + +# Setup include directories +include_directories( + . + ${GSTREAMER_INCLUDE_DIR} + ${GLIB2_INCLUDE_DIR} + ${IDSUEYE_INCLUDE_DIR}) + +# Set install directories +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + if (WIN32) + get_filename_component(_PREFIX "${GSTREAMER_PLUGIN_DIR}/../" REALPATH) + else () + set(_PREFIX "/usr/lib") + endif () + set(CMAKE_INSTALL_PREFIX "${_PREFIX}" + CACHE PATH "Common prefix for all installed files" FORCE) +endif() + +if (NOT PLUGIN_INSTALL_DIR) + set(PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/gstreamer-1.0/" + CACHE PATH "Location to install plugins") +endif() + +if (WIN32 AND NOT PDB_INSTALL_DIR) + set(PDB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/gstreamer-1.0/" + CACHE PATH "Location to install PDB debug files") +endif() + +# Build the IDS uEye plugin +set(SOURCES + sys/idsueye/gstidsueyesrc.c) + +set(HEADERS + sys/idsueye/gstidsueyesrc.h) + +set(libname gstidsueye) + +add_library(${libname} MODULE + ${SOURCES} + ${HEADERS}) + +target_link_libraries(${libname} + ${GLIB2_LIBRARIES} + ${GOBJECT_LIBRARIES} + ${GSTREAMER_LIBRARY} + ${GSTREAMER_BASE_LIBRARY} + ${GSTREAMER_VIDEO_LIBRARY} + ${IDSUEYE_LIBRARIES}) + +if (WIN32) + install(FILES $ DESTINATION ${PDB_INSTALL_DIR} COMPONENT pdb OPTIONAL) +endif() +install(TARGETS ${libname} LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR}) + +# Print configuration summary +message(STATUS "") +message(STATUS "IDS uEye Plugin Build Configuration:") +message(STATUS " GStreamer root: ${GSTREAMER_ROOT}") +message(STATUS " IDS uEye SDK: ${IDSUEYE_DIR}") +message(STATUS " Install prefix: ${CMAKE_INSTALL_PREFIX}") +message(STATUS " Plugin install dir: ${PLUGIN_INSTALL_DIR}") +message(STATUS "") \ No newline at end of file diff --git a/DESIGN_ROLLINGSUM.md b/DESIGN_ROLLINGSUM.md new file mode 100644 index 0000000..be391de --- /dev/null +++ b/DESIGN_ROLLINGSUM.md @@ -0,0 +1,290 @@ +# Rolling Sum Filter - Design Document + +## Overview + +A GStreamer element that drops frames based on rolling mean analysis of a single column of pixels. Inspired by the detection algorithm in `cli.py`, simplified for real-time streaming. + +## Element Name + +**`rollingsum`** - Transform element that analyzes pixel values and selectively drops frames + +## Purpose + +Monitor a vertical column of pixels in video frames, calculate the rolling mean over a time window, and drop frames when the current frame's column mean deviates significantly from the rolling mean baseline. + +## Architecture + +### Base Class +- Inherits from `GstBaseTransform` (similar to [`select`](gst/select/gstselect.c)) +- In-place transform (analysis only, no frame modification) +- Returns `GST_BASE_TRANSFORM_FLOW_DROPPED` to drop frames +- Returns `GST_FLOW_OK` to pass frames + +### Element Structure + +```c +struct _GstRollingSum +{ + GstBaseTransform element; + + /* Properties */ + gint window_size; // Number of frames in rolling window (default: 1000) + gint column_index; // Which column to analyze (default: 1, second column) + gint stride; // Row sampling stride (default: 1, every row) + gdouble threshold; // Deviation threshold for dropping (default: 0.5) + + /* State */ + gdouble *ring_buffer; // Circular buffer of column means + gint ring_index; // Current position in ring buffer + gint frame_count; // Total frames processed + gdouble rolling_mean; // Current rolling mean +}; +``` + +## Algorithm (Simplified from cli.py) + +### Per Frame Processing + +``` +1. Extract column data: + - Select column at column_index + - Sample every stride rows + - Calculate mean of sampled pixels: frame_mean + +2. Update ring buffer: + - Store frame_mean in ring_buffer[ring_index] + - Increment ring_index (wrap around) + +3. Calculate rolling mean: + - Sum values in ring buffer (up to window_size or frame_count) + - Divide by actual window size + +4. Calculate deviation: + - deviation = abs(frame_mean - rolling_mean) + +5. Decision: + - If deviation > threshold: DROP frame + - Else: PASS frame +``` + +### Key Simplifications from cli.py + +- **No EMA tracking**: Use simple rolling mean instead of exponential moving average +- **No variance tracking**: Use fixed threshold instead of dynamic variance-based detection +- **No recording logic**: Just drop/pass, no buffering for output segments +- **No patience mechanism**: Immediate decision per frame + +## Properties + +| Property | Type | Default | Range | Description | +|----------|------|---------|-------|-------------| +| `window-size` | int | 1000 | 1-100000 | Number of frames in rolling window | +| `column-index` | int | 1 | 0-width | Which vertical column to analyze | +| `stride` | int | 1 | 1-height | Row sampling stride (1=all rows) | +| `threshold` | double | 0.5 | 0.0-1.0 | Normalized deviation threshold for dropping | + +## Data Structures + +### Ring Buffer +- Array of doubles sized to `window_size` +- Stores column mean for each processed frame +- Circular wrapping via modulo: `ring_index % window_size` + +### Frame Analysis +``` +For column_index=1, stride=1, frame height=H: +- Extract pixels: frame[:, column_index] +- Sample: frame[::stride, column_index] +- Count: H / stride samples +- Mean: sum(samples) / count +``` + +## Implementation Files + +### Directory Structure +``` +gst/rollingsum/ +├── CMakeLists.txt +├── gstrollingsum.c +└── gstrollingsum.h +``` + +### gstrollingsum.h +- Element type definitions +- Structure declarations +- Property enums +- Function prototypes + +### gstrollingsum.c +- GObject methods (init, dispose, get/set properties) +- GstBaseTransform methods (transform_ip) +- Helper functions (extract_column_mean, update_rolling_mean) +- Plugin registration + +### CMakeLists.txt +- Build configuration (copy from [`gst/select/CMakeLists.txt`](gst/select/CMakeLists.txt)) +- Link GStreamer base and video libraries + +## Video Format Support + +### Initial Implementation +- **Primary target**: Grayscale (GRAY8, GRAY16) +- **Secondary**: Bayer formats (common in machine vision) + +### Caps Filter +```c +static GstStaticPadTemplate sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ( + "video/x-raw, format=(string){GRAY8,GRAY16_LE,GRAY16_BE}; " + "video/x-bayer, format=(string){bggr,grbg,gbrg,rggb}" + ) + ); +``` + +## Pipeline Usage Examples + +### Basic Usage +```bash +gst-launch-1.0 idsueyesrc config-file=config.ini ! \ + rollingsum window-size=1000 column-index=1 threshold=0.5 ! \ + autovideosink +``` + +### Custom Configuration +```bash +gst-launch-1.0 idsueyesrc config-file=config.ini ! \ + rollingsum window-size=5000 column-index=320 stride=2 threshold=0.3 ! \ + queue ! \ + autovideosink +``` + +### With Format Conversion +```bash +gst-launch-1.0 idsueyesrc ! \ + videoconvert ! \ + video/x-raw,format=GRAY8 ! \ + rollingsum ! \ + autovideosink +``` + +## Performance Considerations + +### Memory Usage +- Ring buffer: `window_size * sizeof(double)` = ~8KB for default 1000 frames +- Minimal per-frame allocation + +### CPU Usage +- Column extraction: O(height/stride) +- Rolling mean update: O(1) using incremental sum +- Very lightweight compared to full-frame processing + +### Optimization Opportunities +1. **Incremental mean**: Track sum instead of recalculating +2. **SIMD**: Vectorize column summation +3. **Skip calculation**: Only recalc every N frames if baseline is stable + +## Testing Strategy + +### Unit Tests +- Ring buffer wrapping +- Mean calculation accuracy +- Threshold comparison logic + +### Integration Tests +- Pipeline with videotestsrc +- Pipeline with idsueyesrc +- Frame drop verification +- Property changes during playback + +### Test Cases +1. Static video (all frames similar) → all pass +2. Single bright frame → that frame drops +3. Gradual change → frames pass +4. Periodic pattern → pattern frames drop + +## Future Enhancements + +### Phase 2 (If Needed) +- Add EMA baseline tracking (like cli.py) +- Add variance-based thresholds +- Support multiple columns or regions +- Add metadata output (tag frames with deviation values) +- RGB format support (analyze specific channel) + +### Phase 3 (Advanced) +- Full cli.py recording logic +- Buffer and output segments +- Integration with probe detection systems + +## Integration with Existing Project + +### Build System +Update [`gst/CMakeLists.txt`](gst/CMakeLists.txt): +```cmake +add_subdirectory (rollingsum) +``` + +### Documentation +Update [`README.md`](README.md): +- Add rollingsum to "Other elements" section +- Add pipeline example + +## Mermaid Diagram: Data Flow + +```mermaid +graph TD + A[Video Frame] --> B[Extract Column] + B --> C[Calculate Column Mean] + C --> D[Store in Ring Buffer] + D --> E[Update Rolling Mean] + E --> F{Deviation > Threshold?} + F -->|Yes| G[DROP Frame] + F -->|No| H[PASS Frame] + C --> E + + style G fill:#ff6b6b + style H fill:#51cf66 +``` + +## Mermaid Diagram: Ring Buffer Operation + +```mermaid +graph LR + subgraph Ring Buffer + A[0] --> B[1] + B --> C[2] + C --> D[...] + D --> E[N-1] + E ---|wrap| A + end + + F[New Frame Mean] --> G[ring_index] + G --> A + + H[Rolling Mean] --> I[Sum all values] + I --> J[Divide by count] + + style G fill:#ffd43b +``` + +## Implementation Checklist + +- [ ] Create gst/rollingsum directory +- [ ] Implement gstrollingsum.h +- [ ] Implement gstrollingsum.c +- [ ] Create CMakeLists.txt +- [ ] Update gst/CMakeLists.txt +- [ ] Build and test basic functionality +- [ ] Test with idsueyesrc +- [ ] Update README.md +- [ ] Create feature branch +- [ ] Commit and document + +## References + +- 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) \ No newline at end of file diff --git a/README.md b/README.md index 4cdecc1..32704ef 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,32 @@ GStreamer plugins related to the field of machine vision. +we only care for ids, + +run using +``` +gst-launch-1.0 idsueyesrc config-file=whole-presacler64_autoexp-binningx2.ini ! queue ! autovideosink +``` + +drop frame if sum of image is bigger then 0.5 +``` +gst-launch-1.0 idsueyesrc config-file=whole-presacler64_autoexp-binningx2.ini ! videoconvert ! video/x-raw,format=GRAY8 ! rollingsum window-size=1000 column-index=1 threshold=0.5 ! queue ! autovideosink +``` + +**Note:** The `rollingsum` element analyzes a single column of pixels and drops frames when the column mean deviates from the rolling mean baseline by more than the threshold. Use `videoconvert` to ensure proper format negotiation. + +Additional examples: +```bash +# Analyze column 320 with larger window +gst-launch-1.0 idsueyesrc config-file=whole-presacler64_autoexp-binningx2.ini ! videoconvert ! video/x-raw,format=GRAY8 ! rollingsum window-size=5000 column-index=320 threshold=0.3 ! queue ! autovideosink + +# Use stride for faster processing (sample every 2 rows) +gst-launch-1.0 idsueyesrc config-file=whole-presacler64_autoexp-binningx2.ini ! videoconvert ! video/x-raw,format=GRAY8 ! rollingsum window-size=1000 column-index=1 stride=2 threshold=0.5 ! queue ! autovideosink + +# Lower threshold for more sensitive detection +gst-launch-1.0 idsueyesrc config-file=whole-presacler64_autoexp-binningx2.ini ! videoconvert ! video/x-raw,format=GRAY8 ! rollingsum threshold=0.2 ! queue ! autovideosink +``` + ## Image acquisition elements @@ -35,6 +61,7 @@ GStreamer plugins related to the field of machine vision. - extractcolor: Extract a single color channel - klvinjector: Inject test synchronous KLV metadata - klvinspector: Inspect synchronous KLV metadata +- rollingsum: Drops frames based on rolling mean analysis of a single column - sfx3dnoise: Applies 3D noise to video - videolevels: Scales monochrome 8- or 16-bit video to 8-bit, via manual setpoints or AGC diff --git a/build_idsueye_and_rollingsum.bat b/build_idsueye_and_rollingsum.bat new file mode 100644 index 0000000..94d634d --- /dev/null +++ b/build_idsueye_and_rollingsum.bat @@ -0,0 +1,126 @@ +@echo off +REM Script to build IDS uEye and Rolling Sum plugins + +echo ======================================== +echo Building IDS uEye and Rolling Sum Plugins +echo ======================================== +echo. + +REM Set default GStreamer path (can be overridden by environment variable or command line) +set DEFAULT_GSTREAMER_ROOT=C:\bin\gstreamer\1.0\msvc_x86_64 + +REM Allow override via command line parameter +if not "%~1"=="" ( + set GSTREAMER_ROOT=%~1 + echo Using GStreamer from command line: %GSTREAMER_ROOT% +) else if defined GSTREAMER_ROOT ( + echo Using GStreamer from environment: %GSTREAMER_ROOT% +) else ( + set GSTREAMER_ROOT=%DEFAULT_GSTREAMER_ROOT% + echo Using default GStreamer location: %GSTREAMER_ROOT% +) + +REM Verify GStreamer directory exists +if not exist "%GSTREAMER_ROOT%" ( + echo ERROR: GStreamer directory does not exist: %GSTREAMER_ROOT% + echo. + echo Please provide correct GStreamer path: + echo build_idsueye_and_rollingsum.bat "C:\path\to\gstreamer" + echo. + exit /b 1 +) + +echo. + +REM Create build directory +if not exist build ( + mkdir build + echo Created build directory: build +) else ( + echo Using existing build directory: build +) + +echo. + +cd build + +REM Configure CMake +echo Configuring CMake... +echo. +cmake .. -G "Visual Studio 16 2019" -A x64 ^ + -DGSTREAMER_ROOT="%GSTREAMER_ROOT%" ^ + -DCMAKE_PREFIX_PATH="%GSTREAMER_ROOT%" + +if %ERRORLEVEL% NEQ 0 ( + echo. + echo ======================================== + echo CMake configuration failed! + echo ======================================== + echo. + echo Common issues: + echo 1. GStreamer not properly installed at: %GSTREAMER_ROOT% + echo 2. IDS uEye SDK not found (default: C:\Program Files\IDS\uEye\Develop) + echo 3. Missing GStreamer development files + echo. + echo To specify custom IDS uEye SDK location: + echo set IDSUEYE_DIR=C:\path\to\ueye\sdk + echo build_idsueye_and_rollingsum.bat + echo. + cd .. + exit /b 1 +) + +REM Build the plugins +echo. +echo Building plugins... +echo. +cmake --build . --config Release + +if %ERRORLEVEL% NEQ 0 ( + echo. + echo ======================================== + echo Build failed! + echo ======================================== + cd .. + exit /b 1 +) + +REM Install the plugins +echo. +echo Installing plugins to GStreamer directory... +echo. +cmake --build . --config Release --target install + +if %ERRORLEVEL% NEQ 0 ( + echo. + echo ======================================== + echo Installation failed! + echo ======================================== + echo. + echo You may need administrator privileges to install. + echo Try running this script as administrator. + echo. + cd .. + exit /b 1 +) + +echo. +echo ======================================== +echo Build and installation completed successfully! +echo ======================================== +echo. +echo Installed plugins: +echo - libgstidsueye.dll (IDS uEye camera source) +echo - libgstrollingsum.dll (Rolling sum filter) +echo. +echo Installation location: %GSTREAMER_ROOT%\lib\gstreamer-1.0\ +echo. +echo Verify installation with: +echo gst-inspect-1.0 idsueyesrc +echo gst-inspect-1.0 rollingsum +echo. +echo Example pipeline: +echo gst-launch-1.0 idsueyesrc config-file=config.ini ! rollingsum window-size=1000 column-index=1 threshold=0.5 ! autovideosink +echo. + +cd .. \ No newline at end of file diff --git a/build_idsueye_only.bat b/build_idsueye_only.bat new file mode 100644 index 0000000..fd4783b --- /dev/null +++ b/build_idsueye_only.bat @@ -0,0 +1,100 @@ +@echo off +REM Script to build only IDS uEye plugin + +echo ======================================== +echo Building IDS uEye Plugin Only +echo ======================================== +echo. + +REM Set default GStreamer path (can be overridden by environment variable or command line) +set DEFAULT_GSTREAMER_ROOT=C:\bin\gstreamer\1.0\msvc_x86_64 + +REM Allow override via command line parameter +if not "%~1"=="" ( + set GSTREAMER_ROOT=%~1 + echo Using GStreamer from command line: %GSTREAMER_ROOT% +) else if defined GSTREAMER_ROOT ( + echo Using GStreamer from environment: %GSTREAMER_ROOT% +) else ( + set GSTREAMER_ROOT=%DEFAULT_GSTREAMER_ROOT% + echo Using default GStreamer location: %GSTREAMER_ROOT% +) + +REM Verify GStreamer directory exists +if not exist "%GSTREAMER_ROOT%" ( + echo ERROR: GStreamer directory does not exist: %GSTREAMER_ROOT% + echo. + echo Please provide correct GStreamer path: + echo build_idsueye_only.bat "C:\path\to\gstreamer" + echo. + exit /b 1 +) + +echo. + +REM Create build directory +if not exist build_idsueye ( + mkdir build_idsueye + echo Created build directory: build_idsueye +) + +REM Copy standalone CMakeLists to build directory for configuration +copy /Y CMakeLists_idsueye_only.txt build_idsueye\CMakeLists.txt >nul +echo Using standalone IDS uEye CMakeLists configuration +echo. + +cd build_idsueye + +REM Configure CMake +echo Configuring CMake... +echo. +cmake . -G "Visual Studio 16 2019" -A x64 ^ + -DGSTREAMER_ROOT="%GSTREAMER_ROOT%" ^ + -DCMAKE_PREFIX_PATH="%GSTREAMER_ROOT%" + +if %ERRORLEVEL% NEQ 0 ( + echo. + echo ======================================== + echo CMake configuration failed! + echo ======================================== + echo. + echo Common issues: + echo 1. GStreamer not properly installed at: %GSTREAMER_ROOT% + echo 2. IDS uEye SDK not found (default: C:\Program Files\IDS\uEye\Develop) + echo 3. Missing GStreamer development files + echo. + echo To specify custom IDS uEye SDK location: + echo set IDSUEYE_DIR=C:\path\to\ueye\sdk + echo build_idsueye_only.bat + echo. + cd .. + exit /b 1 +) + +REM Build the plugin +echo. +echo Building gstidsueye plugin... +echo. +cmake --build . --config Release + +if %ERRORLEVEL% NEQ 0 ( + echo. + echo ======================================== + echo Build failed! + echo ======================================== + cd .. + exit /b 1 +) + +echo. +echo ======================================== +echo Build completed successfully! +echo ======================================== +echo. +echo Plugin location: build_idsueye\Release\libgstidsueye.dll +echo. +echo To install to GStreamer plugins directory, run: +echo cmake --build build_idsueye --config Release --target install +echo. + +cd .. \ No newline at end of file diff --git a/cli.py b/cli.py new file mode 100644 index 0000000..7c12ea8 --- /dev/null +++ b/cli.py @@ -0,0 +1,144 @@ +import numpy as np +import imageio.v3 as iio +from tqdm import tqdm +import concurrent.futures +import os + +# --- Configuration --- +DATA_SOURCE = "data/760-004_cropped_ZTjMVf.avi" +RING_BUFFER_SIZE = 32000 +OUTPUT_DIR = "output" + +os.makedirs(OUTPUT_DIR, exist_ok=True) + +write_executor = concurrent.futures.ThreadPoolExecutor(max_workers=2) +futures = [] + +def submit_write_job(ring_buffer, index, length, ring_index): + try: + # copy data so it doesnt run away + data_to_write = np.take(ring_buffer, range(ring_index-length, ring_index), axis=0, mode='wrap').copy() + except Exception as e: + print(f"Error taking data from ring buffer: {e}") + return None + + def _write_task(data): + filename = f"{OUTPUT_DIR}/object-{index-length}-{index}-{length}.png" + iio.imwrite(filename, data) + + future = write_executor.submit(_write_task, data_to_write) + return future + +props = iio.improps(DATA_SOURCE) # overwrite manually if live video source + +window_size = 12000 + +ema_mean = 0.74 +ema_var = 0.05 +baseline_alpha = 0.001 +variance_alpha = 0.01 +default_diff = 0.52 + +stride=1 +frameskip=1 + +postcutoff_variance_threshold = 2.5 +cutoff_variance_threshold = 3.5 +precutoff_variance_threshold = 0.05 + +is_recording = False +recording_length = 0 +patience_default = 600 +patience = patience_default +patience_length = 0 +recorded_images = 0 +frames_since_last_recording = 0 +index = 0 + +ring_buffer = np.zeros((RING_BUFFER_SIZE, props.shape[2], props.shape[3]), dtype=props.dtype) +sum_ring_buffer = np.zeros((RING_BUFFER_SIZE), dtype=np.uint64) +ema_var_ring = np.zeros((RING_BUFFER_SIZE), dtype=np.float32) +sum_buffer = np.zeros_like(sum_ring_buffer[0:window_size]) +stride_indices = np.arange(0, props.shape[2], stride) + +for frame in tqdm(iio.imiter(DATA_SOURCE, plugin="pyav"), total=props.n_images): + effective_window_size = min(index, window_size) + ring_index = index%RING_BUFFER_SIZE + + time_indices = np.arange(ring_index - effective_window_size, ring_index) % RING_BUFFER_SIZE + sum_buffer[:len(time_indices)] = sum_ring_buffer[time_indices[:]] + + # splayed out for perf debugging + s4 = frame[0:1,::stride,1] + frame_mean = np.divide(np.sum(s4),s4.size) + s2 = sum_buffer[:len(time_indices)] + s11 = np.sum(s2) + s1 = np.divide(s11,s2.size) if s2.size > 0 else 0 # Avoid division by zero + s0 = np.abs(frame_mean - s1) + s00 = np.mean(s0) + value = s00 if index > 0 else default_diff + + if ema_mean is None: + ema_mean, ema_var = value, 1.0 + + deviation = value - ema_mean + + ema_mean_temp = (1 - baseline_alpha) * ema_mean + baseline_alpha * value + ema_var_temp = (1 - variance_alpha) * ema_var + variance_alpha * (deviation ** 2) + + ring_buffer[ring_index:ring_index+1, :, :] = frame[0:1,:,:] + sum_ring_buffer[ring_index] = frame_mean + + if is_recording == False or abs(deviation) < 3 * np.sqrt(ema_var): + ema_var = ema_var_temp + ema_mean = ema_mean_temp + if (is_recording == False and ema_var > cutoff_variance_threshold): + is_recording = True + + quiet_indices = np.argwhere(np.take(ema_var_ring, range(ring_index-RING_BUFFER_SIZE, ring_index), mode='wrap') < precutoff_variance_threshold) + + if quiet_indices.size > 0: + last_quiet_index = quiet_indices[-1].item() + recording_length = min(RING_BUFFER_SIZE - last_quiet_index + patience_default, frames_since_last_recording) + else: + recording_length = frames_since_last_recording + patience = patience_default + patience_length = 0 + + if is_recording: + if (ema_var < postcutoff_variance_threshold): + patience -= 1 + patience_length += 1 + if (ema_var >= postcutoff_variance_threshold): + recording_length += 1 + patience_length = 0 + patience = patience_default + if (patience == 0): + print(f"Dumping image starting at index {index-recording_length} ending at {index}, length {recording_length}") + future = submit_write_job(ring_buffer, index, recording_length, ring_index-patience_length) + if future: + futures.append(future) + + ema_var_ring[ring_index-patience_length] = 0.0 + recorded_images += 1 + frames_since_last_recording = patience_length + patience_length = 0 + is_recording = False + + ema_var_ring[ring_index] = ema_var + index += 1 + frames_since_last_recording += 1 + +# handle final recording +if is_recording: + print(f"Dumping final image starting at index {index-recording_length} ending at {index}, length {recording_length}") + final_future = submit_write_job(ring_buffer, index, recording_length, ring_index-patience_length) + if final_future: + futures.append(final_future) + recorded_images += 1 + +# wait for image writes to complete +print("Processing complete. Waiting for all background writing tasks to finish...") +concurrent.futures.wait(futures) +write_executor.shutdown() # Cleanly release resources +print(f"All images written. Recorded {recorded_images} objects.") \ No newline at end of file diff --git a/gst/CMakeLists.txt b/gst/CMakeLists.txt index 41295e2..cd060fb 100644 --- a/gst/CMakeLists.txt +++ b/gst/CMakeLists.txt @@ -10,5 +10,6 @@ if (ENABLE_KLV) endif () add_subdirectory (misb) +add_subdirectory (rollingsum) add_subdirectory (select) add_subdirectory (videoadjust) diff --git a/gst/rollingsum/CMakeLists.txt b/gst/rollingsum/CMakeLists.txt new file mode 100644 index 0000000..e71e0b2 --- /dev/null +++ b/gst/rollingsum/CMakeLists.txt @@ -0,0 +1,28 @@ +set (SOURCES + gstrollingsum.c + ) + +set (HEADERS + gstrollingsum.h) + +include_directories (AFTER + ${ORC_INCLUDE_DIR}) + +set (libname gstrollingsum) + +add_library (${libname} MODULE + ${SOURCES} + ${HEADERS}) + +target_link_libraries (${libname} + ${ORC_LIBRARIES} + ${GLIB2_LIBRARIES} + ${GOBJECT_LIBRARIES} + ${GSTREAMER_LIBRARY} + ${GSTREAMER_BASE_LIBRARY} + ${GSTREAMER_VIDEO_LIBRARY}) + +if (WIN32) + install (FILES $ DESTINATION ${PDB_INSTALL_DIR} COMPONENT pdb OPTIONAL) +endif () +install(TARGETS ${libname} LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR}) \ No newline at end of file diff --git a/gst/rollingsum/gstrollingsum.c b/gst/rollingsum/gstrollingsum.c new file mode 100644 index 0000000..fa56f28 --- /dev/null +++ b/gst/rollingsum/gstrollingsum.c @@ -0,0 +1,457 @@ +/* GStreamer + * Copyright (C) 2024 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/** + * SECTION:element-rollingsum + * + * Drops frames based on rolling mean analysis of a single column. + * Inspired by cli.py detection algorithm, simplified for real-time streaming. + * + * + * Example launch line + * |[ + * gst-launch-1.0 idsueyesrc config-file=config.ini ! rollingsum window-size=1000 column-index=1 threshold=0.5 ! autovideosink + * ]| + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstrollingsum.h" +#include +#include + +enum +{ + PROP_0, + PROP_WINDOW_SIZE, + PROP_COLUMN_INDEX, + PROP_STRIDE, + PROP_THRESHOLD, + PROP_LAST +}; + +#define DEFAULT_PROP_WINDOW_SIZE 1000 +#define DEFAULT_PROP_COLUMN_INDEX 1 +#define DEFAULT_PROP_STRIDE 1 +#define DEFAULT_PROP_THRESHOLD 0.5 + +/* Supported video formats */ +#define SUPPORTED_CAPS \ + GST_VIDEO_CAPS_MAKE("{ GRAY8, GRAY16_LE, GRAY16_BE }") ";" \ + "video/x-bayer, format=(string){bggr,grbg,gbrg,rggb}" + +static GstStaticPadTemplate gst_rolling_sum_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SUPPORTED_CAPS) + ); + +static GstStaticPadTemplate gst_rolling_sum_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SUPPORTED_CAPS) + ); + +/* GObject vmethod declarations */ +static void gst_rolling_sum_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rolling_sum_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_rolling_sum_dispose (GObject * object); + +/* GstBaseTransform vmethod declarations */ +static gboolean gst_rolling_sum_set_caps (GstBaseTransform * trans, + GstCaps * incaps, GstCaps * outcaps); +static GstFlowReturn gst_rolling_sum_transform_ip (GstBaseTransform * trans, + GstBuffer * buf); + +/* GstRollingSum method declarations */ +static void gst_rolling_sum_reset (GstRollingSum * filter); +static gdouble gst_rolling_sum_extract_column_mean (GstRollingSum * filter, + GstBuffer * buf); + +/* setup debug */ +GST_DEBUG_CATEGORY_STATIC (rolling_sum_debug); +#define GST_CAT_DEFAULT rolling_sum_debug + +G_DEFINE_TYPE (GstRollingSum, gst_rolling_sum, GST_TYPE_BASE_TRANSFORM); + +/************************************************************************/ +/* GObject vmethod implementations */ +/************************************************************************/ + +static void +gst_rolling_sum_dispose (GObject * object) +{ + GstRollingSum *filter = GST_ROLLING_SUM (object); + + GST_DEBUG ("dispose"); + + gst_rolling_sum_reset (filter); + + /* chain up to the parent class */ + G_OBJECT_CLASS (gst_rolling_sum_parent_class)->dispose (object); +} + +static void +gst_rolling_sum_class_init (GstRollingSumClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GstBaseTransformClass *gstbasetransform_class = + GST_BASE_TRANSFORM_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (rolling_sum_debug, "rollingsum", 0, + "Rolling Sum Filter"); + + GST_DEBUG ("class init"); + + /* Register GObject vmethods */ + gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_rolling_sum_dispose); + gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_rolling_sum_set_property); + gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_rolling_sum_get_property); + + /* Install GObject properties */ + g_object_class_install_property (gobject_class, PROP_WINDOW_SIZE, + g_param_spec_int ("window-size", "Window Size", + "Number of frames in rolling window", 1, 100000, + DEFAULT_PROP_WINDOW_SIZE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | + GST_PARAM_MUTABLE_PLAYING)); + + g_object_class_install_property (gobject_class, PROP_COLUMN_INDEX, + g_param_spec_int ("column-index", "Column Index", + "Which vertical column to analyze (0-based)", 0, G_MAXINT, + DEFAULT_PROP_COLUMN_INDEX, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | + GST_PARAM_MUTABLE_PLAYING)); + + g_object_class_install_property (gobject_class, PROP_STRIDE, + g_param_spec_int ("stride", "Row Stride", + "Row sampling stride (1 = every row)", 1, G_MAXINT, + DEFAULT_PROP_STRIDE, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | + GST_PARAM_MUTABLE_PLAYING)); + + g_object_class_install_property (gobject_class, PROP_THRESHOLD, + g_param_spec_double ("threshold", "Threshold", + "Normalized deviation threshold for dropping frames", 0.0, 1.0, + DEFAULT_PROP_THRESHOLD, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | + GST_PARAM_MUTABLE_PLAYING)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rolling_sum_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rolling_sum_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "Rolling sum filter", "Filter/Effect/Video", + "Drops frames based on rolling mean analysis of a single column", + "Your Name "); + + /* Register GstBaseTransform vmethods */ + gstbasetransform_class->set_caps = + GST_DEBUG_FUNCPTR (gst_rolling_sum_set_caps); + gstbasetransform_class->transform_ip = + GST_DEBUG_FUNCPTR (gst_rolling_sum_transform_ip); +} + +static void +gst_rolling_sum_init (GstRollingSum * filter) +{ + GST_DEBUG_OBJECT (filter, "init class instance"); + + filter->window_size = DEFAULT_PROP_WINDOW_SIZE; + filter->column_index = DEFAULT_PROP_COLUMN_INDEX; + filter->stride = DEFAULT_PROP_STRIDE; + filter->threshold = DEFAULT_PROP_THRESHOLD; + + filter->ring_buffer = NULL; + filter->ring_index = 0; + filter->frame_count = 0; + filter->rolling_mean = 0.0; + filter->rolling_sum = 0.0; + filter->info_set = FALSE; + + gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE); + + gst_rolling_sum_reset (filter); +} + +static void +gst_rolling_sum_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRollingSum *filter = GST_ROLLING_SUM (object); + + GST_DEBUG_OBJECT (filter, "setting property %s", pspec->name); + + switch (prop_id) { + case PROP_WINDOW_SIZE: + { + gint new_size = g_value_get_int (value); + if (new_size != filter->window_size) { + filter->window_size = new_size; + /* Reallocate ring buffer */ + gst_rolling_sum_reset (filter); + } + break; + } + case PROP_COLUMN_INDEX: + filter->column_index = g_value_get_int (value); + break; + case PROP_STRIDE: + filter->stride = g_value_get_int (value); + break; + case PROP_THRESHOLD: + filter->threshold = g_value_get_double (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rolling_sum_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstRollingSum *filter = GST_ROLLING_SUM (object); + + GST_DEBUG_OBJECT (filter, "getting property %s", pspec->name); + + switch (prop_id) { + case PROP_WINDOW_SIZE: + g_value_set_int (value, filter->window_size); + break; + case PROP_COLUMN_INDEX: + g_value_set_int (value, filter->column_index); + break; + case PROP_STRIDE: + g_value_set_int (value, filter->stride); + break; + case PROP_THRESHOLD: + g_value_set_double (value, filter->threshold); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_rolling_sum_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps) +{ + GstRollingSum *filter = GST_ROLLING_SUM (trans); + + GST_DEBUG_OBJECT (filter, "set_caps"); + + if (!gst_video_info_from_caps (&filter->video_info, incaps)) { + GST_ERROR_OBJECT (filter, "Failed to parse caps"); + return FALSE; + } + + filter->info_set = TRUE; + + GST_DEBUG_OBJECT (filter, "Video format: %s, %dx%d", + gst_video_format_to_string (GST_VIDEO_INFO_FORMAT (&filter->video_info)), + GST_VIDEO_INFO_WIDTH (&filter->video_info), + GST_VIDEO_INFO_HEIGHT (&filter->video_info)); + + return TRUE; +} + +static gdouble +gst_rolling_sum_extract_column_mean (GstRollingSum * filter, GstBuffer * buf) +{ + GstMapInfo map; + gdouble sum = 0.0; + gint count = 0; + gint width, height, stride_bytes; + gint row, col_offset; + guint8 *data; + GstVideoFormat format; + + if (!filter->info_set) { + GST_WARNING_OBJECT (filter, "Video info not set yet"); + return 0.0; + } + + if (!gst_buffer_map (buf, &map, GST_MAP_READ)) { + GST_ERROR_OBJECT (filter, "Failed to map buffer"); + return 0.0; + } + + data = map.data; + width = GST_VIDEO_INFO_WIDTH (&filter->video_info); + height = GST_VIDEO_INFO_HEIGHT (&filter->video_info); + stride_bytes = GST_VIDEO_INFO_PLANE_STRIDE (&filter->video_info, 0); + format = GST_VIDEO_INFO_FORMAT (&filter->video_info); + + /* Check column index is valid */ + if (filter->column_index >= width) { + GST_WARNING_OBJECT (filter, "Column index %d >= width %d, using column 0", + filter->column_index, width); + filter->column_index = 0; + } + + /* Calculate column offset based on format */ + if (format == GST_VIDEO_FORMAT_GRAY8) { + col_offset = filter->column_index; + + /* Sum column values with stride */ + for (row = 0; row < height; row += filter->stride) { + sum += data[row * stride_bytes + col_offset]; + count++; + } + } else if (format == GST_VIDEO_FORMAT_GRAY16_LE || + format == GST_VIDEO_FORMAT_GRAY16_BE) { + col_offset = filter->column_index * 2; + + /* Sum column values with stride */ + for (row = 0; row < height; row += filter->stride) { + guint16 pixel_value; + guint8 *pixel_ptr = &data[row * stride_bytes + col_offset]; + + if (format == GST_VIDEO_FORMAT_GRAY16_LE) { + pixel_value = pixel_ptr[0] | (pixel_ptr[1] << 8); + } else { + pixel_value = (pixel_ptr[0] << 8) | pixel_ptr[1]; + } + + sum += pixel_value; + count++; + } + } else { + /* For Bayer formats, treat as GRAY8 */ + col_offset = filter->column_index; + + for (row = 0; row < height; row += filter->stride) { + sum += data[row * stride_bytes + col_offset]; + count++; + } + } + + gst_buffer_unmap (buf, &map); + + return count > 0 ? sum / count : 0.0; +} + +static GstFlowReturn +gst_rolling_sum_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + GstRollingSum *filter = GST_ROLLING_SUM (trans); + gdouble frame_mean, deviation, old_value; + gint effective_window_size; + + /* Extract column mean from current frame */ + frame_mean = gst_rolling_sum_extract_column_mean (filter, buf); + + /* Store in ring buffer */ + old_value = filter->ring_buffer[filter->ring_index]; + filter->ring_buffer[filter->ring_index] = frame_mean; + + /* Update rolling sum efficiently */ + if (filter->frame_count < filter->window_size) { + /* Still filling the buffer */ + filter->rolling_sum += frame_mean; + filter->frame_count++; + effective_window_size = filter->frame_count; + } else { + /* Buffer is full, replace old value */ + filter->rolling_sum = filter->rolling_sum - old_value + frame_mean; + effective_window_size = filter->window_size; + } + + /* Update rolling mean */ + filter->rolling_mean = filter->rolling_sum / effective_window_size; + + /* Calculate deviation */ + deviation = fabs(frame_mean - filter->rolling_mean); + + /* Normalize deviation (assuming 8-bit equivalent range) */ + gdouble normalized_deviation = deviation / 255.0; + + GST_LOG_OBJECT (filter, + "Frame %d: mean=%.2f, rolling_mean=%.2f, deviation=%.2f (normalized=%.4f)", + filter->frame_count, frame_mean, filter->rolling_mean, deviation, + normalized_deviation); + + /* Advance ring buffer index */ + filter->ring_index = (filter->ring_index + 1) % filter->window_size; + + /* Decision: drop or pass frame */ + if (normalized_deviation > filter->threshold) { + GST_DEBUG_OBJECT (filter, + "Dropping frame %d: deviation %.4f > threshold %.4f", + filter->frame_count, normalized_deviation, filter->threshold); + return GST_BASE_TRANSFORM_FLOW_DROPPED; + } + + return GST_FLOW_OK; +} + +static void +gst_rolling_sum_reset (GstRollingSum * filter) +{ + GST_DEBUG_OBJECT (filter, "reset"); + + /* Free old ring buffer if exists */ + if (filter->ring_buffer) { + g_free (filter->ring_buffer); + } + + /* Allocate new ring buffer */ + filter->ring_buffer = g_new0 (gdouble, filter->window_size); + filter->ring_index = 0; + filter->frame_count = 0; + filter->rolling_mean = 0.0; + filter->rolling_sum = 0.0; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rollingsum", 0, "rollingsum"); + + GST_DEBUG ("plugin_init"); + + GST_CAT_INFO (GST_CAT_DEFAULT, "registering rollingsum element"); + + if (!gst_element_register (plugin, "rollingsum", GST_RANK_NONE, + GST_TYPE_ROLLING_SUM)) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + rollingsum, + "Filter that drops frames based on rolling mean analysis", + plugin_init, GST_PACKAGE_VERSION, GST_PACKAGE_LICENSE, GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN); \ No newline at end of file diff --git a/gst/rollingsum/gstrollingsum.h b/gst/rollingsum/gstrollingsum.h new file mode 100644 index 0000000..02d7193 --- /dev/null +++ b/gst/rollingsum/gstrollingsum.h @@ -0,0 +1,79 @@ +/* GStreamer + * Copyright (C) 2024 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GST_ROLLING_SUM_H__ +#define __GST_ROLLING_SUM_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_ROLLING_SUM \ + (gst_rolling_sum_get_type()) +#define GST_ROLLING_SUM(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ROLLING_SUM,GstRollingSum)) +#define GST_ROLLING_SUM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ROLLING_SUM,GstRollingSumClass)) +#define GST_IS_ROLLING_SUM(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ROLLING_SUM)) +#define GST_IS_ROLLING_SUM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ROLLING_SUM)) + +typedef struct _GstRollingSum GstRollingSum; +typedef struct _GstRollingSumClass GstRollingSumClass; + +/** + * GstRollingSum: + * @element: the parent element. + * + * The opaque GstRollingSum data structure. + */ +struct _GstRollingSum +{ + GstBaseTransform element; + + /* Properties */ + gint window_size; /* Number of frames in rolling window */ + gint column_index; /* Which column to analyze (0-based) */ + gint stride; /* Row sampling stride */ + gdouble threshold; /* Deviation threshold for dropping frames */ + + /* State */ + gdouble *ring_buffer; /* Circular buffer of column means */ + gint ring_index; /* Current position in ring buffer */ + gint frame_count; /* Total frames processed */ + gdouble rolling_mean; /* Current rolling mean */ + gdouble rolling_sum; /* Current rolling sum for efficient mean update */ + + /* Video format info */ + GstVideoInfo video_info; + gboolean info_set; +}; + +struct _GstRollingSumClass +{ + GstBaseTransformClass parent_class; +}; + +GType gst_rolling_sum_get_type(void); + +G_END_DECLS + +#endif /* __GST_ROLLING_SUM_H__ */ \ No newline at end of file diff --git a/whole-presacler64_autoexp-binningx2.ini b/whole-presacler64_autoexp-binningx2.ini new file mode 100644 index 0000000..ec40540 --- /dev/null +++ b/whole-presacler64_autoexp-binningx2.ini @@ -0,0 +1,222 @@ +[Versions] +ueye_api_64.dll=4.93.1730 +ueye_usb_64.sys=4.93.1314 +ueye_boot_64.sys=4.93.1314 + + +[Sensor] +Sensor=UI308xCP-C +Sensor bit depth=0 +Sensor source gain=24 +FPN correction mode=0 +Black reference mode=0 +Sensor digital gain=0 + + +[Image size] +Start X=0 +Start Y=0 +Start X absolute=0 +Start Y absolute=0 +Width=1224 +Height=1026 +Binning=3 +Subsampling=0 + + +[Scaler] +Mode=0 +Factor=0.000000 + + +[Multi AOI] +Enabled=0 +Mode=0 +x1=0 +x2=0 +x3=0 +x4=0 +y1=0 +y2=0 +y3=0 +y4=0 + + +[Shutter] +Mode=0 +Linescan number=0 + + +[Log Mode] +Mode=3 +Manual value=0 +Manual gain=0 + + +[Timing] +Pixelclock=237 +Extended pixelclock range=0 +Framerate=43.894511 +Exposure=1.134676 +Long exposure=0 +Dual exposure ratio=0 + + +[Selected Converter] +IS_SET_CM_RGB32=2 +IS_SET_CM_RGB24=2 +IS_SET_CM_RGB16=2 +IS_SET_CM_RGB15=2 +IS_SET_CM_Y8=2 +IS_SET_CM_RGB8=2 +IS_SET_CM_BAYER=8 +IS_SET_CM_UYVY=2 +IS_SET_CM_UYVY_MONO=2 +IS_SET_CM_UYVY_BAYER=2 +IS_CM_CBYCRY_PACKED=0 +IS_SET_CM_RGBY=8 +IS_SET_CM_RGB30=2 +IS_SET_CM_Y12=2 +IS_SET_CM_BAYER12=8 +IS_SET_CM_Y16=2 +IS_SET_CM_BAYER16=8 +IS_CM_BGR12_UNPACKED=2 +IS_CM_BGRA12_UNPACKED=2 +IS_CM_JPEG=0 +IS_CM_SENSOR_RAW10=8 +IS_CM_MONO10=2 +IS_CM_BGR10_UNPACKED=2 +IS_CM_RGBA8_PACKED=2 +IS_CM_RGB8_PACKED=2 +IS_CM_RGBY8_PACKED=8 +IS_CM_RGB10V2_PACKED=8 +IS_CM_RGB12_UNPACKED=2 +IS_CM_RGBA12_UNPACKED=2 +IS_CM_RGB10_UNPACKED=2 +IS_CM_RGB8_PLANAR=2 + + +[Parameters] +Colormode=0 +Gamma=1.000000 +Hardware Gamma=0 +Blacklevel Mode=0 +Blacklevel Offset=7 +Hotpixel Mode=0 +Hotpixel Threshold=0 +Sensor Hotpixel=0 +Adaptive hotpixel correction enable=0 +Adaptive hotpixel correction mode=0 +Adaptive hotpixel correction sensitivity=3 +GlobalShutter=0 +AllowRawWithLut=0 + + +[Gain] +Master=0 +Red=15 +Green=0 +Blue=32 +GainBoost=0 + + +[Processing] +EdgeEnhancementFactor=0 +RopEffect=0 +Whitebalance=0 +Whitebalance Red=1.000000 +Whitebalance Green=1.000000 +Whitebalance Blue=1.000000 +Color correction=4 +Color_correction_factor=1.000000 +Color_correction_satU=100 +Color_correction_satV=100 +Bayer Conversion=1 +JpegCompression=0 +NoiseMode=0 +ImageEffect=0 +LscModel=0 +WideDynamicRange=0 + + +[Auto features] +Auto Framerate control=0 +Brightness exposure control=1 +Brightness gain control=0 +Auto Framerate Sensor control=0 +Brightness exposure Sensor control=0 +Brightness gain Sensor control=0 +Brightness exposure Sensor control photometry=0 +Brightness gain Sensor control photometry=0 +Brightness control once=0 +Brightness reference=128 +Brightness speed=50 +Brightness max gain=100 +Brightness max exposure=22.646162 +Brightness Aoi Left=0 +Brightness Aoi Top=0 +Brightness Aoi Width=1224 +Brightness Aoi Height=1026 +Brightness Hysteresis=2 +AutoImageControlMode=2 +AutoImageControlPeakWhiteChannel=0 +AutoImageControlExposureMinimum=0.000000 +AutoImageControlPeakWhiteChannelMode=0 +AutoImageControlPeakWhiteGranularity=0 +Auto WB control=0 +Auto WB type=2 +Auto WB RGB color model=1 +Auto WB RGB color temperature=5000 +Auto WB offsetR=0 +Auto WB offsetB=0 +Auto WB gainMin=0 +Auto WB gainMax=100 +Auto WB speed=50 +Auto WB Aoi Left=0 +Auto WB Aoi Top=0 +Auto WB Aoi Width=1224 +Auto WB Aoi Height=1026 +Auto WB Once=0 +Auto WB Hysteresis=2 +Brightness Skip Frames Trigger Mode=4 +Brightness Skip Frames Freerun Mode=4 +Auto WB Skip Frames Trigger Mode=4 +Auto WB Skip Frames Freerun Mode=4 + + +[Trigger and Flash] +Trigger mode=4097 +Trigger timeout=200 +Trigger delay=0 +Trigger debounce mode=0 +Trigger debounce delay time=1 +Trigger burst size=1 +Trigger prescaler frame=64 +Trigger prescaler line=1 +Trigger input=1 +Flash strobe=0 +Flash delay=0 +Flash duration=0 +Flash auto freerun=0 +PWM mode=0 +PWM frequency=20000000 +PWM dutycycle=20000000 +GPIO state=3 +GPIO direction=0 +GPIO1 Config=1 +GPIO2 Config=1 + + +[Vertical AOI Merge Mode] +Mode=0 +Position=0 +Additional Position=0 +Height=2 + + +[Level Controlled Trigger Mode] +Mode=0 + + +[Memory] +Camera memory mode=1