Spaces:
Sleeping
Sleeping
| import cv2 | |
| import numpy as np | |
| import os | |
| from PIL import Image | |
| import fitz # PyMuPDF | |
| class ImagePreprocessor: | |
| """ | |
| Classe per l'acquisizione e pre-elaborazione delle immagini di firme e documenti. | |
| Implementa funzionalità di base come la conversione in scala di grigi, | |
| normalizzazione, scontorno dei timbri, ecc. | |
| """ | |
| def __init__(self): | |
| """Inizializza il preprocessore di immagini.""" | |
| pass | |
| def load_image(self, image_path): | |
| """ | |
| Carica un'immagine da un percorso file. | |
| Args: | |
| image_path (str): Percorso dell'immagine da caricare | |
| Returns: | |
| numpy.ndarray: Immagine caricata in formato BGR | |
| """ | |
| if not os.path.exists(image_path): | |
| raise FileNotFoundError(f"Il file {image_path} non esiste") | |
| # Controlla l'estensione del file | |
| _, ext = os.path.splitext(image_path) | |
| ext = ext.lower() | |
| if ext == '.pdf': | |
| return self.extract_image_from_pdf(image_path) | |
| else: | |
| # Carica l'immagine usando OpenCV | |
| image = cv2.imread(image_path) | |
| if image is None: | |
| raise ValueError(f"Impossibile caricare l'immagine {image_path}") | |
| return image | |
| def extract_image_from_pdf(self, pdf_path, page_num=0): | |
| """ | |
| Estrae un'immagine da un file PDF. | |
| Args: | |
| pdf_path (str): Percorso del file PDF | |
| page_num (int): Numero di pagina da cui estrarre l'immagine (default: 0) | |
| Returns: | |
| numpy.ndarray: Immagine estratta in formato BGR | |
| """ | |
| # Apri il documento PDF | |
| doc = fitz.open(pdf_path) | |
| # Controlla se il numero di pagina è valido | |
| if page_num >= len(doc): | |
| raise ValueError(f"Il PDF ha {len(doc)} pagine, ma è stata richiesta la pagina {page_num}") | |
| # Ottieni la pagina | |
| page = doc.load_page(page_num) | |
| # Renderizza la pagina come immagine | |
| pix = page.get_pixmap(matrix=fitz.Matrix(2, 2)) # Fattore di scala 2 per migliore qualità | |
| # Converti in formato immagine | |
| img_data = pix.samples | |
| # Crea un array numpy dall'immagine | |
| img_array = np.frombuffer(img_data, dtype=np.uint8).reshape(pix.height, pix.width, pix.n) | |
| # Se l'immagine è in formato RGB, converti in BGR per OpenCV | |
| if pix.n == 3: | |
| img_array = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR) | |
| return img_array | |
| def convert_to_grayscale(self, image): | |
| """ | |
| Converte un'immagine in scala di grigi. | |
| Args: | |
| image (numpy.ndarray): Immagine di input in formato BGR | |
| Returns: | |
| numpy.ndarray: Immagine in scala di grigi | |
| """ | |
| if len(image.shape) == 2: | |
| # L'immagine è già in scala di grigi | |
| return image | |
| return cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
| def normalize_image(self, image): | |
| """ | |
| Normalizza un'immagine per migliorare contrasto e luminosità. | |
| Args: | |
| image (numpy.ndarray): Immagine di input (scala di grigi o BGR) | |
| Returns: | |
| numpy.ndarray: Immagine normalizzata | |
| """ | |
| # Converti in scala di grigi se necessario | |
| if len(image.shape) > 2: | |
| gray = self.convert_to_grayscale(image) | |
| else: | |
| gray = image | |
| # Applica equalizzazione dell'istogramma | |
| return cv2.equalizeHist(gray) | |
| def detect_and_extract_stamps(self, image, lower_color=None, upper_color=None): | |
| """ | |
| Rileva e estrae i timbri da un'immagine utilizzando il filtraggio del colore. | |
| Args: | |
| image (numpy.ndarray): Immagine di input in formato BGR | |
| lower_color (numpy.ndarray, optional): Limite inferiore del colore in formato HSV | |
| upper_color (numpy.ndarray, optional): Limite superiore del colore in formato HSV | |
| Returns: | |
| tuple: (immagine_originale_senza_timbri, maschera_timbri, timbri_estratti) | |
| """ | |
| # Valori predefiniti per rilevare timbri blu (comuni nei documenti) | |
| if lower_color is None: | |
| lower_color = np.array([100, 50, 50]) # Blu in HSV | |
| if upper_color is None: | |
| upper_color = np.array([140, 255, 255]) | |
| # Converti l'immagine in HSV | |
| hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) | |
| # Crea una maschera per il colore specificato | |
| mask = cv2.inRange(hsv, lower_color, upper_color) | |
| # Applica operazioni morfologiche per migliorare la maschera | |
| kernel = np.ones((5, 5), np.uint8) | |
| mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) | |
| mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel) | |
| # Estrai i timbri | |
| stamps = cv2.bitwise_and(image, image, mask=mask) | |
| # Crea un'immagine senza timbri | |
| inv_mask = cv2.bitwise_not(mask) | |
| image_without_stamps = cv2.bitwise_and(image, image, mask=inv_mask) | |
| return image_without_stamps, mask, stamps | |
| def threshold_image(self, image, method='adaptive'): | |
| """ | |
| Applica una soglia all'immagine per binarizzarla. | |
| Args: | |
| image (numpy.ndarray): Immagine in scala di grigi | |
| method (str): Metodo di soglia ('simple', 'adaptive', 'otsu') | |
| Returns: | |
| numpy.ndarray: Immagine binaria | |
| """ | |
| if len(image.shape) > 2: | |
| gray = self.convert_to_grayscale(image) | |
| else: | |
| gray = image | |
| if method == 'simple': | |
| _, binary = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY) | |
| elif method == 'adaptive': | |
| binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, | |
| cv2.THRESH_BINARY_INV, 11, 2) | |
| elif method == 'otsu': | |
| _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) | |
| else: | |
| raise ValueError(f"Metodo di soglia non supportato: {method}") | |
| return binary | |
| def resize_image(self, image, width=None, height=None, keep_aspect_ratio=True): | |
| """ | |
| Ridimensiona un'immagine a una larghezza o altezza specificata. | |
| Args: | |
| image (numpy.ndarray): Immagine di input | |
| width (int, optional): Larghezza desiderata | |
| height (int, optional): Altezza desiderata | |
| keep_aspect_ratio (bool): Mantiene il rapporto d'aspetto originale | |
| Returns: | |
| numpy.ndarray: Immagine ridimensionata | |
| """ | |
| if width is None and height is None: | |
| return image | |
| h, w = image.shape[:2] | |
| if keep_aspect_ratio: | |
| if width is None: | |
| aspect_ratio = height / float(h) | |
| dim = (int(w * aspect_ratio), height) | |
| else: | |
| aspect_ratio = width / float(w) | |
| dim = (width, int(h * aspect_ratio)) | |
| else: | |
| dim = (width if width is not None else w, height if height is not None else h) | |
| return cv2.resize(image, dim, interpolation=cv2.INTER_AREA) | |
| def denoise_image(self, image, method='gaussian'): | |
| """ | |
| Applica un filtro di riduzione del rumore all'immagine. | |
| Args: | |
| image (numpy.ndarray): Immagine di input | |
| method (str): Metodo di denoising ('gaussian', 'median', 'bilateral') | |
| Returns: | |
| numpy.ndarray: Immagine filtrata | |
| """ | |
| if method == 'gaussian': | |
| return cv2.GaussianBlur(image, (5, 5), 0) | |
| elif method == 'median': | |
| return cv2.medianBlur(image, 5) | |
| elif method == 'bilateral': | |
| if len(image.shape) > 2: | |
| return cv2.bilateralFilter(image, 9, 75, 75) | |
| else: | |
| # Per immagini in scala di grigi, convertiamo temporaneamente in BGR | |
| temp = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) | |
| temp = cv2.bilateralFilter(temp, 9, 75, 75) | |
| return cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY) | |
| else: | |
| raise ValueError(f"Metodo di denoising non supportato: {method}") | |
| def preprocess_signature(self, image_path, resize_width=800): | |
| """ | |
| Pipeline completa di pre-elaborazione per le firme. | |
| Args: | |
| image_path (str): Percorso dell'immagine della firma | |
| resize_width (int): Larghezza a cui ridimensionare l'immagine | |
| Returns: | |
| dict: Dizionario contenente le diverse fasi di pre-elaborazione | |
| """ | |
| # Carica l'immagine | |
| original = self.load_image(image_path) | |
| # Ridimensiona l'immagine | |
| resized = self.resize_image(original, width=resize_width) | |
| # Converti in scala di grigi | |
| gray = self.convert_to_grayscale(resized) | |
| # Normalizza l'immagine | |
| normalized = self.normalize_image(gray) | |
| # Applica denoising | |
| denoised = self.denoise_image(normalized, method='bilateral') | |
| # Applica soglia | |
| binary = self.threshold_image(denoised, method='adaptive') | |
| # Restituisci tutte le fasi di pre-elaborazione | |
| return { | |
| 'original': original, | |
| 'resized': resized, | |
| 'grayscale': gray, | |
| 'normalized': normalized, | |
| 'denoised': denoised, | |
| 'binary': binary | |
| } | |