Вопрос или проблема
Как улучшить изображения отпечатков пальцев без размывания рельефов? Я использую Django.
В данный момент я работаю над проектом по улучшению отпечатков пальцев с использованием OpenCV, и хотя мой код дает результаты, рельефы на изображениях отпечатков пальцев получаются размытыми.
Вот мой код
import cv2
import numpy as np
import math
import fingerprint_enhancer # Убедитесь, что этот модуль доступен
from django.shortcuts import render
from django.core.files.base import ContentFile
from .forms import HandFingerForm
from .models import ThumbImage
import base64
MAX_IMAGES = 10
def remove_green(img):
empty_img = np.zeros_like(img)
RED, GREEN, BLUE = (2, 1, 0)
reds = img[:, :, RED]
greens = img[:, :, GREEN]
blues = img[:, :, BLUE]
# Создать маску для областей, которые нужно оставить
tmpMask = (greens < 35) | (reds > greens) | (blues > greens)
img[tmpMask == 0] = (0, 0, 0) # Удалить фон с оригинального изображения
empty_img[tmpMask] = (255, 255, 255) # Маска с пальцем в белом
return img, empty_img
def detect_nail(gray_mask_finger):
# Найти контуры на маске изображения
contours, _ = cv2.findContours(gray_mask_finger, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for contour in contours:
# Рассматриваем только достаточно большие контуры как ногти
if cv2.contourArea(contour) > 100: # Настройте этот порог по необходимости
x, y, w, h = cv2.boundingRect(contour)
# Нарисовать прямоугольник вокруг обнаруженного ногтя
cv2.rectangle(gray_mask_finger, (x, y), (x + w, y + h), (255, 0, 0), 2) # Голубой прямоугольник
def process_fingerprint(image):
clip_hist_percent = 25
# Преобразовать в градации серого
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Рассчитать гистограмму уровней серого
hist = cv2.calcHist([gray], [0], None, [256], [0, 256])
hist_size = len(hist)
# Рассчитать кумулятивное распределение из гистограммы
accumulator = []
accumulator.append(float(hist[0]))
for index in range(1, hist_size):
accumulator.append(accumulator[index - 1] + float(hist[index]))
# Найти точки для обрезки
maximum = accumulator[-1]
clip_hist_percent *= (maximum / 100.0)
clip_hist_percent /= 2.0
# Найти левую обрезку
minimum_gray = 0
while accumulator[minimum_gray] < clip_hist_percent:
minimum_gray += 1
# Найти правую обрезку
maximum_gray = hist_size - 1
while accumulator[maximum_gray] >= (maximum - clip_hist_percent):
maximum_gray -= 1
# Рассчитать значения альфа и бета
alpha = 255 / (maximum_gray - minimum_gray)
beta = -minimum_gray * alpha
auto_result = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
gray = cv2.cvtColor(auto_result, cv2.COLOR_BGR2GRAY)
# Вычислить гамму
mid = 0.5
mean = np.mean(gray)
gamma = math.log(mid * 255) / math.log(mean)
# Применить коррекцию гаммы
img_gamma = np.power(auto_result, gamma).clip(0, 255).astype(np.uint8)
g1 = cv2.cvtColor(img_gamma, cv2.COLOR_BGR2GRAY)
# Адаптивное пороговое значение
thresh2 = cv2.adaptiveThreshold(g1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 199, 3)
# Морфологические операции
blur = ((3, 3), 1)
erode_ = (5, 5)
dilate_ = (3, 3)
dilate = cv2.dilate(cv2.erode(cv2.GaussianBlur(thresh2 / 255, blur[0], blur[1]),
np.ones(erode_)), np.ones(dilate_)) * 255
# Улучшение отпечатка пальца
out = fingerprint_enhancer.enhance_Fingerprint(dilate)
return out
def upload_image(request):
if 'upload_count' not in request.session:
request.session['upload_count'] = 0
upload_count = request.session['upload_count']
if request.method == 'POST':
if 'skip' in request.POST:
request.session['upload_count'] = 0
return render(request, 'captureimage.html', {
'message': 'Пропущены оставшиеся загрузки.',
'next_finger': None,
'max_images': MAX_IMAGES
})
finger_number = request.POST.get('finger')
image_data = request.POST.get('image')
if not image_data or ';base64,' not in image_data:
return render(request, 'captureimage.html', {
'error': 'Недопустимые данные изображения',
'next_finger': upload_count + 1,
'max_images': MAX_IMAGES
})
try:
format, imgstr = image_data.split(';base64,')
except ValueError:
return render(request, 'captureimage.html', {
'error': 'Не удалось обработать данные изображения',
'next_finger': upload_count + 1,
'max_images': MAX_IMAGES
})
ext = format.split("https://stackoverflow.com/")[-1]
image_file = ContentFile(base64.b64decode(imgstr), name=f'finger_{finger_number}.{ext}')
form_data = {'finger': finger_number}
form_files = {'image': image_file}
form = HandFingerForm(form_data, form_files)
if form.is_valid():
form.save()
# Загрузить и обработать сохраненное изображение
saved_image_path = form.instance.image.path
image = cv2.imread(saved_image_path, 1) # Загрузить изображение
image = cv2.resize(image, None, fx=0.3, fy=0.3) # Изменить размер
image = cv2.GaussianBlur(image, (3, 3), 0) # Применить гауссово размытие
# Обработка отпечатка пальца
enhanced_image = process_fingerprint(image)
# Удалить зеленый фон
no_green_image, mask_finger = remove_green(enhanced_image)
# Преобразовать в градации серого
gray_mask_finger = cv2.cvtColor(mask_finger, cv2.COLOR_BGR2GRAY)
# Вызов функции для определения ногтя
detect_nail(gray_mask_finger)
request.session['upload_count'] += 1
if request.session['upload_count'] < MAX_IMAGES:
return render(request, 'captureimage.html', {
'message': 'Изображение пальца успешно загружено.',
'next_finger': request.session['upload_count'] + 1,
'max_images': MAX_IMAGES
})
else:
request.session['upload_count'] = 0
return render(request, 'captureimage.html', {
'message': 'Все 10 изображений успешно загружены!',
'next_finger': None,
'max_images': MAX_IMAGES
})
else:
return render(request, 'captureimage.html', {
'error': 'Недопустимые данные формы',
'details': form.errors,
'next_finger': upload_count + 1,
'max_images': MAX_IMAGES
})
return render(request, 'captureimage.html', {
'next_finger': request.session['upload_count'] + 1,
'max_images': MAX_IMAGES
})
#frontend
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Захват изображения отпечатка пальца</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Roboto', sans-serif;
background-color: #f4f4f9;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background: #ffffff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
max-width: 500px;
width: 100%;
text-align: center;
}
h2 {
margin-bottom: 20px;
color: #333;
}
#instructions {
font-size: 1.1em;
margin-bottom: 20px;
color: #555;
}
#video {
width: 100%;
border-radius: 8px;
margin-bottom: 10px;
border: 2px solid #007bff;
}
#preview {
margin: 10px 0;
}
#capturedImage {
max-width: 100%;
border-radius: 8px;
border: 2px solid #28a745;
}
select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
margin-bottom: 20px;
font-size: 1em;
}
.button-group {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
margin-top: 20px;
}
button {
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
padding: 10px 15px;
font-size: 1em;
cursor: pointer;
transition: background-color 0.3s;
flex: 1;
margin: 5px;
}
button:hover {
background-color: #0056b3;
}
.skip-btn {
background-color: #dc3545;
}
.skip-btn:hover {
background-color: #c82333;
}
/* Адаптивные стили */
@media (max-width: 600px) {
.button-group {
flex-direction: column;
}
button {
margin: 5px 0;
}
}
</style>
</head>
<body>
<div class="container">
<h2>Захват изображения отпечатка пальца</h2>
<div id="instructions">
Пожалуйста, выберите палец и поместите его перед камерой при хорошем освещении для получения четкого изображения.
</div>
<!-- Отображение сообщения об успехе или ошибке -->
{% if message %}
<p style="color:green;">{{ message }}</p>
{% elif error %}
<p style="color:red;">{{ error }}</p>
{% endif %}
<div class="video-container">
<!-- Видеопоток -->
<video id="video" autoplay></video>
<!-- Предварительный просмотр захваченного изображения -->
<div id="preview">
<p>Захваченное изображение:</p>
<img id="capturedImage" src="" alt="Изображение не захвачено еще">
</div>
</div>
<!-- Выпадающий список для выбора пальца -->
<form id="fingerForm">
<select id="fingerSelect" name="finger">
<option value="">Выберите палец</option>
<option value="Правый большой палец">Правый большой палец</option>
<option value="Левый большой палец">Левый большой палец</option>
<option value="Правый указательный">Правый указательный</option>
<option value="Левый указательный">Левый указательный</option>
<option value="Правый средний">Правый средний</option>
<option value="Левый средний">Левый средний</option>
<option value="Правый безымянный">Правый безымянный</option>
<option value="Левый безымянный">Левый безымянный</option>
<option value="Правый мизинец">Правый мизинец</option>
<option value="Левый мизинец">Левый мизинец</option>
</select>
</form>
<div class="button-group">
<button id="snap">Захватить изображение отпечатка пальца</button>
<!-- Форма загрузки изображения -->
<form id="imageForm" action="{% url 'upload_image' %}" method="POST" enctype="multipart/form-data">
{% csrf_token %}
<input type="hidden" name="image" id="imageInput">
<button type="submit" id="uploadButton" disabled>Загрузить изображение отпечатка пальца</button>
</form>
<!-- Кнопка пропуска -->
<form action="{% url 'upload_image' %}" method="POST">
{% csrf_token %}
<button type="submit" class="skip-btn" name="skip" value="true">Пропустить оставшиеся</button>
</form>
</div>
</div>
<script>
const video = document.getElementById('video');
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const snap = document.getElementById('snap');
const imageInput = document.getElementById('imageInput');
const capturedImage = document.getElementById('capturedImage');
const uploadButton = document.getElementById('uploadButton');
const fingerSelect = document.getElementById('fingerSelect');
// Доступ к камере с более высоким разрешением для лучшей детализации
navigator.mediaDevices.getUserMedia({ video: { width: 1280, height: 720 } })
.then((stream) => {
video.srcObject = stream;
})
.catch((error) => {
console.error("Ошибка доступа к камере: ", error);
});
// Захват изображения при нажатии кнопки
snap.addEventListener('click', () => {
// Проверка, выбран ли палец
if (fingerSelect.value === "") {
alert("Пожалуйста, выберите палец перед захватом изображения.");
return;
}
// Установить размер холста в соответствии с видеопотоком
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
// Применить фильтр резкости для улучшения деталей отпечатков пальцев
context.filter="contrast(1.5) brightness(1.2)"; // Настроить контраст и яркость для четкости
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// Преобразовать изображение с холста в base64
const dataURL = canvas.toDataURL('image/png');
imageInput.value = dataURL; // Установить base64 изображение в скрытое поле
// Отобразить захваченное изображение в теге img
capturedImage.src = dataURL;
// Установить выбранный палец в данные формы
const fingerInput = document.createElement('input');
fingerInput.type="hidden";
fingerInput.name="finger";
fingerInput.value = fingerSelect.value;
document.getElementById('imageForm').appendChild(fingerInput);
// Включить кнопку загрузки
uploadButton.disabled = false;
});
// Включить отправку формы, когда выбран палец
fingerSelect.addEventListener('change', () => {
if (fingerSelect.value !== "") {
uploadButton.disabled = true; // Сброс состояния кнопки
}
});
</script>
</body>
</html>
#models.py
from django.db import models
class ThumbImage(models.Model):
FINGER_CHOICES = [
('Правый большой палец', 'Правый большой палец'),
('Левый большой палец', 'Левый большой палец'),
('Правый указательный', 'Правый указательный'),
('Левый указательный', 'Левый указательный'),
('Правый средний', 'Правый средний'),
('Левый средний', 'Левый средний'),
('Правый безымянный', 'Правый безымянный'),
('Левый безымянный', 'Левый безымянный'),
('Правый мизинец', 'Правый мизинец'),
('Левый мизинец', 'Левый мизинец'),
]
finger = models.CharField(max_length=50, choices=FINGER_CHOICES)
image = models.ImageField(upload_to='finger_images/')
def __str__(self):
return f"{self.finger} изображение"
В настоящее время я работаю над проектом, который сосредоточен на улучшении изображений отпечатков пальцев с помощью OpenCV. Я реализовал различные техники, включая преобразование изображений в градации серого, применение выравнивания гистограммы, выполнение коррекции гаммы, использование адаптивного порогового значения и выполнение морфологических операций.
Изначально эти шаги, казалось, улучшили контраст и четкость изображений. Однако я столкнулся с проблемой: рельефные узоры на отпечатках пальцев выглядят размытыми, а не четкими. Я ожидал увидеть четкие, хорошо очерченные рельефы, но конечные результаты не отличаются ясностью.
Я был бы рад услышать любые предложения или изменения, которые я могу внести для улучшения четкости рельефов в моих изображениях отпечатков пальцев. Любые советы будут очень полезны!
Ответ или решение
Для улучшения изображений отпечатков пальцев без размытия рифлений, предлагаю внести несколько изменений в ваш код и использовать некоторые дополнительные методы обработки изображений. Основная цель — не потерять резкость линий рифлений отпечатка при применении методов улучшения.
Рекомендации по улучшению изображений отпечатков пальцев:
-
Увеличение четкости с помощью фильтра Шарпе:
Используйте фильтр повышения резкости (Unsharp Mask) после всех операций по улучшению контраста. Это может помочь восстановить четкость рифлений, не приводя к чрезмерному размыванию. -
Морфологические операции:
Попробуйте изменить тип морфологических операций. Иногда, вместо эрозии и дилатации, лучше использовать открытие или закрытие, которые могут улучшить детали рифлений, не размазывая их. -
Замена структурного элемента:
Попробуйте использовать другой структурный элемент для морфологических операций, например, эллиптический или прямоугольный, вместо стандартного квадратного элемента. -
Параметры адаптивной пороговой обработки:
Экспериментируйте с параметрами адаптивной пороговой обработки. Иногда, увеличив размер блока или значение C, можно добиться лучшего разделения фона и рифлений. -
Предварительная обработка изображения:
Перед применением каких-либо методов улучшения попробуйте применить метод обнаружения краев (например, оператор Кэнни), чтобы четко выделить рифления. Это поможет лучше визуализировать детали перед дальнейшей обработкой. -
Использование гистограмной коррекции с ограничением:
Вместо контрастной корректировки, выполните контрастную коррекцию с ограничением (CLAHE), которая лучше сохраняет местные детали и предотвращает потерю информации.
Обновленный код:
Ниже приведен пример того, как можно обновить функцию улучшения отпечатков с учетом вышеуказанных рекомендаций:
def process_fingerprint(image):
clip_hist_percent = 25
# Преобразование в оттенки серого
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# CLAHE для улучшения контраста и резкости
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(gray)
# Адаптивная пороговая обработка
thresh = cv2.adaptiveThreshold(cl1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 199, 3)
# Морфологические операции
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)
# Применение фильтра повышения резкости
sharpened = cv2.addWeighted(morph, 1.5, cv2.GaussianBlur(morph, (0, 0), 5), -0.5, 0)
return sharpened
Заключение:
Эти изменения могут значительно улучшить качество изображений ваших отпечатков пальцев без размытия рифлений. Настоятельно рекомендую поэкспериментировать с различными значениями параметров и фильтрами, чтобы найти наилучший подход для ваших данных. Также можно рассмотреть возможность добавления этапа визуализации, чтобы лучше отслеживать промежуточные результаты на каждом шаге обработки.