Skip to main content

llowCV

A lightweight, cv2-like image processing library powered by Pillow.

OpenCV + NumPy is too heavy for edge devices and PyInstaller executables. llowCV uses only Pillow as its core dependency and mirrors cv2's API names, argument order, and coordinate system — minimizing migration cost.

import llowcv as lcv

img = lcv.imread("input.jpg")
img = lcv.resize(img, (640, 480))
img = lcv.put_text(img, "Hello World", (10, 30), font="Arial.ttf", size=24, color=(255, 255, 255))
lcv.imwrite("out.jpg", img)

Why llowCV?

OpenCV llowCV
Core dependency OpenCV + NumPy Pillow only
Install size ~100 MB ~10 MB
PyInstaller exe Painful Easy
Raspberry Pi / Edge Heavy Lightweight
cv2-compatible API
Unicode text rendering Requires config Just pass a TTF path
Real-time / Video Out of scope

Installation

pip install llowcv

For Matplotlib display support:

pip install llowcv[mpl]

For NumPy / OpenCV interop:

pip install llowcv[cv2]

Color Formats

llowCV uses RGB as its internal format — the opposite of cv2's BGR.

mode Use case Channel order
"RGB" Color image (default) R, G, B
"RGBA" Color with transparency R, G, B, A
"L" Grayscale Luminance only
img = lcv.imread("input.jpg")               # → mode='RGB'
img = lcv.imread("input.png", mode="RGBA")  # → mode='RGBA' (transparency preserved)
img = lcv.imread("input.jpg", mode="L")     # → mode='L'   (grayscale)

BGR and BGRA are not directly supported. Use the interop API to exchange data with cv2:

# cv2 (BGR ndarray) → llowcv (RGB PIL.Image)
img = lcv.from_cv2(cv2_bgr_array)   # BGR → RGB conversion happens automatically

# llowcv (RGB PIL.Image) → cv2 (BGR ndarray)
arr = lcv.as_cv2(img)               # RGB → BGR conversion happens automatically

alpha_composite requires both images to be mode='RGBA'. blend and composite expect mode='RGB'.

Channel operations

# Swap R and B channels (fix a BGR-ordered PIL.Image, or convert back)
img = lcv.bgr2rgb(img)    # equivalent to cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = lcv.rgb2bgr(img)    # same operation (alias)

# Split and merge channels
r, g, b = lcv.split(img)                   # like cv2.split — each channel is mode='L'
img      = lcv.merge([r, g, b])            # like cv2.merge
img      = lcv.merge([r, g, b, a], mode="RGBA")  # works for RGBA too

# Extract a single channel
r_channel = lcv.split(img)[0]              # R channel only, mode='L'

API Reference

Image I/O

img = lcv.imread("input.jpg")               # Read as RGB
img = lcv.imread("input.jpg", mode="L")     # Read as grayscale
lcv.imwrite("out.png", img)                 # Format inferred from extension
lcv.imshow(img)                             # Open with OS default viewer
lcv.imshow(img, backend="mpl")              # Display via Matplotlib (pip install llowcv[mpl])
lcv.imshow(img, backend="mpl", block=False) # Non-blocking display (mpl only)

Transforms

img = lcv.resize(img, (640, 480))                              # Resize
img = lcv.resize(img, (640, 480), interpolation="cubic")       # With interpolation
img = lcv.resize(img, img.size, silent=True)                   # Suppress same-size warning
img = lcv.crop(img, (x, y, w, h))                              # Crop (cv2-style xywh)
img = lcv.rotate(img, 90)                                      # Rotate counter-clockwise
img = lcv.rotate(img, 45, expand=True)                         # Expand canvas to fit
img = lcv.flip(img, 0)                                         # Flip vertical
img = lcv.flip(img, 1)                                         # Flip horizontal
img = lcv.flip(img, -1)                                        # Flip both axes

Interpolation options: "nearest" / "linear" (default) / "bilinear" / "cubic" / "bicubic" / "lanczos"

Filters & Color

img = lcv.blur(img, (5, 5))               # Box blur
img = lcv.blur(img, (1, 1))               # radius=0 — no-op, raises UserWarning
img = lcv.blur(img, (1, 1), silent=True)  # Suppress the warning
img = lcv.sharpen(img, amount=1.5)        # Sharpen (1.0 = no change)
img = lcv.to_gray(img)                    # Convert to grayscale (mode='L')

