Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Framework Facecam

Important

This is an independent community project. It is not affiliated with, sponsored by, endorsed by, or supported by Framework Computer Inc. "Framework" and "Framework Laptop" are trademarks of Framework Computer Inc.

Framework Facecam publishes a stable 1080p virtual camera with NPU-tracked hardware face exposure and global luminance correction. Hardware-calibrated white balance, hue, and saturation remain responsible for color; face-derived chroma correction is disabled by default because skin chroma changes materially with lighting. OpenVINO prefers the NPU and falls back to CPU. OpenCV performs pixel processing on the CPU, with no spatial face filter or halo.

While streaming, the detected face drives the webcam's UVC auto-exposure region. The module can adjust an unreported sensor-gain stage without changing exposure_time_absolute, so face luminance may improve even when the visible shutter control remains constant.

Setup and workflow

  1. Install the system dependencies on Arch Linux:

    sudo pacman -S ffmpeg v4l-utils imagemagick v4l2loopback-dkms \
      openvino-intel-npu-plugin
  2. Create the Python environment:

    uv sync
  3. Calibrate while seated in the normal call position. Use an evenly lit, representative phone portrait. Any image format decodable by ImageMagick or OpenCV is accepted, including JPEG, PNG, HEIC/HEIF, WebP, and TIFF:

    uv run framework-facecam calibrate reference.jpg

    Calibration needs neither OpenVINO model files nor an NPU. It uses OpenCV's bundled Haar cascade, sweeps conservative hue and saturation settings, and compares backlight compensation 1 and 2 while keeping hardware auto exposure enabled. It never sets manual exposure, sharpness, or UVC ROI auto exposure. Results go to $XDG_DATA_HOME/framework-facecam/calibrations/<timestamp> and the selected controls and target go to $XDG_CONFIG_HOME/framework-facecam/config.json. To update only the target, run uv run framework-facecam calibrate-reference IMAGE.

    Then build a matched three-scene tone suite. Run the full hardware calibration above in indoor-good lighting first. For each command below, place the phone lens beside the laptop webcam and take the reference from the same seat, pose, background, and lighting. A timer with the phone's rear 1x camera is ideal. Do not use a selfie taken elsewhere; then immediately capture the matching webcam scene:

    uv run framework-facecam calibrate-scene indoor-good phone-good.jpg
    # Move to ordinary dim or uneven indoor lighting.
    uv run framework-facecam calibrate-scene indoor-bad phone-indoor-bad.jpg
    # Move outdoors, face a bright window, or create severe backlight.
    uv run framework-facecam calibrate-scene outdoor-bad phone-outdoor-bad.jpg
    uv run framework-facecam finalize-calibration

    Use --session NAME on every command to maintain more than one suite. A scene can be rerun safely; only that scene is replaced. Suite artifacts and session.json live under $XDG_DATA_HOME/framework-facecam/calibration-suites/NAME/. Finalization writes all 21 candidate images, a labeled contact-sheet.jpg, and a JSON score report under finalization/. To select a reviewed candidate instead of the recommendation, use, for example:

    uv run framework-facecam finalize-calibration --session default --gamma 0.85

    Three matched references are required because a phone portrait from one lighting condition is not a universal complexion target. In particular, measured skin b* can move substantially between scenes. Each scene keeps its own phone luminance distribution and diagnostic a*/b* medians; phone chroma is never averaged into runtime configuration. White balance, hue, and saturation remain fixed global hardware controls from the indoor-good full calibration, and max_chroma_shift remains zero.

    The suite applies only the fixed global gamma candidates 1.00, 0.95, 0.90, 0.85, 0.80, 0.75, and 0.70 to each raw webcam frame. It scores face L* p10, median, and p90 against that scene's matching phone portrait, emphasizing the worst scene while considering mean error and mildly preferring no correction. It persists one selected value as both min_gamma and max_gamma; it does not add spatial face tone or color filtering.

  4. Download the authoritative Open Model Zoo 2023 FP16 model and verify both files against embedded SHA256 digests:

    uv run framework-facecam download-model
  5. Create the loopback device. The module must use exclusive_caps=1 so video clients see a capture-only camera:

    sudo modprobe v4l2loopback video_nr=10 exclusive_caps=1 \
      card_label='Framework Facecam'

    For persistent setup, install the included module files:

    sudo install -m 0644 systemd/v4l2loopback.conf \
      /etc/modprobe.d/framework-facecam.conf
    sudo install -m 0644 systemd/framework-facecam.modules.conf \
      /etc/modules-load.d/framework-facecam.conf
  6. Start the stream:

    uv run framework-facecam run
  7. Select Framework Facecam in your browser or meeting application. The virtual camera publishes the source-equivalent 1920x1080 at 30 fps by default. Zen/Meet may negotiate only 720p; clients that support 1080p retain the full processed stream. To publish 720p explicitly, run:

    uv run framework-facecam --output-resolution 720p run
  8. Optionally copy systemd/framework-facecam.service to ~/.config/systemd/user/, inspect its paths, then enable it yourself with systemctl --user enable --now framework-facecam. The sample is not installed or enabled by this project.

Commands

framework-facecam capture OUTPUT writes a processed still without requiring a loopback device. framework-facecam show-config prints defaults merged with the persisted config. Global --input-device and --output-device options override device paths without changing the saved config. --output-resolution selects 1080p or 720p without changing the saved output profile.

The service waits on inotify while idle and refreshes a preallocated black frame once per second so applications can discover it. The timed wait sleeps in the kernel; it is not a busy loop. Opening the virtual camera wakes it immediately, starts the physical camera, and switches to the configured live frame rate. Closing the last consumer releases the physical camera immediately.

Performance

Measured on a Framework Laptop 13 Pro with an Intel Core Ultra X7 358H, Linux 7.2.2-cachyos, Python 3.14, OpenVINO 2026.3.1, a 1920x1080 MJPEG input, 1920x1080 I420 output, and face detection at 7.5 Hz. Each sample covered 30 seconds after warmup. CPU is expressed in logical-core units, where 1.0 means one logical CPU fully occupied. Memory covers the entire service cgroup but excludes the consuming OBS/browser/ffmpeg process.

State Delivered rate CPU Service memory GPU engines GPU GTT NPU busy NPU memory NPU state
Idle, no consumer 1 fps placeholder 0.0012 cores 170.75 MiB 0.000000% 3.18 MiB 0.000000% 66.74 MiB 0 MHz, D3hot
Active, 1080p30 consumer 28.3-28.6 fps steady 1.0177 cores 186.94 MiB 0.000000% 3.18 MiB 3.7968% 76.20 MiB 950 MHz, D0

The service opens an Xe render descriptor through the OpenVINO/OpenCL stack, but DRM per-client counters recorded zero render, compute, blit, video, and video-enhance engine cycles during both samples. Pixel transforms run on CPU; face inference runs on NPU. Results will vary with camera format, output size, detector rate, processor, kernel, and OpenVINO version.

Reproduce a 30-second sample with:

python scripts/measure-performance.py --seconds 30

Run it once with no camera consumer for idle figures, then again while OBS or a browser is actively reading Framework Facecam. The script reads service cgroup CPU/memory, Xe DRM per-client engine counters, and Intel NPU sysfs telemetry.

The input must negotiate V4L2 MJPEG at 1920x1080 and 30 fps. Face inference runs at 5-10 Hz while frames remain 30 fps. If NPU compilation is unavailable or fails, a concise log reports CPU fallback; color conversion and resizing never run on the NPU.

License

MIT. See LICENSE.

About

Tune your framework camera to perform way better without making hardware changes

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages