Spaces:
Sleeping
Sleeping
Commit ·
dfd887d
1
Parent(s): 5e28f9f
Feature: Added Enhance Finger Print Image
Browse files- .gitignore +5 -0
- .u2net/u2net.onnx +3 -0
- Dockerfile +9 -2
- __pycache__/FingerprintImageEnhancer.cpython-311.pyc +0 -0
- __pycache__/enhancer.cpython-311.pyc +0 -0
- app.py +84 -3
- enhancer.py +1 -6
- requirements.txt +18 -1
.gitignore
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.jpg
|
| 2 |
+
*.png
|
| 3 |
+
__pycache__/
|
| 4 |
+
venv/
|
| 5 |
+
.env
|
.u2net/u2net.onnx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8d10d2f3bb75ae3b6d527c77944fc5e7dcd94b29809d47a739a7a728a912b491
|
| 3 |
+
size 175997641
|
Dockerfile
CHANGED
|
@@ -4,6 +4,9 @@ FROM python:3.11-slim
|
|
| 4 |
|
| 5 |
ENV PYTHONUNBUFFERED=1
|
| 6 |
|
|
|
|
|
|
|
|
|
|
| 7 |
# Set the working directory
|
| 8 |
WORKDIR /code
|
| 9 |
|
|
@@ -20,11 +23,15 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
| 20 |
COPY requirements.txt .
|
| 21 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 22 |
|
|
|
|
|
|
|
|
|
|
| 23 |
# Copy the rest of the application code
|
| 24 |
COPY . .
|
| 25 |
|
| 26 |
-
#
|
| 27 |
-
|
|
|
|
| 28 |
|
| 29 |
# Expose the port Hugging Face uses
|
| 30 |
EXPOSE 7860
|
|
|
|
| 4 |
|
| 5 |
ENV PYTHONUNBUFFERED=1
|
| 6 |
|
| 7 |
+
# rembg to use a local folder
|
| 8 |
+
ENV U2NET_HOME=/code/.u2net
|
| 9 |
+
|
| 10 |
# Set the working directory
|
| 11 |
WORKDIR /code
|
| 12 |
|
|
|
|
| 23 |
COPY requirements.txt .
|
| 24 |
RUN pip install --no-cache-dir -r requirements.txt
|
| 25 |
|
| 26 |
+
# Create necessary directories and set permissions
|
| 27 |
+
RUN mkdir -p /code/model /code/.u2net && chmod -R 777 /code
|
| 28 |
+
|
| 29 |
# Copy the rest of the application code
|
| 30 |
COPY . .
|
| 31 |
|
| 32 |
+
# --- PRE-DOWNLOAD MODEL ---
|
| 33 |
+
# This "cooks" the 176MB model into your Docker layer so the Space starts instantly
|
| 34 |
+
RUN python -c "from rembg import new_session; new_session('u2net')"
|
| 35 |
|
| 36 |
# Expose the port Hugging Face uses
|
| 37 |
EXPOSE 7860
|
__pycache__/FingerprintImageEnhancer.cpython-311.pyc
DELETED
|
Binary file (16.2 kB)
|
|
|
__pycache__/enhancer.cpython-311.pyc
DELETED
|
Binary file (3.73 kB)
|
|
|
app.py
CHANGED
|
@@ -6,13 +6,22 @@ start_import = time.time()
|
|
| 6 |
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # Hide everything but errors
|
| 7 |
os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # Force CPU mode immediately
|
| 8 |
|
| 9 |
-
|
|
|
|
|
|
|
| 10 |
import cv2
|
| 11 |
import numpy as np
|
| 12 |
import tensorflow as tf
|
| 13 |
from tensorflow.keras.models import load_model
|
| 14 |
from os.path import join, dirname
|
| 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
# Import your custom enhancement functions
|
| 17 |
# Ensure 'enhancer.py' is in the same directory
|
| 18 |
try:
|
|
@@ -33,18 +42,24 @@ MODEL_PATH = join(DIRNAME, 'model', '12_120_fp160.h5')
|
|
| 33 |
# Global model variable
|
| 34 |
model = None
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
def load_and_warmup_model():
|
| 37 |
global model
|
|
|
|
| 38 |
t_start = time.time()
|
| 39 |
try:
|
| 40 |
print(f"Loading model from: {MODEL_PATH}")
|
| 41 |
-
|
| 42 |
|
| 43 |
# Warm-up: Run a dummy inference so the first user request isn't slow
|
| 44 |
print("Warming up model...")
|
| 45 |
dummy_input = np.zeros((1, 160, 160, 1), dtype=np.float32)
|
| 46 |
# Use predict_on_batch for faster single-sample inference
|
| 47 |
-
|
| 48 |
|
| 49 |
print(f"Model loaded and warmed up in: {time.time() - t_start:.2f}s")
|
| 50 |
except Exception as e:
|
|
@@ -53,6 +68,23 @@ def load_and_warmup_model():
|
|
| 53 |
# Load model immediately on startup
|
| 54 |
load_and_warmup_model()
|
| 55 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
def preprocess_fingerprint(image_bytes, image_type):
|
| 57 |
"""
|
| 58 |
image_type: 'contactless' or 'contactbased'
|
|
@@ -156,6 +188,55 @@ def compare_fingerprints():
|
|
| 156 |
}
|
| 157 |
})
|
| 158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
if __name__ == '__main__':
|
| 160 |
# Threaded=False is sometimes safer for TF, but Gunicorn handles the workers in prod.
|
| 161 |
app.run(host='0.0.0.0', port=5000, debug=True)
|
|
|
|
| 6 |
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # Hide everything but errors
|
| 7 |
os.environ['CUDA_VISIBLE_DEVICES'] = '-1' # Force CPU mode immediately
|
| 8 |
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
from flask import Flask, request, jsonify, Response
|
| 12 |
import cv2
|
| 13 |
import numpy as np
|
| 14 |
import tensorflow as tf
|
| 15 |
from tensorflow.keras.models import load_model
|
| 16 |
from os.path import join, dirname
|
| 17 |
|
| 18 |
+
|
| 19 |
+
from rembg import remove, new_session # <--- Added rembg
|
| 20 |
+
from PIL import Image
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
# Set model path for rembg (helpful for Docker/HuggingFace)
|
| 24 |
+
os.environ['U2NET_HOME'] = join(dirname(__file__), '.u2net')
|
| 25 |
# Import your custom enhancement functions
|
| 26 |
# Ensure 'enhancer.py' is in the same directory
|
| 27 |
try:
|
|
|
|
| 42 |
# Global model variable
|
| 43 |
model = None
|
| 44 |
|
| 45 |
+
# --- Global Sessions ---
|
| 46 |
+
# Load rembg session once to avoid reload overhead on every request
|
| 47 |
+
rembg_session = new_session("u2net")
|
| 48 |
+
siamese_model = None
|
| 49 |
+
|
| 50 |
def load_and_warmup_model():
|
| 51 |
global model
|
| 52 |
+
MODEL_PATH = join(dirname(__file__), 'model', '12_120_fp160.h5')
|
| 53 |
t_start = time.time()
|
| 54 |
try:
|
| 55 |
print(f"Loading model from: {MODEL_PATH}")
|
| 56 |
+
siamese_model = load_model(MODEL_PATH, compile=False)
|
| 57 |
|
| 58 |
# Warm-up: Run a dummy inference so the first user request isn't slow
|
| 59 |
print("Warming up model...")
|
| 60 |
dummy_input = np.zeros((1, 160, 160, 1), dtype=np.float32)
|
| 61 |
# Use predict_on_batch for faster single-sample inference
|
| 62 |
+
siamese_model.predict_on_batch([dummy_input, dummy_input])
|
| 63 |
|
| 64 |
print(f"Model loaded and warmed up in: {time.time() - t_start:.2f}s")
|
| 65 |
except Exception as e:
|
|
|
|
| 68 |
# Load model immediately on startup
|
| 69 |
load_and_warmup_model()
|
| 70 |
|
| 71 |
+
def remove_bg_from_cv2(cv2_img):
|
| 72 |
+
"""
|
| 73 |
+
Converts CV2 image to PIL, removes background,
|
| 74 |
+
and returns back to CV2 (BGR).
|
| 75 |
+
"""
|
| 76 |
+
# Convert BGR to RGB for PIL
|
| 77 |
+
img_rgb = cv2.cvtColor(cv2_img, cv2.COLOR_BGR2RGB)
|
| 78 |
+
pil_img = Image.fromarray(img_rgb)
|
| 79 |
+
|
| 80 |
+
# Remove background
|
| 81 |
+
output_pil = remove(pil_img, session=rembg_session)
|
| 82 |
+
|
| 83 |
+
# Convert back to CV2 (and handle transparency)
|
| 84 |
+
# Note: rembg returns RGBA. We convert to BGR for your enhancer.
|
| 85 |
+
output_cv2 = cv2.cvtColor(np.array(output_pil), cv2.COLOR_RGBA2BGR)
|
| 86 |
+
return output_cv2
|
| 87 |
+
|
| 88 |
def preprocess_fingerprint(image_bytes, image_type):
|
| 89 |
"""
|
| 90 |
image_type: 'contactless' or 'contactbased'
|
|
|
|
| 188 |
}
|
| 189 |
})
|
| 190 |
|
| 191 |
+
@app.route('/enhance', methods=['POST'])
|
| 192 |
+
def enhance_image_api():
|
| 193 |
+
"""
|
| 194 |
+
API Endpoint: /enhance
|
| 195 |
+
Method: POST
|
| 196 |
+
Form-Data: 'image' (file)
|
| 197 |
+
Returns: JPEG image bytes of the enhanced fingerprint
|
| 198 |
+
"""
|
| 199 |
+
# 1. Validation
|
| 200 |
+
if 'image' not in request.files:
|
| 201 |
+
return jsonify({"error": "No image file provided. Use key 'image'."}), 400
|
| 202 |
+
|
| 203 |
+
file = request.files['image']
|
| 204 |
+
|
| 205 |
+
if file.filename == '':
|
| 206 |
+
return jsonify({"error": "No selected file"}), 400
|
| 207 |
+
|
| 208 |
+
try:
|
| 209 |
+
# 2. Decode Image
|
| 210 |
+
file_bytes = np.frombuffer(file.read(), np.uint8)
|
| 211 |
+
img = cv2.imdecode(file_bytes, cv2.IMREAD_COLOR)
|
| 212 |
+
|
| 213 |
+
if img is None:
|
| 214 |
+
return jsonify({"error": "Failed to decode image"}), 400
|
| 215 |
+
|
| 216 |
+
# 1. Background Removal
|
| 217 |
+
no_bg = remove_bg_from_cv2(img)
|
| 218 |
+
|
| 219 |
+
# 3. Apply Enhancement Pipeline (Contactless Logic: Basic -> Advanced)
|
| 220 |
+
# Note: basicEnhancing handles grayscale conversion internally if needed
|
| 221 |
+
step1 = basicEnhancing(no_bg)
|
| 222 |
+
|
| 223 |
+
# advancedEnhancing expects the output from basicEnhancing
|
| 224 |
+
final_enhanced = advancedEnhancing(step1)
|
| 225 |
+
|
| 226 |
+
# 4. Encode Result to JPEG
|
| 227 |
+
# Using High Quality (95) to preserve ridge details
|
| 228 |
+
success, encoded_image = cv2.imencode('.jpg', final_enhanced, [int(cv2.IMWRITE_JPEG_QUALITY), 95])
|
| 229 |
+
|
| 230 |
+
if not success:
|
| 231 |
+
return jsonify({"error": "Failed to encode resulting image"}), 500
|
| 232 |
+
|
| 233 |
+
# 5. Return Image Response
|
| 234 |
+
return Response(encoded_image.tobytes(), mimetype='image/jpeg')
|
| 235 |
+
|
| 236 |
+
except Exception as e:
|
| 237 |
+
print(f"Enhancement Error: {e}")
|
| 238 |
+
return jsonify({"error": str(e)}), 500
|
| 239 |
+
|
| 240 |
if __name__ == '__main__':
|
| 241 |
# Threaded=False is sometimes safer for TF, but Gunicorn handles the workers in prod.
|
| 242 |
app.run(host='0.0.0.0', port=5000, debug=True)
|
enhancer.py
CHANGED
|
@@ -32,7 +32,7 @@ def advancedEnhancing(img):
|
|
| 32 |
|
| 33 |
if __name__ == '__main__':
|
| 34 |
# --- Execution Logic ---
|
| 35 |
-
image_path = r"C:\
|
| 36 |
# image_path = r"C:\SagarKV\sol9x\geekykant\contactless_2d_fingerprint_images\first_session\p247\p1.bmp"
|
| 37 |
# image_path = r"C:\SagarKV\sol9x\geekykant\contact-based_fingerprints\second_session\13_3.jpg"
|
| 38 |
original = cv2.imread(image_path)
|
|
@@ -69,8 +69,3 @@ if __name__ == '__main__':
|
|
| 69 |
|
| 70 |
print(f"Comparison strip saved successfully as: {os.path.abspath(output_path)}")
|
| 71 |
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
|
|
|
| 32 |
|
| 33 |
if __name__ == '__main__':
|
| 34 |
# --- Execution Logic ---
|
| 35 |
+
image_path = r"C:\SagarKV\sol9x\geekykant\contact-contactless-fingerprint\website\app\contactless_to_contactbased_fp_matching\finger_no_bg.png"
|
| 36 |
# image_path = r"C:\SagarKV\sol9x\geekykant\contactless_2d_fingerprint_images\first_session\p247\p1.bmp"
|
| 37 |
# image_path = r"C:\SagarKV\sol9x\geekykant\contact-based_fingerprints\second_session\13_3.jpg"
|
| 38 |
original = cv2.imread(image_path)
|
|
|
|
| 69 |
|
| 70 |
print(f"Comparison strip saved successfully as: {os.path.abspath(output_path)}")
|
| 71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
|
@@ -3,11 +3,13 @@ albucore==0.0.24
|
|
| 3 |
albumentations==2.0.8
|
| 4 |
annotated-types==0.7.0
|
| 5 |
astunparse==1.6.3
|
|
|
|
| 6 |
blinker==1.9.0
|
| 7 |
certifi==2026.1.4
|
| 8 |
charset-normalizer==3.4.4
|
| 9 |
click==8.3.1
|
| 10 |
colorama==0.4.6
|
|
|
|
| 11 |
fingerprint-feature-extractor==0.0.10
|
| 12 |
fingerprint_enhancer==0.0.14
|
| 13 |
Flask==3.1.2
|
|
@@ -18,40 +20,55 @@ google-pasta==0.2.0
|
|
| 18 |
grpcio==1.76.0
|
| 19 |
gunicorn==24.0.0
|
| 20 |
h5py==3.15.1
|
|
|
|
| 21 |
idna==3.11
|
| 22 |
ImageIO==2.37.2
|
| 23 |
itsdangerous==2.2.0
|
| 24 |
Jinja2==3.1.6
|
| 25 |
jsonpickle==4.1.1
|
|
|
|
|
|
|
| 26 |
keras==3.13.1
|
| 27 |
lazy_loader==0.4
|
| 28 |
libclang==18.1.1
|
|
|
|
| 29 |
Markdown==3.10.1
|
| 30 |
markdown-it-py==4.0.0
|
| 31 |
MarkupSafe==3.0.3
|
| 32 |
mdurl==0.1.2
|
| 33 |
ml_dtypes==0.5.4
|
|
|
|
| 34 |
namex==0.1.0
|
| 35 |
networkx==3.6.1
|
| 36 |
-
|
|
|
|
|
|
|
| 37 |
opencv-python==4.13.0.90
|
| 38 |
opencv-python-headless==4.13.0.90
|
| 39 |
opt_einsum==3.4.0
|
| 40 |
optree==0.18.0
|
| 41 |
packaging==26.0
|
| 42 |
pillow==12.1.0
|
|
|
|
|
|
|
| 43 |
protobuf==6.33.4
|
| 44 |
pydantic==2.12.5
|
| 45 |
pydantic_core==2.41.5
|
| 46 |
Pygments==2.19.2
|
|
|
|
|
|
|
| 47 |
PyYAML==6.0.3
|
|
|
|
|
|
|
| 48 |
requests==2.32.5
|
| 49 |
rich==14.2.0
|
|
|
|
| 50 |
scikit-image==0.26.0
|
| 51 |
scipy==1.17.0
|
| 52 |
simsimd==6.5.12
|
| 53 |
six==1.17.0
|
| 54 |
stringzilla==4.6.0
|
|
|
|
| 55 |
tensorboard==2.20.0
|
| 56 |
tensorboard-data-server==0.7.2
|
| 57 |
tensorflow==2.20.0
|
|
|
|
| 3 |
albumentations==2.0.8
|
| 4 |
annotated-types==0.7.0
|
| 5 |
astunparse==1.6.3
|
| 6 |
+
attrs==25.4.0
|
| 7 |
blinker==1.9.0
|
| 8 |
certifi==2026.1.4
|
| 9 |
charset-normalizer==3.4.4
|
| 10 |
click==8.3.1
|
| 11 |
colorama==0.4.6
|
| 12 |
+
coloredlogs==15.0.1
|
| 13 |
fingerprint-feature-extractor==0.0.10
|
| 14 |
fingerprint_enhancer==0.0.14
|
| 15 |
Flask==3.1.2
|
|
|
|
| 20 |
grpcio==1.76.0
|
| 21 |
gunicorn==24.0.0
|
| 22 |
h5py==3.15.1
|
| 23 |
+
humanfriendly==10.0
|
| 24 |
idna==3.11
|
| 25 |
ImageIO==2.37.2
|
| 26 |
itsdangerous==2.2.0
|
| 27 |
Jinja2==3.1.6
|
| 28 |
jsonpickle==4.1.1
|
| 29 |
+
jsonschema==4.26.0
|
| 30 |
+
jsonschema-specifications==2025.9.1
|
| 31 |
keras==3.13.1
|
| 32 |
lazy_loader==0.4
|
| 33 |
libclang==18.1.1
|
| 34 |
+
llvmlite==0.46.0
|
| 35 |
Markdown==3.10.1
|
| 36 |
markdown-it-py==4.0.0
|
| 37 |
MarkupSafe==3.0.3
|
| 38 |
mdurl==0.1.2
|
| 39 |
ml_dtypes==0.5.4
|
| 40 |
+
mpmath==1.3.0
|
| 41 |
namex==0.1.0
|
| 42 |
networkx==3.6.1
|
| 43 |
+
numba==0.63.1
|
| 44 |
+
numpy==2.3.5
|
| 45 |
+
onnxruntime==1.23.2
|
| 46 |
opencv-python==4.13.0.90
|
| 47 |
opencv-python-headless==4.13.0.90
|
| 48 |
opt_einsum==3.4.0
|
| 49 |
optree==0.18.0
|
| 50 |
packaging==26.0
|
| 51 |
pillow==12.1.0
|
| 52 |
+
platformdirs==4.5.1
|
| 53 |
+
pooch==1.8.2
|
| 54 |
protobuf==6.33.4
|
| 55 |
pydantic==2.12.5
|
| 56 |
pydantic_core==2.41.5
|
| 57 |
Pygments==2.19.2
|
| 58 |
+
PyMatting==1.1.15
|
| 59 |
+
pyreadline3==3.5.4
|
| 60 |
PyYAML==6.0.3
|
| 61 |
+
referencing==0.37.0
|
| 62 |
+
rembg==2.0.72
|
| 63 |
requests==2.32.5
|
| 64 |
rich==14.2.0
|
| 65 |
+
rpds-py==0.30.0
|
| 66 |
scikit-image==0.26.0
|
| 67 |
scipy==1.17.0
|
| 68 |
simsimd==6.5.12
|
| 69 |
six==1.17.0
|
| 70 |
stringzilla==4.6.0
|
| 71 |
+
sympy==1.14.0
|
| 72 |
tensorboard==2.20.0
|
| 73 |
tensorboard-data-server==0.7.2
|
| 74 |
tensorflow==2.20.0
|