Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP]+GD Przejście między zdjęciami
Forum PHP.pl > Forum > PHP
Kazaan
Witam, jak w temacie, chodzi mi o stworzenie skryptu dzieki ktoremu uzyskal bym przejscie między dwoma zdjęciami przy użyciu gd...
macie jakies pomysły ? jedyne co przychodzi mi do glowy to stopniowe nakladanie transparentu z jednej strony zdjecia do polowy, i tak przygotowane zdjecie nalozyc na kolejne. Kwestia jak to zrobic ?
thek
GD niestety nie ma nic do zaoferowania sensownego by zrobić takie przejście płynne i, co tu dużo mówić ładnie oraz naturalnie wyglądające, trzeba się posiłkować niestety dość wolno działającymi skryptami, a do tego mało wydajnymi, bo liczenie jest dla każdego piksela obrazu. Sam kiedyś podobny filtr pisałem ale w Matlabie. To przypomina symulację nadawania przezroczystości dynamicznie. Jak działał?
1. Brałem dwa obrazy A i B o tej samej rozdzielczości (jeśli nie były - przeskalowywałem do identycznej)
2. W pętli po całej szerokości robiłem wzorek mniej więcej taki
  1. $A = //pierwszy obrazek w formacie GDimage
  2. $B = //drugi obrazek w fomacie GDimage
  3. $wynik = = imagecreatetruecolor($szerokosc, $wysokosc);
  4. for($i = 0; $i<$szerokosc; $i++) {
  5. $stopienA = $i/$szerokosc;
  6. $stopienB = 1 - $i/$szerokosc;
  7. for( $j = 0; $j< $wysokosc; $j++ ) {
  8. $colorA = imagecolorsforindex($im, imagecolorat($A, $i, $j) );
  9. $colorB = imagecolorsforindex($im, imagecolorat($B, $i, $j) );
  10. $color = imagecolorallocate($wynik, (int)($colorA['red']*$stopienA+$colorB['red']*$stopienB), (int)($colorA['green']*$stopienA+$colorB['green']*$stopienB), (int)($colorA['blue']*$stopienA+$colorB['blue']*$stopienB) );
  11. imagesetpixel($wynik, $i, $j, $color);
  12. }
  13. }

Możesz owszem kombinować z nadawaniem obu obrazkom tylko poziomu alfa w pętli, ale kod nie różni się wiele od tego powyżej. Różnica leży bowiem tylko w tym jak piksele obrazu traktujesz. A efekt wcale lepszy nie jest. Tylko zwracaj uwagę na szerokość i wysokość, bo we wszystkich 3 muszą być identyczne. Jeśli nie będą, to musisz sam sobie przeskalować A, B i wynik by były! Wydajne to nie jest, ale niestety GD nie ma wsparcia dla nadawania w prosty sposób alfy konkretnemu pikselowi lub grupie pikseli i trzeba to samemu implementować poprzez manipulacją alfy dla każdego piksela z osobna lub poprzez cudowanie. Możesz ewentualnie jeden z obrazów użyć od razu jako wynikowy i to będzie jedyna optymalizacja w sumie.

PS... Musisz uważać na zaokrąglenia dla wartości skrajnych. Jeśli trzeba będzie to daj zaokrąglenie w dół bo jest pewniejsze w tym wypadku.
Kazaan
Wielkie dzieki thek! chociaz akurat ta wypowiedzia bardzo mnie zniecheciles do GD biggrin.gif wiec postanowilem zakupic hosting z imagemagick (1and1.pl), ktory jest o niebo lepszy, choc juz pojawily sie pierwsze problemy... Kod ktory ma robic to co opisalem w 1 poscie wyglada tak:

exec('fota1.gif -extent 500x100 fota2.gif \
maska.png -gravity East -composite wynik.jpg',$out,$err);


Tak przynajmniej opisuja to w manualu, niestety wynik tej funkcji to fota2.gif z nalozona czarna maska, i na to wklejona bez maski fota1.gif

Ma ktos doswiadczenie z IM i wie co tu nie gra ?
aeaeae
Cytat
trzeba się posiłkować niestety dość wolno działającymi skryptami, a do tego mało wydajnymi, bo liczenie jest dla każdego piksela obrazu


