Home
Imparare a programmare con PHP: filtrare le immagini

19 Novembre 2018

Imparare a programmare con PHP: filtrare le immagini

di

Occuparsi di grafica attraverso la programmazione? È possibile. Il coding apre nuovi mondi per chi desidera migliorarsi.

Un filtro trasforma con metodi matematici i singoli pixel di un’immagine generando una nuova versione dell’immagine originale. La libreria GD di PHP mette a disposizione funzioni già pronte per questo compito. Per imparare, vediamo come creare alcuni semplici filtri leggendo e scrivendo i valori dei singoli pixel. Useremo le seguenti funzioni di GD (che di seguito proviamo direttamente nella shell PHP):

  • imagecolorat(), che restituisce il colore di un pixel date le sue coordinate x e y;
  • imagesetpixel(), che imposta (in inglese to set) il colore del pixel di un’immagine.
php > $image = imagecreatetruecolor(1, 1); // Immagine di un solo pixel
php > $color = imagecolorat($image, 0, 0); // Recupera il valore del pixel
php > echo $color; // Il pixel è nero di default, tutti i bit sono a 0, 0
php > imagesetpixel($image, 0, 0, 0xff0000); // Colora il pixel (0,0) di rosso 
Creare un’immagine speculare

Partiamo da un programma che, data un’immagine, crea la sua immagine speculare (in inglese specchio si dice mirror), che contiene cioè gli stessi pixel, però invertiti orizzontalmente. Crea l’app ImageMirror con dentro il file index.php e la sottodirectory images con le immagini originali (qui usiamo hand.jpg, presa da Pixabay, che puoi vedere qui sotto).

Immagine allo specchio

A sinistra, l’immagine originale (hand.jpg) e, a destra, la sua speculare (hand_out.jpg).

La prima parte di index.php è costituita dalla funzione imageMirror(), che effettua il mirroring passando in rassegna ciascuna riga dell’immagine e scambiando il primo pixel con l’ultimo, il secondo con il penultimo e così via.

< ? php
/* Restituisce l'immagine speculare in orizzontale dell'immagine specificata */
function imageMirror($image) {
 $w = imagesx($image);
 $h = imagesy($image);
 for ($y = 0; $y < $h; $y++) { // Per ogni riga
 for ($x1 = 0; $x1 < $w / 2; $x1++) { // Per la prima metà delle colonne
 $p1 = imagecolorat($image, $x1, $y); // Recupera il pixel a sx
 $x2 = $w - $x1 - 1; // Calcola l'ascissa del corrispondente pixel a dx
 $p2 = imagecolorat($image, $x2, $y); // Recupera il pixel a dx
 imagesetpixel($image, $x1, $y, $p2); // Scambia i pixel: (x1, y) <- p2
 imagesetpixel($image, $x2, $y, $p1); // e (x2, y) <- p1
 }
 }
 return $image; // Restituisce l'immagine modificata
}

La seconda parte recupera il nome dell’immagine (utilizzando la variabile globale $_GET), apre il file JPEG e salva su file l’immagine restituita da imageMirror().

// basename() fa in modo che l'utente possa specificare solo il nome
// di un file (senza estensione .jpg) che dev'essere presente in "./images/"
if (isset($_REQUEST['file'])) {
 $basename = basename($_REQUEST['file']); // Non considera le estensioni
 $filenameIn = "./images/" . $basename . ".jpg"; // Filename di input
 $image = imageMirror(imagecreatefromjpeg($filenameIn));
 $filenameOut = "./images/" . $basename . "_out.jpg"; // Filename di output
 if (imagejpeg($image, $filenameOut)) { // Crea l'immagine speculare
 echo "Immagine speculare salvata correttamente!";
 } else {
 echo "Errore nel salvataggio (controlla i permessi di scrittura)!";
 }
} else {
 echo "Aggiungi all'indirizzo il nome del file senza estensione.";
 echo "Es. http://localhost/cap09/ImageMirror/?file=hand";
}

