Как работать с типами файлов изображений HEIC в Python
Формат высокоэффективного файла изображения (HEIF) используется по умолчанию при передаче изображения с iPhone на устройство OSX. Я хочу редактировать и изменять эти файлы.HEIC с помощью Python.
Я мог бы изменить настройки телефона, чтобы сохранить их в формате JPG по умолчанию, но это не решает проблему возможности работать с типом файла от других. Я все еще хочу иметь возможность обрабатывать файлы HEIC для преобразования файлов, извлечения метаданных и т. Д. ( Пример использования - геокодирование)
подушка
Вот результат работы с Python 3.7 и Pillow при попытке прочитать файл этого типа.
$ ipython
Python 3.7.0 (default, Oct 2 2018, 09:20:07)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from PIL import Image
In [2]: img = Image.open('IMG_2292.HEIC')
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
<ipython-input-2-fe47106ce80b> in <module>
----> 1 img = Image.open('IMG_2292.HEIC')
~/.env/py3/lib/python3.7/site-packages/PIL/Image.py in open(fp, mode)
2685 warnings.warn(message)
2686 raise IOError("cannot identify image file %r"
-> 2687 % (filename if filename else fp))
2688
2689 #
OSError: cannot identify image file 'IMG_2292.HEIC'
Похоже, что поддержка в python-pillow была запрошена ( # 2806), но есть проблемы с лицензированием / патентами, препятствующие этому.
ImageMagick + Wand
Похоже, что ImageMagick может быть вариантом. После выполнения brew install imagemagick
а также pip install wand
Однако я был неудачным.
$ ipython
Python 3.7.0 (default, Oct 2 2018, 09:20:07)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from wand.image import Image
In [2]: with Image(filename='img.jpg') as img:
...: print(img.size)
...:
(4032, 3024)
In [3]: with Image(filename='img.HEIC') as img:
...: print(img.size)
...:
---------------------------------------------------------------------------
MissingDelegateError Traceback (most recent call last)
<ipython-input-3-9d6f58c40f95> in <module>
----> 1 with Image(filename='ces2.HEIC') as img:
2 print(img.size)
3
~/.env/py3/lib/python3.7/site-packages/wand/image.py in __init__(self, image, blob, file, filename, format, width, height, depth, background, resolution, pseudo)
4603 self.read(blob=blob, resolution=resolution)
4604 elif filename is not None:
-> 4605 self.read(filename=filename, resolution=resolution)
4606 # clear the wand format, otherwise any subsequent call to
4607 # MagickGetImageBlob will silently change the image to this
~/.env/py3/lib/python3.7/site-packages/wand/image.py in read(self, file, filename, blob, resolution)
4894 r = library.MagickReadImage(self.wand, filename)
4895 if not r:
-> 4896 self.raise_exception()
4897
4898 def save(self, file=None, filename=None):
~/.env/py3/lib/python3.7/site-packages/wand/resource.py in raise_exception(self, stacklevel)
220 warnings.warn(e, stacklevel=stacklevel + 1)
221 elif isinstance(e, Exception):
--> 222 raise e
223
224 def __enter__(self):
MissingDelegateError: no decode delegate for this image format `HEIC' @ error/constitute.c/ReadImage/556
Есть ли другие альтернативы, чтобы сделать преобразование программно?
15 ответов
Рассмотрите возможность использования PIL в сочетании с Pillow-Heif :
pip3 installl pillow-heif
from PIL import Image
from pillow_heif import register_heif_opener
register_heif_opener()
image = Image.open('image.heic')
Ребята, вы должны проверить эту библиотеку, это обертка Python 3 для библиотеки libheif, она должна служить вашей цели преобразования файлов, извлечения метаданных:
https://github.com/david-poirier-csn/pyheif
https://pypi.org/project/pyheif/
Пример использования:
import whatimage
import pyheif
from PIL import Image
def decodeImage(bytesIo):
fmt = whatimage.identify_image(bytesIo)
if fmt in ['heic', 'avif']:
i = pyheif.read_heif(bytesIo)
# Extract metadata etc
for metadata in i.metadata or []:
if metadata['type']=='Exif':
# do whatever
# Convert to other file format like jpeg
s = io.BytesIO()
pi = Image.frombytes(
mode=i.mode, size=i.size, data=i.data)
pi.save(s, format="jpeg")
...
Я был весьма успешен с пакетом Wand: Установите Wand:https://docs.wand-py.org/en/0.6.4/ Код для преобразования:
from wand.image import Image
import os
SourceFolder="K:/HeicFolder"
TargetFolder="K:/JpgFolder"
for file in os.listdir(SourceFolder):
SourceFile=SourceFolder + "/" + file
TargetFile=TargetFolder + "/" + file.replace(".HEIC",".JPG")
img=Image(filename=SourceFile)
img.format='jpg'
img.save(filename=TargetFile)
img.close()
Вот еще одно решение для преобразования в
jpg
при сохранении метаданных нетронутыми. Он основан на
mara004
решение выше, однако я не смог извлечь временную метку изображения таким образом, поэтому мне пришлось добавить некоторый код. Положите
heic
файлы в
dir_of_interest
перед применением функции:
import os
from PIL import Image, ExifTags
from pillow_heif import register_heif_opener
from datetime import datetime
import piexif
import re
register_heif_opener()
def convert_heic_to_jpeg(dir_of_interest):
filenames = os.listdir(dir_of_interest)
filenames_matched = [re.search("\.HEIC$|\.heic$", filename) for filename in filenames]
# Extract files of interest
HEIC_files = []
for index, filename in enumerate(filenames_matched):
if filename:
HEIC_files.append(filenames[index])
# Convert files to jpg while keeping the timestamp
for filename in HEIC_files:
image = Image.open(dir_of_interest + "/" + filename)
image_exif = image.getexif()
if image_exif:
# Make a map with tag names and grab the datetime
exif = { ExifTags.TAGS[k]: v for k, v in image_exif.items() if k in ExifTags.TAGS and type(v) is not bytes }
date = datetime.strptime(exif['DateTime'], '%Y:%m:%d %H:%M:%S')
# Load exif data via piexif
exif_dict = piexif.load(image.info["exif"])
# Update exif data with orientation and datetime
exif_dict["0th"][piexif.ImageIFD.DateTime] = date.strftime("%Y:%m:%d %H:%M:%S")
exif_dict["0th"][piexif.ImageIFD.Orientation] = 1
exif_bytes = piexif.dump(exif_dict)
# Save image as jpeg
image.save(dir_of_interest + "/" + os.path.splitext(filename)[0] + ".jpg", "jpeg", exif= exif_bytes)
else:
print(f"Unable to get exif data for {filename}")
Добавляя к ответу Дэниала, мне просто пришлось немного изменить массив байтов, чтобы получить действительный поток данных для дальнейшей работы. Первые 6 байтов - это "Exif\x00\x00". Если отбросить их, вы получите исходный формат, который можно передать в любой инструмент обработки изображений.
import pyheif
import PIL
import exifread
def read_heic(path: str):
with open(path, 'rb') as file:
image = pyheif.read_heif(file)
for metadata in image.metadata or []:
if metadata['type'] == 'Exif':
fstream = io.BytesIO(metadata['data'][6:])
# now just convert to jpeg
pi = PIL.Image.open(fstream)
pi.save("file.jpg", "JPEG")
# or do EXIF processing with exifread
tags = exifread.process_file(fstream)
По крайней мере, у меня это сработало.
Вы можете использовать
pillow_heif
библиотека для чтения изображений HEIF способом, совместимым с PIL.
В приведенном ниже примере будет импортировано изображение HEIF и сохранено в
png
формат.
from PIL import Image
import pillow_heif
heif_file = pillow_heif.read_heif("HEIC_file.HEIC")
image = Image.frombytes(
heif_file.mode,
heif_file.size,
heif_file.data,
"raw",
)
image.save("./picture_name.png", format="png")
Это позволит получить данные exif из файла heic
import pyheif
import exifread
import io
heif_file = pyheif.read_heif("file.heic")
for metadata in heif_file.metadata:
if metadata['type'] == 'Exif':
fstream = io.BytesIO(metadata['data'][6:])
exifdata = exifread.process_file(fstream,details=False)
# example to get device model from heic file
model = str(exifdata.get("Image Model"))
print(model)
Начиная с версии, это становится намного проще.
Сохранение 8/10/12-битных файлов HEIF в 8/16-битный PNG с использованием OpenCV:
import numpy as np
import cv2
from pillow_heif import open_heif
heif_file = open_heif("images/rgb12.heif", convert_hdr_to_8bit=False, bgr_mode=True)
np_array = np.asarray(heif_file)
cv2.imwrite("image.png", np_array)
Для версий <0.10.0
Пример работы с битовыми файлами HEIF HDR (10/12) с использованием OpenCV и подушки-heif:
import numpy as np
import cv2
import pillow_heif
heif_file = pillow_heif.open_heif("images/rgb12.heif", convert_hdr_to_8bit=False)
heif_file.convert_to("BGRA;16" if heif_file.has_alpha else "BGR;16")
np_array = np.asarray(heif_file)
cv2.imwrite("rgb16.png", np_array)
Входной файл для этого примера может быть 10- или 12-битным файлом.
Работает отлично... (даже в Windows)
import glob
from PIL import Image
from pillow_heif import register_heif_opener
register_heif_opener()
for heic_pic_name in glob.glob("*.heic"): #searching .heic images in existing folder
my_pic = Image.open(heic_pic_name) #opening .heic images
jpg_pic_name = heic_pic_name.split('.')[0]+'.jpg' #creating new names for .jpg images
my_pic.save(jpg_pic_name, format="JPEG", optimize = True, quality = 100) #saving
Просто на заметку: сегодня был первый выпуск подушки , поддерживающей 64-битные окна.
Теперь он поддерживает почти все платформы, кроме Windows Arm и 32-битных систем.
В этой теме два человека показали его основное использование.
Я сталкиваюсь с той же проблемой, что и вы, желая решения CLI. Делая дальнейшие исследования, кажется, ImageMagick требует libheif
библиотека делегатов. Сама библиотека libheif, похоже, также имеет некоторые зависимости.
У меня не было успеха в том, чтобы заставить кого-то из них работать, но я буду продолжать пытаться. Я предлагаю вам проверить, доступны ли эти зависимости для вашей конфигурации.
первый ответ работает, но поскольку он просто вызывает save с объектом BytesIO в качестве аргумента, он фактически не сохраняет новый файл jpeg, но если вы создаете новый объект File с
import whatimage
import pyheif
from PIL import Image
def decodeImage(bytesIo):
fmt = whatimage.identify_image(bytesIo)
if fmt in ['heic', 'avif']:
i = pyheif.read_heif(bytesIo)
# Convert to other file format like jpeg
s = open('my-new-image.jpg', mode='w')
pi = Image.frombytes(
mode=i.mode, size=i.size, data=i.data)
pi.save(s, format="jpeg")
Я использую библиотеку подушки_heif. Например, я использую этот скрипт, когда у меня есть папка с файлами HEIF, которые я хочу преобразовать в png.
from PIL import Image
import pillow_heif
import os
from tqdm import tqdm
import argparse
def get_images(heic_folder):
# Get all the heic images in the folder
imgs = [os.path.join(heic_folder, f) for f in os.listdir(heic_folder) if f.endswith('.HEIC')]
# Name of the folder where the png files will be stored
png_folder = heic_folder + "_png"
# If it doesn't exist, create the folder
if not os.path.exists(png_folder):
os.mkdir(png_folder)
for img in tqdm(imgs):
heif_file = pillow_heif.read_heif(img)
image = Image.frombytes(
heif_file.mode,
heif_file.size,
heif_file.data,
"raw",
)
image.save(os.path.join(png_folder,os.path.basename(img).split('.')[0])+'.png', format("png"))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Convert heic images to png')
parser.add_argument('heic_folder', type=str, help='Folder with heic images')
args = parser.parse_args()
get_images(args.heic_folder)
Похоже, что есть решение под названием heic-to-jpg, но я не очень уверен, как это будет работать в colab.
Простое решение после просмотра нескольких ответов от людей.
Пожалуйста, установите
whatimage
,
pyheif
а также
PIL
библиотеки перед запуском этого кода.
[ПРИМЕЧАНИЕ]: я использовал эту команду для установки.
python3 -m pip install Pillow
Кроме того, с помощью Linux было намного проще установить все эти библиотеки. Я рекомендую
WSL
для окон.
- код
import whatimage
import pyheif
from PIL import Image
import os
def decodeImage(bytesIo, index):
with open(bytesIo, 'rb') as f:
data = f.read()
fmt = whatimage.identify_image(data)
if fmt in ['heic', 'avif']:
i = pyheif.read_heif(data)
pi = Image.frombytes(mode=i.mode, size=i.size, data=i.data)
pi.save("new" + str(index) + ".jpg", format="jpeg")
# For my use I had my python file inside the same folder as the heic files
source = "./"
for index,file in enumerate(os.listdir(source)):
decodeImage(file, index)