Gdyby GD miała jakąś funkcję wbudowaną do tego, to ta funkcja nie liczyłaby dla każdego piksela? :/
Kazaan
Przetestowalem pare gotowcow ze strony http://www.imagemagick.org/Usage/photos/ i musze stwierdzic ze 2/3 przykladow wogole nie dziala, nie wiedziec czemu "/
thek
Gdyby GD miało wsparcie, to pewnie z racji na zastosowanie doszły by funkcje nadające kolor alfa dla: piksela, wiersza, kolumny obrazu. Bo to co robię tutaj, to efekt podobny do zmiany przezroczystości obrazów kolumnami (od lewej do prawej). Jeśli zamieniłbym od 4 linijki słowa $wysokosc i szerokosc to byłoby to analogiczne do przenikania się obrazów wierszami (od góry do dołu). I gdyby było wsparcie dla nadawania takiego, to bym tykał tylko kanał alfa, a nie grzebał się z choćby imagecolorallocatealpha dla każdego z obrazków, których złożenie potem było by równie problematyczne.
aeaeae
Ok, w php "tykałbyś" kanał alfa kolumnami, ale i tak funkcja "tykająca" musiałaby "tyknąć" każdy jeden piksel wybranej kolumny osobno.
Ktoś wie jak to się ma do wydajności?
thek
To co Ty chcesz osiągnąć w GD wiązało by się z:
1. Ustawieniem imagealphablending na true
2. Wybrania source image
3. Takiego zmodyfikowania obrazka dołączanego, aby jego parametry alpha każdego piksela tworzyły maskę.
4. Połączenia obrazków.

Tak czy inaczej nie obejdzie się bez pętli po całym obrazku dołączanym, który "wprowadzi" odpowiednią maskę do pikseli rozkładając odpowiednio poziomy przezroczystości między 0 (pokrywający) a 127 (całkowicie przezroczysty). Obliczenia ciut mniej wymagające ($i*127/$szerokosc), ale wydajność sobie przetestuj sam, bo dojdzie jeszcze dość kosztowne złożenie obrazków na końcu. Ogólnie da się, ale są to już przetwarzania obrazów dość pamięciożerne, więc nie licz na super wydajność niezależnie czy GD czy Imagemagick. IM ma przewagę, bo jest wykonywany w powłoce, co dość dużo mu daje na plus w tym wypadku.
aeaeae
Kazaan, napisz dokładniej jak to ma wyglądać. Może np. obrazki mają po 300px szerokości a nachodzą na siebie tylko na szerokości 100px, tak że tworzą razem 500px?

Sposób podany przez użytkownika thek można zmodyfikować w taki sposób, że $stopienA i $stopienB nie są liniowo a np wykładniczo zależne od szerokości - wtedy dla wąskich kolumn przejścia między obrazkami można osiągnąć lepszy efekt.
Kazaan
Cytat(aeaeae @ 7.03.2011, 15:31:17 ) *
Kazaan, napisz dokładniej jak to ma wyglądać. Może np. obrazki mają po 300px szerokości a nachodzą na siebie tylko na szerokości 100px, tak że tworzą razem 500px?

Sposób podany przez użytkownika thek można zmodyfikować w taki sposób, że $stopienA i $stopienB nie są liniowo a np wykładniczo zależne od szerokości - wtedy dla wąskich kolumn przejścia między obrazkami można osiągnąć lepszy efekt.


Dokladnie tak jak napisales, przykladowo mam 2 obrazki kazdy po 500px i grafika wyjsciowa ma miec 900px...
thek
W takim razie więc:
1. Stwórz obraz o ostatecznej wielkości.
2. Skopiuj na niego obrazek A
3. Ustaw alpha blending na true
4. Dla obrazka B znajdź część wspólną obrazka A i B (100px) oraz w skrypcie ustaw odpowiednio wartości dla alpha przeliczając sobie.
5. Skopiuj na wynikowy obrazek ten fragment.
6. Wyłącz alpha blending
7. Skopiuj pozostałą część obrazka B na wynikowy.
8. Zapisz plik.
9. Jeśli nie zadziała to sprawdź czy aby kanałem alpha nie trzeba się zabawić poprzez imagesavealpha.

Tak minimalizujesz obniżenie wydajności poprzez operację tylko dla części wspólnej obu.
Kazaan
Cytat(thek @ 7.03.2011, 16:17:45 ) *
W takim razie więc:
1. Stwórz obraz o ostatecznej wielkości.
2. Skopiuj na niego obrazek A
3. Ustaw alpha blending na true
4. Dla obrazka B znajdź część wspólną obrazka A i B (100px) oraz w skrypcie ustaw odpowiednio wartości dla alpha przeliczając sobie.
5. Skopiuj na wynikowy obrazek ten fragment.
6. Wyłącz alpha blending
7. Skopiuj pozostałą część obrazka B na wynikowy.
8. Zapisz plik.
9. Jeśli nie zadziała to sprawdź czy aby kanałem alpha nie trzeba się zabawić poprzez imagesavealpha.

Tak minimalizujesz obniżenie wydajności poprzez operację tylko dla części wspólnej obu.



Jak napisaleś tak 'prawie' zarobilem biggrin.gif
Po pierwsze robie to w 2 krokach:

1. Na pierwszy obrazek nakładam maskę z gradientem biało -> czarnym - i na podstawie maski wyliczany jest narastajacy transparent
2. Na drugi obrazek nakladam odwrotną maskę.
3. Składam dwa odpowiednio przygotowane obrazki w jeden, z tym że mam jedna uwage, alpha blending musi byc ustawiona na false.

