Conversion d’image avec Python multiprocessing

Face à la nécessité de redimensionner et de convertir une multitude d’images, un problème récurrent se pose : la plupart des applications ne permettent que le traitement séquentiel des fichiers en file d’attente. Malgré la puissance de calcul de mon processeur, cette tâche s’annonce fastidieuse. Dans mon cas, près de 4000 fichiers, chacun dépassant les 20 Mo, nécessitent une transformation. En explorant les possibilités d’optimisation, j’ai découvert la piste du multiprocessing.

Un cœur de processeur est limité à exécuter une instruction à la fois. Le multiprocessing offre la possibilité de paralléliser le travail sur différents cœurs, permettant ainsi à chaque cœur de traiter une image simultanément. A une époque où les processeurs peuvent proposer plus de 10 cœurs, le gain de temps devient rapidement conséquent.

Python – Pillow – Multiprocessing

Contexte du code ci-dessous :

  • Réduction de la taille des images à une largeur ne dépassant pas les 3000px – resolution=3000
  • Conversion des images au format TIF une fois redimensionnées au format PNG
  • Traitement parallélisé de la file d’attente sur au maximum 7 cœurs – Pool(min(cpu_count(), 7)).
  • Par défaut il y a une limitation sur la taille maximum des images traitées. Les images étant de « confiance » pour une exécution locale, j’ai supprimé la limite en utilisant : Image.MAX_IMAGE_PIXELS = None
  • En bonus : mise en place de la barre de progression de la librairie alive_progress qui permet de suivre l’avancée du travail et d’avoir une estimation du temps restant jusqu’à la fin (temps estimé sur la base du travail accompli)

C’est partie pour le code

import os
from PIL import Image
from alive_progress import alive_bar
from multiprocessing import Pool, cpu_count

source_dir = "chemin du répertoire source"
destination_dir = "chemin du répertoire destination"

def process_file(args):
    root, file, destination_dir, resolution = args
    try:
        Image.MAX_IMAGE_PIXELS = None

        
        source_file_path = os.path.join(root, file)
        relative_path = os.path.relpath(root, source_dir)
        destination_path = os.path.join(destination_dir, relative_path)
        if not os.path.exists(destination_path):
            os.makedirs(destination_path)
        
        filename, file_extension = os.path.splitext(file)
        destination_file_path = os.path.join(destination_path, f"{filename}.png")
        with Image.open(source_file_path) as img:
            img = img.convert('RGB')
            img.thumbnail((resolution, resolution))
            img.save(destination_file_path, dpi=(resolution, resolution))
            return ("👉 Copie de " + filename + " 	█ dans 	█ " + destination_path)
            
            
    except Exception as e:
        print(f"Erreur lors du traitement du fichier {filename}: {e}")

def reduire_resolution_et_sauvegarder_en_png(source_dir, destination_dir, resolution):
    try:
        

        files_to_process = []
        for root, dirs, files in os.walk(source_dir):
            for file in files:
                if file.lower().endswith('.tif'):
                    files_to_process.append((root, file))

        with Pool(min(cpu_count(), 7)) as p:
            args_list = [(root, file, destination_dir, resolution) for root, file in files_to_process]
            
            with alive_bar(len(files_to_process), title="⏳ Traitement des fichiers") as bar:
                for _ in p.imap_unordered(process_file, args_list):
                    
                    bar()
                    if _ is not None:
                       print (_)
                    

        
    except Exception as e:
        print(f"Erreur lors de la copie des fichiers .tif de {source_dir} vers {destination_dir}: {e}")



if __name__ == '__main__':

    # Exemple d'utilisation

    reduire_resolution_et_sauvegarder_en_png(source_dir, destination_dir, resolution=3000)

Publié

dans

par

Étiquettes :

Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.