Создание миниатюр строго заданного размера из изображений. Динамический метод.
Создание миниатюр - достаточно распостраненная задача, с которой неминуемо приходится сталкиваться тем, чей сайт или скрипт обрабатывают пользовательское изображение для создания его уменьшенной копии.
С подобной задачей пришлось столкнуться и мне. Причем исследование интернета по этому вопросу не дало четкого решения моей проблемы. Свой вариант решения я в конечном итоге нашел и в этой статье постараюсь в доступной форме изложить принципы, которые использовал сам.
Надеюсь, они окажутся полезными и Вам. Обязательно хочу оговориться, что мои размышления никоим образом не претендуют на "научность" или даже правильность, однако меня вполне устраивает результат работы кода, построенного по принципам, изложенным в статье.
Постановка задачи: необходимо уменьшить размеры любого изображения таким образом, чтобы полученная миниатюра имела СТРОГО ЗАДАННЫЕ размеры.
В моем случае такой вопрос возник при создании блока обработки пользовательских изображений в скрипте u-administrator. Давайте рассмотрим рисунки ниже, чтобы получить представление, о чем собственно идет речь.
Итак, перед нами исходное изображение и требуемая миниатюра.
Рис.1
Разумеется сейчас мы рассматриваем наиболее жесткий случай: одно из изображений имеет альбомное расположение, а другое-книжное.
В данной статье я не буду вдаваться в подробности стандартных методов уменьшения, мы постараемся рассмотреть все "на пальцах". Итак, мы можем получить в общем виде следующие варианты соотношений исходного изображения и миниатюры после уменьшения:
Рис.2
Несмотря на существование нескольких методов миниатюризации, трудно не согласиться, что в данном случае возможно всего лишь 2 принципиальных варианта. В первом варианте большее изображение "вписывается" в миниатюру целиком, однако часть поля миниатюры оказывается "пустой". Во втором варианте от него отсекаются какие-то части, чтобы полностью заполнить ВЕСЬ размер миниатюры.
Согласно моим "техническим условиям", размер миниатюры жестко задан, следовательно вариант №1 Рис.2 никак не подходит.
Вам наверное стало интересно, почему, собственно, в моем случае такие жесткие требования? Все дело в том, что согласно алгоритму блока изображений скрипта u-administrator, пользователь заранее задает ширину и высоту будущей миниатюры. Причем эти значения сразу закладываются в будущую строку <img...... width=... height=... >. Если размеры полученной при обработке миниатюры не будут соответствовать размерам width=... height=..., то на экране миниатюра будет растянута по горизонтали или вертикали.
"Какие проблемы", скажете Вы. Нужно наоборот: вначале сделать миниатюру, а потом уже ее фактические размеры использовать в строке <img...... width=... height=... >. Однако в этом случае пользователь скрипта, который задавал размеры будущей миниатюры исходя из дизайна своей страницы, получит кукиш: будет соблюден только один размер (ширина или высота).
Но хватит отступлений, вернемся к нашему методу обработки. Итак, мы условились что если пропорции исходного изображения и миниатюры значительно отличаются, то обрезки нам не избежать. А если незначительно отличаются? Тогда мы можем просто проигнорировать незначительные искажения. Как же мы можем заранее оценить пропорции? Давайте остановимся на этом моменте подробнее.
Рис.3
Давайте вначале найдем основные соотношения сторон исходного изображения и миниатюры.
wx_ratio = w / x
hy_ratio = h / y
Пояснения здесь не нужны. Мы получили 2 коэффициента. Где w,h - значения ширины и высоты исходного изображения (W,H), а x,y - соответственно миниатюры (X,Y).
Далее я ввожу коэффициент диспропорции k_dis. Этот коэффициент и будет определять, насколько отличаются пропорции исходного изображения и миниатюры. Чтобы этот коэффициент был всегда не меньше 1, нам необходимо сравнить wx_ratio и hy_ratio:
if wx_ratio >= hy_ratio
then k_dis = wx_ratio / hy_ratio
if wx_ratio < hy_ratio
then k_dis = hy_ratio / wx_ratio
Здесь тоже все понятно. Определяем большую величину и находим коэффициент диспропорции k_dis, который не меньше 1. Именно этот коэффициент и определяет реальную диспропорцию всех сторон изображений. Можно теперь условиться, что если диспропорция невелика, то нам нет необходимости ничего обрезать. Простое уменьшение исходного изображения в wx_ratio раз позволит нам получить желаемое. Примем максимальный допуск в 10%. Теперь можно считать, что если мы имеем k_dis <1.1, то перед нами простейший случай практически пропорциональных изображений и голову дальше ломать не нужно. А вот если
k_dis > 1.1, тогда есть все основания задуматься.
Теперь у нас появляется новая задача. Необходимо отрезать часть исходного изображения с наименьшим ущербом для информативности самого изображения.
Существуют определенные правила, которые позволяют в большинстве случаев "выдернуть" из изображения самую информативную его часть.
Рис.4
Как видим на Рис.5, в случае горизонтальной диспропорции, вырезается строго центральная часть, а в случае вертикальной -обрезка делается таким образом, чтобы треть от всего обрезаемогобыла сверху и 2/3 снизу.
Таким образом, мы имеем очень большие шансы, что самая значимая часть изображения будет сохранена, а обрезаны менее значимые участки. Обратите внимание: ключевым фактором для выбора способа обрезки служит именно диспропорция по горизонтали или вертикали!
Остается определить, как именно нам узнать, что диспропорция больше, например, по вертикали. Очень просто-мы все уже узнали, когда выполнили ранее эту строку:
if wx_ratio < hy_ratio
Именно выполнение этого условия и говорит нам, что диспропорция по вертикали больше. Следовательно, обрезать придется согласно Рис.4 вариант 2.
Соответственно если wx_ratio >= hy_ratio, то мы делаем вывод, что диспропорция по горизонтали больше и делаем обрезку согласно Рис.4 вариант 1.
Давайте теперь подведем итоги:
Общий алгоритм
wx_ratio = w / x
hy_ratio = h / y
if wx_ratio >= hy_ratio
then k_dis = wx_ratio / hy_ratio
else k_dis = hy_ratio / wx_ratio
endif
if k_dis <1.1
then [простое уменьшение изображения ]
else
if wx_ratio >= hy_ratio
then [обрезка согласно Рис.4 вариант 1 (горизонт.)]
else [обрезка согласно Рис.4 вариант 2 (вертик.)]
endif
endif
Вот мы и получили структуру скрипта, который будет сам динамически подбирать способ для обработки исходного изображения, чтобы получить миниатюру строго заданного размера.
Осталось рассмотреть всего один вопрос. Как именно произвести разметку и обрезку изображения. Стоящая перед нами задача достаточно проста. Необходимо таким образом изменить размеры миниатюры или исходного изображения, чтобы меньшее изображение оказалось по ширине или высоте полностью вписано в большее. Для этого мы уменьшаем большее, или увеличиваем меньшее.
Рис.5
Разумеется, делаем мы все это только в расчетах. А вот коэффициент, на который нужно умножить размеры миниатюры, чтобы вписать ее в исходное изображение, или разделить размеры исходного, чтобы вписалась миниатюра - нам уже известен. Помните, в самом начале мы нашли эти соотношения:
wx_ratio = w / x
hy_ratio = h / y
Наименьшее из этих значений и будет нашим коэффициентом. В этой статье мы рассмотрим способ с увеличением размеров миниатюры до размеров исходного изображения. Потом производится обрезка. И потом уже обрезанное изображение физически уменьшается до исходных размеров миниатюры.
Рис.6
А теперь рассмотрим формулы, выполняющие вышесказанное. Они очень просты.
new_x = round( x * min( wx_ratio, hy_ratio ) )
new_y = round( y * min( wx_ratio, hy_ratio ) )
Мы вычислили размеры согласно Рис.6 варианты №1,2
. Где функция min находит наименьшее из значений, а функция round округляет полученное значение до пиксела. Теперь несложно найти общую высоту обрезаемого участка
h_cut = h_src-new_y
и ее частей (сответственно 1/3 сверху и 2/3 снизу. Аналогично мы можем подсчитать общую ширину обрезаемого участка в случае горизонтальной диспропорции:
w_cut = w_src-new_x
Части под обрезку будут
w_cut/2
слева и справа.
Вот, пожалуй, и все.В этой статье я не привожу код для осуществления данного алгоритма. Дело в том, что практические реализации могут сильно отличаться в зависимости от Ваших задач. Однако, уверен, что Вам не составит труда переложить приведенный алгоритм на Ваш язык программирования, в случае, если Вы столкнулись с похожей проблемой. Очень надеюсь, что эта статья поможет Вам разобраться в сути проблемы, а предложенное мною решение окажется полезным и для Вас.
Протестировать работу алгоритма можно здесь: http://demo.u-administrator.com/index.php
03.01.09
Автор: Орлов Евгений
http://www.u-administrator.com
|