Artikel top billede

(Foto: Computerworld)

Lav en skræddersyet billedbehandler

Med en webserver og scriptsproget PHP er det nemt at lege
med grafik.

Af Redaktionen, Alt om Data

Denne artikel er oprindeligt bragt på Alt om Data. Computerworld overtog i november 2022 Alt om Data. Du kan læse mere om overtagelsen her.

Muligheden for at manipulere billeder er en funktion, som flere og flere webprogrammører føjer til deres repertoire. Det skyldes fremkomsten af Web 2.0 og kravet om at gøre sites mere sociale med thumbnails, avatarer og så videre. Auktionssider og reklamesider har brug for en metode til at acceptere brugerens upload af et billede af det, der skal sælges. Og hvad er et nyt forum uden avatarer?

Det er imidlertid ikke nok blot at understøtte HTTP-filupload, for man modtager billeder af alle mulige størrelser og dimensioner. Mange af de nyeste digitalkameraer går op til 10 megapixel, så standardfilstørrelserne er i meget høj opløsning (tusinder og atter tusinder af pixel) og fylder adskillige megabyte.

Det betyder, at man skal kunne tage uploadede billeder og vride dem ned i størrelse, så de passer til det pågældende website, og man skal kunne gemme dem i det format, man foretrækker. Der opstår måske også behov for at give brugerne mulighed for at justere det endelige billede eller for selv at gøre det automatisk. Det er især nødvendigt, når man har skåret et billede ned i thumbnailstørrelse, for man får næsten altid brug for at skærpe det.

Før du begynder, skal du sikre dig, at din webserver har GD Lib kompileret i PHI, for det er den eneste måde, disse funktioner virker på.

Upload af billeder

I dette tilfælde har vi begrænset de understøttede filtyper til GIF, PNG og JPG. I praksis vil du opdage, at disse tre er nok til de fleste formål.

Den følgende stump kode er en del af den færdige projektfil, der hedder ”picproc.php” (se www.aod.dk/download). Den indeholder den indledende kode, som beder webserveren håndtere PHP. Derefter kommer den HTML, som programmet bruger:

<?php
echo <<<_END
<font face=”courier new ”size=4><p re><ul><b><u>PHP PICTURE PROCESSOR</u>
<form method=post action=picproc.php enctype=multipart/form data UPLOAD :<input type=file name=p size=1>
ACTION :<select name=a><option
value=0>No Action<option
value=1>Sharpen<option
value=2>Blur<option
value=3>Brighten<option
value=4>Darken<option
value=5>More Contrast<option
value=6>Less Contrast<option
value=7>Greyscale<option
value=8>Invert<option
value=9>Reder<option
value=10>Greener<option
value=11>Bluer<option
value=12>Edge Detect<option
value=13>Emboss<option
value=14>Sketchify</select>
RESIZE :W <input type=text
name=w size=1>H <input
type=text name=h size=1>
<ul><input type=submit></
form></pre>
_END;

Koden begynder med at indstille skrifttypen, så den passer til en enkel formatering. Tags’ene <pre> og <ul> har til formål at tvinge alt det følgende output til at fremstå nøjagtig som vist og indrykket fra venstre margen. Så bliver der skrevet en overskrift. Efter denne enkeltlinje-sideopsætning skal vi til kernen i koden, hvor en form bliver begyndt. Den sender sit input som multipartdata til picproc.php.

Dernæst bliver inputtene defineret. De omfatter den fil, der skal sendes (der fremkommer automatisk en ”Browse”-knap, når programmet bliver kørt), og den handling, der skal udføres. Det omfatter 14 dele.

Til sidst er der mulighed for at ændre størrelse på den uploadede fil, idet der kan være input i to felter, ”W” og ”H” for henholdsvis bredde og højde. Når alt det er gjort, bliver ”Submit”-knappen tilføjet, og formen bliver lukket.

Gem det uploadede billede

Den kode, der er vist ovenfor, viser den HTML-form, der kræves til at uploade et billede, udføre en af 14 handlinger på det og/eller ændre dets størrelse. Der er nogle ting, vi skal gøre, når billedet ankommer til serveren. Den første er at befolke de variabler, vi skal bruge:

