Modificar la relación de aspecto de todas las imágenes dentro un directorio

  • 10 Jul 2019
  • PHP, convert

Escenario

Tenemos dentro una carpeta una serie de imágenes con distinta relación de aspecto (o ratio de aspecto, o aspect ratio en inglés) y distinto tamaño. El objetivo es recortar (crop) las fotos en la medida que sea necesario para lograr que todas las imágenes tengan una misma relación de aspecto indicada

Solución

Un script en PHP que calcula en base a la dimensión de la imagen cuanto y donde es necesario cortar para obtener una relación de aspecto determinada. Con esta información, se construyen las opciones al comando convert que se ejecuta posteriormente utilizando exec.

<?php

if (count($_SERVER['argv']) < 3) {
  print "Usage\n";
  print "  " . $_SERVER['SCRIPT_FILENAME'] . " input output ratio\n";
  exit();
}

define("DIR",$_SERVER['argv'][1]);
define("DIROUT",$_SERVER['argv'][2]);
define("RATIO",$_SERVER['argv'][3]);

$input = realpath(DIR);
$output = realpath(DIROUT);

$fraction = explode("/", RATIO);
if (count($fraction) == 1) {
  $ratio = RATIO;
} else {
  $ratio = $fraction[0]/$fraction[1];
}

print $ratio."\n";

if (!$handle = opendir($input)) {
  print "Can't open input directory: " . DIR . "\n";
  return;
}

$objects = new RecursiveIteratorIterator(
  new RecursiveDirectoryIterator($input), 
  RecursiveIteratorIterator::SELF_FIRST
);

foreach ($objects as $file => $object) {

  // handle only jpg files
  if (pathinfo($file, PATHINFO_EXTENSION) != 'jpg') {
    continue;
  }

  // get image size
  $img_info = array();
  exec("convert " . escapeshellarg($file) . " -print \"%w,%h\" /dev/null", $img_info);
  $img_size = explode(",", $img_info[0]);
  $img_w = $img_size[0];
  $img_h = $img_size[1];

  // tall or wide?
  if ($img_w >= $img_h) {
    $img_ratio = $img_w/$img_h;
    $min = "img_h";
    $max = "img_w";
  } else {
    $img_ratio = $img_h/$img_w;
    $min = "img_w";
    $max = "img_h";
  }

  // crop and offset
  if ($img_ratio < $ratio) {
    $var = "new_".$max;
    $$var = $$max;

    $var2 = "new_".$min;
    $$var2 = round($$var/$ratio);
  } else {
    $var = "new_".$min;
    $$var = $$min;

    $var_new_max = "new_".$max;
    $$var_new_max = round($$var*$ratio);
  }

  // create output directory
  $output_path = $output . substr($file, strlen($input));
  if (!file_exists(dirname($output_path))) { 
    mkdir(dirname($output_path));
  }

  $cmd  = "convert -quality 90 " . escapeshellarg($file);
  $cmd .= " -crop {$new_img_w}x{$new_img_h}+";
  $cmd .= abs(floor((intval($img_w) - intval($new_img_w)) / 2)). "+";
  $cmd .= floor((intval($img_h) - intval($new_img_h)) / 2);
  $cmd .= " +repage " . escapeshellarg($output_path);

  print "\nconvert " . pathinfo($file,  PATHINFO_BASENAME). "\n";
  print "  from: {$img_w} x {$img_h}\n";
  print "    to: {$new_img_w} x {$new_img_h}\n";

  exec($cmd);

}

closedir($handle);

Uso

Vamos con un ejemplo: Para modificar el ratio a 16/9 de todas las imágenes dentro input y guardarlas dentro el directorio output:

php script.php input output 16/9