PHP

PHP: пропорциональное уменьшение изображения

Фух. Наконец-то появилось свободное время — завершил все заказы по верстке, написал с нуля небольшой веб сервис (о нём я расскажу позже. В конце августа или в начале сентября), а теперь напишу небольшой урок по php. Хотя в дальнейшем я буду публиковать новые уроки по программированию. Буду рассматривать свои «старые» скрипты, а также делиться опытом в «прямом эфире» — т.е. научился чему-либо, попробую научить и вас.

Сегодня я разберу скрипт своего старого музыкального портала, который я написал в далёком 2005 году, когда ещё никто и не мечтал о верстке дивами и все верстали таблицами. =) Тогда наверно и не существовало html-верстальщиков — ибо один человек мог написать с нуля статическую домашнюю страничку и гордиться этим. Следовательно, мой сайт был сверстан таблицами и у меня появилась проблема. Поскольку любой пользователь мог добавить в галерею сайта фотографии, то фотографий в галерее было много и они были разного размера, но фотогалерея сайта была задумана так, чтобы фотографии размещались по 4 штуки на строку и было 5 строк на странице. Естественно, в полный размер фото бы не уместились и появился бы горизонтальный скролл. Тогда я решил написать скрипт, который при загрузке фото на сервер, создавал бы уменьшенную копию фотографии с шириной 150 пикселей, не теряя при этом пропорций фотографии. Забыл сказать, данный скрипт расчитан на jpg изображения т.к. подавляющее большинство фотографий делаются в этом формате.

Ниже представлен весь необходимый нам, для выполнения нашей задачи, код. Приступаю к комментированию.

<?php
//Определяем размер фотографии — ширину и высоту
$size=GetImageSize ("photo.jpg");
//Создаём новое изображение из «старого»
$src=ImageCreateFromJPEG ("photo.jpg");
//Берём числовое значение ширины фотографии, которое мы получили в первой строке и записываем это число в переменную
$iw=$size[0];
//Проделываем ту же операцию, что и в предыдущей строке, но только уже с высотой.
$ih=$size[1];
//Ширину фотографии делим на 150 т.к. на выходе мы хотим получить фото шириной в 150 пикселей. В результате получаем коэфициент соотношения ширины оригинала с будущей превьюшкой.
$koe=$iw/150;
//Делим высоту изображения на коэфициент, полученный в предыдущей строке, и округляем число до целого в большую сторону — в результате получаем высоту нового изображения.
$new_h=ceil ($ih/$koe);
//Создаём пустое изображение шириной в 150 пикселей и высотой, которую мы вычислили в предыдущей строке.
$dst=ImageCreateTrueColor (150, $new_h);
//Данная функция копирует прямоугольную часть изображения в другое изображение, плавно интерполируя пикселные значения таким образом, что, в частности, уменьшение размера изображения сохранит его чёткость и яркость.
ImageCopyResampled ($dst, $src, 0, 0, 0, 0, 150, $new_h, $iw, $ih);
//Сохраняем полученное изображение в формате JPG
ImageJPEG ($dst, "small_photo.jpg", 100);
imagedestroy($src);
?>

Пример работы скрипта вы можете посмотреть здесь. Исходное изображение находится тут. Если хотите, то можете уменьшить любое другое изображение. Для этого передайте скрипту адрес изображения в виде http://www.zhitenev.ru/demo/img-resize/?photo=http://site.ru/1.jpg, где «http://site.ru/1.jpg» — путь к изображению.

