Engine Development Guide

This guide covers everything you need to build a VentOCR Engine — a pluggable OCR module that analyzes ventilator display video frames and outputs structured data. Engines are developed outside the VentOCR environment and loaded dynamically at runtime.

1. What an Engine Does

An Engine processes ventilator display video frames via OCR and returns structured DataFrames containing per-frame measurements (e.g., VTE, EdiPeak) and breath boundaries (BreathMark). VentOCR loads engines dynamically via importlib.import_module(f"Engine.{engine_name}.core").

2. Execution Flow

Video uploaded
  ↓
Extract frames (Main.py saves color JPEGs to disk)
  ↓
initial_ocr(frames, ...)    ← color frames via generator, one at a time
  ↓
CSV saved per segment
  ↓
post_ocr(df, ...)           ← combined DataFrame from all segments
  ↓
extract_waves(...)           ← optional: per-breath waveform CSVs
  ↓
Results archived

3. Directory Structure

Engine/<engine_key>/
    __init__.py              # empty
    core.py                  # ENGINE_VERSION, VENTOCR_TESTED + functions
    Settings.py              # ROI coordinates, reference paths
    schema.py                # ENGINE_SETTINGS_SCHEMA
    engine_options.json      # default settings
    ExtractWaves.py          # waveform extraction (if extract_waves implemented)
    layout.jpg               # ventilator layout reference
    sample_frame.jpg         # reference for abnormal detection
    sample.mp4               # REQUIRED test video (3-5 min)
    test_spec.json           # REQUIRED accuracy specification
    test.py                  # REQUIRED test script
    expected_output/         # REQUIRED ground truth CSVs
        Frames.csv
        Breaths.csv
        BreathCSVs/          # if extract_waves implemented
    README.md
    CHANGELOG.md
    CLAUDE.md                # definition file for Claude-assisted development

4. Version Management

ENGINE_VERSION = "1.0.0"       # engine release version (managed by developer)
VENTOCR_TESTED = "0.5.5"       # VentOCR version this engine was tested against

ENGINE_VERSION — your engine's own semantic version. Update this with each release.

VENTOCR_TESTED — the VentOCR version you verified compatibility against. Update when you test with a newer VentOCR release.

Maintain a CHANGELOG.md in your engine directory to track changes.

5. Frame Input Specification

CRITICAL: The frames parameter is a generator of color frames (BGR, numpy array). Each frame is yielded once and discarded. Do NOT call list(frames) — a 5-hour recording at 4K would consume several terabytes of memory.

Sequential Processing

for frame_count, color_frame in enumerate(frames):
    # color_frame: numpy array (H, W, 3), BGR format
    gray = np.dot(color_frame[..., :3], [0.114, 0.587, 0.299]).astype(np.uint8)
    # ... process frame

Random Access (Past Frames)

If your algorithm needs to access a previous frame (e.g., midpoint between two detected events), read it from disk via frames_dir:

frame = cv2.imread(os.path.join(frames_dir, f"frame_{index:08d}.jpg"))

Main.py saves every frame as a JPEG before yielding it to the generator, so all frames are available on disk for random access.

6. initial_ocr() REQUIRED

def initial_ocr(frames, total_frames, subfolder, settings={},
                progress_callback=None, frames_dir=None) -> pd.DataFrame
ParameterTypeDescription
framesGenerator[np.ndarray]Color frames (BGR). One at a time. Do not list().
total_framesintTotal frame count
subfolderstrSegment identifier
settingsdictFrom engine_options.json
progress_callbackcallable or Nonecallback(current, total, message)
frames_dirstrDirectory with saved JPEGs for random access

Required Output Columns

ColumnTypeDescription
Frameint0-indexed frame number
BreathMarkint (0 or 1)1 = breath boundary detected

Add engine-specific value columns as needed (e.g., VTE, EdiPeak, VTE_raw, VTE_conf).

7. post_ocr() REQUIRED

