Играем с изображением в PHP
В данном материале мы рассмотрим некоторые примеры манипуляции с
изображением при помощи библиотеки 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
/>';
?>
|
Результат:
Вот еще примеры. Первое изображение получено при помощи фактора
20, а второе – 500:
Управление яркостью
Давайте теперь попробуем поиграть с яркостью изображения.
Нижеприведенная функция добавляет целое число (одно и тоже) к каждому
каналу пиксела. Если мы вызываем функцию с положительным значением –
яркость увеличивается, если с отрицательным – уменьшается.
<?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 />';
?>
|
Смотрим на результат:
Меняем местами цвета
Давайте теперь займемся сменой цветов. Это означает, что мы можем
взять, скажем, количество красных цветов и заменить их, например, на
количество синих цветов. Возможные варианты:
- 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
|
RGB -> BGR
|
RGB -> BRG
|
RGB -> GBR
|
RGB -> GRB
|
Удаление или насыщение цветов
Далее рассматриваем еще 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; }
?>
|
Результаты:
Удаляем красный
|
Удаляем зеленый
|
Удаляем синий
|
Удаляем красный и зеленый
|
Удаляем зеленый и синий
|
Удаляем красный и синий
|
Насыщаем красный
|
Насыщаем зеленый
|
Насыщаем синий
|
Насыщаем красный и зеленый
|
Насыщаем зеленый и синий
|
Насыщаем красный и синий
|
Делаем негатив
Эта функция очень проста – у вас много красного? Значит сделаем мало. И
так далее.
<?php function
negative($pixel) { return new
Pixel(
255
- $pixel->g,
255
- $pixel->r,
255
- $pixel->b ); } ?>
|
Результат:
Оттенки серого (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
); }
?>
|
Результат:
Черно-белое
В отличие от оттенков серого, черно-белое изображение имеет только
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):
Отсечение
Не знаю, насколько эта функция может оказаться полезной. Она занимается
удалением пограничных значений (переходов) цвета, заменя их чистым цветом:
если у вас было 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):
Корректировка контраста
Эта операция не является операцией над пикселом в чистом виде,
поскольку принимает во внимание информацию обо всех пикселях для принятия
решения о том, как поступить с данным конкретным. Настройка контраста
нуждается в так называемой средней яркости. Чтобы высчитать среднюю
яркость вам необходима формула, приведенная в функции ниже.
<?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 >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$luminance_running_sum +=
(0.30 *
$r) +
(0.59 *
$g) +
(0.11 *
$b);
}
}
$total_pixels
= $x_dimension
* $y_dimension;
return $luminance_running_sum /
$total_pixels;
} ?>
|
Конечное же преобразование контраста очень просто:
<?php function
contrast($pixel, $factor, $average_luminance) {
return new Pixel(
$pixel->r * $factor + (1 - $factor) * $average_luminance,
$pixel->g * $factor + (1 - $factor) * $average_luminance,
$pixel->b * $factor + (1 - $factor) * $average_luminance
); }
?>
|
Результаты (0.5 и 1.5
соответственно):
Соль и перец
Эта функция в целом случайным образом «разбрызгивает» по изображению
белые и черные точки.
<?php function
saltAndPepper($pixel, $factor) { $black = (int)($factor/2 + 1);
$white
= (int)($factor/2 - 1);
$random
= mt_rand(0, $factor);
$new_channel
= false;
if ($random
== $black) {
$new_channel = 0;
} if
($random ==
$white) {
$new_channel = 255;
} if
(is_int($new_channel)) {
return new
Pixel($new_channel, $new_channel, $new_channel); } else {
return
$pixel;
} } ?>
|
Пример ($factor = 20):
Гамма-коррекция
<?php function
gamma($pixel, $factor) { return new
Pixel(
pow($pixel->r / 255, $factor) * 255,
pow($pixel->g / 255, $factor) * 255,
pow($pixel->b / 255, $factor) * 255 ); } ?>
|
Пример ($factor = 2.2):
Послесловие
Если вы, экспериментируя с этим классом, изобрели еще какой-либо
интересный эффект - опубликуйте свою функцию в комментариях внизу этой
страницы и я добавлю ее в эту статью!
Источник: www.kuzma.russofile.ru
|