Programowanie w języku PHP » FAQ

Jak nałożyć jeden obrazek na drugi (coś w stylu znaku wodnego)

  • 0 komentarzy
  • 1511 odsłon
  • Oceń ten tekst jako pierwszy
Ostatnio z nudów zacząłem skrobać funkcję to robiącą z czystej nudy i ciekawości ponieważ nigdy wcześniej się w takie rzeczy nie bawiłem, więc otworzyłem manual php na stronce o bibliotece GD i po pół godziny wyskrobałem te oto funkcje, które sobie teraz tutaj pozwolę zamieścić :)

A więc aby nałożyć jeden obrazek na drugi wystarczy dodać do swojego skryptu (lub includować z innego) następujące funkcje:

<?php
 
function gpixel($im,$x,$y){
 return Array((($rgb=ImageColorAt($im,$x,$y))>>16) & 0xff,($rgb>>8) & 0xff,$rgb & 0xff);
}
 
function spixel($im,$x,$y,$color){
 ImageSetPixel($im,$x,$y,ImageColorAllocate($im,$color[0],$color[1],$color[2]));
}
 
function aspixel($pix1,$pix2,$tol=2){
 for($i=0,$podobne=true;$i<3;$i++)
  if(($pix1[$i]+$tol<$pix2[$i]) || ($pix1[$i]-$tol>$pix2[$i])) $podobne=false;
 return $podobne;
}
 
function srednia($l1,$l2,$k){
 return abs(round($l1-($l1-$l2)*$k));
}
 
function naloz($obrazek1,$obrazek2,$obrazek,$alpha=50,$nakryj=false,$tolerance=2){
 $szer=imagesx($obrazek1);  $wys=imagesy($obrazek1);
 $szer2=imagesx($obrazek2); $wys2=imagesy($obrazek2);
 if($nakryj) $trans=gpixel($obrazek2,1,1);
 
 for($i=0,$a=$alpha/100;$i<$szer;$i++){
  for($j=0;$j<$wys;$j++){
   $pixel1=gpixel($obrazek1,$i,$j);
   if($i<$szer2 && $j<$wys2) $pixel2=gpixel($obrazek2,$i,$j); else $pixel2=$pixel1;
 
   $pixel3=Array();
   if(($nakryj && aspixel($pixel2,$trans,$tolerance)) || aspixel($pixel1,$pixel2,0))
    $pixel3=$pixel1; else
    for($k=0;$k<3;$k++) $pixel3[$k]=srednia($pixel1[$k],$pixel2[$k],$a);
   spixel($obrazek,$i,$j,$pixel3);
  }
 }
}
 
?>


i teraz może omówię po części do czego one służą. Główną funkcją jest tutaj funkcja nazwana "naloz" (od "nałóż" jak coś :)) która korzysta z pozostałych funkcji dla ułatwienia sobie życia.

Pierwsza funkcja która wabi się "gpixel" (od Get Pixel), pobiera z obrazka w zmiennej $im pixel o współrzędnych $x oraz $y, a następnie zwraca kolor tego pixela w tablicy która przyjmie postać [0]=czerwony, [1]=zielony, [2]=niebieski.

Kolejna funkcja "spixel" (od Set Pixel) ustawia pixel na obrazku $im w podanej pozycji ($x, $y) - kolor pixela powinien być w postaci takiej jaką zwraca funkcja "gpixel".

Trzecia funkcja "aspixel" (od nie wiem czego :)) porównuje dwa kolory RGB i zwraca true jeśli są podobne lub false w przeciwnym wypadku, funkcja ta posiada zmienną $tol która powinna być liczbą całkowitą oznaczającą stopień podobieństwa, przykładowo jeśli ustawi się ją na 255 to każdy pixel będzie podobny do drugiego :)

Ostatnia funkcja "srednia" po prostu liczy średnią z uwzględnieniem wartości alpha (tutaj $k), czyli jeżeli alpha=0 to średnia a i b będzie równa a, jeżeli natomiast alpha=1 to średnia będzie równa b, wartość 0.5 oznacza po prostu średnią arytmetyczną tych dwóch liczb.

