web-dev-qa-db-fra.com

Scale Image Using PHP et maintenir le ratio d'aspect

En gros, je veux télécharger une image (que j'ai triée) et la réduire à certaines contraintes, telles que la largeur et la hauteur maximales, tout en maintenant les proportions de l'image d'origine.

Imagick n’est pas installé sur le serveur, sinon ce serait facile.

Toute aide est appréciée, comme toujours ... Merci.

EDIT: Je n'ai pas besoin de tout le code ou quoi que ce soit, juste un Push dans la bonne direction serait fantastique.

13
Steve_M

J'avais écrit un morceau de code comme celui-ci pour un autre projet que j'ai réalisé. Je l'ai copié ci-dessous, peut-être besoin d'un peu de bricolage! (Cela nécessite la bibliothèque Gd)

Ce sont les paramètres dont il a besoin:

$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

Il réduira ou augmentera une image à la largeur ou hauteur maximale

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;
}
11
Phil Cross

En fait, la solution acceptée est pas la solution correcte. La raison est simple: il y aura des cas où le rapport de l'image source et le rapport de l'image de destination seront différents. Tout calcul devrait refléter cette différence.

Veuillez noter les lignes pertinentes de l'exemple donné sur le site PHP.net:

$ratio_orig = $width_orig/$height_orig;

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

L'exemple complet peut être trouvé ici: http://php.net/manual/en/function.imagecopyresampled.php

Il existe d'autres réponses (avec des exemples) sur stackoverflow à des questions similaires (la même question formulée d'une manière différente) qui souffrent du même problème.

Exemple:

Supposons que nous ayons une image de 1630 x 2400 pixels que nous souhaitons redimensionner automatiquement en conservant le rapport de format de 160 x 240. Faisons quelques calculs en prenant la solution acceptée:

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

hauteur = 240 largeur = 1630 * (160/2400) = 1630 * 0.0666666666666667 = 108.6666666666667 108.6 x 240 ce n'est pas la bonne solution.

La prochaine solution proposée est la suivante:

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

height = 240; width = 1630/2400 * 240 = 163C'est mieux (car il conserve le rapport de format), mais dépasse la largeur maximale acceptée.

Les deux échouent.

Nous faisons le calcul selon la solution proposée par PHP.net:width = 160 Height = 160/(1630/2400) = 160/0.679166666666667 = 235.5828220858896 (la clause else). 160 x 236 (arrondi) est la bonne réponse.

17
Florin Sima

Formule ne permet pas de conserver les proportions ..__ Il devrait être: hauteur d'origine/largeur d'origine x nouvelle largeur = nouvelle hauteur

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

Je pensais à la réalisation de cet objectif et je suis arrivé avec une jolie solution qui fonctionne dans tous les cas .......... Disons que vous souhaitez redimensionner les images lourdes que les utilisateurs téléchargent sur votre site mais dont vous avez besoin pour conserver le rapport. Alors je suis venu avec ceci:

<?php
// File 
$filename = 'test.jpg';



// Get sizes
list($width, $height) = getimagesize($filename);
//obtain ratio
$imageratio = $width/$height;

if($imageratio >= 1){
    $newwidth = 600;
    $newheight = 600 / $imageratio; 
}
else{
     $newidth = 400;
     $newheight = 400 / $imageratio;
};




// Load
$thumb = imagecreatetruecolor($newwidth, $newheight);
$source = imagecreatefromjpeg($filename);

// Resize
imagecopyresized($thumb, $source, 0, 0, 0, 0, $newwidth, $newheight, $width, 
$height);

// Output
imagejpeg($thumb, "img/test.jpg");
imagedestroy();
?>

Dans ce cas, si la largeur est supérieure à la hauteur, je voulais une largeur de 600px et si la hauteur était supérieure à la largeur, je voulais une largeur de 400px. 

1
Pit B. Groove

J'ai trouvé un moyen mathématique de faire ce travail

Repo Github - https://github.com/gayanSandamal/easy-php-image-resizer

Exemple en direct - https://plugins.nayague.com/easy-php-image-resizer/

<?php
//path for the image
$source_url = '2018-04-01-1522613288.PNG';

//separate the file name and the extention
$source_url_parts = pathinfo($source_url);
$filename = $source_url_parts['filename'];
$extension = $source_url_parts['extension'];

//define the quality from 1 to 100
$quality = 10;

//detect the width and the height of original image
list($width, $height) = getimagesize($source_url);
$width;
$height;

//define any width that you want as the output. mine is 200px.
$after_width = 200;

