""" Module de conversion pour les formats d'images modernes Supporte les formats courants des appareils Android et iOS """ import os import subprocess from pathlib import Path import tempfile import shutil import logging from typing import List, Optional, Tuple from .config import settings # Configuration du logging logger = logging.getLogger("cheque_scanner_api.conversion") # Formats modernes pris en charge MODERN_FORMATS = { # Formats iPhone "heic": "HEIC (High Efficiency Image Format) - iPhone", "heif": "HEIF (High Efficiency Image Format) - iPhone", # Formats Android haute qualité "webp": "WebP - Android", "dng": "DNG (Digital Negative) - Android RAW", # Formats communs haute résolution "cr2": "Canon RAW Format", "arw": "Sony RAW Format", "nef": "Nikon RAW Format", "raw": "Format RAW générique", } # Mapping des extensions aux formats de sortie recommandés FORMAT_MAPPING = { "heic": "jpg", "heif": "jpg", "webp": "png", "dng": "jpg", "cr2": "jpg", "arw": "jpg", "nef": "jpg", "raw": "jpg", } def is_modern_format(filename: str) -> bool: """ Vérifie si le fichier est dans un format moderne qui nécessite une conversion Args: filename (str): Nom du fichier à vérifier Returns: bool: True si le fichier est dans un format moderne, False sinon """ ext = Path(filename).suffix.lower().lstrip(".") return ext in MODERN_FORMATS def get_supported_input_formats() -> List[str]: """ Renvoie la liste des formats d'entrée pris en charge Returns: List[str]: Liste des extensions de fichiers pris en charge """ return list(MODERN_FORMATS.keys()) + list(settings.ALLOWED_EXTENSIONS) def convert_heic_to_jpg(input_path: str, output_path: str) -> bool: """ Convertit un fichier HEIC en JPG en utilisant pillow-heif Args: input_path (str): Chemin du fichier HEIC output_path (str): Chemin de sortie pour le fichier JPG Returns: bool: True si la conversion a réussi, False sinon """ try: import pillow_heif from PIL import Image # Enregistrer le décodeur HEIF pillow_heif.register_heif_opener() # Ouvrir et convertir l'image img = Image.open(input_path) img.convert("RGB").save(output_path, format="JPEG", quality=95) return True except ImportError: logger.warning("pillow-heif n'est pas installé, utilisation de la méthode alternative") return _convert_with_imagemagick(input_path, output_path) except Exception as e: logger.error(f"Erreur lors de la conversion HEIC: {str(e)}") return False def convert_webp_to_png(input_path: str, output_path: str) -> bool: """ Convertit un fichier WebP en PNG Args: input_path (str): Chemin du fichier WebP output_path (str): Chemin de sortie pour le fichier PNG Returns: bool: True si la conversion a réussi, False sinon """ try: from PIL import Image img = Image.open(input_path) img.save(output_path, format="PNG") return True except Exception as e: logger.error(f"Erreur lors de la conversion WebP: {str(e)}") return _convert_with_imagemagick(input_path, output_path) def _convert_with_imagemagick(input_path: str, output_path: str) -> bool: """ Méthode alternative utilisant ImageMagick pour la conversion Args: input_path (str): Chemin du fichier d'entrée output_path (str): Chemin du fichier de sortie Returns: bool: True si la conversion a réussi, False sinon """ try: # Vérifier si ImageMagick est installé subprocess.run(["which", "convert"], check=True, capture_output=True) # Conversion avec ImageMagick result = subprocess.run( ["convert", input_path, output_path], check=True, capture_output=True ) return result.returncode == 0 except subprocess.CalledProcessError as e: logger.error(f"Erreur ImageMagick: {e.stderr.decode() if e.stderr else str(e)}") return False except Exception as e: logger.error(f"Erreur lors de la conversion avec ImageMagick: {str(e)}") return False def convert_raw_to_jpg(input_path: str, output_path: str) -> bool: """ Convertit un fichier RAW (DNG, CR2, ARW, NEF, etc.) en JPG Args: input_path (str): Chemin du fichier RAW output_path (str): Chemin de sortie pour le fichier JPG Returns: bool: True si la conversion a réussi, False sinon """ try: # Essayer d'abord avec rawpy import rawpy import imageio with rawpy.imread(input_path) as raw: rgb = raw.postprocess(use_camera_wb=True, half_size=False, no_auto_bright=False) imageio.imsave(output_path, rgb) return True except ImportError: logger.warning("rawpy n'est pas installé, utilisation de la méthode alternative") return _convert_with_imagemagick(input_path, output_path) except Exception as e: logger.error(f"Erreur lors de la conversion RAW: {str(e)}") return _convert_with_imagemagick(input_path, output_path) def convert_file(input_path: str, output_dir: str = None) -> Tuple[bool, Optional[str], Optional[str]]: """ Convertit un fichier d'un format moderne vers un format standard Args: input_path (str): Chemin du fichier à convertir output_dir (str, optional): Répertoire de sortie pour le fichier converti Si non spécifié, utilise le même répertoire que le fichier d'entrée Returns: Tuple[bool, Optional[str], Optional[str]]: - Succès de la conversion (bool) - Chemin du fichier converti (str ou None si échec) - Message d'erreur (str ou None si succès) """ input_path = Path(input_path) # Vérifier si le fichier existe if not input_path.exists(): return False, None, f"Le fichier {input_path} n'existe pas" # Obtenir l'extension du fichier ext = input_path.suffix.lower().lstrip(".") # Si l'extension est déjà dans les formats autorisés, pas besoin de conversion if ext in settings.ALLOWED_EXTENSIONS: return True, str(input_path), None # Vérifier si le format est pris en charge if ext not in MODERN_FORMATS: return False, None, f"Format non pris en charge: {ext}" # Déterminer le format de sortie output_ext = FORMAT_MAPPING.get(ext, "jpg") # Déterminer le chemin de sortie if output_dir: output_dir = Path(output_dir) output_dir.mkdir(parents=True, exist_ok=True) output_path = output_dir / f"{input_path.stem}.{output_ext}" else: output_path = input_path.with_suffix(f".{output_ext}") # Effectuer la conversion en fonction du format success = False if ext in ["heic", "heif"]: success = convert_heic_to_jpg(str(input_path), str(output_path)) elif ext == "webp": success = convert_webp_to_png(str(input_path), str(output_path)) elif ext in ["dng", "cr2", "arw", "nef", "raw"]: success = convert_raw_to_jpg(str(input_path), str(output_path)) else: # Fallback sur ImageMagick pour les autres formats success = _convert_with_imagemagick(str(input_path), str(output_path)) if success: return True, str(output_path), None else: return False, None, f"Échec de la conversion du fichier {input_path}"