Jaki jest najskuteczniejszy sposób zmiany rozmiaru dużych obrazów w PHP?
Obecnie używam funkcji GD imagecopyresampled do robienia zdjęć w wysokiej rozdzielczości i czystego zmniejszania ich rozmiaru do rozmiaru do przeglądania w Internecie (około 700 pikseli szerokości i 700 pikseli wysokości).
Działa to świetnie w przypadku małych (poniżej 2 MB) zdjęć, a cała operacja zmiany rozmiaru zajmuje mniej niż sekundę na serwerze. Jednak witryna docelowo będzie obsługiwać fotografów, którzy mogą przesyłać zdjęcia o rozmiarze do 10 MB (lub obrazy o rozmiarze do 5000x4000 pikseli).
Wykonywanie tego rodzaju operacji zmiany rozmiaru z dużymi obrazami ma tendencję do zwiększania zużycia pamięci o bardzo duży margines (większe obrazy mogą zwiększyć użycie pamięci dla skryptu powyżej 80 MB). Czy istnieje sposób, aby operacja zmiany rozmiaru była bardziej wydajna? Czy powinienem używać alternatywnej biblioteki obrazów, takiej jak ImageMagick ?
W tej chwili kod zmiany rozmiaru wygląda mniej więcej tak
function makeThumbnail($sourcefile, $endfile, $thumbwidth, $thumbheight, $quality) {
// Takes the sourcefile (path/to/image.jpg) and makes a thumbnail from it
// and places it at endfile (path/to/thumb.jpg).
// Load image and get image size.
$img = imagecreatefromjpeg($sourcefile);
$width = imagesx( $img );
$height = imagesy( $img );
if ($width > $height) {
$newwidth = $thumbwidth;
$divisor = $width / $thumbwidth;
$newheight = floor( $height / $divisor);
} else {
$newheight = $thumbheight;
$divisor = $height / $thumbheight;
$newwidth = floor( $width / $divisor );
}
// Create a new temporary image.
$tmpimg = imagecreatetruecolor( $newwidth, $newheight );
// Copy and resize old image into new image.
imagecopyresampled( $tmpimg, $img, 0, 0, 0, 0, $newwidth, $newheight, $width, $height );
// Save thumbnail into a file.
imagejpeg( $tmpimg, $endfile, $quality);
// release the memory
imagedestroy($tmpimg);
imagedestroy($img);
Oto fragment z dokumentów php.net, których użyłem w projekcie i działa dobrze:
<? function fastimagecopyresampled (&$dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h, $quality = 3) { // Plug-and-Play fastimagecopyresampled function replaces much slower imagecopyresampled. // Just include this function and change all "imagecopyresampled" references to "fastimagecopyresampled". // Typically from 30 to 60 times faster when reducing high resolution images down to thumbnail size using the default quality setting. // Author: Tim Eckel - Date: 09/07/07 - Version: 1.1 - Project: FreeRingers.net - Freely distributable - These comments must remain. // // Optional "quality" parameter (defaults is 3). Fractional values are allowed, for example 1.5. Must be greater than zero. // Between 0 and 1 = Fast, but mosaic results, closer to 0 increases the mosaic effect. // 1 = Up to 350 times faster. Poor results, looks very similar to imagecopyresized. // 2 = Up to 95 times faster. Images appear a little sharp, some prefer this over a quality of 3. // 3 = Up to 60 times faster. Will give high quality smooth results very close to imagecopyresampled, just faster. // 4 = Up to 25 times faster. Almost identical to imagecopyresampled for most images. // 5 = No speedup. Just uses imagecopyresampled, no advantage over imagecopyresampled. if (empty($src_image) || empty($dst_image) || $quality <= 0) { return false; } if ($quality < 5 && (($dst_w * $quality) < $src_w || ($dst_h * $quality) < $src_h)) { $temp = imagecreatetruecolor ($dst_w * $quality + 1, $dst_h * $quality + 1); imagecopyresized ($temp, $src_image, 0, 0, $src_x, $src_y, $dst_w * $quality + 1, $dst_h * $quality + 1, $src_w, $src_h); imagecopyresampled ($dst_image, $temp, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $dst_w * $quality, $dst_h * $quality); imagedestroy ($temp); } else imagecopyresampled ($dst_image, $src_image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h); return true; } ?>
http://us.php.net/manual/en/function.imagecopyresampled.php#77679
źródło
$quality + 1
z($quality + 1)
? W obecnej sytuacji po prostu zmieniasz rozmiar za pomocą bezużytecznego dodatkowego piksela. Gdzie jest kontrola zwarcia, kiedy$dst_w * $quality
jest>$src_w
?imagecopyresized()
. Zasadniczo polega to najpierw na zmianie rozmiaru obrazu do rozsądnego rozmiaru (final dimensions
pomnożonego przezquality
), a następnie ponowne próbkowanie, a nie po prostu ponowne próbkowanie obrazu w pełnym rozmiarze. Może to skutkować niższą jakością obrazu końcowego, ale zużywa znacznie mniej zasobów dla większych obrazów niżimagecopyresampled()
sam, ponieważ algorytm ponownego próbkowania musi domyślnie radzić sobie tylko z obrazem o rozmiarze 3x ostatecznych wymiarów, w porównaniu do obrazu w pełnym rozmiarze ( który może być znacznie większy, szczególnie w przypadku zdjęć, których rozmiar jest zmieniany w przypadku miniatur).phpThumb używa ImageMagicka, gdy tylko jest to możliwe dla szybkości (w razie potrzeby cofa się do GD) i wydaje się, że całkiem dobrze buforuje, aby zmniejszyć obciążenie serwera. Jest dość lekki do wypróbowania (aby zmienić rozmiar obrazu, po prostu wywołaj phpThumb.php z zapytaniem GET, które zawiera nazwę pliku graficznego i wymiary wyjściowe), więc możesz dać mu szansę, aby sprawdzić, czy spełnia Twoje potrzeby.
źródło
W przypadku większych obrazów użyj libjpeg, aby zmienić rozmiar podczas ładowania obrazu w ImageMagick, a tym samym znacznie zmniejszyć zużycie pamięci i poprawić wydajność, nie jest to możliwe w przypadku GD.
$im = new Imagick(); try { $im->pingImage($file_name); } catch (ImagickException $e) { throw new Exception(_('Invalid or corrupted image file, please try uploading another image.')); } $width = $im->getImageWidth(); $height = $im->getImageHeight(); if ($width > $config['width_threshold'] || $height > $config['height_threshold']) { try { /* send thumbnail parameters to Imagick so that libjpeg can resize images * as they are loaded instead of consuming additional resources to pass back * to PHP. */ $fitbyWidth = ($config['width_threshold'] / $width) > ($config['height_threshold'] / $height); $aspectRatio = $height / $width; if ($fitbyWidth) { $im->setSize($config['width_threshold'], abs($width * $aspectRatio)); } else { $im->setSize(abs($height / $aspectRatio), $config['height_threshold']); } $im->readImage($file_name); /* Imagick::thumbnailImage(fit = true) has a bug that it does fit both dimensions */ // $im->thumbnailImage($config['width_threshold'], $config['height_threshold'], true); // workaround: if ($fitbyWidth) { $im->thumbnailImage($config['width_threshold'], 0, false); } else { $im->thumbnailImage(0, $config['height_threshold'], false); } $im->setImageFileName($thumbnail_name); $im->writeImage(); } catch (ImagickException $e) { header('HTTP/1.1 500 Internal Server Error'); throw new Exception(_('An error occured reszing the image.')); } } /* cleanup Imagick */ $im->destroy();
źródło
Z twojego pytania, wygląda na to, że jesteś trochę nowy w GD, podzielę się swoim doświadczeniem, może to trochę nie na temat, ale myślę, że będzie to pomocne dla kogoś nowego w GD jak ty:
Krok 1, zweryfikuj plik. Użyj następującej funkcji, aby sprawdzić, czy
$_FILES['image']['tmp_name']
plik jest prawidłowym plikiem:function getContentsFromImage($image) { if (@is_file($image) == true) { return file_get_contents($image); } else { throw new \Exception('Invalid image'); } } $contents = getContentsFromImage($_FILES['image']['tmp_name']);
Krok 2, pobierz format pliku Wypróbuj następującą funkcję z rozszerzeniem finfo, aby sprawdzić format pliku (zawartość). Powiedziałbyś, dlaczego nie użyjesz po prostu
$_FILES["image"]["type"]
do sprawdzenia formatu pliku? Ponieważ sprawdza TYLKO rozszerzenie pliku, a nie zawartość pliku, jeśli ktoś zmieni nazwę pliku pierwotnie nazwanego świat.png na świat.jpg ,$_FILES["image"]["type"]
zwróci jpeg, a nie png, więc$_FILES["image"]["type"]
może zwrócić zły wynik.function getFormatFromContents($contents) { $finfo = new \finfo(); $mimetype = $finfo->buffer($contents, FILEINFO_MIME_TYPE); switch ($mimetype) { case 'image/jpeg': return 'jpeg'; break; case 'image/png': return 'png'; break; case 'image/gif': return 'gif'; break; default: throw new \Exception('Unknown or unsupported image format'); } } $format = getFormatFromContents($contents);
Krok 3, Pobierz zasób GD Pobierz zasób GD z wcześniejszej zawartości:
function getGDResourceFromContents($contents) { $resource = @imagecreatefromstring($contents); if ($resource == false) { throw new \Exception('Cannot process image'); } return $resource; } $resource = getGDResourceFromContents($contents);
Krok 4, uzyskaj wymiar obrazu Teraz możesz uzyskać wymiar obrazu za pomocą następującego prostego kodu:
$width = imagesx($resource); $height = imagesy($resource);
Zobaczmy teraz, jaką zmienną otrzymaliśmy z oryginalnego obrazu:
$contents, $format, $resource, $width, $height OK, lets move on
Krok 5, oblicz argumenty obrazu o zmienionym rozmiarze Ten krok jest związany z twoim pytaniem, celem poniższej funkcji jest uzyskanie argumentów zmiany rozmiaru dla funkcji GD
imagecopyresampled()
, kod jest trochę długi, ale działa świetnie, ma nawet trzy opcje: stretch, shrink i wypełnij.stretch : rozmiar obrazu wyjściowego jest taki sam, jak nowy ustawiony wymiar. Nie zachowa stosunku wysokości do szerokości.
shrink : rozmiar obrazu wyjściowego nie przekroczy nowego wymiaru, który podasz, i zachowaj stosunek wysokości do szerokości obrazu.
fill : rozmiar obrazu wyjściowego będzie taki sam jak nowy wymiar, który podasz, wrazie potrzeby przycina i zmienia rozmiar obrazu, zachowując stosunek wysokości do szerokości. Ta opcja jest tym, czego potrzebujesz w swoim pytaniu.
function getResizeArgs($width, $height, $newwidth, $newheight, $option) { if ($option === 'stretch') { if ($width === $newwidth && $height === $newheight) { return false; } $dst_w = $newwidth; $dst_h = $newheight; $src_w = $width; $src_h = $height; $src_x = 0; $src_y = 0; } else if ($option === 'shrink') { if ($width <= $newwidth && $height <= $newheight) { return false; } else if ($width / $height >= $newwidth / $newheight) { $dst_w = $newwidth; $dst_h = (int) round(($newwidth * $height) / $width); } else { $dst_w = (int) round(($newheight * $width) / $height); $dst_h = $newheight; } $src_x = 0; $src_y = 0; $src_w = $width; $src_h = $height; } else if ($option === 'fill') { if ($width === $newwidth && $height === $newheight) { return false; } if ($width / $height >= $newwidth / $newheight) { $src_w = (int) round(($newwidth * $height) / $newheight); $src_h = $height; $src_x = (int) round(($width - $src_w) / 2); $src_y = 0; } else { $src_w = $width; $src_h = (int) round(($width * $newheight) / $newwidth); $src_x = 0; $src_y = (int) round(($height - $src_h) / 2); } $dst_w = $newwidth; $dst_h = $newheight; } if ($src_w < 1 || $src_h < 1) { throw new \Exception('Image width or height is too small'); } return array( 'dst_x' => 0, 'dst_y' => 0, 'src_x' => $src_x, 'src_y' => $src_y, 'dst_w' => $dst_w, 'dst_h' => $dst_h, 'src_w' => $src_w, 'src_h' => $src_h ); } $args = getResizeArgs($width, $height, 150, 170, 'fill');
Krok 6, zmiana rozmiaru obrazu Użyj
$args
,$width
,$height
,$format
i $ zasób dostaliśmy od góry do następujących funkcji i uzyskać nowy zasób o zmienionym rozmiarze:function runResize($width, $height, $format, $resource, $args) { if ($args === false) { return; //if $args equal to false, this means no resize occurs; } $newimage = imagecreatetruecolor($args['dst_w'], $args['dst_h']); if ($format === 'png') { imagealphablending($newimage, false); imagesavealpha($newimage, true); $transparentindex = imagecolorallocatealpha($newimage, 255, 255, 255, 127); imagefill($newimage, 0, 0, $transparentindex); } else if ($format === 'gif') { $transparentindex = imagecolorallocatealpha($newimage, 255, 255, 255, 127); imagefill($newimage, 0, 0, $transparentindex); imagecolortransparent($newimage, $transparentindex); } imagecopyresampled($newimage, $resource, $args['dst_x'], $args['dst_y'], $args['src_x'], $args['src_y'], $args['dst_w'], $args['dst_h'], $args['src_w'], $args['src_h']); imagedestroy($resource); return $newimage; } $newresource = runResize($width, $height, $format, $resource, $args);
Krok 7, pobierz nową zawartość , użyj następującej funkcji, aby pobrać zawartość z nowego zasobu GD:
function getContentsFromGDResource($resource, $format) { ob_start(); switch ($format) { case 'gif': imagegif($resource); break; case 'jpeg': imagejpeg($resource, NULL, 100); break; case 'png': imagepng($resource, NULL, 9); } $contents = ob_get_contents(); ob_end_clean(); return $contents; } $newcontents = getContentsFromGDResource($newresource, $format);
Krok 8 pobierz rozszerzenie , użyj następującej funkcji, aby uzyskać rozszerzenie z formatu obrazu (uwaga, format obrazu nie jest równy rozszerzeniu obrazu):
function getExtensionFromFormat($format) { switch ($format) { case 'gif': return 'gif'; break; case 'jpeg': return 'jpg'; break; case 'png': return 'png'; } } $extension = getExtensionFromFormat($format);
Krok 9 zapisz obraz Jeśli mamy użytkownika o imieniu mike, możesz wykonać następujące czynności, zapisze on w tym samym folderze co ten skrypt php:
$user_name = 'mike'; $filename = $user_name . '.' . $extension; file_put_contents($filename, $newcontents);
Krok 10 zniszcz zasoby Nie zapomnij zniszczyć zasobów GD!
imagedestroy($newresource);
lub możesz napisać cały swój kod w klasie i po prostu użyć następującego:
public function __destruct() { @imagedestroy($this->resource); }
WSKAZÓWKI
Nie radzę konwertować formatu pliku przesłanego przez użytkownika, napotkasz wiele problemów.
źródło
Sugeruję, abyś popracował nad tym w następujący sposób:
Aby używać programu ImageMagick w tle, przenieś przesłane pliki do folderu tymczasowego i zaplanuj zadanie CRON, które „konwertuje” wszystkie pliki do formatu jpeg i odpowiednio zmieni ich rozmiar. Zobacz składnię poleceń pod adresem: imagemagick-command line processing
Możesz zapytać użytkownika, że plik został przesłany i zaplanowany do przetworzenia. Zadanie CRON można zaplanować na codzienne uruchamianie w określonych odstępach czasu. Obraz źródłowy można usunąć po przetworzeniu, aby mieć pewność, że nie zostanie przetworzony dwukrotnie.
źródło
Słyszałem wiele rzeczy o bibliotece Imagick, niestety nie mogłem jej zainstalować na swoim komputerze w pracy ani w domu (i wierz mi, spędziłem wiele godzin na wszelkiego rodzaju forach).
Afterwords zdecydowałem się wypróbować tę klasę PHP:
http://www.verot.net/php_class_upload.htm
Jest całkiem fajny i mogę zmienić rozmiar wszystkich rodzajów obrazów (mogę je również przekonwertować na JPG).
źródło
ImageMagick jest wielowątkowy, więc wydaje się być szybszy, ale w rzeczywistości zużywa o wiele więcej zasobów niż GD. Jeśli uruchomiłeś równolegle kilka skryptów PHP, wszystkie przy użyciu GD, pokonałyby one ImageMagick w szybkości dla prostych operacji. ExactImage jest mniej wydajne niż ImageMagick, ale dużo szybsze, chociaż nie jest dostępne w PHP, musisz je zainstalować na serwerze i uruchomić
exec
.źródło
W przypadku większych obrazów użyj phpThumb () . Oto jak go używać: http://abcoder.com/php/problem-with-resizing-corrupted-images-using-php-image-functions/ . Działa również w przypadku dużych uszkodzonych obrazów.
źródło