Text Drawing

img = lcv.put_text(
    img,
    text="Hello, World!",
    org=(10, 40),              # Bottom-left origin (cv2-compatible)
    font="path/to/font.ttf",
    size=32,
    color=(255, 255, 255),
)

org uses the same bottom-left origin as cv2.putText. font accepts any TTF or OTF file path — Unicode text (CJK, emoji, etc.) works out of the box.

Compositing

out = lcv.blend(img1, img2, alpha=0.5)        # Alpha blend (0.0=img1, 1.0=img2)
out = lcv.composite(img1, img2, mask)          # Mask composite (mask=0→img1, 255→img2)
out = lcv.alpha_composite(dst, src)            # RGBA alpha compositing

NumPy / OpenCV Interop (opt-in)

Available after pip install llowcv[cv2]:

arr = lcv.as_cv2(img)    # PIL.Image (RGB) → ndarray (BGR)
img = lcv.from_cv2(arr)  # ndarray (BGR) → PIL.Image (RGB)
arr = lcv.as_numpy(img)  # PIL.Image → RGB ndarray
arr = lcv.to_bgr(arr)    # RGB ndarray → BGR ndarray

Camera

with lcv.Camera(index=0) as cam:
    frame = cam.capture()          # Returns PIL.Image (RGB)
    lcv.imwrite("frame.jpg", frame)

Uses Windows Media Foundation on Windows and v4l2 on Linux.

Global Configuration

# Suppress all no-op warnings at once
lcv.config.warn_noop = False

# Or control via environment variable (0 / false / no / off to disable)
# LLOWCV_WARN_NOOP=0 python main.py

When warn_noop is enabled, a UserWarning is raised for:

  • resize: dsize matches the input image size
  • blur: ksize results in no blurring (e.g. (1, 1))
  • imshow: block=False used with the pillow backend

Design Principles

  • Pillow-only core — NumPy and OpenCV are strictly opt-in
  • Immutable returns — every API returns a new PIL.Image; no in-place mutation, no thread-safety concerns
  • cv2-compatible API — same names, argument order, and coordinate conventions as OpenCV
  • No Video / GUI / ML — for real-time processing, use OpenCV

Migrating from cv2

# Before (cv2)
import cv2
img = cv2.imread("input.jpg")
img = cv2.resize(img, (640, 480))
cv2.imwrite("out.jpg", img)

# After (llowcv)
import llowcv as lcv
img = lcv.imread("input.jpg")
img = lcv.resize(img, (640, 480))
lcv.imwrite("out.jpg", img)

Key differences:

cv2 llowcv
Image type numpy.ndarray (BGR) PIL.Image (RGB)
In-place ops Common None — always returns new image
waitKey / GUI loop Required Not needed

Requirements

  • Python >= 3.10
  • Pillow (core)
  • matplotlib ([mpl] optional)
  • numpy + opencv-python ([cv2] optional)
  • numpy + opencv-python ([cv2] extra, optional)

License

MIT License

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

llowcv-0.1.1.tar.gz (92.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

llowcv-0.1.1-py3-none-any.whl (21.8 kB view details)

Uploaded Python 3

File details

Details for the file llowcv-0.1.1.tar.gz.

File metadata

  • Download URL: llowcv-0.1.1.tar.gz
  • Upload date:
  • Size: 92.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for llowcv-0.1.1.tar.gz
Algorithm Hash digest
SHA256 b55b287daf2ff0aa30b868ba84fb50faff94c10f77cf46ab9b704a45a18b45ae
MD5 f079c9a7bad3f183c779204d25671082
BLAKE2b-256 864455e89eb9c29ebf9c3c220c657444e123f31d35015b0074e6122792d46ae1

See more details on using hashes here.

Provenance

The following attestation bundles were made for llowcv-0.1.1.tar.gz:

Publisher: publish.yml on Moge800/llowCV

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file llowcv-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: llowcv-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for llowcv-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b4b6b1b39f369cda538f329ffca70dc8e85ff4ddbadc084723dbee45354bdec3
MD5 b7db808fca65d5afb3ff816ffd1c9700
BLAKE2b-256 4c403494fbf05c4a8007b062280973cd398efe64c5294c7f2c2b9717e1695776

See more details on using hashes here.

Provenance

The following attestation bundles were made for llowcv-0.1.1-py3-none-any.whl:

Publisher: publish.yml on Moge800/llowCV

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page