Funkcja "naloz" korzystając z powyższych funkcja, pobiera każdy pixel z obrazka $obrazek1 oraz $obrazek2, następnie liczy ich średnią i ustawia otrzymany pixel w obrazku $obrazek. Funkcja ta posiada również parametry:
 $alpha (to samo co wyżej, tylko tym razem wartość o 0-100),

 $nakryj to parametr określający czy $obrazek2 posiada przeźroczyste tło czy ma być nałożone jak jest, jeżeli wartość ta więc przyjmie wartość true to górny lewy pixel obrazka $obrazek2 jest ustawiany jako kolor przeźroczysty a następnie jest ignorowany w przypadku wystąpienia, parametr ten jest powiązany z kolejnym $tolerance który określa tolerancję różnicy dla pixeli tła, format jpg jest kompresowany algorytmem stratnym więc jeżeli pierwszy pixel będzie #ffffff to kolejne mogą być od niego minimalnie różne, np #fefcff, w tym celu należy zwiększać parametr $tolerance aby tło było dobrze rozpoznawane (kto wie o czym mówię ?).
Czyli w skrócie im gorsza jakość obrazka $obrazek2 tym większy powinien być tolerance w przypadku ustawienia true parametru $nakryj

Poniżej przedstawiam sposób w jaki można wykorzystać powyższą funkcję:

<?php
 
$alpha=30;
 // 0   = drugi obrazek bardziej przeźroczysty
 // 50  = obrazki równo przeźroczyste
 // 100 = pierwszy obrazek bardziej przeźroczysty
 
$nakryj=true;
 // true  = drugi obrazek ma mieć przeźroczyste tło
 // false = oba obrazki są tak samo ważne
 
$obrazek1=ImageCreateFromJpeg('jpg1.jpg'); // obrazek źródłowy
$obrazek2=ImageCreateFromJpeg('jpg2.jpg'); // obrazek do nałożenia
$obrazek=ImageCreateTrueColor(imagesx($obrazek1),imagesy($obrazek1)); // obrazek roboczy
 
naloz($obrazek1,$obrazek2,$obrazek,$alpha,$nakryj,50);
 
Header('Content-type: image/jpeg'); // ustawienie Content-type (nagłówek dla przeglądarki)
ImageJpeg($obrazek); // przesłanie do przeglądarki obrazka
 
ImageDestroy($obrazek1); // zniszcz je !
ImageDestroy($obrazek2);
ImageDestroy($obrazek);
 
?>


Ponieważ powyższe funkcje operują na pixelach więc mogą być bardzo długo wykonywane dla większych obrazków.

Teraz przedstawiam przykładowe obrazki uzyskane przy pomocy tych funkcji, w każdym przypadku obrazkami wejściowymi są:

jpg1.jpg:

JPG1

jpg2.jpg:

JPG2

Pierwszy przykład:

naloz($obrazek1,$obrazek2,$obrazek,  70  ,  false  ,50);


(zauważ że parametr tolerance trzeba było ustawić aż na 50 ponieważ jakość jpg2.jpg jest bardzo słaba)

Wynik:

Alpha = 70, Naloz = false

Drugi przykład:

naloz($obrazek1,$obrazek2,$obrazek,  30  ,  false  ,50);


Wynik:

Alpha = 30, Naloz = false

Trzeci przykład:

naloz($obrazek1,$obrazek2,$obrazek,  70  ,  true  ,50);


Wynik:

Alpha = 70, Naloz = true

Czwarty przykład:

naloz($obrazek1,$obrazek2,$obrazek,  30  ,  true  ,50);


Wynik:

Alpha = 30, Naloz = true

Do działania wymagana jest aktywna biblioteka GD2 w PHP :)
Może się komuś przyda, pozdro (chociaż pewnie jest jakaś wbudowana funkcja w php, albo gotowe i lepsze na innej stronce, ale tak się chociaż ja czegoś nauczyłem :P)