przykładowy kod (nie mój) nakładajacy maske na obrazek:

  1. function nalozMaske( &$picture, $mask, $name) {
  2. // Get sizes and set up new picture
  3. $xSize = imagesx( $mask );
  4. $ySize = imagesy( $mask );
  5. $newPicture = imagecreatetruecolor( $xSize, $ySize );
  6. imagesavealpha( $newPicture, true );
  7. imagefill( $newPicture, 0, 0, imagecolorallocatealpha( $newPicture, 0, 0, 0, 127 ) );
  8.  
  9.  
  10. // Perform pixel-based alpha map application
  11. for( $x = 0; $x < $xSize; $x++ ) {
  12. for( $y = 0; $y < $ySize; $y++ ) {
  13. $alpha = imagecolorsforindex( $mask, imagecolorat( $mask, $x, $y ) );
  14. $alpha = 127 - floor( $alpha[ 'red' ] / 2 );
  15. $color = imagecolorsforindex( $picture, imagecolorat( $picture, $x, $y ) );
  16. imagesetpixel( $newPicture, $x, $y, imagecolorallocatealpha( $newPicture, $color[ 'red' ], $color[ 'green' ], $color[ 'blue' ], $alpha ) );
  17. }
  18. }
  19.  
  20. // Copy back to original picture
  21. imagedestroy( $picture );
  22. $picture = $newPicture;
  23. imagepng($picture,$name);
  24. }


Z nakładanie fotek chyba sobie poradzicie.


A tu mam pytanie do znawców imagemagick: jest to kod ktory powinien robic dokladnie to co bylo opisane wyzej, z ta roznica ze w 2 linijkach.
Kod jest skopiowany z manuala, zmienilem tylko nazwy plikow, pytanie brzmi: dlaczego to nie dziala?

  1. obrazek1.png -extent 800x200 obrazek2.png \
  2. maska.png -gravity East -composite fota_wynik.jpg
  3.  



Duże podziękowania dla thek! smile.gif


thek
Z alphablendingiem trzeba uważać jak sam zauważyłeś. Zwróć uwagę, że ja raz go włączam, a raz wyłączam bo to zależy od tego co robię. U Ciebie w kodzie zwróć uwagę na funkcję imagesavealpha, która załatwia problem przezroczystości. Ogólnie jeśli coś Ci nie wychodzi z przezroczystością to jest to niemal zawsze wina albo imagesavealpha albo alphablendingu źle ustawionych i trzeba to sobie samemu dopasować.

To co napisałeś o wyłączeniu alphablendingu wynika z dokumentacji imagesavealpha -> You have to unset alphablending (imagealphablending($im, false)), to use it. Ot cała tajemnica. Ja kombinowałem bez funkcji imagesavealpha i musiałem w odpowiednich miejscach włączać i wyłączać alphablending. Stąd ciut inaczej to wyglądało, ale efekt powinien być identyczny. Najważniejsze, że osiągnąłeś co trzeba i tylko to się liczy.


A co do IM, to nie wiem czy zauważyłeś, ale zapisujesz obrazek jako jpg. A ten nie wspiera obsługi przezroczystości i stąd mogą się pojawić problemy. Obejściem jest zastosowanie jako wyjścia pliku png lub skryptu, który symuluje przezroczystość w sposób jaki zaprezentowałem w swoim pierwszym poście. Bierzesz nakładające się punkty i obliczasz średnią obu obrazów dla danego punktu. Przykładowo w 3/4 obszaru wspólnego, patrząc od lewej strony, piksel obrazu A będzie miał przezroczystość 3/4, a B będzie miał 1/4. Jaki efekt? Wynikowy piksel to będzie:
(1-3/4) * A( R, G, B ) + (1-1/4) * B( R, G, B )
Co wywoła wrażenie przenikania się obu obrazów, choć przezroczystości się tu nie ustawia wcale. Zwyczajnie kolory jednego z obrazów zaczynają stopniowo dominować nad drugim. taka sztuczka pozwalająca oszukać oko bez usiekania się do masek przezroczystości w formatach nie obsługujących jej, a mających złożenie wykonać. Można w ten sposób utworzyć maskę obrazu z "wagami przezroczystości" przy składaniu dwóch obrazów. Jeśli chce się składać kilka obrazów, to niestety, ale dla każdego obrazu trzeba taką utworzyć i suma wag wszystkich pikseli w maskach będących nad sobą powinna równać się 1. W ten sposób określasz lepszą lub gorszą widoczność konkretnego obrazka. Niestety im więcej zdjęć tym większe rozmycie i uśrednienie kolorów, przez co obraz staje się jedną, wielką plamą wink.gif
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.