An educational C++ implementation of core image-processing operations and edge-based message encoding, built without relying on a high-level computer-vision library.
This project explores how fundamental image-processing operations work at matrix and pixel level. It implements a small grayscale image-processing pipeline using custom C++ classes for matrix storage, convolution, sharpening, Sobel edge detection, and message encoding/decoding.
The objective is not to replace libraries such as OpenCV. Instead, the project demonstrates the algorithms and low-level engineering concepts behind common image-processing operations, including dynamic memory management, copy semantics, operator overloading, kernel-based filtering, gradient calculation, and least-significant-bit manipulation.
ImageMatrix stores a grayscale image as a dynamically allocated two-dimensional matrix of double values. It provides:
- Construction from dimensions, another matrix, or a text-based image file
- Deep-copy construction and assignment
- Matrix addition and subtraction
- Scalar multiplication
- Bounds-checked pixel access and updates
- Explicit resource cleanup
Convolution applies a custom kernel to an image and supports:
- Arbitrary kernel dimensions
- Configurable stride
- Optional zero padding
- Output-dimension calculation
- Deep copying of kernel data
For an input image (I) and kernel (K), each output value is computed from the weighted neighbourhood of the corresponding input location.
ImageSharpening uses a 3 × 3 mean-blur kernel followed by an unsharp-masking operation:
[ I_{sharp} = I + k(I - I_{blurred}) ]
The resulting pixel values are clipped to the valid grayscale range of 0–255.
EdgeDetector applies horizontal and vertical Sobel kernels:
[ G_x = \begin{bmatrix} -1 & 0 & 1 \ -2 & 0 & 2 \ -1 & 0 & 1 \end{bmatrix}, \qquad G_y = \begin{bmatrix} -1 & -2 & -1 \ 0 & 0 & 0 \ 1 & 2 & 1 \end{bmatrix} ]
The gradient magnitude is calculated as:
[ G = \sqrt{G_x^2 + G_y^2} ]
Pixels whose magnitude exceeds the image-wide average magnitude are returned as edge locations.
The project also experiments with hiding text at edge-pixel locations:
EncodeMessageselects detected edge positions and modifies pixel information using character transformations and least-significant-bit operations.DecodeMessagereads least-significant bits from edge pixels, groups them into 7-bit segments, and converts the segments back to ASCII characters.ImageProcessorconnects sharpening, edge detection, encoding, and decoding into higher-level workflows.
This component is an educational steganography experiment rather than a production-ready or cryptographically secure encoding system.
flowchart TD
A[Grayscale matrix] --> B[Mean blur]
B --> C[Unsharp masking]
C --> D[Sobel gradients]
D --> E[Edge locations]
E --> F[Encode or decode message]
.
├── ImageMatrix.h/.cpp # Matrix storage, copy semantics, and arithmetic
├── ImageLoader.h/.cpp # Loading numeric grayscale matrices from text files
├── Convolution.h/.cpp # Configurable 2D convolution
├── ImageSharpening.h/.cpp # Mean filtering and unsharp masking
├── EdgeDetector.h/.cpp # Sobel gradients and edge selection
├── EncodeMessage.h/.cpp # Edge-based message encoding experiment
├── DecodeMessage.h/.cpp # LSB extraction and ASCII reconstruction
├── ImageProcessor.h/.cpp # High-level processing pipeline
├── main.cpp # Minimal development entry point
├── CMakeLists.txt # CMake build configuration
└── README.md
ImageLoader currently expects a text file containing whitespace-separated grayscale values. Each row represents one image row:
12 18 25 31
20 45 80 42
17 53 91 36
10 22 30 19
All rows should have the same number of values. Typical pixel values are in the range 0–255.
The current version does not directly decode PNG, JPEG, or other compressed image formats.
- A C++14-compatible compiler
- CMake 3.26 or newer for the provided CMake configuration
git clone https://github.com/003733m/manualimageprocess.git
cd manualimageprocess
cmake -S . -B build
cmake --build buildg++ -std=c++14 *.cpp -I. -o manualimageprocessRun the compiled development executable with:
./manualimageprocessOn Windows, run:
manualimageprocess.exe
The processing classes can be used from a custom main.cpp as follows:
#include <iostream>
#include "ImageMatrix.h"
#include "ImageSharpening.h"
#include "EdgeDetector.h"
int main() {
ImageMatrix image("image.txt");
ImageSharpening sharpener;
ImageMatrix sharpened = sharpener.sharpen(image, 2.0);
EdgeDetector detector;
auto edges = detector.detectEdges(sharpened);
std::cout << "Detected edge pixels: " << edges.size() << '\n';
return 0;
}- C++ class design and separation of responsibilities
- Dynamic two-dimensional array allocation
- Destructors and deep-copy semantics
- Copy constructors and copy-assignment operators
- Operator overloading
- Kernel-based spatial filtering
- Padding, stride, and output-shape calculation
- Unsharp masking
- Sobel gradient estimation
- Threshold-based edge selection
- Bitwise operations and LSB-based data representation
- CMake-based multi-file project organisation
This repository is an educational implementation and remains a work in progress.
main.cppis currently a minimal matrix-construction test, not a complete command-line interface.- Image input is limited to whitespace-separated grayscale matrices.
- Processed images are not yet exported to standard image formats.
- The encoding pipeline does not currently include capacity validation or robust message-length metadata.
- The encoding method is not designed for security or cryptographic use.
- Automated tests and benchmark comparisons have not yet been added.
- Manual memory management can be modernised using standard containers and RAII.
- Replace raw matrix ownership with
std::vectoror another RAII-based container - Add dimension checks to matrix arithmetic
- Add a complete command-line interface
- Support standard grayscale image formats
- Export processed matrices as image files
- Add unit tests for matrix, convolution, and edge-detection operations
- Add message-capacity and bounds validation
- Store message length explicitly for reliable decoding
- Add before/after examples and numerical validation
- Compare manual outputs with a reference implementation such as OpenCV
High-level computer-vision libraries make convolution and edge detection easy to call, but they can hide the underlying mechanics. Implementing these operations manually provides practical insight into how image matrices, kernels, gradients, memory ownership, and bit-level transformations work.
The project therefore serves as a foundation for understanding both classical computer vision and the tensor operations used in modern machine-learning systems.
Mehmet Ali Toy
Computer Engineering graduate, Hacettepe University
- GitHub: 003733m
- LinkedIn: Mehmet Ali Toy
This repository is maintained as an educational and portfolio project.