Skip to main content

Lightweight cv2-like image processing library powered by Pillow

Project description

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

Project details


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.

Supported by

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