照片指纹提取:线条细化后噪声与断连问题的滤波优化问询
指纹纹线提取与滤波优化求助
我正在完成生物识别课程项目,需要从方向场中提取指纹类型(包括弓形、倾向弓形、环形、涡形、双环形),并从纹线里提取细节点(minutiae)。
目前已成功从样本照片中提取出纹线,但尝试高斯滤波器、双边滤波器、Gabor滤波器等各类滤波方法后,结果仍存在大量噪声,且纹线不连续,无法用于细节点提取,现寻求滤波相关的优化建议。
以下是完整可运行的Python代码、效果示例图及样本照片:
import cv2 import numpy as np import math import matplotlib.pyplot as plt # Hardcoded path to input image IMAGE_PATH = '20241108_171310.jpg' # Replace with the path to your image # Extracts the finger def crop_image(image): hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # I use skin tone to get the boundaries. There may be better options. lower_skin = np.array([0, 0, 80], dtype=np.uint8) upper_skin = np.array([7, 255, 255], dtype=np.uint8) lower_skin2 = np.array([170, 0, 80], dtype=np.uint8) upper_skin2 = np.array([179, 255, 255], dtype=np.uint8) skin_mask = cv2.bitwise_or(cv2.inRange(hsv_image, lower_skin, upper_skin), cv2.inRange(hsv_image, lower_skin2, upper_skin2)) skin_mask = cv2.GaussianBlur(skin_mask, (51, 51), 20) skin_mask = cv2.inRange(skin_mask, 200, 255) contours, _ = cv2.findContours(skin_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return image largest_contour = max(contours, key=cv2.contourArea) blacked = cv2.bitwise_and(image, image, mask=skin_mask) x, y, w, h = cv2.boundingRect(largest_contour) h = min(int(1.5 * w), h) cropped_image = blacked[y:y + h, x:x + w] return cropped_image # Scales to target resolution def scale_image(image, final_width): coefficient = final_width / image.shape[1] return cv2.resize(image, (int(image.shape[1] * coefficient), int(image.shape[0] * coefficient))) # Calculates and draws the direction field out of sobel gradients def draw_directions(directions, image, tile_size): for y in range(directions.shape[0]): for x in range(directions.shape[1]): cx = x * tile_size + tile_size // 2 cy = y * tile_size + tile_size // 2 dir_x, dir_y = np.cos(directions[y, x]), np.sin(directions[y, x]) magnitude = math.sqrt(dir_x ** 2 + dir_y ** 2) dir_x /= magnitude dir_y /= magnitude line_length = 15 x_end = cx + int(dir_x * line_length / 2) y_end = cy + int(dir_y * line_length / 2) x_start = cx - int(dir_x * line_length / 2) y_start = cy - int(dir_y * line_length / 2) cv2.line(image, (x_start, y_start), (x_end, y_end), color=(0, 0, 255), thickness=2) def get_directions(grad_x, grad_y, image_shape, tile_size): directions = np.zeros((image_shape[0] // tile_size, image_shape[1] // tile_size), dtype=np.float32) for y in range(image_shape[0] // tile_size): for x in range(image_shape[1] // tile_size): tile_grad_x = grad_x[y * tile_size:(y + 1) * tile_size, x * tile_size:(x + 1) * tile_size] tile_grad_y = grad_y[y * tile_size:(y + 1) * tile_size, x * tile_size:(x + 1) * tile_size] a = 2 * np.sum(tile_grad_x * tile_grad_y) b = np.sum(tile_grad_x ** 2 - tile_grad_y ** 2) directions[y, x] = 0.5 * np.arctan2(a, b) + np.pi / 2 return directions def extract_lines(grayscale_image): # Enhance contrast clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) enhanced = clahe.apply(grayscale_image) # Apply stronger Gaussian blur enhanced = cv2.GaussianBlur(enhanced, (5, 5), 3) # Adaptive thresholding with adjusted parameters thresholded = cv2.adaptiveThreshold(enhanced, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, -2) # Morphological operations kernel = np.ones((2, 2), np.uint8) thresholded = 255 - cv2.erode(255 - thresholded, kernel, iterations=1) # Enhance continuity thresholded = enhance_continuity(thresholded) return thresholded def enhance_continuity(binary_image): kernel = np.ones((3, 3), np.uint8) # Close small gaps closed = cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel) # Remove small noise opened = cv2.morphologyEx(closed, cv2.MORPH_OPEN, kernel) return opened def thin_lines(binary_image): # Ensure ridges are white, valleys are black for thinning operation thinned = cv2.ximgproc.thinning(binary_image) return thinned def connect_ridges(thinned_image, max_gap=5): result = thinned_image.copy() # Find endpoints kernel = np.array([[1, 1, 1], [1, 10, 1], [1, 1, 1]], dtype=np.uint8) conv = cv2.filter2D(result.astype(np.float32), -1, kernel) endpoints = np.where((conv == 11) & (result == 255)) # Connect nearby endpoints for i in range(len(endpoints[0])): y1, x1 = endpoints[0][i], endpoints[1][i] for j in range(i + 1, len(endpoints[0])): y2, x2 = endpoints[0][j], endpoints[1][j] dist = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) if 0 < dist <= max_gap: cv2.line(result, (x1, y1), (x2, y2), 255, 1) return result if __name__ == "__main__": # Load image image = cv2.imread(IMAGE_PATH) if image is None: print(f"Error: Unable to load image at {IMAGE_PATH}") exit() # Process image cropped_image = crop_image(image) scaled_image = scale_image(cropped_image, 768) grayscale_image = cv2.cvtColor(scaled_image, cv2.COLOR_BGR2GRAY) # Enhanced preprocessing grayscale_image = cv2.GaussianBlur(grayscale_image, (5, 5), 3) # Sobel gradients sobel_x = cv2.Sobel(grayscale_image, cv2.CV_32F, 1, 0, ksize=3) sobel_y = cv2.Sobel(grayscale_image, cv2.CV_32F, 0, 1, ksize=3) # Get and draw directions directions = get_directions(sobel_x, sobel_y, grayscale_image.shape, 32) image_directions = scaled_image.copy() draw_directions(directions, image_directions, 32) # Extract and enhance lines extracted_lines = extract_lines(grayscale_image) enhanced_lines = enhance_continuity(extracted_lines) # Thin lines and connect ridges thinned_lines = thin_lines(enhanced_lines) connected_lines = connect_ridges(thinned_lines, max_gap=30) # So that the first two displayed images are not blue image = cv2.cvtColor(image,cv2.COLOR_BGR2RGB) cropped_image = cv2.cvtColor(cropped_image, cv2.COLOR_BGR2RGB) # Display results images = [image, cropped_image, image_directions, 255 - extracted_lines, 255 - enhanced_lines, 255 - thinned_lines, 255 - connected_lines] titles = ["Original Image", "Cropped Image", "Orientation Field", "Extracted Lines", "Enhanced Lines", "Thinned Lines", "Connected Lines"] plt.figure(figsize=(35, 10)) for i in range(len(images)): plt.subplot(1, 7, i + 1) plt.imshow(images[i], cmap='gray' if i > 1 else None) plt.title(titles[i]) plt.axis('off') plt.tight_layout() plt.show()
样本照片与处理效果


内容的提问来源于stack exchange,提问作者ampersander
相关产品推荐
相关产品推荐

