from PIL import Image, UnidentifiedImageError import os def get_pixel_color_as_rgb(image_obj, x=0, y=0): """ Получает цвет пикселя (x, y) и возвращает его как кортеж RGB. Обрабатывает различные режимы изображения. """ try: # Сначала обрабатываем режимы, которые не возвращают кортеж RGB(A) напрямую if image_obj.mode == 'L': # Оттенки серого gray_value = image_obj.getpixel((x, y)) return (gray_value, gray_value, gray_value) elif image_obj.mode == 'LA': # Оттенки серого с альфа-каналом gray_value, _ = image_obj.getpixel((x, y)) # Альфа не используется для цвета фона return (gray_value, gray_value, gray_value) elif image_obj.mode == 'P': # Для палитры конвертируем в RGBA, чтобы получить фактический цвет # (включая возможную прозрачность палитры), затем берем RGB. # getpixel() для 'P' возвращает индекс палитры. converted_img = image_obj.convert('RGBA') pixel_color_tuple = converted_img.getpixel((x, y)) elif image_obj.mode not in ('RGB', 'RGBA'): # Для других режимов (например, CMYK, YCbCr) конвертируем в RGB converted_img = image_obj.convert('RGB') pixel_color_tuple = converted_img.getpixel((x, y)) else: # Режимы RGB или RGBA pixel_color_tuple = image_obj.getpixel((x, y)) # pixel_color_tuple теперь должен быть кортежем (R,G,B) или (R,G,B,A) if isinstance(pixel_color_tuple, tuple): return pixel_color_tuple[:3] # Берем только RGB компоненты else: # Этого не должно произойти, если логика выше верна print(f"Предупреждение: Не удалось получить кортеж цвета для пикселя ({x},{y}) изображения (режим: {image_obj.mode}). Используется черный цвет (0,0,0).") return (0,0,0) except Exception as e: print(f"Предупреждение: Исключение при определении цвета пикселя ({x},{y}) для изображения (режим: {image_obj.mode}). Используется черный цвет (0,0,0). Ошибка: {e}") return (0, 0, 0) def resize_and_pad_image(image_path, output_path, target_width, target_height): """ Масштабирует изображение, чтобы оно поместилось в target_width x target_height, сохраняя пропорции. Оставшееся пространство заполняется цветом фона оригинального изображения (цвет пикселя в верхнем левом углу (0,0)). """ try: original_image = Image.open(image_path) except FileNotFoundError: print(f"Ошибка: Файл не найден по пути {image_path}") return False except UnidentifiedImageError: print(f"Ошибка: Не удалось идентифицировать файл как изображение: {image_path}") return False except Exception as e: print(f"Ошибка при открытии изображения {image_path}: {e}") return False # 1. Определить цвет фона (пиксель (0,0) исходного изображения) bg_color_rgb = get_pixel_color_as_rgb(original_image, 0, 0) original_width, original_height = original_image.size # 2. Масштабировать изображение с сохранением пропорций ratio_w = target_width / original_width ratio_h = target_height / original_height scale_ratio = min(ratio_w, ratio_h) new_internal_width = int(original_width * scale_ratio) new_internal_height = int(original_height * scale_ratio) try: resampling_filter = Image.Resampling.LANCZOS except AttributeError: # Для старых версий Pillow (< 9.1.0) resampling_filter = Image.ANTIALIAS resized_image = original_image.resize((new_internal_width, new_internal_height), resampling_filter) # 3. Создать новое изображение (холст) размером target_width x target_height, залитое цветом фона final_image = Image.new('RGB', (target_width, target_height), bg_color_rgb) # 4. Рассчитать позицию для вставки отмасштабированного изображения по центру холста x_offset = (target_width - new_internal_width) // 2 y_offset = (target_height - new_internal_height) // 2 # 5. Вставить отмасштабированное изображение на холст if resized_image.mode == 'RGBA' or resized_image.mode == 'LA': final_image.paste(resized_image, (x_offset, y_offset), resized_image) elif resized_image.mode == 'P' and 'transparency' in resized_image.info: # Для палитрных изображений с прозрачностью, конвертируем в RGBA для корректной вставки # и используем альфа-канал преобразованного изображения как маску converted_for_paste = resized_image.convert('RGBA') final_image.paste(converted_for_paste, (x_offset, y_offset), converted_for_paste) else: final_image.paste(resized_image, (x_offset, y_offset)) try: final_image.save(output_path) # print(f"Изображение '{os.path.basename(image_path)}' обработано и сохранено как '{output_path}'") return True except Exception as e: print(f"Ошибка при сохранении изображения {output_path}: {e}") return False def process_images_in_directory(input_dir, output_dir, target_width=300, target_height=250): """ Обрабатывает все изображения в input_dir, масштабирует их до target_width x target_height с заполнением фона и сохраняет в output_dir. """ if not os.path.isdir(input_dir): print(f"Ошибка: Входной каталог '{input_dir}' не найден.") return if not os.path.exists(output_dir): try: os.makedirs(output_dir) print(f"Создан выходной каталог: '{output_dir}'") except OSError as e: print(f"Ошибка: Не удалось создать выходной каталог '{output_dir}'. {e}") return supported_extensions = ('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff', '.webp') processed_count = 0 failed_count = 0 print(f"\nНачало обработки изображений из каталога: '{input_dir}'") print(f"Результаты будут сохранены в каталог: '{output_dir}'") print(f"Целевой размер: {target_width}x{target_height} пикселей.") for filename in os.listdir(input_dir): if filename.lower().endswith(supported_extensions): input_image_path = os.path.join(input_dir, filename) # Сохраняем с тем же именем в выходной каталог output_image_path = os.path.join(output_dir, filename) # Опционально: добавить префикс/суффикс, чтобы избежать перезаписи при одинаковых именах, # если выходной каталог совпадает с входным (не рекомендуется) # output_image_path = os.path.join(output_dir, f"processed_{filename}") # print(f"Обработка файла: {filename}...") if resize_and_pad_image(input_image_path, output_image_path, target_width, target_height): print(f"Успешно: {filename} -> {output_image_path}") processed_count += 1 else: print(f"Ошибка обработки файла: {filename}") failed_count += 1 # else: # print(f"Пропуск файла (неподдерживаемый тип): {filename}") print(f"\nОбработка завершена.") print(f"Всего успешно обработано изображений: {processed_count}") print(f"Не удалось обработать файлов: {failed_count}") # --- Пример использования --- if __name__ == '__main__': # 1. Укажите путь к каталогу с вашими исходными изображениями input_images_directory = r"C:\images\src\\" # 2. Укажите путь к каталогу, куда будут сохраняться обработанные изображения output_images_directory = r"C:\images\dst\\" # Создадим демонстрационные каталоги, если они не существуют # В реальном использовании убедитесь, что 'input_folder' существует и содержит изображения. if not os.path.exists(input_images_directory): os.makedirs(input_images_directory) print(f"Создан демонстрационный каталог для входных изображений: '{input_images_directory}'") print(f"Пожалуйста, поместите ваши изображения в этот каталог и запустите скрипт снова.") # Пример копирования тестового изображения, если оно есть рядом со скриптом # Замените 'YOUR_TEST_IMAGE.jpg' на имя вашего файла, если хотите протестировать # import shutil # test_image_filename = 'ChatGPT Image 5 июн. 2025 г., 12_21_52.jpg' # Имя файла, который вы загрузили # if os.path.exists(test_image_filename) and not os.path.exists(os.path.join(input_images_directory, test_image_filename)): # try: # shutil.copy(test_image_filename, input_images_directory) # print(f"Тестовое изображение '{test_image_filename}' скопировано в '{input_images_directory}' для примера.") # except Exception as e: # print(f"Не удалось скопировать тестовое изображение '{test_image_filename}': {e}") process_images_in_directory(input_images_directory, output_images_directory, 300, 250)