$p=$_FILES [p ][name ];
$a=$_POST [a ];
$w=$_POST [w ];
$h=$_POST [h ];
$t=”pic.jpg ”;
$rn=rand(0,65535);

Hvis der er uploadet en fil, er ”$p” filens indhold. ”$a” er den handling, der skal udføres på filen eller ”No action”, hvis den er indstillet til 0. Hvis de er indstillet, indeholder ”$w” og ”$h” de nye værdier for bredde og højde, der gælder for billedet. ”$t” er en enkel strengvariabel, der indeholder filens navn, som det er gemt på serveren, mens ”$rn” er et vilkårligt tal, som vi skal vende tilbage til.

Den variabel, der interesserer os nu, er imidlertid $p, for hvis den er indstillet, er et billede blevet uploadet, og vi skal håndtere det således:

if ($p){
move_uploaded_file($_
FILES [p ][tmp_name ],$t);
switch($_FILES [p ][type ]){
case “image/gif ”:$s=imagecreatefro
mgif($t);break;
case “image/jpeg ”:$s=imagecreatef
romjpeg($t);break;
case “image/png ”:$s=imagecreatefr
ompng($t);break;}
@imagejpeg($s,$t);}
else $s=@imagecreatefromjpeg($t);

Det første, vi gør efter at have konstateret, at et billede er blevet uploadet, er at gemme det som fil på serveren ved hjælp af den værdi, der er gemt i $t (”pic.jpg”). Grunden til, at efternavnet ”jpg” bliver brugt her, er, at det er det format, som billeder bliver gemt i til intern brug.

De næste få linjer kode afgør den billedtype, der blev uploadet og derefter konverteret fra det format til det interne billedformat PHP, før det blev gemt igen som jpg-fil. Bemærk, at der ikke er nogen fejlkontrol her for andre mimetyper, der bliver uploadet. Det går uden for denne workshops rammer og er noget, du selv må tage stilling til.

I slutningen er der et ”else”, der kører, hvis der ikke blev uploadet et billede. Hvis det er tilfældet, bliver der gjort et forsøg på at indlæse et tidligere gemt billede til videre manipulation. Det er uændret, for vi vil ikke blive ved med at gen-uploade hovedbilledet for at prøve forskellige effekter, medmindre vi gerne vil gå tilbage til det oprindelige.

Bemærk brugen af ”@”-tegnet som præfiks til funktionskaldet. Det undertrykker eventuelle fejlmeddelelser, hvis der endnu ikke er uploadet et billede. Eftersom der ikke her er plads til at indbygge alle mulige fejlkontroller, er denne teknik blevet brugt mange steder for at sikre, at programoutputtet er rent. Hvis andre mennesker skal bruge denne kode, bør man først fjerne alle @-tegnene for at fange fejlmeddelelser og afgøre, hvordan de skal håndteres.

Billedbehandlingen

Nu skal vi så i gang med at behandle billederne. Og på dette stadie burde vi nu have et originalt billede gemt på serveren som pic.jpg, indlæst i PHP’s billedområde og parat til manipulation med denne kode:

switch($a){
case 1:@imageconvolution($s,
array(array(1,1,1),array(1,16,1),
array(1,1,1)),8,0);break;
case 2:@image filter($s,IMG_
FILTER_GAUSSIAN_BLUR);break;
case 3:@image filter($s,IMG_
FILTER_BRIGHTNESS,20);break;
case 4:@image filter($s,IMG_
FILTER_BRIGHTNESS,20);break;
case 5:@image filter($s,IMG_
FILTER_CONTRAST,20);break;
case 6:@image filter($s,IMG_
FILTER_CONTRAST,20);break;
case 7:@image filter($s,IMG_
FILTER_GRAYSCALE);break;
case 8:@image filter($s,IMG_
FILTER_NEGATE);break;
case 9:@image filter($s,IMG_
FILTER_COLORIZE,128,0,0,50);break;
case 10:@image filter($s,IMG_
FILTER_COLORIZE,0,128,0,50);break;
case 11:@image filter($s,IMG_
FILTER_COLORIZE,0,0,128,50);break;
case 12:@image filter($s,IMG_
FILTER_EDGEDETECT);break;
case 13:@image filter($s,IMG_
FILTER_EMBOSS);break;
case 14:@image filter($s,IMG_
FILTER_MEAN_REMOVAL);break;}

