Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

C++ Grayscale Image-Processing Prototype

A small, self-contained C++17 project implementing basic grayscale image-processing operations without relying on an image-processing library for the core algorithms.

The project was built as focused evidence of C++, standard-library containers, numerical programming, testing, and modular software-design fundamentals.

Features

  • ASCII PGM (P2) input and output
  • Pixel ownership using std::vector<unsigned char>
  • Matrix-style pixel access through an Image class
  • Binary thresholding
  • 3×3 box-blur convolution
  • Sobel edge detection using explicit X and Y kernels
  • Validation of PGM headers, dimensions, and pixel values
  • Focused automated tests for the image-processing operations
  • CMake-based build and CTest integration

Processing pipeline

The executable reads examples/input_16x16.pgm and applies the operations in this order:

P2 PGM input
    -> binary threshold (threshold = 128)
    -> 3×3 box blur
    -> Sobel edge detection

It writes:

  • examples/output_threshold.pgm
  • examples/output_blur.pgm
  • examples/output_edges.pgm

Each operation also has an independent function interface, so it can be tested or reused separately from the command-line pipeline.

Project structure

.
├── CMakeLists.txt
├── include/
│   ├── Image.hpp
│   ├── ImageOperations.hpp
│   └── PgmIO.hpp
├── src/
│   ├── Image.cpp
│   ├── ImageOperations.cpp
│   ├── PgmIO.cpp
│   └── main.cpp
├── tests/
│   └── image_operations_tests.cpp
└── examples/
    ├── input_4x3.pgm
    ├── input_16x16.pgm
    ├── output_threshold.pgm
    ├── output_blur.pgm
    └── output_edges.pgm

Requirements

  • A C++17-compatible compiler
  • CMake 3.20 or later

The project has been configured with warning flags for AppleClang, Clang, and GCC:

-Wall -Wextra -Wpedantic

Build

Run from the repository root:

cmake -S . -B build
cmake --build build

Run

Run the executable from the repository root so its relative examples/ paths resolve correctly:

./build/image_tool

The program reports the processed image dimensions and the paths of the three generated outputs.

On a multi-configuration CMake generator, the executable may instead be located under a configuration directory such as build/Debug/.

Tests

Build the project, then run:

ctest \
  --test-dir build \
  --output-on-failure

The current test executable checks:

  • threshold behaviour immediately below, at, and above the threshold
  • the centre value of a 3×3 box blur on a known matrix
  • preservation of blur border pixels
  • zero interior Sobel response for a constant image
  • a positive Sobel response at a synthetic vertical edge

The test suite currently focuses on the numerical image operations. Automated PGM parser tests are a planned improvement.

Supported image format

The current prototype reads ASCII PGM (P2) files with:

  • positive width and height
  • a declared maximum grayscale value from 1 to 255
  • whitespace-separated integer pixel values
  • # comments in the header or pixel data
  • pixel values within the file's declared range

The writer produces ASCII PGM (P2) files with a maximum grayscale value of 255.

For inputs whose declared maximum value is below 255, the current reader preserves the numeric pixel values rather than rescaling them. Consequently, exact intensity-preserving round trips are currently intended for inputs whose declared maximum value is 255.

Binary PGM (P5), PNG, and JPEG are not supported.

Design

Image

Image owns its grayscale pixels in a one-dimensional std::vector<unsigned char>. The at(x, y) accessors centralise the conversion from two-dimensional coordinates to a vector index and provide const and non-const access.

PGM I/O

PGM parsing and writing are isolated from the numerical operations. The reader:

  • skips PGM comments
  • validates integer tokens
  • rejects unsupported magic numbers
  • rejects non-positive dimensions
  • validates the declared maximum value
  • validates every pixel against the declared range
  • reports truncated input

Image operations

The algorithms accept their source as const Image& and return a new Image. This keeps the input unchanged and makes each operation straightforward to test independently.

Algorithms

Thresholding

Pixels below the selected threshold are set to black (0). Pixels equal to or above the threshold are set to white (255).

For the executable's threshold of 128:

pixel < 128  -> 0
pixel >= 128 -> 255

3×3 box blur

Each interior output pixel is the integer average of its 3×3 neighbourhood:

1/9 × [1 1 1
       1 1 1
       1 1 1]

The blur starts with a copy of the input, so its outer one-pixel border remains unchanged. Images smaller than 3×3 are returned unchanged.

Sobel edge detection

The implementation applies explicit Sobel X and Y kernels:

Gx = [-1  0  1]    Gy = [ 1  2  1]
     [-2  0  2]         [ 0  0  0]
     [-1  0  1]         [-1 -2 -1]

Gradient magnitude is calculated using:

sqrt(gx² + gy²)

The result is clamped to the grayscale range 0-255. Sobel is evaluated only for interior pixels; because the output image is initially zero-filled, its outer one-pixel border is black.

Border handling

The two neighbourhood-based operations currently use different explicit border policies:

  • 3×3 box blur: copy the input border unchanged
  • Sobel edge detection: leave the output border at zero

Both policies avoid sampling outside the image. Production implementations might instead use zero padding, coordinate clamping, reflection, or another policy selected for the application.

Limitations

  • Supports only ASCII PGM (P2)
  • Uses fixed processing parameters in the executable
  • Uses a fixed 3×3 box-blur kernel
  • Uses integer division for the box-blur average
  • Clamps Sobel magnitude rather than normalising it across the image
  • Does not rescale PGM inputs whose declared maximum value is below 255
  • Processes images sequentially on the CPU
  • Does not include performance benchmarks
  • PGM error paths are not yet covered by automated tests
  • Intended as a focused learning prototype, not production or medical-device software

What I learned

  • representing a two-dimensional image in a one-dimensional std::vector
  • designing a small C++ class with const and non-const pixel access
  • separating image representation, file I/O, algorithms, and application flow
  • parsing and validating a simple external file format
  • implementing thresholding and convolution with explicit loops
  • combining Sobel X and Y gradients into an edge magnitude
  • testing numerical operations with small matrices whose results are known
  • building and testing a multi-file C++17 project with CMake and CTest

Possible next steps

  • add automated tests for valid and malformed PGM files
  • either restrict input to a maximum value of 255 or rescale other valid PGM ranges
  • accept input/output paths and operation parameters as command-line arguments
  • make border handling configurable and consistent across operations
  • support binary PGM (P5)
  • benchmark larger images and investigate performance improvements

Project status

  • C++17 project and compiler-warning configuration
  • Grayscale pixels stored in std::vector
  • Matrix-style pixel indexing through Image
  • P2 PGM input and output
  • PGM input validation
  • Binary thresholding
  • 3×3 box blur
  • Sobel edge detection
  • Focused image-operation tests
  • CMake build and CTest integration
  • Automated PGM parser tests
  • Command-line input and output paths

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages