Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ImageFlow

Note: This README was generated by an AI from the project headers and source.

A C image processing library with heterogeneous compute support. Devices and operations are both registered into runtime dispatch tables at load time, so adding a new backend or a new operation no longer means touching a hand-written switch statement — you add an entry to a .def file and drop in an implementation.

Features

  • GPU-accelerated image processing via CUDA and HIP (AMD ROCm)
  • OpenMP CPU fallback when no GPU is available
  • Device-agnostic dispatch — devices (CPU, CUDA, HIP, ...) and operations (GRAYSCALE, INVERT, BRIGHTNESS, ...) are both driven by X-macro .def lists, so new ones can be added without touching the scheduler or pipeline code
  • Self-registering backends — each backend registers its device availability and its per-op implementations via load-time constructors (IF_CONSTRUCTOR), no manual wiring or init calls required
  • Pipeline API — build a sequence of operations, run them against an image in one call
  • Automatic device management — host↔device transfers are handled generically at device-boundary crossings inside the scheduler via a per-device load/retrieve/free dispatch table; callers don't touch device memory directly
  • Pipeline reordering optimizer — reorders operations before execution to minimize host↔device transfers, generalized to any number of devices, with four aggression levels (safe → stencil → reduction → morph)
  • C11 core with C++ support for GPU backends

Building

Requirements

  • CMake ≥ 3.20
  • A C11 compiler
  • OpenMP (optional but recommended)
  • CUDA toolkit (optional)
  • ROCm / HIP (optional)

At least one of CUDA or HIP is needed for GPU acceleration. If neither is found, the build falls back to OpenMP only. Even when built in, CUDA/HIP devices are only actually used if a GPU is detected at runtime — each backend probes its own availability on startup and enables or disables itself accordingly.

Steps

mkdir build && cd build
cmake ..
make -j$(nproc)

CMake will automatically detect CUDA and HIP and enable them if found. No flags needed.

API

Image I/O

#include <ImageFlow/io/image.h>

IF_image_t img;
IF_loadImage(&img, "photo.jpg");
// ... process ...
IF_storeImage(&img, "out.jpg");
IF_freeImage(&img);

IF_image_t holds pixel data as a normalized float* buffer (values in [0.0, 1.0]) with width, height, and channel count. Supported formats: PNG, BMP, JPG/JPEG.

Pipeline

Operations are queued into an IF_Flow_t pipeline and executed in one call against an image. Builder functions are generated automatically from operations/operations.def, one per registered operation:

#include <ImageFlow/pipeline.h>
#include <ImageFlow/scheduler/scheduler.h>

IF_image_t img, img_out;
IF_CHECK(IF_loadImage(&img, "img.jpg"));

IF_Flow_t flow;
IF_flow_init(&flow);

IF_flow_Invert(flow);
IF_flow_Grayscale(flow);
IF_flow_Brightness(flow, 0.5f);

IF_CHECK(IF_flow_run(flow, &img, &img_out));
IF_CHECK(IF_storeImage(&img_out, "out.jpg"));

IF_flow_free(flow);
IF_freeImage(&img);
IF_freeImage(&img_out);

IF_flow_run hands the pipeline off to a scheduler. The scheduler is responsible for:

  • checking, per operation, whether its preferred device is enabled and has a registered implementation, falling back to CPU otherwise
  • managing host↔device transfers generically at device-boundary crossings, via the per-device load/retrieve/free dispatch table
  • optionally reordering operations to minimize those transfers
  • executing each operation via the dispatch layer

A specific scheduler can be selected explicitly:

IF_CHECK(IF_flow_run_sched(flow, &img, IF_SCHEDULER_REORDER_O0, &img_out));

Schedulers

Scheduler Description
IF_SCHEDULER_CPU CPU-only execution via OpenMP; skips GPU dispatch entirely
IF_SCHEDULER_LINEAR Naive sequential scheduler; runs operations in pipeline order and transfers data at device boundaries
IF_SCHEDULER_REORDER_O0 Reorders pipeline at safe aggression (POINT and METADATA ops only), then runs linearly. Default.
IF_SCHEDULER_REORDER_O1 Reorders allowing STENCIL ops to cross barriers, then runs linearly
IF_SCHEDULER_REORDER_O2 Reorders allowing STENCIL and REDUCTION ops to cross barriers, then runs linearly
IF_SCHEDULER_REORDER_O3 Reorders allowing all op types (including MORPH) to cross barriers, then runs linearly

