A classical face-recognition library for the JVM — Eigenfaces, Fisherfaces, LBPH — wrapped in a Spring Boot REST API and a Picocli CLI.
Status · Quick Start · Algorithms · REST API · CLI · Benchmarks · Contributing
Actively maintained. Originally released in 2014 as a university project (
TSCD+ Swing GUI); fully rewritten for v2 as a service-shaped library with clean-architecture layering, a REST/CLI surface, and a classical-vision pipeline. v2 is not yet published to Maven Central — install from source (see below). See ROADMAP.md for what is landed vs. planned and CHANGELOG.md for recent changes.
This repository is a learning-friendly reference implementation of the classical face-recognition pipeline — not a competitor to dlib, OpenCV DNN, or modern CNN-based libraries. If you need state-of-the-art accuracy, bring a real model via the Deep-learning backend.
- Three classical feature extractors — Eigenfaces (PCA), Fisherfaces (LDA), LBPH
- Pluggable
FeatureExtractor/FaceClassifier/FaceDetectorinterfaces - Clean architecture —
domain/application/infrastructure/apilayers - REST API with OpenAPI 3 / Swagger UI, request correlation IDs, validation, rate limiting, Prometheus metrics
- CLI (Picocli) for
enroll,train,recognize,serve,benchmark - Model persistence — auto-save/auto-load + REST export/import
- Docker image — multi-stage build, non-root user, container-aware JVM
- Experimental ONNX backend scaffold for FaceNet/ArcFace-style embeddings (bring your own weights)
| Capability | State | Notes |
|---|---|---|
| Eigenfaces extractor | Stable | PCA via JAMA |
| Fisherfaces extractor | Stable | LDA on top of PCA |
| LBPH extractor | Stable | Uniform LBP histograms, configurable grid |
| KNN classifier | Stable | Euclidean / Cosine / Manhattan / Chi-square |
| Haar cascade face detector | Experimental | Vendored stub; real cascade data required for production |
| Face aligner (eye-centered affine) | Stable (opt-in) | Enabled via facerecognition.image.face-alignment |
| Model persistence | Stable | FileModelRepository + TrainedModel |
| REST + OpenAPI | Stable | /api/v1/* + /swagger-ui.html |
| Prometheus / health / metrics | Stable | Micrometer + custom ModelReadyHealthIndicator |
| Rate limiting | Stable | Per-IP token bucket (Bucket4j) |
| CLI (picocli) | Stable | serve, recognize, enroll, train, benchmark |
| ONNX deep-learning extractor | Experimental scaffold | Bring your own model weights |
| Published Maven artifact | Planned | Not yet on Central |
- JDK 17+
- Maven 3.9+
git clone https://github.com/prasadus92/face-recognition.git
cd face-recognition
mvn clean packageThe build produces two jars in target/:
face-recognition-<version>.jar— library jarface-recognition-<version>-exec.jar— Spring Boot executable (REST + CLI)
java -jar target/face-recognition-*-exec.jar
# → http://localhost:8080/swagger-ui.html
# → http://localhost:8080/actuator/healthjava -jar target/face-recognition-*-exec.jar --help
java -jar target/face-recognition-*-exec.jar enroll --image john.jpg --name "John Doe"
java -jar target/face-recognition-*-exec.jar train
java -jar target/face-recognition-*-exec.jar recognize --image unknown.jpgdocker build -t face-recognition:latest .
docker run --rm -p 8080:8080 -v "$PWD/data:/app/data" face-recognition:latestimport com.facerecognition.application.service.FaceRecognitionService;
import com.facerecognition.infrastructure.extraction.EigenfacesExtractor;
import com.facerecognition.infrastructure.classification.KNNClassifier;
import com.facerecognition.domain.model.FaceImage;
import com.facerecognition.domain.model.RecognitionResult;
import java.io.File;
FaceRecognitionService service = FaceRecognitionService.builder()
.extractor(new EigenfacesExtractor(10))
.classifier(new KNNClassifier())
.build();
service.enrollFromFile(new File("john.jpg"), "John Doe");
service.enrollFromFile(new File("jane.jpg"), "Jane Smith");
service.train();
RecognitionResult result = service.recognizeFromFile(new File("unknown.jpg"));
result.getBestMatch().ifPresent(match ->
System.out.printf("%s (conf=%.2f)%n",
match.getIdentity().getName(), match.getConfidence()));| Algorithm | Idea | Best for | Tradeoffs |
|---|---|---|---|
| Eigenfaces (PCA) | Project faces onto principal components of training set. | Fast, controlled environments. | Sensitive to lighting and pose. |
| Fisherfaces (LDA) | Maximize between-class / within-class scatter. | Varied lighting with ≥2 samples/identity. | Requires multiple labelled samples per identity. |
| LBPH | Concatenate histograms of local binary patterns over a grid. | Texture-based matching under lighting changes. | Pose-sensitive; higher memory. |
The classical extractors live under com.facerecognition.infrastructure.extraction and are selected declaratively via application.yml (facerecognition.extraction.algorithm = eigenfaces | fisherfaces | lbph | onnx) or programmatically via the service builder.
An OnnxDeepFeatureExtractor scaffold is included for running modern embedding networks (FaceNet, ArcFace, etc.) via ONNX Runtime. It ships without model weights — you must supply your own (.onnx) and configure the model path in application.yml. See docs/onnx.md (forthcoming) for the contract.
| Endpoint | Method | Purpose |
|---|---|---|
/api/v1/enroll |
POST multipart | Register a face sample for a name |
/api/v1/recognize |
POST multipart | Identify a face in an uploaded image |
/api/v1/train |
POST | Train / retrain on all enrolled samples |
/api/v1/identities |
GET | List enrolled identities (paginated) |
/api/v1/identities/{id} |
GET / PATCH / DELETE | Read, update, soft-delete an identity |
/api/v1/model/status |
GET | Model state, algorithm, identity counts |
/api/v1/model/export |
POST | Download a serialized TrainedModel |
/api/v1/model/import |
POST multipart | Load a previously exported model |
/actuator/health |
GET | Liveness + custom model-ready indicator |
/actuator/prometheus |
GET | Micrometer metrics in Prometheus format |
/swagger-ui.html |
GET | Interactive OpenAPI 3 docs |
Every response that represents an error uses the ErrorResponse shape and carries a traceId (echoed from the X-Request-ID request header or generated if absent).
- Rate limit: per-IP token bucket (default 60 req/min, configurable via
facerecognition.ratelimit.*). - Upload limit:
spring.servlet.multipart.max-file-size(10 MB default). - CORS: disabled by default; enable via
facerecognition.cors.*. - Authentication: no auth by default. Deploy behind an API gateway / reverse proxy, or enable the optional API-key filter (
facerecognition.security.api-key).
Usage: face-recognition [COMMAND]
Commands:
enroll Enroll a face image under a name.
train Train or retrain the model.
recognize Recognize a face from an image or directory.
benchmark Run accuracy/performance benchmarks on a dataset.
serve Start the HTTP API server.
All commands honour --model <path> and --config <application.yml> for reproducible runs.
All runtime settings live in src/main/resources/application.yml and can be overridden via environment variables (FACERECOGNITION_EXTRACTION_ALGORITHM=lbph) or a custom application.yml passed to --spring.config.location. Core keys:
facerecognition:
detection:
min-face-size: 30
min-confidence: 0.5
extraction:
algorithm: eigenfaces # eigenfaces | fisherfaces | lbph | onnx
num-components: 10
onnx:
model-path: "" # path to a .onnx model for the deep backend
classification:
algorithm: knn
k-neighbors: 3
distance-metric: euclidean # euclidean | cosine | manhattan | chi_square
recognition:
threshold: 0.6
quality:
min-score: 0.3
image:
target-width: 100
target-height: 100
face-alignment: true
model:
auto-save: true
auto-load: true
save-path: data/models/default.frm
ratelimit:
enabled: true
requests-per-minute: 60Honest disclaimer. The numbers currently checked into docs/benchmarks/ come from the bundled micro-dataset (
src/test/resources/datasets/mini/) and are not directly comparable to LFW or Yale B. Classical algorithms on aligned frontal faces typically land in the 80–95% range depending on dataset; don't expect deep-learning-level accuracy.
Run the benchmark suite locally:
mvn -P benchmarks exec:java
# or via the CLI
java -jar target/face-recognition-*-exec.jar benchmark \
--dataset src/test/resources/datasets/mini \
--algorithm all \
--report docs/benchmarks/mini.jsonThe benchmark harness lives under com.facerecognition.benchmark and reports top-1 accuracy, per-stage latency, and confusion matrices as JSON / Markdown.
- Logs — structured via Logback, per-request
traceIdvia MDC; file appender rolls daily with 1 GB cap. - Metrics — custom Micrometer timers
facerecognition.detect,facerecognition.extract,facerecognition.match,facerecognition.recognize.total; counters for recognitions / enrollments / errors. - Health —
/actuator/healthexposes a custommodel-readycomponent reflectingFaceRecognitionService#isTrained(). - Tracing — propagate
X-Request-IDfrom your upstream and it will appear in every log line and response body.
face-recognition/
├── src/
│ ├── main/
│ │ ├── java/com/facerecognition/
│ │ │ ├── FaceRecognitionApplication.java # Spring Boot entry point
│ │ │ ├── config/ # @ConfigurationProperties + bean factories
│ │ │ ├── domain/ # Pure domain model + service interfaces
│ │ │ │ ├── model/
│ │ │ │ └── service/
│ │ │ ├── application/service/ # Orchestrator (FaceRecognitionService)
│ │ │ ├── infrastructure/
│ │ │ │ ├── classification/
│ │ │ │ ├── detection/
│ │ │ │ ├── extraction/
│ │ │ │ ├── persistence/
│ │ │ │ └── preprocessing/
│ │ │ ├── api/
│ │ │ │ ├── rest/ # Controllers, DTOs, advice, filters
│ │ │ │ └── cli/ # Picocli commands
│ │ │ └── benchmark/ # Accuracy + performance harness
│ │ └── resources/
│ │ ├── application.yml
│ │ └── logback-spring.xml
│ └── test/java/com/facerecognition/ # Unit + integration tests
├── docs/
│ ├── architecture.md
│ ├── benchmarks/
│ └── onnx.md
├── .github/
│ ├── workflows/ # CI, CodeQL, Dependabot, release
│ ├── ISSUE_TEMPLATE/
│ └── PULL_REQUEST_TEMPLATE.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── SECURITY.md
├── ROADMAP.md
├── Dockerfile
└── pom.xml
Contributions are very welcome — bug fixes, extractors, distance metrics, deep-learning adapters, documentation. Start with CONTRIBUTING.md, which covers the development loop, coding standards, and how to run the full test + quality-gate suite locally. Please also read the Code of Conduct.
Report vulnerabilities privately — see SECURITY.md. Do not file public issues for security bugs.
This implementation builds on foundational papers:
- Turk, M. & Pentland, A. (1991). Eigenfaces for Recognition.
- Belhumeur, P. N., Hespanha, J. P. & Kriegman, D. J. (1997). Eigenfaces vs. Fisherfaces: Recognition Using Class Specific Linear Projection.
- Ahonen, T., Hadid, A. & Pietikäinen, M. (2006). Face Description with Local Binary Patterns.
- Schroff, F., Kalenichenko, D. & Philbin, J. (2015). FaceNet: A Unified Embedding for Face Recognition and Clustering.
- Deng, J., Guo, J., Xue, N. & Zafeiriou, S. (2019). ArcFace: Additive Angular Margin Loss for Deep Face Recognition.
Licensed under the Apache License, Version 2.0 — see License.txt. The project was originally released under the GNU General Public License v3.0 in 2014 and was relicensed to Apache 2.0 in April 2026; the License.txt file explains the transition in full.
- JAMA for numerical linear algebra.
- Spring Boot, picocli, Micrometer, Bucket4j, springdoc-openapi.
- Maintainer: Prasad Subrahmanya · prasadus92@gmail.com