Вот и всё. Для решения задачи нам потребовалось всего лишь 9 строк кода. Удачного вам кодинга и работающего кода. До следующего поста!

  • А можно примерчик или демо какое нибудь? 🙂

  • Конечно можно. Через пару минут будет. 😉

  • 😛 Вот именно это я и искал. Спасибо.

  • Пользуйтесь.

  • спасибо и пример даже есть

  • Не за что. Пример сделал по просьбе neweravin.

  • Михаил

    Только по моему тут опечатка в строке $src=ImageCreateFromJPEG («photos.jpg»);
    Должно быть $src=ImageCreateFromJPEG («photo.jpg»);

  • Все коротко просто и ясно. Воспользуюсь с разрешения автора на своем сайте 🙂

  • Саша: Пожалуйста, пользуйтесь. 😉

  • Макс

    Все гениальное просто! Спасибо.

  • Макс: Согласен. =) Не за что.

  • Сергей

    отличная статейка, автору +5))

  • Павел

    Хорошая статья , мне помогла . 🙂

  • Отличный алгоритм по уменьшению!!! Все понятно, да еще и подробные комментарии к каждой строчке. Переделал твой пример в виде функции для GIF, JPEG, PNG, думал скопировать сюда — мало ли кому пригодится, да не стал — т.к. в коментарии кусок кода, наверное, не даст разместить.

    • Комментарии всё дадут разместить 😉 А вот я — нет, но код не является спамом, поэтому пропущу его.

      • Отличный алгоритм

        Тогда вот код:

        function resizeImage($source, $newWidth)
        { // start resizeImage()
        $sizeArr=GetImageSize($source);
        //Берём числовое значение ширины фотографии, которое мы получили в первой строке и записываем это число в переменную
        $iw=$sizeArr[0];
        //Проверяем, если размер ширины до которой мы хотим уменьшить ($newWidth), больше размера, загруженного пользователем фото, то уменьшть нет смысла, выход из функции
        if ($newWidth >= $iw) return;
        //Проделываем ту же операцию, что и в предыдущей строке, но только уже с высотой.
        $ih=$sizeArr[1];
        //Ширину фотографии делим на $newWidth т.к. на выходи мы хотим получить фото шириной в $newWidth пикселей. В результате получаем коэфициент соотношения ширины оригинала с будущей превьюшкой.
        $koe=$iw/$newWidth;
        //Делим высоту изображения на коэфициент, полученный в предыдущей строке, и округляем число до целого в большую сторону — в результате получаем высоту нового изображения.
        $new_h=ceil($ih/$koe);
        //Определяем тип, загруженного пользователем изображения 1 = GIF, 2 = JPG, 3 = PNG
        $type=$sizeArr[2];
        //Создаём новое изображение из «старого». Основываясь на типе загруженного изображения пользователем, выбираем соответствующую функцию.
        if ($type==1) $src=imagecreatefromgif($source);
        else
        if ($type==2) $src=imagecreatefromjpeg($source);
        else
        if ($type==3) $src= imagecreatefrompng($source);
        else
        return;
        //Создаём пустое изображение шириной в $newWidth пикселей и высотой, которую мы вычислили в предыдущей строке.
        $dst=ImageCreateTrueColor($newWidth, $new_h);
        //Данная функция копирует прямоугольную часть изображения в другое изображение, плавно интерполируя пикселные значения таким образом, что, в частности, уменьшение размера изображения сохранит его чёткость и яркость.
        ImageCopyResampled($dst, $src, 0, 0, 0, 0, $newWidth, $new_h, ImageSX ($src), ImageSY ($src));
        //Сохраняем полученное изображение в соответствуещем формате GIF, JPG или PNG
        if ($type==1) imagegif($dst, $source);
        else
        if ($type==2) imagejpeg($dst, $source, 100);
        else
        if ($type==3) imagepng($dst, $source, 0);
        else
        return;
        } // end resizeImage()

        • Никита

          — Еще бы пример. как потом сохранить данное изображение.

          • Так оно же сохраняется в файл. 😯

        • Попытаюсь прикрутить скрипт к ТОП 10 мудулю новостей на сайте

  • Спасибо, Илья.
    Искал пол часа в инете эту страницу:) 😀
    Добавьте пожалуйста кэш по типу этого скрипта:

    $image = »; // в переменной $image будет храниться имя файла, который в данный момент обрабатывается

    define(‘CACHE_DIR’,’uploads/copy’); // директория для хранения кеша (она должна уже существовать)
    include(‘funcopy.php’);

    $name = CACHE_DIR.’/’.$image.’.jpg’; // имя превьюшки (последнее «.jpg» — не ошибка!)

    // если кешированный эскиз уже существует, отдать его и завершить работу

    if (file_exists($name))
    {
    header(‘Content-type: image/jpeg’);
    readfile($name);
    exit();
    }

    // иначе — создаём новый эскиз

    ob_start();
    $res = send_thumbnail($image,200,150); // $res — результат создания эскиза (TRUE или FALSE)
    $content = ob_get_contents(); // записываем в $content содержимое эскиза

    if ($res && (@$fp = fopen($name,’wb+’))) // если всё хорошо, создаём файлик с кешем и пишем туда содержимое
    {
    fputs($fp,$content);
    fclose($fp);
    ob_end_flush(); // выводим эскиз
    }else
    {
    @ob_end_clean(); // очищаем буфер и рапортуем об ошибке
    header(‘Content-type: text/html’); // функция для создания preview отправляет заголовок Content-type: image/jpeg, а буферизация вывода заголовки пропускает
    die(‘Could not create preview! Reason: ‘.d_error());
    }

  • Ура!!!
    всё)
    Прикрутил, можете посмотреть результат:) на моём сайте
    Только осталось решить проблемы с выводом в двиге.

    • Вверху над записями маленькие картинки — это оно?

  • Угу, где в самом центре jquery прокрутка. 😀
    Сейчас осталось оптимизировать под ie и запустить волну регистрации.
    Всем удачи!

  • Ещё опубликую хак на dle сайтах
    Ссылочку попозже дам:)

  • Денис

    Люди а не подскажите как сделать уменьшения изображения перед загрузкой на сервер??

    • Денис уменьшить фото перед загрузкой не возможно. Однако, я понял, что вы хотите. Для этого вам надо взять пхп скрипт загрузки изображения на хостинг и передать из него переменную с именем загруженного файла, а затем уменьшить этим скриптом свежезагруженный файл.

  • Денис

    я так понял, что сначала надо загрузить, а потом уменьшать! спасибо за ответ!

  • Спасибо за статью, всё доступно и понятно… Очень помогло мне 😉

  • revers

    Только вот… А если изображение было менее 150 пикселей что оно с ним сделает?
    То что мы, вернее Вы 😉 ограничили ширину 150-ю пикселями это хорошо, но вот высота ведь ничем не ограничена?!!! А если кто то возьмёт и попробует загрузить на сервер изображение 200 на 10 000 пикселей? 😕 ❓

    • Тогда оно уменьшиться до 150 на пропорциональную высоту. Данный скрипт предназначен чтобы уменьшать картинки, но не портить их пропорции.

      На сайте, для которого я делал этого скрипт, было важно чтобы картинки в 4 столбца не растягивали сайт в ширину, а в высоту можно.

  • Павел

    Илья, так держать! Очень понятно. Спасибо)

    • Павел Не за что. =) Стараюсь делать всё наиболее понятным.

  • Влад Турчинский

    Автору статьи большое спасибо ))
    сам работу с картинками в пхп как ни старался — не осилил.
    возникла точь в точь такая же задача как у тебя (за исключением того что я делаю wap сайт) — искал в интернете решение, но лучше чем у тебя не нашел ))
    даже вроде понял немного )

  • Ann

    Большое спасибо статья очень помогла в работе )))))

  • Igor

    Интересно, а почему в 2005 никто не мечтал о верстке дивами? И почему не было верстальщиков? Может, вы просто мало тогда знали?

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

  • Max

    Классный урок, сейчас буду разбирать но по описанию все понятно вроде бы.

    Хорошо бы еще работающий пример в архиве. Просто не все программисты есть и те кто учатся на этом.

  • Max

    Вопрос такой куда сохраниться картинка которая была обработана или мы ее на месте обрабатываем?

  • Max

    Все я во всем разобрался. Из того что я находил это самый простой пример пропорционального изменения размера изображения. Автор супер )))) с удовольствием буду следить за блогом.

  • Николай

    Спасибо, Илья! Столкнулся с подобной задачей. Много искал инфы на эту тему, но везде либо сложно объясняется, либо непонятно. А в Вашем примере все коротко и ясно. И все работает!

  • Гость

    Куда именно надо вставлять это код ?

    • Гость Это готовый скрипт, он может работать самостоятельно. А вообще, зависит от того, что вы хотите с ним сделать =)

  • спасибо
    мне нужно было только по высоте сделать
    все получилось 😉

  • Спасибо Илья все просто и понятно.

  • Прекрасно работающий скрипт, обязательно применю на практике:razz:

  • Леха

    Илья, спасибо, взял на вооружение, а на сколько сложнее сделать скрипт который бы поддерживал и png u gif?

    • Ответ для Леха: не за что. В принципе не очень сложно, надо сначала определить тип файла, а затем, в зависимости от типа файла, выполнять определённый скрипт. Если сами не сможете разобраться — сообщите, как будет свободная минутка напишу скриптик

  • Необходимо также в конце освобождать память, man: imagedestroy()

  • Игорь

    зачем в 17 строке вызывать ImageSX и ImageSY, когда данные о ширине и высоте оригинала хранятся в $iw и $ih ??

    • Ответ для Игорь: исправил. Вот это стыд — не заметил =). Давно писал скрипт — тогда запросто мог такие ошибки допустить.

  • Максим

    Странно, скрипт прексасно справляется сшириной, но высоту практически не трогает

    • Ответ для Максим: Вы имеете в виду, что он уменьшает ширину, но не трогает высоту, т.е. деформирует изображение?

  • Вован

    Спасибо большое, но не пойму, как выводится изображение? это все поместить в img src=»» или как?

    • Да, просто в src=»» укажите адрес данного скрипта.

  • Вован

    простите! это опять я. а в
    $size=GetImageSize («photo.jpg»);
    //Создаём новое изображение из «старого»
    $src=ImageCreateFromJPEG («photo.jpg»);
    эти строчки указывается адрес изображения?
    у меня ничего не получается(((( может каких-то библиотек не хватает?

    • Вован

      вбил адресс на изображение в переменную и толку нуль

      • Да, там указывается путь к изображению.
        Нужна библиотека gd2 — включите её, раскомментировав строчку в файле php.ini

  • Вот это самый лучший скрипт из всех какие я нашел. Респект 😉

  • Артур

    Хороший скрип + все понятно объяснил. Спасибо!

  • Trae

    отличный скрипт, очень прост, то что нада 😉

  • Гость

    с png не канает…… 😕 😡 ругается на ImageCreateFromJPEG… нет ли какой-нить универсальной функции, чтоб работала со всеми форматами?

  • А как сделать тоже самое, только для высоты 200 ??

    • Ответ для Антон: Поделите на 200 высоту, вместо ширины на 150.. По аналогии. Читайте код

  • Константин

    Кратко и просто. Спасибо!

  • Респект Автору. Все просто и понятно. Оч лего оптимизировать под свои нужды данный код. Разрешения фоток, работа с разными расширениями изображений на основе его проверки… В общем он дал основу, а дальше уж сами. Всё супер, спасибо.

  • Александр

    здравствуйте Илья, у меня такой вопрос,если вам не трудно то прошу ответить))
    Мне нужно взять из БД изображение большого размера и вывести на странице уже уменьшенное.
    как это сделать????? зарание благодарен))

    • Ответ для Александр: У вас в БД храниться сама картинка или лишь ссылка на неё?

      • Александр

        ссылка на картинку

      • Александр

        Спасибо уже не нужно,узнал что уменьшение «на лету» сильно загружает сайт и может привести к ошибкам((
        Лучше сделать миниатюру и вывести в нужное место.

        • Именно так. Хотя предлагаю выход из ситуации.
          В базе к каждому файлу даёте параметр — который означает существует ли файл миниатюры или нет. Если миниатюры нет — вы берёте ссылку и создаёте файл миниатюры «на лету», затем записывайте в эту же запись адрес к миниатюре и в следующий раз при надобности просто загружаете готовую миниатюру.

          Как видите — вы обрабатываете каждое изображение лишь однажды, например при добавлении большой фотографии на сервер — вы создаёте миниатюры, а затем используете уже ранее созданные миниатюры не вынуждая сервер каждый раз генерировать их снова

  • Maksimchik

    Ага. А если изображение разных типов?!

    • Ответ для Maksimchik: В смысле?? Разных форматов??

  • михаил

    пасиб, то что искал 🙂

  • Alex

    Ок, спасибо. То что надо.

  • Спасибо за скрипт, то, что искал.

  • СПАСИБО!
    То что искал. Все четко и понятно.

  • Александр

    Спасибо! То, что искал!