//resize only when the original image is larger than expected with.
//this helps you to avoid from unwanted resizing.
if ($width > $after_width) {

    //get the reduced width
    $reduced_width = ($width - $after_width);
    //now convert the reduced width to a percentage and round it to 2 decimal places
    $reduced_radio = round(($reduced_width / $width) * 100, 2);

    //ALL GOOD! let's reduce the same percentage from the height and round it to 2 decimal places
    $reduced_height = round(($height / 100) * $reduced_radio, 2);
    //reduce the calculated height from the original height
    $after_height = $height - $reduced_height;

    //Now detect the file extension
    //if the file extension is 'jpg', 'jpeg', 'JPG' or 'JPEG'
    if ($extension == 'jpg' || $extension == 'jpeg' || $extension == 'JPG' || $extension == 'JPEG') {
        //then return the image as a jpeg image for the next step
        $img = imagecreatefromjpeg($source_url);
    } elseif ($extension == 'png' || $extension == 'PNG') {
        //then return the image as a png image for the next step
        $img = imagecreatefrompng($source_url);
    } else {
        //show an error message if the file extension is not available
        echo 'image extension is not supporting';
    }

    //HERE YOU GO :)
    //Let's do the resize thing
    //imagescale([returned image], [width of the resized image], [height of the resized image], [quality of the resized image]);
    $imgResized = imagescale($img, $after_width, $after_height, $quality);

    //now save the resized image with a suffix called "-resized" and with its extension. 
    imagejpeg($imgResized, $filename . '-resized.'.$extension);

    //Finally frees any memory associated with image
    //**NOTE THAT THIS WONT DELETE THE IMAGE
    imagedestroy($img);
    imagedestroy($imgResized);
}
?>
0
Gayan Sandamal
<?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);
    }
}
?>

De plus, j’ai utilisé la réponse acceptée, mais dans certains cas, le rapport n’est pas respecté. J'ai trouvé de bonnes réponses sur le forum, je les ai rassemblées et finalement créé une classe qui redimensionne une image. Comme fonction supplémentaire, vous pouvez mettre un texte en filigrane.

vous pouvez voir ce qui se passe lorsque vous choisissez de rogner ou non, sinon une zone transparente sera ajoutée à la nouvelle image redimensionnée.

Cet exemple est plus que demandé, mais je pense que c'est un bon exemple.

0
coban

Je sais que vous recherchez un diviseur qui permettra de redimensionner votre image proportionnellement. Cochez cette demo

Comment obtenir mathématiquement notre diviseur

supposons que notre image originale ait une largeur x et une hauteur y; x = 300 et y = 700

La hauteur maximale et la largeur maximale est de 200;

Premièrement, nous allons vérifier quelle dimension de l’image est supérieure à l’autre . Notre hauteur (y) est supérieure à la largeur (x) 

Deuxièmement, nous vérifions si notre hauteur est supérieure à notre hauteur maximale . Dans notre cas, notre hauteur est supérieure à la hauteur maximale. Dans le cas où la hauteur est inférieure à la hauteur maximale, nous définissons notre nouvelle hauteur à notre hauteur d'origine.

Enfin, nous cherchons notre diviseur comme indiqué ci-dessous

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)

et vice versa pour la largeur si elle est supérieure à la hauteur

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

Voir l'exemple complet et essayez-le en utilisant la démo de travail .

0
Daniel Nyamasyo

fonctionne parfaitement pour moi

    static function getThumpnail($file){

    $THUMBNAIL_IMAGE_MAX_WIDTH  = 150; # exmpl.
    $THUMBNAIL_IMAGE_MAX_HEIGHT = 150;

    $src_size = filesize($file);
    $filename = basename($file);

    list($src_width, $src_height, $src_type) = getimagesize($file);
    $src_im = false;
    switch ($src_type) {
        case IMAGETYPE_GIF  : $src_im = imageCreateFromGif($file);  break;
        case IMAGETYPE_JPEG : $src_im = imageCreateFromJpeg($file); break;
        case IMAGETYPE_PNG  : $src_im = imageCreateFromPng($file);  break;
        case IMAGETYPE_WBMP  : $src_im = imagecreatefromwbmp($file);  break;
    }   
    if ($src_im === false) { return false; }

    $src_aspect_ratio = $src_width / $src_height;
    $thu_aspect_ratio = $THUMBNAIL_IMAGE_MAX_WIDTH / $THUMBNAIL_IMAGE_MAX_HEIGHT;

    if ($src_width <= $THUMBNAIL_IMAGE_MAX_WIDTH && $src_height <= $THUMBNAIL_IMAGE_MAX_HEIGHT) {
        $thu_width  = $src_width;
        $thu_height = $src_height;
    } elseif ($thu_aspect_ratio > $src_aspect_ratio) {
        $thu_width  = (int) ($THUMBNAIL_IMAGE_MAX_HEIGHT * $src_aspect_ratio);
        $thu_height = $THUMBNAIL_IMAGE_MAX_HEIGHT;
    } else {
        $thu_width = $THUMBNAIL_IMAGE_MAX_WIDTH;
        $thu_height = (int) ($THUMBNAIL_IMAGE_MAX_WIDTH / $src_aspect_ratio);
    }

    $thu_im = imagecreatetruecolor($thu_width, $thu_height);
    imagecopyresampled($thu_im, $src_im, 0, 0, 0, 0, $thu_width, $thu_height, $src_width, $src_height);

    $dst_im    = imagecreatetruecolor($THUMBNAIL_IMAGE_MAX_WIDTH,$THUMBNAIL_IMAGE_MAX_WIDTH);
    $backcolor = imagecolorallocate($dst_im,192,192,192);
    imagefill($dst_im,0,0,$backcolor);
    imagecopy($dst_im, $thu_im, (imagesx($dst_im)/2)-(imagesx($thu_im)/2), (imagesy($dst_im)/2)-(imagesy($thu_im)/2), 0, 0, imagesx($thu_im), imagesy($thu_im));
    imagedestroy($src_im);
    imagedestroy($thu_im);
    }
0
gungott