Web-development / Статьи / Главная страница
29-01-2008

Играем с изображением в PHP

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

Оригинал на http://www.phpied.com/image-fun/
Перевод Феськов Кузьма (kuzma@russofile.ru, http://php.russofile.ru)


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

Например, мы можем сделать негатив изображения. Для этого берем каждый пиксель в изображении и заменяем его противоположным цветом.
Хорошо, но как это работает? Очень просто. Я беру картинку PNG, прохожу по каждому пикселю этого изображения и передаю его функции, которая принимает пиксель в качестве параметра. Функция возвращает мне новый пиксель. Я складываю все новые пиксели и получаю новое изображение.
Класс для работы с пикселом
Для начала нам понадобится класс для работы с пикселом. Он очень прост – содержит в себе три значения: красного, зеленого и синего.
<?php
class Pixel {
    function
Pixel($r, $g, $b)
    {
        
$this->r = ($r > 255) ? 255 : (($r < 0) ? 0 : (int)($r));
        
$this->g = ($g > 255) ? 255 : (($g < 0) ? 0 : (int)($g));
        
$this->b = ($b > 255) ? 255 : (($b < 0) ? 0 : (int)($b));
    }
}
?>
Этот класс имеет только одну функциюконструктор, которая сохраняет RGB значения пикселя.
Чтобы создать красный пиксел, вы просто делаете:
<?php
$red
= new Pixel(255, 0, 0);
?>
Класс манипуляций с пикселами: главный метод
Далее мы создаем класс, который проделывает фактические действия с изображением. Назовем его Image_PixelOperations(). Я не стал писать удобного интерфейса для обработки различных форматов картинок, я думаю, что вы можете развить его самостоятельно. Все, что мне было нужно – это более простой метод для открытия PNG файлов, который бы проходил по каждому пикселу файла, вызывает функцию, получает новый пиксел и присваивает его новому изображению. Далее приведу текст метода:
<?php
class Image_PixelOperations {
        
    function
pixelOperation(
            
$input_image,
            
$output_image,
            
$operation_callback,
            
$factor = false
            
)
    {
        
        
$image = imagecreatefrompng($input_image);
        
$x_dimension = imagesx($image);
        
$y_dimension = imagesy($image);
        
$new_image = imagecreatetruecolor($x_dimension, $y_dimension);
        
        if (
$operation_callback == 'contrast') {
            
$average_luminance = $this->getAverageLuminance($image);
        } else {
            
$average_luminance = false;
        }
        
        for (
$x = 0; $x < $x_dimension; $x++) {
            for (
$y = 0; $y < $y_dimension; $y++) {
        
                
$rgb = imagecolorat($image, $x, $y);
                
$r = ($rgb >> 16) & 0xFF;
                
$g = ($rgb >> 8) & 0xFF;
                
$b = $rgb & 0xFF;
        
                
$pixel = new Pixel($r, $g, $b);
                
$pixel = call_user_func(
                    
$operation_callback,
                    
$pixel,
                    
$factor,
                    
$average_luminance
                
);
        
                
$color = imagecolorallocate(
                    
$image,
                    
$pixel->r,
                    
$pixel->g,
                    
$pixel->b
                
);
                
imagesetpixel($new_image, $x, $y, $color);
            }
        
        }
        
        
imagepng($new_image, $output_image);
    }
}
        
?>
Метод принимает путь до файла. Никаких проверок не производит, предполагая, что это правильный PNG файл. Второй параметр – это имя нового файла изображения. Третий – функция, которая будет вызываться для каждого пикселя. И последний параметр – это любой дополнительный параметр, который мы хотели бы передать в вызываемую для пиксела функцию.
Добавляем шумов
И так, пришло время, чтобы написать первую функцию обработки пиксела: addNoise(). Добавление шума к изображению означает добавление случайного значения к каждому каналу пикселя (если у вас возник вопрос, что такое канал, то я отвечу – уровень красного цвета в пикселе = канал, тоже самое с зеленым и синим). Далее привожу функцию:
<?php
    
function addNoise($pixel, $factor)
    {
        
$random = mt_rand(-$factor, $factor);
        return new
Pixel(
                    
$pixel->r + $random,
                    
$pixel->g + $random,
                    
$pixel->b + $random
                
);
    }
?>
Что представляет из себя функция? Она получает случайное число в указанном пользователем диапазоне ($factor). И добавляет его к значению каналов пиксела. Пользователь указывает диапазон уровня шума: 0 – нет шума, 255 – очень много шума.
Давайте проверять! Создаем простую HTML форму:
<form method="get">
    <input name="image" />
    <input type="submit" />
</form>
Она принимает параметр – названия файла с изображением. После получения этого параметра я создаю новый объект класса для работы с пикселами:
<?php
if (!empty($_GET['image'])) {
        
    
$po =& new Image_PixelOperations();
        
}
?>
Далее я показываю оригинальное изображение, а затем результат обработки.
<?php
    
echo 'Оригинал: <br /><img src="'. $_GET['image'] .'" />';
    echo
'<hr />';
        
    
// Шумы
    
$noise = 100;
    
$po->pixelOperation($_GET['image'], 'result_noise.png', array($po, 'addNoise'), $noise);
    echo
'<br />Добаляем шумы (factor '. $noise .'): <br /><img src="result_noise.png" />';
    echo
'<hr />';
        
?>
Результат:
Рис. 1
Вот еще примеры. Первое изображение получено при помощи фактора 20, а второе – 500:
Рис. 2 Рис. 3
Управление яркостью
Давайте теперь попробуем поиграть с яркостью изображения. Нижеприведенная функция добавляет целое число (одно и тоже) к каждому каналу пиксела. Если мы вызываем функцию с положительным значением – яркость увеличивается, если с отрицательным – уменьшается.
<?php
function adjustBrightness($pixel, $factor)
{
    return new
Pixel(
    
$pixel->r + $factor,
    
$pixel->g + $factor,
    
$pixel->b + $factor
    
);
}
?>
Чтобы протестировать эту функцию выполните следующий код:
<?php
$brightness
= 50;
$po->pixelOperation($_GET['image'], 'result_bright.png', array($po, 'adjustBrightness'), $brightness);
echo
'<br />Ярче: <br /><img src="result_bright.png" />';
$brightness = -50;
$po->pixelOperation($_GET['image'], 'result_dark.png', array($po, 'adjustBrightness'), $brightness);
echo
'<br />Темнее: <br /><img src="result_dark.png" />';
echo
'<hr />';
?>
Смотрим на результат:
Рис. 4 Рис. 5
Меняем местами цвета
Давайте теперь займемся сменой цветов. Это означает, что мы можем взять, скажем, количество красных цветов и заменить их, например, на количество синих цветов. Возможные варианты:
  • RGB to RBG
  • RGB to BGR
  • RGB to BRG
  • RGB to GBR
  • RGB to GRB
Давайте посмотрим, как выглядит функция:
<?php
function swapColors($pixel, $factor)
{     
    switch (
$factor) {         
        case
'rbg':         
        return new
Pixel(         
        
$pixel->r,         
        
$pixel->b,         
        
$pixel->g         
        
);         
        break;         
        case
'bgr':         
        return new
Pixel(         
        
$pixel->b,         
        
$pixel->g,         
        
$pixel->r         
        
);         
        break;         
        case
'brg':         
        return new
Pixel(         
        
$pixel->b,         
        
$pixel->r,         
        
$pixel->g         
        
);         
        break;         
        case
'gbr':         
        return new
Pixel(         
        
$pixel->g,         
        
$pixel->b,         
        
$pixel->r         
        
);         
        break;         
        case
'grb':         
        return new
Pixel(         
        
$pixel->g,         
        
$pixel->r,         
        
$pixel->b         
        
);         
        break;         
        default:         
        return
$pixel;         
    }         
}
?>
Тестируем:
RGB -> RBG
Рис. 6
RGB -> BGR
Рис. 7
RGB -> BRG
Рис. 8
RGB -> GBR
Рис. 9
RGB -> GRB
Рис. 10
Удаление или насыщение цветов
Далее рассматриваем еще 2 функции. Первая – устанавливает значение цвета в 0 (например, нет красного). Вторая – наоборот увеличивает колличество цвета до максимального значения, или сразу 2 канала. Таким образом, мы имеем 6 вариантов значений для каждого метода.
  • Удаление (или насыщение) красный
  • Удаление (или насыщение) зеленый
  • Удаление (или насыщение) синий
  • Удаление (или насыщение) красный и зеленый в то же самое время
  • Удаление (или насыщение) красный и синий
  • Удаление (или насыщение) зеленый и синий
Код:
<?php
function removeColor($pixel, $factor)
{
    if (
$factor == 'r' ) {
        
$pixel->r = 0;
    }
    if (
$factor == 'g' ) {
        
$pixel->g = 0;
    }
    if (
$factor == 'b' ) {
        
$pixel->b = 0;
    }
    if (
$factor == 'rb' || $factor == 'br') {
        
$pixel->r = 0;
        
$pixel->b = 0;
    }
    if (
$factor == 'rg' || $factor == 'gr') {
        
$pixel->r = 0;
        
$pixel->g = 0;
    }
    if (
$factor == 'bg' || $factor == 'gb') {
        
$pixel->b = 0;
        
$pixel->g = 0;
    }
    return
$pixel;
}

function
maxColor($pixel, $factor)
{
    if (
$factor == 'r' ) {
        
$pixel->r = 255;
    }
    if (
$factor == 'g' ) {
        
$pixel->g = 255;
    }
    if (
$factor == 'b' ) {
        
$pixel->b = 255;
    }
    if (
$factor == 'rb' || $factor == 'br') {
        
$pixel->r = 255;
        
$pixel->b = 255;
    }
    if (
$factor == 'rg' || $factor == 'gr') {
        
$pixel->r = 255;
        
$pixel->g = 255;
    }
    if (
$factor == 'bg' || $factor == 'gb') {
        
$pixel->b = 255;
        
$pixel->g = 255;
    }
    return
$pixel;
}
?>
Результаты:
Удаляем красный
Рис. 11
Удаляем зеленый
Рис. 12
Удаляем синий
Рис. 13
Удаляем красный и зеленый
Рис. 14
Удаляем зеленый и синий
Рис. 15
Удаляем красный и синий
Рис. 16
Насыщаем красный
Рис. 17
Насыщаем зеленый
Рис. 18
Насыщаем синий
Рис. 19
Насыщаем красный и зеленый
Рис. 20
Насыщаем зеленый и синий
Рис. 21
Насыщаем красный и синий
Рис. 22
Делаем негатив
Эта функция очень проста – у вас много красного? Значит сделаем мало. И так далее.
<?php
function negative($pixel)
{
    return new
Pixel(
    
255 - $pixel->g,
    
255 - $pixel->r,
    
255 - $pixel->b
    
);
}
?>
Результат:
Рис. 23
Оттенки серого (Grayscale)
Не знаю, в курсе вы или нет, но оттенок серого получается уравниванием R, G, B каналов. Более темные участки имеют больше насыщения, светлые – меньше.
Чтобы привести изображение к оттенкам серого мы должны взять среднее число насыщения каналов и установить их на среднее число.
<?php
function greyscale($pixel)
{
    
$pixel_average = ($pixel->r + $pixel->g + $pixel->b) / 3;

    return new
Pixel(
    
$pixel_average,
    
$pixel_average,
    
$pixel_average
    
);
}
?>
Результат:
Рис. 24
Черно-белое
В отличие от оттенков серого, черно-белое изображение имеет только 2 цвета: черный (0,0,0) и белый (255,255,255). $factor мы будем использовать для определения границы того, что считать черным, а что – белым. Простота логики в том, что мы суммируем R+G+B и смотрим, к чему значение ближе – к 255 или к 0. Использование $factor позволит нам внести некоторую гибкость в алгоритм (внести некоторую субъективность):
<?php
function blackAndWhite($pixel, $factor)
{
    
$pixel_total = ($pixel->r + $pixel->g + $pixel->b);

    if (
$pixel_total > (((255 + $factor) / 2) * 3)) {
        
// белый
        
$pixel->r = 255;
        
$pixel->g = 255;
        
$pixel->b = 255;
    } else {
        
$pixel->r = 0;
        
$pixel->g = 0;
        
$pixel->b = 0;
    }

    return
$pixel;
}

?>
Результат ($factor = 20):
Рис. 25
Отсечение
Не знаю, насколько эта функция может оказаться полезной. Она занимается удалением пограничных значений (переходов) цвета, заменя их чистым цветом: если у вас было 5, 155, 250 станет – 0, 155, 255. $factor дает нам гибкость в рисунке. Пока я вижу нужность этой функции для уменьшения размера изображения.
<?php
function clip($pixel, $factor)
{
    if (
$pixel->r > 255 - $factor) {
        
$pixel->r = 255;
    }
    if (
$pixel->r < $factor) {
        
$pixel->r = 0;
    }
    if (
$pixel->g > 255 - $factor) {
        
$pixel->g = 255;
    }
    if (
$pixel->g < $factor) {
        
$pixel->g = 0;
    }
    if (
$pixel->b > 255 - $factor) {
        
$pixel->b = 255;
    }
    if (
$pixel->b < $factor) {
        
$pixel->b = 0;
    }

    return
$pixel;
}
?>
Результат ($factor = 100):
Рис. 26
Корректировка контраста
Эта операция не является операцией над пикселом в чистом виде, поскольку принимает во внимание информацию обо всех пикселях для принятия решения о том, как поступить с данным конкретным. Настройка контраста нуждается в так называемой средней яркости. Чтобы высчитать среднюю яркость вам необходима формула, приведенная в функции ниже.
<?php
function getAverageLuminance($image)
{
    
$luminance_running_sum = 0;
    
$x_dimension = imagesx($image);
    
$y_dimension = imagesy($image);
    for (
$x = 0; $x < $x_dimension; $x++) {
        for (
$y = 0; $y < $y_dimension; $y++) {

            
$rgb = imagecolorat($image, $x, $y);
            
$r = ($rgb >> <font color="


Комментарии к статье "Играем с изображением в PHP" (0)



Вам есть, что сказать?




  Введите код