Scrivi nella barra degli indirizzi del browser http://localhost/cap09/ImageMirror/?file=hand. In questo modo esegui lo script di default (cioè index.php) di ImageMirror passandogli con il metodo get il parametro file con valore hand. Se vuoi elaborare altre immagini JPEG copiale in images e aggiorna l’URL dell’applicazione con il nome della nuova immagine senza estensione. L’app visualizza un messaggio di errore se il file non esiste oppure se la directory images non ha i permessi di scrittura per l’utente che esegue il web server Apache.

Creare filtri sui colori

Per filtrare i colori utilizziamo imagecolorsforindex() che restituisce un array con i valori per i canali R, G, B e A (Alpha, che qui non consideriamo perché le immagini sono del tutto opache, cioè senza trasparenza). Vediamo alcuni esempi:

php > $image = imagecreatetruecolor(1, 1); // Immagine di un solo pixel
php > imagesetpixel($image, 0, 0, 0xa36d3e); // Pixel (0,0) marrone scuro
php > $color = imagecolorat($image, 0, 0); // Recupera il valore del pixel
php > echo $color; // Valore del pixel in decimale (indice di colore) 10710334
php > echo dechex($color); // Controlla il valore in esadecimale a36d3e
php > $rgba = imagecolorsforindex($image, $color);
php > print_r($rgba); // Array dei canali RGBA
Array
(
 [red] => 163
 [green] => 109
 
  [blue] => 62
 [alpha] => 0
)
php > echo $rgba["red"]; // Valore in decimale della componente rossa

Crea ora l’applicazione ImageFilter. Lo script libs/functions.php riportato di seguito contiene due utili funzioni. La seconda, indexForColors(), serve per creare un valore (o indice) di colore a partire dalle componenti RGB. Per farlo usa l’operatore di shift binario >> che sposta verso destra i singoli bit di un certo numero di posizioni, inserendo degli zeri nelle posizioni che si liberano a sinistra, e l’operatore di OR bitwise | che effettua una somma binaria bit a bit. Non ci addentriamo nel dettaglio della spiegazione, che puoi comunque intuire facendo riferimento alla prossima figura.

Lo standard ARGB

Formato ARGB con l’indicazione dei 32 bit utilizzati per i canali Alpha (alfa) e RGB.

< ? php
/* Restituisce l'array delle componenti RGB di un pixel di un'immagine */
function getRGB($image, $x, $y) {
 $color = imagecolorat($image, $x, $y);
 $rgba = imagecolorsforindex($image, $color);
 return [$rgba["red"], $rgba["green"], $rgba["blue"]];
}
/* Restituisce l'indice di colore corrispondente alle componenti RGB */
function indexForColors($r, $g, $b) {
 return $r << 16 | $g << 8 | $b; // Shift a sx ("<<") e OR bitwise ("|")
}

Il primo filtro di colore restituisce un’immagine in scala di grigi (vedi la seconda immagine dentro la serie seguente). Tenendo conto che un grigio ha lo stesso valore per tutte le componenti, impostiamo la media RGB come valore per tutti e tre i canali.

Immagini filtrate per colore

Immagini originale (lorenzo.jpg) e filtrate in scala di grigi, bianco e nero e il solo canale rosso.

< ? php
require "libs/functions.php"; // Usa: getRGB() e indexForColors()
$image = imagecreatefromjpeg("images/lorenzo.jpg");
$image = filterGreys($image);
header('Content-type: image/jpeg');
imagejpeg($image);
/* Ritorna la versione in scala di grigi di un'immagine */
function filterGreys($image) {
 $width = imageSX($image);
 $height = imageSY($image);
 // Crea un'immagine nuova di lavoro con le stesse dimensioni dell'originale
 $newImage = imagecreatetruecolor($width, $height);
 for ($y = 0; $y < $height; $y++) { // Scorri le righe
 for ($x = 0; $x < $width; $x++) { // Scorri le colonne
 list($r, $g, $b) = getRGB($image, $x, $y); // Leggi i valori RGB
 // FILTRO: parte specifica di trasformazione dei valori RGB
 $r = $g = $b = ($r + $g + $b) / 3; // Stesso valore per R, G e B
 $color = indexForColors($r, $g, $b); // Crea il valore (indice) di colore
 imagesetpixel($newImage, $x, $y, $color); // Scrivi i valori RGB
 }
 }
 return $newImage;
}

