Archiwum

Posty oznaczone ‘obrazki’

Jak przycinać i skalować obrazki?

Kwiecień 17th, 2009 eshaem 1 komentarz

Odwiecznym problemem przy wyświetlaniu obrazków użytkowników jest przycinanie i skalowanie ich w taki sposób, który pasuje do wyglądu serwisu. Problem na pierwszy rzut oka może wydawać się dość trudny ze względu na różnorodność obrazków, które może wysłać użytkownik, posiada on jednak proste i eleganckie rozwiązanie.

lena

Lena - obiekt naszych testów

Na potrzeby wypisu założmy, że zajmujemy się przeskalowaniem powyższego obrazka z rozmiarów 300 x 300 px na 200 x 100 px. Można pomyśleć o najprostszym rozwiązaniu, czyli przeskalowaniu albo wycięciu odpowiedniego kawłka z oryginalnego obrazka. Żadne z tych rozwiązań nie jest jednak dobre, pierwsze nie poradzi sobie z plikami, w których stosunek szerokości do długości jest inny niż docelowy, drugą metodą wytniemy jedynie fragment z obrazka pomijając być może istotną część interesującą użytkowników naszego serwisu. (W szczególności problem pojawia się problem przy dużych obrazkach, którą część wycinać? Przedstawiony poniżej przykład jest nieco tendecyncyjny ze względu na małą wielkość obrazka.)

Skalowanie

Skalowanie

Wycinanie

Wycinanie

Potrzebujemy więc rozwiązanie, które pozwoli uwzględnić różny stosunek wysokości do szerokości obrazka dostarczonego przez użytkownika, oraz umożliwi pokazanie jego jak największej zawartości. Najlepiej więc będzie jeżeli będziemy tylko ścinać obraz z góry i dołu, lub lewej i prawej a następnie skalować go do żądanego rozmiaru. Niech iw i ih oznaczają odpowiednio szerokość i wysokość obrazka wejściowego, natomiast ow i oh analogicznie dla obrazka wyjściowego. Obliczamy stosunki szerokości do wysokości dla obrazków:

  • Ri = iw/ih – w naszym przypadku wynosi on 300/300 = 1
  • Ro = ow/oh – w naszym przypadku wynosi on 200/100 = 2

Ponieważ Ro jest większy niż Ri, czyli w obrazku wyjściowym przypada więcej pikseli szerokości na piksel wysokości, to powinniśmy wyciąć możliwe najmniejszy kawałek góry i dołu z oryginalnego obrazka tak aby uzyskać w nim współczynnik Ro. Wymiary pasów, które powinniśmy wyciąć obliczamy następująco:

  • dh = iw/ro – w naszym przypadku 300/2 = 150

Ponieważ sumaryczna szerokość pasa wynosi 150, to przytniemy z góry i dołu pasy o szerokości 300 i wysokości 75 i skalujemy to co zostało do rozmiaru 200 x 100 px, przyjmując optymistycznie, że na skraju obrazka znajdują się zawsze najmniej istotne treści.

Fragment obrazka do przeskalowania

Fragment obrazka do przeskalowania

Wynik działania metody

Wynik działania metody

Sytuacja gdy Ro jest mniejsze od Ri jest analogiczna, nie będę więc jej omawiał. Przedstawiony algorytm jest bardzo prosty i w pseudokodzie wygląda następująco:

Ri = iw / ih
Ro = ow / oh

if (Ri  <= Ro)
{
new_h = ow / Ro
segment = (ih-newh)/2
zetnij pas o wysokości segment z góry i dołu i przeskaluj do rozmiarów ow x oh px
}
else
{
neww = Ro * ih
segment = (iw – neww) / 2
zetnij pas o szerokości segment z lewej i prawej i przeskaluj do rozmiarów ow x oh px
}

Przy implementacji  powyższego rozwiązania w języku PHP do ścinania i skalowania przydatna może okazać się funkcja imagecopyresampled().