All Data Sets Used From MNIST
The datasets you see above were used to train my model.
So how can it get over 90% accuracy with so few images?
The key lies in my extraction algorithm, where each test result is saved as a 2-byte integer.
There are 5 tests in total:
Test 1: 16 values → 32 bytes
Test 2: 16 values → 32 bytes
Test 3: 14 values → 28 bytes
Test 4: 8 values → 16 bytes
Test 5: 6 values → 12 bytes
That’s 120 bytes per image, and with 70 images, that’s just 8,400 bytes total — under 10 KB!
This kind of compact extraction is perfect for low-power hardware like FPGAs and microcontrollers.
It proves that you can still get a lot out of very little — without relying on massive data centers, expensive GPUs, or huge datasets.
This project is my proof-of-concept showing that AI can be efficient, explainable, and hardware-friendly — especially when we move beyond the limitations of traditional Von Neumann architectures.
So now, let me explain what the code is actually doing — just on a surface level.
We start with any image. But what is an image, really?
It's just a 2D array of pixel values. If it's a grayscale image, each pixel holds a value between 0 and 255.
We then apply a threshold — usually right around the middle.
Any pixel value above the threshold becomes a 1 (white), and anything below becomes a 0 (black).
This gives us a clean binary image where the digit is represented by 1s and 0s.
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 0 0 0 0 0
0 0 0 1 1 0 0 0 0 1 1 0 0 0
0 0 1 1 0 0 0 0 0 0 1 1 0 0
0 1 1 0 0 0 0 0 0 0 0 1 1 0
0 1 1 0 0 0 0 0 0 0 0 1 1 0
0 1 1 0 0 0 0 0 0 0 0 1 1 0
0 0 1 1 0 0 0 0 0 0 1 1 0 0
0 0 0 1 1 0 0 0 0 1 1 0 0 0
0 0 0 0 1 1 1 1 1 1 0 0 0 0
0 0 0 1 1 0 0 0 0 1 1 0 0 0
0 0 1 1 0 0 0 0 0 0 1 1 0 0
0 1 1 0 0 0 0 0 0 0 0 1 1 0
0 1 1 0 0 0 0 0 0 0 0 1 1 0
01 1 0 0 0 0 0 0 0 0 1 1 0
0 0 1 1 0 0 0 0 0 0 1 1 0 0
0 0 0 1 1 0 0 0 0 1 1 0 0 0
0 0 0 0 1 1 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
So then, that's how we can extract data from these 0s and 1s — treating them just like a matrix.
So now that we have a clean binary matrix of 1s and 0s, the first step in working with it is to trim the edges, removing all the extra white space around the digit so we can focus only on the part that matters.
The first process trims the edges of the input image to remove unnecessary white space, so the digit can be processed without any outside noise or padding.
By cropping out the extra borders, the program focuses only on the digit itself. This ensures that every image is centered and scaled properly, which makes later feature extraction more reliable and consistent — regardless of how the digit was originally written.
This test divides the image into 4 quadrants, then runs directional checks from all 4 sides — top, bottom, left, and right.
For each direction, it counts how many times the pixel value changes from white to black (or black to white) as it moves through each quadrant. These transitions help describe the strokes and structural edges of the digit.
4 directions × 4 quadrants = 16 transition values
Each transition count is saved as a 2-byte integer
Total: 16 values = 32 bytes
This test divides the image into 16 equal blocks (a 4×4 grid). For each block, it calculates how many black pixels are present — effectively measuring how much "ink" is in that section.
This helps the system understand the density distribution of the digit — for example, how much space a “0” fills versus a “1”.
16 blocks = 16 density values
Each value saved as a 2-byte integer
Total: 16 values = 32 bytes
In this test, the digit's key points are selected (such as corners or curve tips), and each point's distance from a fixed reference point (like the image origin) is calculated using the distance formula.
These distances represent the spatial layout of the digit.
14 key points = 14 distance values
Each distance saved as a 2-byte integer
Total: 14 values = 28 bytes
This test checks how continuous the digit is along its outer edges.
The image is sliced into multiple horizontal segments. For each one, the algorithm measures the number of black pixels that appear near the left and right boundaries — revealing how much of the digit touches the edges at different heights.
4 segments × 2 sides = 8 values
Each saved as a 2-byte integer
Total: 8 values = 16 bytes
This is a simplified version of the quadrant-based pattern test.
Instead of doing full directional sweeps, it checks only a few strategic transition points across broader zones of the image. This gives a more generalized picture of how strokes are placed, while using less data.
6 strategic transition zones = 6 values
Each saved as a 2-byte integer
Total: 6 values = 12 bytes
Each of the five tests pulls out specific, useful patterns from the digit image. Instead of using complex algorithms or large models, the program reduces each image into just 120 bytes of raw data.
This makes it practical for small-scale hardware like FPGAs or microcontrollers, where memory and power are limited. The focus is on efficiency and clarity, and getting only what’s needed and ignoring the rest.
Even with this small dataset size, the system reaches over 90 percent accuracy. It shows that with the right process, it’s possible to get solid results without needing large datasets or powerful machines.
Try it yourself by downloading my java file
⬇️ GitHub Link Download Link ⬇️
https://github.com/faisalKab/DigitRecognizer-Java