Skip to content

Instantly share code, notes, and snippets.

@jedisct1
Created October 4, 2012 03:06
Show Gist options
  • Save jedisct1/3831233 to your computer and use it in GitHub Desktop.
Save jedisct1/3831233 to your computer and use it in GitHub Desktop.
Image auto cropping
<?php
namespace Spot;
define('USE_GDLIB', FALSE);
class ImageAutoCrop {
protected static function get_line_contrast($img, $y) {
$contrast = 0;
$x = 0;
$oldlevel = FALSE;
if (USE_GDLIB === FALSE) {
$width = imageWidth($img);
} else {
$width = imagesx($img);
}
do {
if (USE_GDLIB === FALSE) {
list($r_, $g_, $b_, $a_) = get($img, $x, $y);
$r = (int) round($r_ * 255.0);
$g = (int) round($g_ * 255.0);
$b = (int) round($b_ * 255.0);
} else {
$rgb = imagecolorat($img, $x, $y);
$r = ($rgb >> 16) & 0xff;
$g = ($rgb >> 8) & 0xff;
$b = $rgb & 0xff;
}
$level = ($r * 77 + $g * 151 + $b * 28 + 128) >> 8;
if ($oldlevel !== FALSE) {
if ($oldlevel > $level) {
$contrast += $oldlevel - $level;
} else {
$contrast += $level - $oldlevel;
}
}
$oldlevel = $level;
$x++;
} while ($x < $width);
return $contrast;
}
protected static function get_column_contrast($img, $x) {
$contrast = 0;
$y = 0;
$oldlevel = FALSE;
if (USE_GDLIB === FALSE) {
$width = imageHeight($img);
} else {
$width = imagesy($img);
}
do {
if (USE_GDLIB === FALSE) {
list($r_, $g_, $b_, $a_) = get($img, $x, $y);
$r = (int) round($r_ * 255.0);
$g = (int) round($g_ * 255.0);
$b = (int) round($b_ * 255.0);
} else {
$rgb = imagecolorat($img, $x, $y);
$r = ($rgb >> 16) & 0xff;
$g = ($rgb >> 8) & 0xff;
$b = $rgb & 0xff;
}
$level = ($r * 77 + $g * 151 + $b * 28 + 128) / 256;
if ($oldlevel !== FALSE) {
if ($oldlevel > $level) {
$contrast += $oldlevel - $level;
} else {
$contrast += $level - $oldlevel;
}
}
$oldlevel = $level;
$y++;
} while ($y < $width);
return $contrast;
}
protected static function get_max_cropping_ratio() {
return 0.25;
}
protected static function get_contrast_barrier_for_cropping() {
return 0.032;
}
protected static function get_autocrop_edges($img, $contrast_barrier_for_cropping,
$max_cropping_ratio, $x0, $y0, $x1, $y1) {
$width = 1 + $x1 - $x0;
$height = 1 + $y1 - $y0;
if ($width < 2 || $height < 2) {
return FALSE;
}
$line_contrasts = array();
$total_contrast = 0;
$y = $y0;
do {
$line_contrasts[$y] = self::get_line_contrast($img, $y);
$total_contrast += $line_contrasts[$y];
$y++;
} while ($y <= $y1);
$cutoff_contrast = round($total_contrast * $contrast_barrier_for_cropping);
$local_contrast = 0;
$crop_y0 = $y0;
$crop_y0_max = $y0 + floor($height * $max_cropping_ratio);
do {
$local_contrast += $line_contrasts[$crop_y0];
if ($local_contrast >= $cutoff_contrast) {
break;
}
$crop_y0++;
} while ($crop_y0 <= $y1 && $crop_y0 < $crop_y0_max);
$local_contrast = 0;
$crop_y1 = $y1;
$crop_y1_min = $y1 - floor($height * $max_cropping_ratio);
do {
$local_contrast += $line_contrasts[$crop_y1];
if ($local_contrast >= $cutoff_contrast) {
break;
}
$crop_y1--;
} while ($crop_y1 >= $y0 && $crop_y1 > $crop_y1_min);
$local_contrast = 0;
$crop_x0 = $x0;
$crop_x0_max = $x0 + floor($width * $max_cropping_ratio);
do {
$local_contrast += self::get_column_contrast($img, $crop_x0);
if ($local_contrast >= $cutoff_contrast) {
break;
}
$crop_x0++;
} while ($crop_x0 <= $x1 && $crop_x0 < $crop_x0_max);
$local_contrast = $x0;
$crop_x1 = $x1;
$crop_x1_min = $x1 - floor($width * $max_cropping_ratio);
do {
$local_contrast += self::get_column_contrast($img, $crop_x1);
if ($local_contrast >= $cutoff_contrast) {
break;
}
$crop_x1--;
} while ($crop_x1 >= $x0 && $crop_x1 > $crop_x1_min);
return array('crop_x0' => $crop_x0, 'crop_x1' => $crop_x1,
'crop_y0' => $crop_y0, 'crop_y1' => $crop_y1);
}
static function autocrop($img) {
if (USE_GDLIB === FALSE) {
$width = imageWidth($img);
$height = imageHeight($img);
} else {
$width = imagesx($img);
$height = imagesy($img);
}
$edges = self::get_autocrop_edges($img, self::get_contrast_barrier_for_cropping(),
self::get_max_cropping_ratio(),
0, 0, $width - 1, $height -1);
if ($edges === FALSE) {
return FALSE;
}
$crop_x0 = $edges['crop_x0'];
$crop_x1 = $edges['crop_x1'];
$crop_y0 = $edges['crop_y0'];
$crop_y1 = $edges['crop_y1'];
$new_edges = self::get_autocrop_edges($img, self::get_contrast_barrier_for_cropping(),
self::get_max_cropping_ratio(),
$crop_x0, $crop_y0, $crop_x1, $crop_y1);
$new_center_x = ($new_edges['crop_x0'] + $new_edges['crop_x1']) / 2;
$new_center_y = ($new_edges['crop_y0'] + $new_edges['crop_y1']) / 2;
$new_width = 1 + $crop_x1 - $crop_x0;
$new_height = 1 + $crop_y1 - $crop_y0;
$original_ratio = (double) $width / (double) $height;
$new_ratio = (double) $new_width / (double) $new_height;
$new_height_with_xcrop = $new_width / $original_ratio;
$new_width_with_ycrop = $new_height * $original_ratio;
$xcrop = FALSE;
$ycrop = FALSE;
if ($new_height_with_xcrop / (double) $height < $new_width_with_ycrop / (double) $width) {
$ycrop = TRUE;
} else {
$xcrop = TRUE;
}
if ($xcrop === TRUE) {
$t = $new_height_with_xcrop / 2;
if ($new_center_y < $t) {
$crop_y0 = 0;
} elseif ($new_center_y >= $height - $t) {
$crop_y0 = $height - $new_height_with_xcrop;
} else {
$crop_y0 = $new_center_y - $t;
}
$new_height = $new_height_with_xcrop;
} else {
$t = $new_width_with_ycrop / 2;
if ($new_center_x < $t) {
$crop_x0 = 0;
} elseif ($new_center_x >= $width - $t) {
$crop_x0 = $width - $new_width_with_ycrop;
} else {
$crop_x0 = $new_center_x - $t;
}
$new_width = $new_width_with_ycrop;
}
return array('offsetx' => (int) $crop_x0, 'offsety' => (int) $crop_y0,
'width' => (int) $new_width, 'height' => (int) $new_height);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment