Создание миниатюр изображений с помощью пакета PIL

sozdanie miniatjur izobrazhenij s pomoshhju paketa pil Экскурсия по tkinter, часть 1

Как уже упоминалось, пакет PIL позволяет не только отображать картинки в графическом интерфейсе, но и выполнять массу других операций над ними. В его составе имеются инструменты для изменения размеров изображений, преобразования из одного формата в другой и так далее. Один из таких инструментов предоставляет возможность генерировать «миниатюры» изображений из оригиналов. Такие миниатюры могут отображаться в веб-страницах или в инструментах выбора графического интерфейса, дающих пользователю возможность открывать полноразмерные изображения.

В примере 8.45 приводится конкретная реализация этой идеи — он генерирует миниатюры изображений, используя PIL, и отображает их на кнопках, которые открывают соответствующие оригинальные картинки в случае щелчка мышью. В результате получается графический интерфейс, очень напоминающий интерфейс проводников по файловой системе, ставший стандартом в современных операционных системах.

Но данный интерфейс реализован на языке Python, что дает нам полный контроль над его поведением и возможность повторного использования в наших приложениях. Фактически мы повторно будем использовать функцию makeThumbs, уже реализованную в других примерах. Как обычно, этот пример демонстрирует некоторые основные преимущества, характерные для открытого программного обеспечения.

Пример 8.45. PP4E\Gui\PIL\viewer_thumbs.py

выводит все изображения, имеющиеся в каталоге, в виде миниатюр на кнопках, щелчок на которых приводит к выводу полноразмерного изображения; требует наличия пакета PIL для отображения JPEG-файлов и создания миниатюр; что сделать: добавить прокрутку, если в окне выводится слишком много миниатюр!

import os, sys, math

from tkinter import *

from PIL import Image # <== required for thumbs

from PIL.ImageTk import PhotoImage # <== required for JPEG display

def makeThumbs(imgdir, size=(100, 100), subdir=’thumbs’):

создает миниатюры для всех изображений в каталоге; для каждого изображения создается и сохраняется новая миниатюра или загружается существующая;

при необходимости создает каталог thumb;

возвращает список кортежей (имя_файла_изображения, объект_миниатюры);

для получения списка файлов миниатюр вызывающая программа может также воспользоваться функцией listdir в каталоге thumb; для неподдерживаемых типов файлов может возбуждать исключение IOError, или другое;

ВНИМАНИЕ: можно было бы проверять время создания файлов;

thumbdir = os.path.join(imgdir, subdir)

if not os.path.exists(thumbdir):

os.mkdir(thumbdir)

thumbs = []

for imgfile in os.listdir(imgdir):

thumbpath = os.path.join(thumbdir, imgfile)

if os.path.exists(thumbpath):

thumbobj = Image.open(thumbpath) # использовать существующую thumbs.append((imgfile, thumbobj))

else:

print(‘making’, thumbpath)

imgpath = os.path.join(imgdir, imgfile)

try:

imgobj = Image.open(imgpath) # создать новую миниатюру imgobj.thumbnail(size, Image.ANTIALIAS) # фильтр, дающий

#   лучшее качество при

#   уменьшении размеров

imgobj.save(thumbpath) # тип определяется расширением thumbs.append((imgfile, imgobj))

except: # не всегда IOError

print(“Skipping: “, imgpath) return thumbs

class ViewOne(Toplevel):

открывает одно изображение в новом окне; ссылку на объект PhotoImage требуется сохранить: изображение будет утрачено при утилизации объекта;

def __init__(self, imgdir, imgfile):

Toplevel.__init__(self)

self.title(imgfile)

imgpath = os.path.join(imgdir, imgfile)

imgobj = PhotoImage(file=imgpath)

Label(self, image=imgobj).pack()

print(imgpath, imgobj.width(), imgobj.height()) # размер в пикселях self.savephoto = imgobj # сохранить ссылку

# на изображение

def viewer(imgdir, kind=Toplevel, cols=None):

создает окно с миниатюрами для каталога с изображениями: по одной кнопке с миниатюрой для каждого изображения;

используйте параметр kind=Tk, чтобы вывести миниатюры в главном окне, или Frame (чтобы прикрепить к фрейму); значение imgfile изменяется в каждой итерации цикла: ссылка на значение должна сохраняться по умолчанию;

объекты PhotoImage должны сохраняться: иначе при утилизации изображения будут уничтожены;

компонует в ряды фреймов (в противоположность сеткам, фиксированным размерам, холстам);

win = kind()

win.title(‘Viewer: ‘ + imgdir)

quit = Button(win, text=’Quit’, command=win.quit, bg=’beige’) # добавить quit.pack(fill=X, side=BOTTOM) # первой, чтобы урезалась последней thumbs = makeThumbs(imgdir) if not cols:

cols = int(math.ceil(math.sqrt(len(thumbs)))) # фиксированное или N x N

savephotos = [] while thumbs:

thumbsrow, thumbs = thumbs[:cols], thumbs[cols:]

row = Frame(win)

row.pack(fill=BOTH)

for (imgfile, imgobj) in thumbsrow:

photo = PhotoImage(imgobj)

link = Button(row, image=photo)

handler = lambda savefile=imgfile: ViewOne(imgdir, savefile)

link.config(command=handler)

link.pack(side=LEFT, expand=YES)

savephotos.append(photo) return win, savephotos

if __name__ == ‘__main__’:

imgdir = (len(sys.argv) > 1 and sys.argv[1]) or ‘images’ main, save = viewer(imgdir, kind=Tk) main.mainloop()

Просьба обратить внимание, что функция viewer передает значение imgfile lambda-выражению, генерирующему обработчик, в виде значения по умолчанию аргумента. Это объясняется тем, что imgfile является переменной цикла, и если не сохранять текущее ее значение, все обработчики получат значение переменной, установленное в последней итерации (все кнопки будут открывать одно и то же изображение!). Отметьте также, что мы сохраняем ссылки на объекты изображений в списке — если этого не сделать, изображения будут уничтожены при утилизации их объектов сборщиком мусора, даже если в этот момент они будут отображаться на экране. Чтобы избежать такой неприятности, мы сохраняем ссылки на объекты в долгоживущем списке.

На рис. 8.45 изображено главное окно с миниатюрами, доступными для выбора, созданное сценарием из примера 8.45 при попытке просмотра содержимого каталога по умолчанию images, находящегося в дереве примеров (размер окна был изменен при подготовке снимка с экрана для книги). Как и прежде, вы можете передать сценарию имя другого каталога, чтобы просмотреть его содержимое (например, имя каталога, где вы храните свои цифровые фотографии). Щелчок на кнопке с миниатюрой в главном окне открывает соответствующее изображение в новом окне, как показано на рис. 8.46.

На данный момент большая часть примера 8.45 должна быть вам понятна. В нем кнопки с миниатюрами прикрепляются к рядам фреймов, как и в предыдущих примерах (смотрите альтернативные варианты компоновки форм ввода, реализованные выше в этой главе). Основная часть программного кода, использующего пакет PIL, находится в функции makeThumbs. Он открывает, создает и сохраняет изображения миниатюр, если ранее они не были сохранены (то есть не были кэшированы) в локальных файлах. В данной реализации миниатюры сохраняются в том же формате, что и оригинальные, полноразмерные изображения.

Мы также использовали фильтр PIL ANTIALIAS, дающий наилучшее качество при уменьшении размеров изображения, что особенно заметно для изображений в формате GIF с низким разрешением. Создание миниатюры сводится, по сути, к изменению размеров с сохранением оригинальных пропорций. Из-за невозможности охватить все особенности здесь за дополнительными подробностями, касающимися API, я отсылаю вас к пакету PIL и документации по нему.

В следующей главе мы еще раз коротко вернемся к проблеме создания миниатюр, когда будем создавать кнопки для панели инструментов.

Рис. 8.45. Простой графический интерфейс выбора миниатюр, простые ряды фреймов

 

 

Рис. 8.46. Окно с полноразмерным изображением

Однако, прежде чем двинуться дальше, быстро познакомимся с тремя вариантами реализации отображения миниатюр — в первом из них делается акцент на оценке производительности, а в других двух будет улучшена, вероятно, не самая удачная компоновка миниатюр, изображенная на рис. 8.45.

Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011

Оцените статью
Секреты программирования
Добавить комментарий