All projects

Coursework · Jun 2024

Jajarija: Fingerprint Matcher

A semester-closing experiment that taught me the algorithm is only as useful as the heuristics around its data.

My contribution

Built the parallel matching pipeline, Boyer-Moore search, bitmap conversion, database access, and results integration in a three-person team.

Jajarija: Fingerprint Matcher screenshot 1
1 / 3

Learning a new stack under pressure

Jajarija was the final project of our Algorithm Strategies semester. It brought together KMP, Boyer-Moore, Levenshtein distance, regular expressions, and encryption in a desktop application that identified someone from a fingerprint image.

This was my first time working with C# and Avalonia. Every mistake could lead to another rebuild and wait before testing, which encouraged me to catch problems while writing instead of relying on the compiler and runtime later.

The name has no deep story. We asked GPT for one, received Jajarija, and decided to use it.

The course required us to match fingerprints using string-search algorithms. A fingerprint bitmap could be reduced to light and dark pixels, represented as bits, and packed into characters. Part of one fingerprint could then become a pattern searched inside another fingerprint string.

The algorithm was not what worried me most. I immediately started thinking about which area to sample, how large the sample should be, what brightness separated a ridge from the background, and what would happen when two scans differed slightly.

The heuristic work around the algorithm

I owned much of the matching pipeline because string searching was the main course material at the time and the part I found most interesting.

Choosing the sampled region required experimentation. Too little made the pattern less distinctive, while the wrong area could miss useful fingerprint detail. The sample also had to align with the packed bytes or a valid pattern would never appear as an exact substring. The final implementation tried multiple heights instead of trusting one location.

Our original approach depended more heavily on exact matching. Testing showed that alteration, noise, or a different sampled region could make the same fingerprint look different enough to miss.

We developed a search cascade during implementation. It tried exact matching across several regions first, then compared complete fingerprints using Levenshtein distance and accepted the strongest result above a similarity threshold.

similarity⁡(s,t)=(1−dLev(s,t)max⁡(∣s∣,∣t∣))×100%\operatorname{similarity}(s,t) = \left(1 - \frac{d_{\mathrm{Lev}}(s,t)}{\max(|s|,|t|)}\right) \times 100\%
When exact search failed, normalized Levenshtein distance turned the full fingerprint strings into a fallback similarity percentage.

KMP and Boyer-Moore in practice

KMP used prefix information to avoid repeating comparisons and offered predictable linear behavior. Boyer-Moore used the bad-character rule to skip ahead when a mismatch allowed it.

Our Boyer-Moore implementation did not include the good-suffix rule described in the README, so it was a simplified version. I no longer remember which algorithm consistently won our timing comparisons, but implementing both made their different strategies clearer than reading pseudocode in class.

Seeing the first fingerprint match correctly brought the normal satisfaction of finally making an algorithm work. It was also only the beginning.

Once we tried altered fingerprints and inconvenient edge cases, we had to revise the matcher significantly. Each failure pushed us to reconsider the sampled region, cutoff, fallback behavior, or image encoding.

The algorithm was only half the solution

The assignment included bonus requirements involving encrypted personal data and Indonesian alay names. We built a regex matcher that made vowels optional and treated common number substitutions as possible letter equivalents.

We also implemented AES-256 as an educational application of the cryptography material we had learned. A production system would use a trusted cryptographic library instead of a hand-written implementation.

Jajarija was designed to demonstrate string searching, not compete with a real biometric system. Without that course constraint, I would use fingerprint-specific features such as ridge endings and geometric relationships.

The project taught me that applying a textbook algorithm requires heuristic judgment. KMP and Boyer-Moore could only search the representation we gave them. How we prepared the data determined whether the result was useful.

What stayed with me

  • The algorithm is only as useful as the representation it receives.
  • Exact matching becomes fragile when real data gets messy.
  • Heuristics often take more work than the textbook algorithm.
  • A course constraint can produce an unrealistic but valuable experiment.

Run it locally

Clone the source repository or download the project ZIP, then run these commands from the project folder.

Explore the complete project

Download the source, assets, and included documentation as a ZIP archive.

Download project ZIP
bash

Database and data

$ cp .env.example .env
$ docker compose up -d
$ npm install
$ node Faker/insertdata.mjs
$ dotnet run --project Converter/Converter.csproj
$ node Faker/insertImage.mjs

# the Converter step is required: it re-writes biodata as AES ciphertext,
# which is what the app decrypts when it displays a match

Run

$ dotnet run --project src/src.csproj

Use it

# Start Investigating, Pilih Citra, then pick a query print such as
# test/Altered/Altered-Easy/1__M_Left_index_finger_CR.BMP, choose KMP or BM,
# and press Search. 6,000 reference prints ship with the repository.

# This download carries fingerprints for persons 1-25 rather than the full
# 55,000-image set, so it stays small. See test/SAMPLE.md.