Ridimensiona l'image usando PHP e mantenendo il rapporto di aspetto

Fondamentalmente voglio caricare un'image (che ho ordinato) e ridurla a determinati vincoli come la width e l'altezza massime ma mantenere le proporzioni dell'image originale.

Non ho installato Imagick sul server, altrimenti sarebbe facile.

Qualsiasi aiuto è apprezzato come sempre. Grazie.

EDIT: Non ho bisogno di tutto il codice o altro, solo una spinta nella giusta direzione sarebbe fantastico.

Avevo scritto un pezzetto di codice come questo per un altro progetto che ho fatto. L'ho copiato qui sotto, potrebbe aver bisogno di un po 'di armeggiare! (Richiede la libreria GD)

Questi sono i parametri di cui ha bisogno:

$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 

Ridurrà o ridurrà un'image alla width o all'altezza massima

 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; } 

In realtà la soluzione accettata non è la soluzione corretta. Il motivo è semplice: ci saranno casi in cui il rapporto tra l'image sorgente e il rapporto dell'image di destinazione sarà diverso. Qualsiasi calcolo dovrebbe riflettere questa differenza.

Si prega di notare le linee rilevanti dall'esempio fornito sul sito Web PHP.net:

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

L'esempio completo può essere trovato qui: http://php.net/manual/en/function.imagecopyresampled.php

Ci sono altre risposte (con esempi) sullo stackoverflow a domande simili (la stessa domanda formulata in un modo diverso) che soffrono dello stesso problema.

Esempio:

Diciamo che abbiamo un'image di 1630 x 2400 pixel che vogliamo ridimensionare automaticamente mantenendo le proporzioni a 160 x 240. Facciamo un po 'di math prendendo la soluzione accettata:

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

altezza = 240 width = 1630 * (160/2400) = 1630 * 0,0666666666666667 = 108,6666666666667 108,6 x 240 non è la soluzione corretta.

La prossima soluzione proposta è la seguente:

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

altezza = 240; width = 1630/2400 * 240 = 163 È migliore (poiché mantiene le proporzioni), ma ha superato la width massima accettata.

Entrambi falliscono.

Facciamo i conti in base alla soluzione proposta da PHP.net: width = 160 altezza = 160 / (1630/2400) = 160 / 0,6791666666666667 = 235,5828220858896 (la clausola else ). 160 x 236 (arrotondato) è la risposta corretta.

Formule è sbagliato per mantenere le proporzioni. Dovrebbe essere: altezza originale / width originale x nuova width = nuova altezza

 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 arrays 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(arrays($rect[0],$rect[2],$rect[4],$rect[6])); $maxX = max(arrays($rect[0],$rect[2],$rect[4],$rect[6])); $minY = min(arrays($rect[1],$rect[3],$rect[5],$rect[7])); $maxY = max(arrays($rect[1],$rect[3],$rect[5],$rect[7])); return arrays( "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); } } ?> 

Inoltre sto usando la risposta accettata ma non mantiene il rapporto in alcuni casi. Ho trovato alcune buone risposte sul forum e le ho messe insieme e finalmente ho creato una class che ridimensiona un'image. Come function extra puoi mettere un text filigrana.

puoi vedere cosa succede quando scegli di ritagliare o less, se non verrà aggiunta un'area trasparente alla nuova image ridimensionata.

Questo esempio è più che richiesto, ma penso che sia un buon esempio.

So che stai cercando un divisore che consenta di ridimensionare l'image in modo proporzionale. Controlla questa demo

Come get mathmente il nostro divisore

supponiamo che la nostra image originale abbia width x e altezza y; x = 300 e y = 700

L'altezza massima e la width massima sono 200;

Innanzitutto, controlleremo quale dimensione dell'image è maggiore dell'altra. La nostra altezza (y) è maggiore della width (x)

In secondo luogo, controlliamo se la nostra altezza è maggiore della nostra altezza massima. Nel nostro caso, la nostra altezza è maggiore dell'altezza massima. In un caso in cui less che l'altezza massima, abbiamo impostato la nostra nuova altezza alla nostra altezza originale.

Alla fine, cerchiamo il nostro divisore come mostrato di seguito

 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) 

e viceversa per la width se è maggiore dell'altezza

 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 ); } 

Guarda l'esempio completo e provalo usando la demo funzionante .

Ecco un'applicazione completa su cui ho lavorato duramente per includere le operazioni più comuni come scalare e ridimensionare, miniatura, preservare le proporzioni, convertire il tipo di file, cambiare la qualità / dimensione del file e altro …

 <?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 //##//