Som man kan se, bliver mange af de handlinger, der er understøttet af vores program, implementeret ved hjælp af funktionen ”imagefilter()”. Den er indbygget i det GD-bibliotek, der bruges af PHP, og giver mulighed for en lang række transformeringer. Vi nævner kun få eksempler i denne artikel.

Funktionen tager det billede, der skal manipuleres, en hel-talfiltertype og parametre (de er somme tider valgfri og somme tider obligatoriske, afhængig af filtertypen).

Vi har som udgangspunkt valgt værdier for disse handlinger, der virker fornuftige, og som kan bruges igen og igen til at opnå flere ændringer. Man kan eksperimentere med værdierne for at få de resultater, man ønsker.

Den eneste undtagelse er brugen af funktionen ”imageconvolution()” for at opnå større skarphed. Den er brugt, fordi der ikke er nogen skærpefiltertype til imagefilter(). Der er mange andre effekter, som man kan opnå med denne funktion, og den er værd at se nærmere på.

Billedets størrelse

Når kodesegmentet er eksekveret, burde vi have et billede i hukommelsen, parat til at blive sendt tilbage til serveren. Men der mangler en sidste test for det tilfældes skyld, at brugeren har ændret størrelse på billedet:

if ($w){
list($tw,$th)=getimagesize($t);
$s1=imagecreatetruecolor($w,$h);
imagecopyresampled($s1,$s,0,0,0,0,
$w,$h,$tw,$th);
imagejpeg($s1,$t);
imagedestroy($s1);}
else @imagejpeg($s,$t);
@imagedestroy($s);

Det er meget rodet simpelthen at droppe eller tilføje lodrette eller vandrette linjer for at ændre billedets dimensioner. Den bedste måde at ændre billedets størrelse på består i at sample det. Det betyder, at det bevarer så meget af integriteten som muligt, når man tilføjer eller fjerner data.

For at gøre det bliver billedets nuværende dimensioner noteret og gemt i ”$tw” og ”$th”. Der bliver nu oprettet et nyt workspace i ”$s1” med bredde og højde angivet i inputtet.

Ud fra disse data bliver det oprindelige billede re-samplet, så det udfylder det nye workspace, og gemt tilbage til serven ved hjælp af funktionen ”imagejpeg()”. For at føre midlertidig hukommelse tilbage til serveren, bliver funktionen ”imagedestroy()” også kaldt.

Man kan se, at denne kode kun bliver eksekveret, hvis $w er blevet defineret. Det er en hurtig måde at sikre sig, at en redimensionering er på plads. Det bliver ikke tjekket, om $w er fornuftig, eller om $h overhovedet eksisterer. Derfor giver elendigt input elendige resultater, uden at der bliver tilføjet passende fejltjek.

Til sidst er vi parat til at vise billedet – hvis det eksisterer:

@imagedestroy($s);
if (file_exists($t))echo “<img src=$t?rn=$rn>”;

Først bliver det midlertidige workspace, der er brugt til billedet, returneret til serveren. Hvis filen pic.jpg eksisterer på serveren, bliver den vist ved hjælp af HTML-tag’en <img>.

Nu kan vi se formålet med den vilkårlige $rn-variabel, der blev defineret tidligere. Den er knyttet til billede-url’en i formen ”pic.jpg?rn=nnn” for at sikre, at webbrowseren downloader og viser billedet direkte fra serveren – snarere end fra dens cache – hver gang applikationen kører.

Hvis man samler disse segmenter og gemmer dem som programmet picproc.php, har man en basal billedbehandler med nogle få linjer PHP. Ved hjælp af programmets struktur kan man nemt tilføje masser af øget funktionalitet og nye egenskaber.

Flere oplysninger

