Skip to content

omniflare/imagick

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

imagick: Simple C Image Processing with PPM

This project is a minimal C library and CLI for learning and experimenting with image processing. It works with the PPM (Portable Pixmap) format, making it easy to manipulate raw RGB pixel data and build your own filters.


Chapter 0: PPM Format & Converting Images

This project works with the PPM (Portable Pixmap) image format, which is simple and human-readable. There are two main variants:

  • P3: ASCII (plain text) format. Each pixel's RGB values are written as numbers in text. Easy to debug, but large and slow to load.
  • P6: Binary format. Each pixel's RGB values are stored as raw bytes. Much smaller and faster.

PPM File Structure:

P6
<width> <height>
<max_val>
<binary RGB data>
  • The header contains the magic number (P3 or P6), image dimensions, and the maximum color value (usually 255 or 65535).
  • For P3, pixel data is ASCII numbers; for P6, it's binary bytes.

Converting JPEG/PNG to PPM using ImageMagick:

# For P3 (ASCII, uncompressed):
magick cat.jpeg -compress none cat.ppm

# For P6 (binary, default):
magick cat.jpeg cat.ppm

P3 is useful for debugging, but P6 is recommended for performance and file size.



Chapter 1: Loading and Saving PPM Images

This chapter introduces the core image-handling code used to load, manipulate, and save P6 PPM images. The code is designed for clarity and learning, not for speed or advanced features.

📁 Data Structures (image.h)

typedef struct {
  uint16_t r, g, b;
} pixel_t;

typedef struct {
  unsigned int width;
  unsigned int height;
  unsigned int max_val;
  pixel_t **pixels;
} image_t;
  • pixel_t: Represents a single RGB pixel, with up to 16 bits per channel.
  • image_t: Represents the entire image, including dimensions, max value, and a 2D array of pixels.

⚙️ Key Functions (image.c)

  • load_image(): Opens a binary PPM file (P6), parses the header, and reads pixel data into a heap-allocated 2D array. Uses mmap() for efficient file access. Supports both 8-bit and 16-bit per channel images (max_val > 255 ⇒ 2 bytes per channel).
  • copy_image(): Creates a deep copy of the image structure and pixel data.
  • save_image(): Writes the image to a file in PPM P6 format, handling both 8-bit and 16-bit output.
  • free_image(): Frees all dynamically allocated memory associated with the image.

Example Usage

image_t *img = load_image("cat.ppm");
// ... manipulate pixels ...
save_image("cat_out.ppm", img);
free_image(img);

📌 Note on max_val

The max_val field in the PPM header determines the pixel value range:

  • ≤ 255: Each color component is 1 byte.
  • > 255: Each component is stored in 2 bytes (high bit-depth).


Chapter 2: Color Handling (color.c, color.h)

These files provide color conversion utilities and helpers for color-based effects.

🟢 Data Structures

typedef struct {
  uint16_t r, g, b;
} rgb_t;

typedef struct {
  float h, s, v;
} hsv_t;

🔄 Key Functions

  • hsv_to_rgb(hsv_t hsv): Converts HSV to RGB.
  • rgb_to_hsv(uint16_t r, uint16_t g, uint16_t b): Converts RGB to HSV.
  • rgb_to_luminance(uint16_t r, uint16_t g, uint16_t b): Computes perceived brightness (used for grayscale).

These are used for filters like grayscale, hue shift, and more.



Chapter 3: Basic Filters (filters.c, filters.h)

This module implements a variety of pixel-wise image filters. All operate directly on the pixel data, modifying the image in-place.

🛠️ Available Filters

  • Brightness/Contrast: Adjusts overall lightness or contrast.
  • Inversion: Inverts all colors.
  • Grayscale: Converts to grayscale using luminance.
  • Hue Shift: Rotates hue in HSV space.
  • Sepia: Applies a warm, old-photo effect.
  • Vintage: Blends original and grayscale, tweaks color balance.
  • Vignette: Darkens corners for a spotlight effect.
  • Posterize: Reduces the number of color levels.

🧩 Example: Applying a Filter

brightness(img, 1.2f); // Increase brightness by 20%
contrast(img, 0.8f);   // Reduce contrast
inversion(img);        // Invert colors


Chapter 4: Convolution Filters (convolution_filters.c)

This module implements classic convolution-based effects using 3x3 kernels. These filters consider each pixel and its neighbors, enabling effects like blurring and edge detection.

🧮 Available Convolution Filters

  • Blur: Softens the image by averaging neighboring pixels (repeated for stronger effect).
  • Smooth: Weighted blur for gentle smoothing.
  • Sharpen: Enhances edges and details.
  • Emboss: Creates a 3D relief effect.
  • Edge Detection: Uses the Sobel operator to highlight edges (converts to grayscale first).

⚡ How It Works

Each filter defines a 3x3 kernel (matrix of weights). The kernel is applied to each pixel and its neighbors, computing a weighted sum for each color channel. Results are clamped to valid pixel values.



Chapter 5: Building and Running

🛠️ Requirements

  • GCC or Clang (C compiler)
  • make
  • ImageMagick (for converting images to PPM)

⚡ Build & Run

make           # Build the project
./main         # Run the CLI (see main.c for usage)

📝 Notes

  • Input images must be in PPM format (see Chapter 0).
  • See main.c for CLI usage and filter examples.


Outro & Credits

Thanks to this YouTube video for teaching the basics of the PPM format and parsing in C.

Project: omniflare/imagick

About

A simple image processing tool using C, that works on PPM format of image.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published