The reorder optimizer groups operations by device affinity within each segment, minimizing host↔device transfers without changing observable output. Its sort key is now device-count agnostic: instead of a single GPU/CPU polarity flip, the device ordering itself is reversed on odd-priority tiers, so the device at the boundary between two tiers always matches on both sides — this generalizes cleanly to any number of registered devices. The aggression level controls which operation types are allowed to cross segment barriers:

  • O0 (safe): only POINT and METADATA ops are reordered — these are trivially commutative
  • O1: additionally allows STENCIL ops to cross barriers
  • O2: additionally allows REDUCTION ops to cross barriers
  • O3: additionally allows MORPH ops to cross barriers — use only if you understand the commutativity implications

IF_SCHEDULER_LINEAR is the reference implementation and the baseline against which the reorder schedulers are validated. IF_SCHEDULER_REORDER_O0 is the default used by IF_flow_run.

Available operations

Defined in include/ImageFlow/operations/operations.def as IF_OP_DEF(id, name, traversal_type, args):

Builder function Description
IF_flow_Grayscale(flow) Rec. 601 luminance grayscale
IF_flow_Invert(flow) Per-channel inversion (v' = 1 - v)
IF_flow_Brightness(flow, factor) Multiply channels by factor, clamped to 1.0

Devices

Defined in include/ImageFlow/devices/devices.def. Currently CPU, CUDA, and HIP. Each backend registers its own availability at load time (e.g. the CUDA backend probes cudaGetDeviceCount in a constructor and enables/disables IF_DEV_CUDA accordingly); the CPU backend is always enabled. IF_enabled_gpu() returns the first enabled GPU device, falling back to IF_DEV_CPU if none is available.

Extending ImageFlow

Adding a new device or a new operation is table-driven — no scheduler, pipeline, or dispatch code needs to change. See CONTRIBUTING.md for the walkthrough.

Error handling

All functions return IF_error_t. The IF_CHECK macro propagates errors with file and line information:

IF_CHECK(IF_loadImage(&img, "photo.jpg"));

On error it prints to stderr and returns from the calling function. For manual inspection:

const char *msg = IF_strerror(err);
IF_logError(stderr, err);

Project Structure

include/ImageFlow/
  constructor.h            — portable "run before main" constructor macro (GCC/Clang/MSVC)
  error.h                  — error codes, IF_CHECK, allocation macros
  io/image.h               — IF_image_t and I/O functions
  devices/
    devices.def            — X-macro list of supported devices
    devices.h              — device enum/names, enable/disable + query, IF_enabled_gpu()
    img_loader.h           — per-device host<->device load/retrieve/free dispatch tables
  operations/
    operations.def         — X-macro list of supported operations (id, name, traversal type, args)
    op_args.h               — tagged union of operation-specific arguments
    op_constructor.h        — IF_OP_IMPL macro: declares + self-registers a (dev, op) implementation
    operations.h            — op descriptors, dispatch table, IF_op_execute
  pipeline.h               — IF_Flow_t pipeline builder (flow builder fns generated from operations.def)
  scheduler/
    scheduler.h            — IF_flow_run / IF_flow_run_sched
    linear.h               — sequential scheduler implementation
    reorder.h              — pipeline reordering optimizer
    cpu.h                  — CPU-only scheduler
  backends/
    cpu.h                  — OpenMP CPU backend
    cuda.h                 — CUDA backend
    hip.h                  — HIP/ROCm backend (low maintenance)
src/
  error.c
  io/image.c
  operations.c
  pipeline.c
  devices/devices.c
  scheduler/
    scheduler.c
    linear.c
    reorder.c
    cpu.c
  backends/
    cpu.c
    cuda.cu
    hip.cpp

Notes

  • Multi-GPU / cross-accelerator parallelism (e.g. NCCL-style same-vendor multi-GPU) is not planned. Dispatch is no longer hardcoded to "one GPU" though — the scheduler can route each operation to whichever registered device it prefers, and the goal is to extend this toward single-wafer-scale chips, not to fan work out across multiple same-vendor accelerators in parallel.
  • HIP/ROCm support is low-maintenance — the backend exists but is not actively tested due to lack of AMD hardware.
  • Intel GPU / oneAPI / SYCL support is not planned.
  • MPI / distributed execution is not planned. Adding it would require reworking IF_OpArgs_t and the dispatch model, since it currently assumes pointer arguments stay local to one process (see the warning in op_args.h).

License

MIT License. Copyright (c) 2026 Daniele Savino. Do whatever you want with it.

About

Modular C image processing pipeline with pluggable devices, operations, and schedulers — CUDA/HIP/OpenMP backends currently registered.

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages