Запись изображений после обработки: инструменты Python
Сохранение изображений в Python: основные методы
При работе с графическими данными на языке Python часто возникает необходимость записать результат обработки в файл. Существует несколько способов, каждый из которых подходит для определённых сценариев. Рассмотрим наиболее распространённые библиотеки и приёмы.
Основной и наиболее производительный способ: функция cv2.imwrite() из библиотеки OpenCV
OpenCV оптимизирован для работы с массивами NumPy и поддерживает множество форматов (PNG, JPEG, BMP, WebP, TIFF и другие). Функция imwrite() ожидает имя файла и массив данных. Возвращает True при успешной записи.
import cv2
import numpy as np
# Создание тестового изображения (жёлтый квадрат 200x200)
img = np.zeros((200, 200, 3), dtype=np.uint8)
img[:,:] = [0, 255, 255] # BGR: жёлтый
# Сохранение в файл
success = cv2.imwrite('yellow_square.png', img)
if not success:
print('Ошибка записи')
else:
print('Файл yellow_square.png создан')
Python image save (сохранить изображение в python)
Параметры качества можно передать через список в третьем аргументе, например: cv2.imwrite('photo.jpg', img, [cv2.IMWRITE_JPEG_QUALITY, 90]).
Типичные ошибки при использовании cv2.imwrite:
- Если указанная директория не существует, файл не создаётся, а функция возвращает False. Следует предварительно проверить наличие папки или создать её с помощью os.makedirs().
- Изображение должно быть 8-битным (dtype uint8) для большинства форматов. Если данные имеют тип float, их необходимо привести к диапазону 0-255 и преобразовать в uint8.
- OpenCV использует порядок каналов BGR, тогда как многие библиотеки (например, PIL) используют RGB. При сохранении изображения, полученного из другого источника, может потребоваться конвертация cv2.cvtColor(img, cv2.COLOR_RGB2BGR).
- Для форматов с альфа-каналом (PNG) массив должен иметь 4 канала (BGRA).
Как сохранить изображение с помощью библиотеки Pillow (PIL)?
Pillow предоставляет интуитивно понятный метод save() у объекта Image. Формат определяется по расширению файла. Поддерживаются все популярные растровые форматы.
from PIL import Image
# Создание синего изображения 150x150
img = Image.new('RGB', (150, 150), (0, 0, 255))
img.save('blue.png')
print('Изображение blue.png сохранено')
Дополнительные параметры передаются по ключу: img.save('photo.jpg', quality=80). Для PNG можно указать optimize=True для уменьшения размера.
Возможные проблемы:
- Если файл заблокирован другой программой, возникнет ошибка PermissionError. Необходимо закрыть файл в других приложениях.
- При сохранении изображений с альфа-каналом режим изображения должен быть 'RGBA' или 'PA', иначе прозрачность не сохранится.
Как задать уровень сжатия и качество при сохранении?
Наиболее часто настройка качества требуется для форматов JPEG и WebP. В Pillow используется параметр quality (0-100, по умолчанию 75). В OpenCV параметр передаётся через список флагов.
# Пример с PIL – сохранение JPEG с качеством 85
img_pil.save('compressed.jpg', quality=85)
# Пример с OpenCV – качество 90
cv2.imwrite('compressed.jpg', img_cv, [cv2.IMWRITE_JPEG_QUALITY, 90])
Для формата WebP доступны параметры lossless (True/False) и quality (0-100). В OpenCV: cv2.imwrite('img.webp', img_cv, [cv2.IMWRITE_WEBP_QUALITY, 80]).
Рекомендации:
- Для JPEG качество ниже 50 приводит к заметным артефактам. Для фотографий рекомендуется 85-95.
- Для PNG сжатие не теряет качество, но можно использовать optimize=True для уменьшения размера.
Как сохранить изображение с альфа-каналом (прозрачностью)?
Для сохранения прозрачности в PNG изображение должно содержать альфа-канал. В Pillow достаточно создать изображение в режиме 'RGBA' и сохранить как PNG. OpenCV требует массив BGRA (4 канала).
# Pillow: RGBA
img_rgba = Image.new('RGBA', (100, 100), (255, 0, 0, 127)) # красный полупрозрачный
img_rgba.save('semi_red.png')
# OpenCV: BGRA
img_bgra = np.zeros((100, 100, 4), dtype=np.uint8)
img_bgra[:,:,:3] = [0, 0, 255] # BGR – синий
img_bgra[:,:,3] = 128 # альфа-канал
cv2.imwrite('semi_blue.png', img_bgra)
Внимание:
- Если сохранить RGBA изображение из OpenCV без четвёртого канала, белая заливка заменит прозрачные области.
- Некоторые форматы (JPEG) не поддерживают альфа-канал; при попытке сохранения PNG с прозрачностью в JPEG канал будет отброшен.
Как сохранить изображение в байтовый поток (BytesIO) без создания файла?
Это удобно для отправки изображения по сети или встраивания в HTML-код без записи на диск. Используется объект BytesIO из модуля io.
from PIL import Image
from io import BytesIO
img = Image.new('RGB', (50, 50), 'purple')
buffer = BytesIO()
img.save(buffer, format='PNG')
data = buffer.getvalue()
print(f'Размер буфера: {len(data)} байт')
Для OpenCV аналогично: ret, buf = cv2.imencode('.png', img_cv); data = buf.tobytes().
Ошибка:
- При использовании imencode() необходимо указать расширение с точкой, например '.jpg'. Если расширение не поддерживается, функция вернёт False.
Как сохранить массив NumPy в виде сырых данных (без формата изображения)?
Для научных или промежуточных данных, когда не требуется декодирование, можно сохранить массив в формате .npy. Это быстро и не приводит к потерям.
import numpy as np
data = np.random.randint(0, 256, (300, 400, 3), dtype=np.uint8)
np.save('raw_data.npy', data)
loaded = np.load('raw_data.npy')
Такой файл не является изображением и открывается только средствами NumPy. Для визуализации требуется пересохранить в один из графических форматов.
Как сохранить изображение с метаданными (например, EXIF) в формате TIFF?
Библиотека tifffile позволяет указывать метаданные при записи TIFF-файлов. Это полезно для хранения дополнительной информации о снимке.
import tifffile
import numpy as np
img = np.ones((100, 100, 3), dtype=np.uint8) * 200
tifffile.imwrite('with_metadata.tif', img,
metadata={'ImageDescription': 'Тестовый снимок',
'Software': 'Python'})
Примечание:
- Для установки tifffile используйте pip install tifffile.
- Метаданные могут не читаться старыми программами, не поддерживающими TIFF-теги.
Расширенные примеры сохранения изображений
1. Пакетное сохранение нескольких изображений
import cv2
import numpy as np
# Генерируем список изображений
images = []
for i in range(5):
img = np.zeros((100, 100, 3), dtype=np.uint8)
img[:,:,0] = i * 50 # меняем синий канал
images.append(img)
# Сохраняем каждое с уникальным именем
for idx, img in enumerate(images):
filename = f'image_{idx}.jpg'
cv2.imwrite(filename, img)
print(f'{filename} сохранён')
Результат:
Созданы файлы image_0.jpg, image_1.jpg, ..., image_4.jpg с различными оттенками синего.
2. Сохранение после применения фильтра Гаусса
import cv2
import numpy as np
img = np.random.randint(0, 256, (200, 200, 3), dtype=np.uint8)
blurred = cv2.GaussianBlur(img, (5, 5), 1.5)
cv2.imwrite('blurred_example.png', blurred)
print('Размытое изображение сохранено')
Результат:
Файл blurred_example.png с размытым изображением.
3. Сохранение WebP с lossless сжатием
import cv2
import numpy as np
img = cv2.imread('input_image.jpg') # предположим, что такой файл есть
if img is not None:
cv2.imwrite('output_lossless.webp', img, [cv2.IMWRITE_WEBP_QUALITY, 100])
print('WebP без потерь сохранён')
else:
print('Изображение не найдено')
Результат:
Файл output_lossless.webp с максимально возможным качеством.
4. Сохранение в память и загрузка обратно
from PIL import Image
from io import BytesIO
img = Image.new('RGB', (64, 64), 'green')
buf = BytesIO()
img.save(buf, format='PNG')
buf.seek(0)
img_loaded = Image.open(buf)
print(f'Изображение загружено из памяти: {img_loaded.size}')
Результат:
Изображение загружено из памяти: (64, 64)
5. Сохранение с использованием scikit-image
from skimage import io, data
img = data.coffee() # загружаем тестовое изображение
io.imsave('coffee_copy.png', img)
print('Файл coffee_copy.png создан')
Результат:
Файл coffee_copy.png с копией тестового изображения.
6. Сохранение изображения с плавающей точкой в формат HDR (EXR)
import imageio
import numpy as np
hdr_img = np.random.uniform(0, 1.0, (100, 100, 3)).astype(np.float32)
imageio.imwrite('hdr_output.exr', hdr_img)
print('HDR изображение сохранено')
Результат:
Файл hdr_output.exr, содержащий данные с плавающей точкой.
7. Сохранение grayscale изображения с применением цветовой карты (colormap)
import matplotlib.pyplot as plt
import numpy as np
gray = np.linspace(0, 1, 256).reshape(16, 16)
colored = plt.cm.viridis(gray)
plt.imsave('colormap_output.png', colored)
print('Изображение с цветовой картой сохранено')
Результат:
Файл colormap_output.png с псевдоцветами на основе карты viridis.
8. Сохранение анимированного GIF с помощью imageio
import imageio
import numpy as np
frames = []
for i in range(10):
frame = np.zeros((50, 50, 3), dtype=np.uint8)
frame[i*5:i*5+10, 20:30] = [255, 0, 0]
frames.append(frame)
imageio.mimsave('animation.gif', frames, duration=0.2)
print('GIF анимация создана')
Результат:
Файл animation.gif, содержащий 10 кадров с движущимся красным прямоугольником.