Масштабирование изображения с использованием PHP и поддержание соотношения сторон

В основном я хочу загрузить изображение (которое я отсортировал) и масштабировать его до определенных ограничений, таких как максимальная ширина и высота, но сохранить соотношение сторон исходного изображения.

У меня нет Imagick, установленного на сервере, иначе это было бы легко.

Любая помощь приветствуется, как всегда. Благодарю.

EDIT: мне не нужен весь код или что-то еще, просто толчок в правильном направлении будет фантастическим.

Solutions Collecting From Web of "Масштабирование изображения с использованием PHP и поддержание соотношения сторон"

Я написал такой код для другого проекта, который я сделал. Я скопировал его ниже, может потребоваться немного возиться! (Требуется библиотека GD)

Это необходимые параметры:

$image_name - Name of the image which is uploaded $new_width - Width of the resized photo (maximum) $new_height - Height of the resized photo (maximum) $uploadDir - Directory of the original image $moveToDir - Directory to save the resized image 

Он уменьшит или уменьшит изображение до максимальной ширины или высоты

 function createThumbnail($image_name,$new_width,$new_height,$uploadDir,$moveToDir) { $path = $uploadDir . '/' . $image_name; $mime = getimagesize($path); if($mime['mime']=='image/png') { $src_img = imagecreatefrompng($path); } if($mime['mime']=='image/jpg' || $mime['mime']=='image/jpeg' || $mime['mime']=='image/pjpeg') { $src_img = imagecreatefromjpeg($path); } $old_x = imageSX($src_img); $old_y = imageSY($src_img); if($old_x > $old_y) { $thumb_w = $new_width; $thumb_h = $old_y*($new_height/$old_x); } if($old_x < $old_y) { $thumb_w = $old_x*($new_width/$old_y); $thumb_h = $new_height; } if($old_x == $old_y) { $thumb_w = $new_width; $thumb_h = $new_height; } $dst_img = ImageCreateTrueColor($thumb_w,$thumb_h); imagecopyresampled($dst_img,$src_img,0,0,0,0,$thumb_w,$thumb_h,$old_x,$old_y); // New save location $new_thumb_loc = $moveToDir . $image_name; if($mime['mime']=='image/png') { $result = imagepng($dst_img,$new_thumb_loc,8); } if($mime['mime']=='image/jpg' || $mime['mime']=='image/jpeg' || $mime['mime']=='image/pjpeg') { $result = imagejpeg($dst_img,$new_thumb_loc,80); } imagedestroy($dst_img); imagedestroy($src_img); return $result; } 

На самом деле принятое решение не является правильным решением. Причина проста: будут случаи, когда отношение исходного изображения и отношение целевого изображения будут разными. Любой расчет должен отражать эту разницу.

Обратите внимание на соответствующие строки из примера, приведенного на веб-сайте PHP.net:

 $ratio_orig = $width_orig/$height_orig; if ($width/$height > $ratio_orig) { $width = $height*$ratio_orig; } else { $height = $width/$ratio_orig; } 

Полный пример можно найти здесь: http://php.net/manual/en/function.imagecopyresampled.php

Есть другие ответы (примеры) на stackoverflow на похожие вопросы (тот же вопрос сформулирован по-другому), которые страдают от одной и той же проблемы.

Пример:

Предположим, у нас есть изображение размером 1630 x 2400 пикселей, которое мы хотим автоматически изменить, сохраняя соотношение сторон к 160 x 240. Давайте сделаем некоторую математику, приняв принятое решение:

 if($old_x < $old_y) { $thumb_w = $old_x*($new_width/$old_y); $thumb_h = $new_height; } 

высота = 240 ширина = 1630 * (160/2400) = 1630 * 0.0666666666666667 = 108.6666666666667 108.6 x 240 это неправильное решение.

Следующее предлагаемое решение следующее:

 if($old_x < $old_y) { $thumb_w = $old_x/$old_y*$newHeight; $thumb_h = $newHeight; } 

высота = 240; width = 1630/2400 * 240 = 163 Лучше (поскольку он поддерживает соотношение сторон), но он превысил максимально допустимую ширину.

Оба терпят неудачу.

Мы делаем математику в соответствии с решением, предложенным PHP.net: width = 160 height = 160 / (1630/2400) = 160 / 0.6791666666666667 = 235.5828220858896 (предложение else ). 160 x 236 (округленный) – правильный ответ.

Формула неверна для сохранения соотношения сторон. Это должно быть: оригинальная высота / оригинальная ширина x новая ширина = новая высота

 function createThumbnail($imageName,$newWidth,$newHeight,$uploadDir,$moveToDir) { $path = $uploadDir . '/' . $imageName; $mime = getimagesize($path); if($mime['mime']=='image/png'){ $src_img = imagecreatefrompng($path); } if($mime['mime']=='image/jpg'){ $src_img = imagecreatefromjpeg($path); } if($mime['mime']=='image/jpeg'){ $src_img = imagecreatefromjpeg($path); } if($mime['mime']=='image/pjpeg'){ $src_img = imagecreatefromjpeg($path); } $old_x = imageSX($src_img); $old_y = imageSY($src_img); if($old_x > $old_y) { $thumb_w = $newWidth; $thumb_h = $old_y/$old_x*$newWidth; } if($old_x < $old_y) { $thumb_w = $old_x/$old_y*$newHeight; $thumb_h = $newHeight; } if($old_x == $old_y) { $thumb_w = $newWidth; $thumb_h = $newHeight; } $dst_img = ImageCreateTrueColor($thumb_w,$thumb_h); imagecopyresampled($dst_img,$src_img,0,0,0,0,$thumb_w,$thumb_h,$old_x,$old_y); // New save location $new_thumb_loc = $moveToDir . $imageName; if($mime['mime']=='image/png'){ $result = imagepng($dst_img,$new_thumb_loc,8); } if($mime['mime']=='image/jpg'){ $result = imagejpeg($dst_img,$new_thumb_loc,80); } if($mime['mime']=='image/jpeg'){ $result = imagejpeg($dst_img,$new_thumb_loc,80); } if($mime['mime']=='image/pjpeg'){ $result = imagejpeg($dst_img,$new_thumb_loc,80); } imagedestroy($dst_img); imagedestroy($src_img); return $result; } 
 <?php Class ResizedImage { public $imgfile; public $string = ''; public $new_width = 0; public $new_height = 0; public $angle = 0; public $max_font_size = 1000; public $cropped = false;//whether crop the original image if h or w > new h or w public $font = 'fonts/arialbd.ttf'; private $img; private $trans_colour; private $orange; private $white; private $whitetr; private $blacktr; public function PrintAsBase64() { $this->SetImage(); ob_start(); imagepng($this->img); $b64img = ob_get_contents(); ob_clean(); imagedestroy($this->img); $b64img = base64_encode($b64img); echo($b64img); } public function PrintAsImage() { $this->SetImage(); header('Content-type: image/png'); imagepng($this->img); imagedestroy($this->img); } private function SetImage() { if ($this->imgfile == '') {$this->imgfile='NoImageAvailable.jpg';} $this->img = imagecreatefromstring(file_get_contents($this->imgfile)); $this->trans_colour = imagecolorallocatealpha($this->img, 0, 0, 0, 127); $this->orange = imagecolorallocate($this->img, 220, 210, 60); $this->white = imagecolorallocate($this->img, 255,255, 255); $this->whitetr = imagecolorallocatealpha($this->img, 255,255, 255, 95); $this->blacktr = imagecolorallocatealpha($this->img, 0, 0, 0, 95); if ((!$this->cropped) && ($this->string !='')) {$this->watermarkimage();} if (($this->new_height > 0) && ($this->new_width > 0)) {$this->ResizeImage();}; if (($this->cropped) && ($this->string !='')) {$this->watermarkimage();} imageAlphaBlending($this->img, true); imageSaveAlpha($this->img, true); } //// private function ResizeImage() { # v_fact and h_fact are the factor by which the original vertical / horizontal # image sizes should be multiplied to get the image to your target size. $v_fact = $this->new_height / imagesy($this->img);//target_height / im_height; $h_fact = $this->new_width / imagesx($this->img);//target_width / im_width; # you want to resize the image by the same factor in both vertical # and horizontal direction, so you need to pick the correct factor from # v_fact / h_fact so that the largest (relative to target) of the new height/width # equals the target height/width and the smallest is lower than the target. # this is the lowest of the two factors if($this->cropped) { $im_fact = max($v_fact, $h_fact); } else { $im_fact = min($v_fact, $h_fact); } $new_height = round(imagesy($this->img) * $im_fact); $new_width = round(imagesx($this->img) * $im_fact); $img2 = $this->img; $this->img = imagecreatetruecolor($new_width, $new_height); imagecopyresampled($this->img, $img2, 0, 0, 0, 0, $new_width, $new_height, imagesx($img2), imagesy($img2)); $img2 = $this->img; $this->img = imagecreatetruecolor($this->new_width, $this->new_height); imagefill($this->img, 0, 0, $this->trans_colour); $dstx = 0; $dsty = 0; if ($this->cropped) { if (imagesx($this->img) < imagesx($img2)) { $dstx = round((imagesx($this->img)-imagesx($img2))/2); } if (imagesy($this->img) < imagesy($img2)) { $dsty = round((imagesy($this->img)-imagesy($img2))/2); } } else { if (imagesx($this->img) > imagesx($img2)) { $dstx = round((imagesx($this->img)-imagesx($img2))/2); } if (imagesy($this->img) > imagesy($img2)) { $dsty = round((imagesy($this->img)-imagesy($img2))/2); } } imagecopy ( $this->img, $img2, $dstx, $dsty, 0, 0, imagesx($img2) , imagesy($img2)); imagedestroy($img2); } //// private function calculateTextBox($text,$fontFile,$fontSize,$fontAngle) { /************ simple function that calculates the *exact* bounding box (single pixel precision). The function returns an associative array with these keys: left, top: coordinates you will pass to imagettftext width, height: dimension of the image you have to create *************/ $rect = imagettfbbox($fontSize,$fontAngle,$fontFile,$text); $minX = min(array($rect[0],$rect[2],$rect[4],$rect[6])); $maxX = max(array($rect[0],$rect[2],$rect[4],$rect[6])); $minY = min(array($rect[1],$rect[3],$rect[5],$rect[7])); $maxY = max(array($rect[1],$rect[3],$rect[5],$rect[7])); return array( "left" => abs($minX) - 1, "top" => abs($minY) - 1, "width" => $maxX - $minX, "height" => $maxY - $minY, "box" => $rect ); } private function watermarkimage($font_size=0) { if ($this->string == '') {die('Watermark function call width empty string!');} $box = $this->calculateTextBox($this->string, $this->font, $font_size, $this->angle); while ( ($box['width'] < imagesx($this->img)) && ($box['height'] < imagesy($this->img)) && ($font_size <= $this->max_font_size) ) { $font_size++; $box = $this->calculateTextBox($this->string, $this->font, $font_size, $this->angle); } $font_size--; $box = $this->calculateTextBox($this->string, $this->font, $font_size, $this->angle); $vcenter = round((imagesy($this->img) / 2) + ($box['height'] / 2)); $hcenter = round((imagesx($this->img) - $box['width']) / 2 ); imagettftext($this->img, $font_size, $this->angle, $hcenter, $vcenter, $this->blacktr, $this->font, $this->string); imagettftext($this->img, $font_size, $this->angle, $hcenter+1, $vcenter-2, $this->whitetr, $this->font, $this->string); } } ?> 

Также я использовал принятый ответ, но в некоторых случаях он не поддерживает соотношение. Я нашел несколько хороших ответов на форуме и собрал их вместе и, наконец, создал класс, который изменяет размер изображения. В качестве дополнительной функции u можно поместить текст водяного знака.

u может видеть, что происходит, когда вы выбираете обрезку или нет, если не будет добавлена ​​прозрачная область для нового измененного изображения.

Этот пример более чем задан, но я думаю, что это хороший пример.

Я знаю, что вы ищете делителя, который позволит пропорционально изменить размер изображения. Проверьте эту демонстрационную версию

Как получить наш дивизор математически

позволяет предположить, что наше исходное изображение имеет ширину x и высоту y; x = 300 и y = 700

Максимальная высота и максимальная ширина 200;

Сначала мы проверим, какой размер изображения больше, чем другой. Наша высота (y) больше ширины (x)

Во-вторых, мы проверяем, превышает ли наш рост максимальную высоту. Для нашего случая наша высота больше максимальной. В случае, когда он меньше максимальной высоты, мы устанавливаем нашу новую высоту на нашу первоначальную высоту.

Наконец, мы ищем наш делитель, как показано ниже

 if y is set to maximum height 200 and max-y=200; y=max-y, that is if y=max-y what about x=? that is, if 700 is resized to 200 what about 300? 700=200 300=? new width = (200 (new height) * 300(width)) / 700 (height) so our divisor is divisor= new height (300) / height(700) new width = divisor * width or width / (1/divisor) 

и наоборот для ширины, если она больше высоты

 if ($width > $height) { if($width < $max_width) $newwidth = $width; else $newwidth = $max_width; $divisor = $width / $newwidth; $newheight = floor( $height / $divisor); } else { if($height < $max_height) $newheight = $height; else $newheight = $max_height; $divisor = $height / $newheight; $newwidth = floor( $width / $divisor ); } 

Посмотрите полный пример и попробуйте его с помощью рабочей демонстрации .

Вот комплексное приложение, в котором я много работал над ним, чтобы включить в него самые распространенные операции, такие как масштабирование и масштабирование, эскиз, сохранение пропорций, преобразование типа файла, изменение качества / размера файла и многое другое …

 <?php //##// Resize (and convert) image (Scale up & scale down, thumbnail, preserve aspect ratio) //##// /////////////////////////////////////////////// ///////////////// Begin.Setup ///////////////// // Source File: $src_file = "/your/server/path/to/file.png";// png or jpg files only // Resize Dimensions: // leave blank for no size change (convert only) // if you specify one dimension, the other dimension will be calculated according to the aspect ratio // if you specify both dimensions system will take care of it depending on the actual image size // $newWidth = 2000; // $newHeight = 1500; // Destination Path: (optional, if none: download image) $dst_path = "/your/server/path/new/"; // Destination File Name: (Leave blank for same file name) // $dst_name = 'image_name_only_no_extension'; // Destination File Type: (Leave blank for same file extension) // $dst_type = 'png'; $dst_type = 'jpg'; // Reduce to 8bit - 256 colors (Very low quality but very small file & transparent PNG. Only for thumbnails!) // $palette_8bit = true; ///////////////// End.Setup ///////////////// /////////////////////////////////////////////// if (!$dst_name){$dst_name = strtolower(pathinfo($src_file, PATHINFO_FILENAME));} if (!$dst_type){$dst_type = strtolower(pathinfo($src_file, PATHINFO_EXTENSION));} if ($palette_8bit){$dst_type = 'png';} if ($dst_path){$dst_file = $dst_path . $dst_name . '.' . $dst_type;} $mime = getimagesize($src_file);// Get image dimensions and type // Destination File Parameters: if ($dst_type == 'png'){ $dst_content = 'image/png'; $quality = 9;// All same quality! 0 too big file // 0(no comp.)-9 (php default: 6) } elseif ($dst_type == 'jpg'){ $dst_content = 'image/jpg'; $quality = 85;// 30 Min. 60 Mid. 85 Cool. 90 Max. (100 Full) // 0-100 (php default: 75) } else { exit('Unknown Destination File Type'); } // Source File Parameters: if ($mime['mime']=='image/png'){$src_img = imagecreatefrompng($src_file);} elseif ($mime['mime']=='image/jpg'){$src_img = imagecreatefromjpeg($src_file);} elseif ($mime['mime']=='image/jpeg'){$src_img = imagecreatefromjpeg($src_file);} elseif ($mime['mime']=='image/pjpeg'){$src_img = imagecreatefromjpeg($src_file);} else {exit('Unknown Source File Type');} // Define Dimensions: $old_x = imageSX($src_img); $old_y = imageSY($src_img); if ($newWidth AND $newHeight){ if($old_x > $old_y){ $new_x = $newWidth; $new_y = $old_y / $old_x * $newWidth; } elseif($old_x < $old_y){ $new_y = $newHeight; $new_x = $old_x / $old_y * $newHeight; } elseif($old_x == $old_y){ $new_x = $newWidth; $new_y = $newHeight; } } elseif ($newWidth){ $new_x = $newWidth; $new_y = $old_y / $old_x * $newWidth; } elseif ($newHeight){ $new_y = $newHeight; $new_x = $old_x / $old_y * $newHeight; } else { $new_x = $old_x; $new_y = $old_y; } $dst_img = ImageCreateTrueColor($new_x, $new_y); if ($palette_8bit){//////// Reduce to 8bit - 256 colors //////// $transparent = imagecolorallocatealpha($dst_img, 255, 255, 255, 127); imagecolortransparent($dst_img, $transparent); imagefill($dst_img, 0, 0, $transparent); imagecopyresampled($dst_img,$src_img,0,0,0,0,$new_x,$new_y,$old_x,$old_y);// Great quality resize. imagetruecolortopalette($dst_img, false, 255); imagesavealpha($dst_img, true); } else { // Check image and set transparent for png or white background for jpg if ($dst_type == 'png'){ imagealphablending($dst_img, false); imagesavealpha($dst_img, true); $transparent = imagecolorallocatealpha($dst_img, 255, 255, 255, 127); imagefilledrectangle($dst_img, 0, 0, $new_x, $new_y, $transparent); } elseif ($dst_type == 'jpg'){ $white = imagecolorallocate($dst_img, 255, 255, 255); imagefilledrectangle($dst_img, 0, 0, $new_x, $new_y, $white); } imagecopyresampled($dst_img,$src_img,0,0,0,0,$new_x,$new_y,$old_x,$old_y);// Great quality resize. } // Skip the save to parameter using NULL, then set the quality; imagejpeg($dst_img);=> Default quality if ($dst_file){ if ($dst_type == 'png'){ imagepng($dst_img, $dst_file, $quality); } elseif ($dst_type == 'jpg'){ imagejpeg($dst_img, $dst_file, $quality); } } else { header('Content-Disposition: Attachment;filename=' . $dst_name . '.' . $dst_type);// comment this line to show image in browser instead of download header('Content-type: ' . $dst_content); if ($dst_type == 'png'){ imagepng($dst_img, NULL, $quality); } elseif ($dst_type == 'jpg'){ imagejpeg($dst_img, NULL, $quality); } } imagedestroy($src_img); imagedestroy($dst_img); //##// END : Resize image (Scale Up & Down) (thumbnail, bigger image, preserve aspect ratio) END //##//