Hvis du er interesseret i billedmanipulation og vil gå videre med dette projekt, bør du kigge på http://uk2.php.net/manual/en/ref.image.php, hvor du kan finde over 100 funktioner, som man kan bruge til at opnå enhver tænkelig grafisk manipulation.

De funktioner, der navnlig er interessante, omfatter paletmanipulation; tegning af linjer, rektangler og mangekanter; brug af skrifttyper, fills og borders; billedtiling, alfablending og billedmerging. Her får man en enorm palet at trække på, så man kan lave et meget avanceret program.

Glem navnlig ikke den stærke funktion imageconvolution(). Den kan udføre mange manipulationer, der ikke er med som standard. Prøv for eksempel disse to funktionskald:

imageconvolution($image,array(array(2,0,0),array(0,-1,0),array(0,0,-1)),1,127);
imageconvolution($image,array(array(-1,-1,-1),array(-1,16,-1),array(-1,-1,-1)),8,0);

De henholdsvis præger og skærper et billede på baggrund af en tre gange tre-matrix, der bliver brugt over hele billedet. Prøv at ændre parametrene for at se, hvilke effekter du kan opnå.

Lav dine egne grafikfunktioner

Der er mange måder at forbedre dette program på. Den første er at sikre tilstrækkelig fejlsøgning. Når man modtager uploads til en offentlig server, er det afgørende, at man fjerner enhver mulighed for at blive hacket.

Man kan også tilføje en funktion, der gør det muligt for brugerne kun at forstørre en bestemt del af billedet. Man kan for eksempel bruge et image-map af billedet som del af inputtet. Så bliver det område, der er omgivet af museklikket, forstørret, så det fylder hele billedet. Denne teknik bruges ofte til at hjælpe brugerne med at vælge thumbnails ud fra større billeder.

Man kan nøjes med at tage dele af projektet, for mange af dem er nyttige i sig selv. Man kan for eksempel skrive en funktion, der automatisk ændrer et uploadet billede størrelse til ikke over 100 pixel i bredde og højde (eller hvad man nu foretrækker) og derefter skærpe resultatet (fordi resampling normalt virker slørende på thumbnails).

Funktionen imagefilter() giver et godt udvalg af færdiglavede billedtransformationer. Funktionkaldets format er:

image filter($image,filtertype [,$arg1 ,$arg4 ])

Du vil se, at “$image” er en billedressource; der kan være op til fire argumenter I “4arg1” til “$arg4”, og “filtertype” kan være en af de følgende:

IMG_FILTER_NEGATE
Ændrer alle billedets farver

IMG_FILTER_GRAYSCALE
Konverterer billedet til gråtoner

IMG_FILTER_BRIGHTNESS:
Ændrer billedets lysstyrke – brug arg1 til at indstille lysstyrken

IMG_FILTER_CONTRAST
Ændrer billedets kontrast – arg1 til at indstille kontrasten

IMG_FILTER_COLORIZE
Svarer til IMG_FILTER_GRAYSCALE, bortset fra at man specificerer farven – brug arg1, arg2 og arg3 i form af rød, grøn og blå, og arg4 til alfakanalen (hver farve går fra 0 til 255)

IMG_FILTER_EDGEDETECT
Bruger kantdetektion til at fremhæve billedets kanter

IMG_FILTER_EMBOSS
Præger billedet

IMG_FILTER_GAUSSIAN_BLUR
Slører billedet ved hjælp af Gaussmetoden

IMG_FILTER_SELECTIVE_BLUR
Slører billedet

IMG_FILTER_MEAN_REMOVAL
Bruger mean removal til at opnå en ”skitseeffekt”.

IMG_FILTER_SMOOTH
Gør billedet blødere – arg1 til at indstille blødheden.

Somme tider har imagefilter() ikke lige den transformation, man vil bruge. Det er i orden, for man kan lave sin egen. Lad imageconvolution() lave numre som prægning og skærpning på et billede, baseret på en 3x3-matrix.

[themepacific_accordion]
[themepacific_accordion_section title="Fakta"]

Det skal du bruge...

[/themepacific_accordion_section]
[/themepacific_accordion]