Fai attenzione, poiché nella riga di codice del listato precedente:

list($r, $g, $b) = getRGB($image, $x, $y); // Leggi i valori RGB

usiamo l’assegnazione multipla, un espediente sintattico che permette di assegnare il primo valore di un array alla prima variabile in una lista, il secondo alla seconda variabile eccetera. Ecco un altro esempio:

php > list($a, $b, $c) = [3, 7, 2]; // Equivale a: $a = 3; $b = 7; $c = 2;

Vediamo ora solo la parte specifica di trasformazione dei valori RGB per le due ultime immagini della figura precedente. Per il bianco e nero (black_and_white.php), usiamo l’operatore ternario per confrontare la media dei valori delle componenti (il valore per creare il grigio visto prima) con un valore di soglia (in inglese threshold): se la media è superiore o uguale alla soglia allora il pixel diventa nero, altrimenti diventa bianco.

$threshold = 75; // Soglia per scegliere tra il bianco e il nero
$color1 = 0xFFFFFF; // Colore da usare per un valore sopra la soglia
$color2 = 0x000000; // Colore da usare per un valore sotto la soglia
$average = ($r + $g + $b) / 3; // Media di R, G e B
$color = ($average >= $threshold) ? $color1 : $color2;

Puoi creare altri filtri rimpiazzando nel codice del listato precedente la riga contrassegnata con FILTRO:. Per esempio, per ottenere l’ultima immagine della figura appena vista, usiamo la seguente istruzione per “fare passare” solo la componente rossa:

list($r, $g, $b) = [$r, 0, 0];

Per ottenere il negativo di un’immagine (seconda immagine nella prossima figura), calcoliamo i complementari delle componenti RGB (cioè i valori che servono per raggiungere il valore massimo di 255):

$r = 255 - $r; // Valore complementare per il canale rosso
$g = 255 - $g; // ...per quello verde
$b = 255 - $b; // ...e per quello blu

Terminiamo il capitolo con lo script fourcolors.php, che ha tre soglie e quattro intervalli di valore, ciascuno associato a un colore diverso (terza immagine nella figura che stai per vedere). Per questa associazione, la formula che abbiamo utilizzato è la seguente:

$n = 4; // Numero di colori associati a sottointervalli di [0, 255]
$colors = [0xffff00, 0x00ff00, 0xff0000, 0x0000ff]; // $n colori
$average = ($r + $g + $b) / 3; // Media di R, G e B
$index = floor($average / ceil(255 / $n)); // Calcolo l'indice...
$color = $colors[$index]; // ...per scegliere il colore nell'array dei colori

Immagini filtrate di una tigre

Immagini di una tigre: originale, negativa, quadricolore e bicolore.

Variazioni sul tema

Partendo dal codice qui presentato puoi espandere la conoscenza della materia, per esempio in questi modi.

  1. Crea l’app Watermark2, che alterna due immagini come watermark (per esempio la mascotte e il logo di PHP) disponendole a scacchiera.
  2. Crea l’app ImageMirror2, che crea l’immagine speculare in verticale.
  3. Crea lo script twocolor.php modificando i due colori di black_and_white.php per ottenere un effetto simile a quello dell’ultima immagine dell’ultima figura, agendo sui valori di colore e sul valore di soglia.

L'autore

  • Maurizio Boscaini
    Maurizio Boscaini insegna Informatica all’ITI Marconi e all’Università di Verona. Sperimenta tecnologie per la didattica e si occupa di coding anche nella scuola elementare e media.
  • Massimiliano Masetti
    Massimiliano Masetti insegna Informatica all’IIS Cerebotani ed è consulente ICT specializzato nella realizzazione di applicazioni web. Esperto di linguaggi di programmazione, crede che JavaScript sia una scatola cinese che riserva sempre nuove scoperte.

Iscriviti alla newsletter

Novità, promozioni e approfondimenti per imparare sempre qualcosa di nuovo

Gli argomenti che mi interessano:
Iscrivendomi dichiaro di aver preso visione dell’Informativa fornita ai sensi dell'art. 13 e 14 del Regolamento Europeo EU 679/2016.