238 lines
7.6 KiB
Python
238 lines
7.6 KiB
Python
"""
|
|
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}" |