def post_ocr(df, Analyze_Id, user_ID, settings={}) -> (df_frames, df_breaths)
ParameterTypeDescription
dfpd.DataFrameOutput from initial_ocr()
Analyze_IdintAnalysis ID
user_IDstrUser identifier
settingsdictFrom engine_options.json

Columns That Must Be Added to df_frames

ColumnTypeDescription
BreathNumberint1-indexed, cumsum of BreathMark
Timestr"HH:MM:SS:CC" format (CC = centiseconds)
AlertstrEmpty string or alert reason
Abnormalint (0 or 1)1 = abnormal frame

df_breaths: One row per BreathNumber (typically the first frame of each breath).

8. extract_waves() OPTIONAL

def extract_waves(df_frames, df_breaths, frames_root, output_dir, user_ID,
                  settings={}, overwrite=False, archive_dir=None) -> (int, pd.DataFrame)

VentOCR checks hasattr(Engine, 'extract_waves') before calling. Implement this if your engine extracts waveform data (pressure, flow, volume, etc.).

Per-Breath CSV Format

X,Pressure,Flow,Volume
1,12.3,5.6,120.5
2,12.5,5.8,121.0
...

If archive_dir is specified, write a fields.txt metadata file listing wave names and output fields.

9. Settings System

schema.py

Defines UI form fields for engine settings. Supported types: int, float, bool, list, multiselect

engine_options.json

Default values matching the schema structure. Loaded by VentOCR and passed as the settings dict.

Settings.py

Static configuration: ROI coordinates, reference image paths, detection parameters. Use ROI_BY_RESOLUTION for multi-resolution support.

10. Test Specification

The test_spec.json file declares what your engine outputs and the accuracy threshold for each output. This is verified at registration time.

{
  "sample_video": "sample.mp4",
  "sample_duration": "3-5 min",
  "outputs": {
    "VTE": {
      "type": "numeric",
      "metric": "match_rate",
      "threshold": 0.90,
      "tolerance": 5.0,
      "unit": "mL",
      "description": "VTE values within ±5.0 mL of expected"
    },
    "BreathMark": {
      "type": "binary",
      "metric": "count_tolerance",
      "threshold": 1,
      "description": "Total breath count within ±1 of expected"
    }
  },
  "waves": ["Pressure", "Flow", "Volume"]
}

Available Metrics

MetricDescriptionThreshold Meaning
match_rateFraction of values within toleranceMinimum rate (0.0–1.0)
count_toleranceAbsolute difference in total countMaximum allowed difference
rmseRoot mean squared errorMaximum allowed RMSE
correlationPearson correlation coefficientMinimum correlation (0.0–1.0)

Expected Output

Place ground truth CSVs in expected_output/. Run your engine against sample.mp4 with verified results, then save the output as the expected baseline.

11. Testing & Verification

CLI Testing (During Development)

cd Engine/your_engine/
python test.py

This runs initial_ocr → post_ocr → extract_waves against sample.mp4, compares output to expected_output/, and reports PASS/FAIL per metric.

Web UI Verification (At Registration)

When registering an engine through VentOCR, the same test is executed automatically. Engines that fail to meet their declared thresholds cannot be registered.

12. Database Registration

-- Engine table
INSERT INTO Engine (Engine_Key, Display_Name, Developer_Name, Ventilator_Family,
                    Layout_Description, Sort_Order, Is_Active)
VALUES ('your_engine', 'Your Engine Name', 'Developer', 'Ventilator Family',
        'Layout description', 0, 1);

-- Engine-Ventilator mapping
INSERT INTO Engine_Ventilator_Map (Engine_Key, Ventilator_ID, Sort_Order, Is_Active)
VALUES ('your_engine', 1, 0, 1);
Registration is typically done through database migrations in migrate_db.py. Contact the VentOCR team for Ventilator_ID assignments.

13. Get Started

Download the scaffold template to start building your engine. It includes all required files with stub implementations, the test framework, and a CLAUDE.md definition file for AI-assisted development.

Download Engine Scaffold (.zip)