Skip to content

Instantly share code, notes, and snippets.

@jwage
Last active June 3, 2017 22:22
Show Gist options
  • Save jwage/45a9eee0d2ae51f2fc81 to your computer and use it in GitHub Desktop.
Save jwage/45a9eee0d2ae51f2fc81 to your computer and use it in GitHub Desktop.
<?php
class SmartSquareFilter extends AbstractImagickFilter
{
protected function doApply(\Imagick $im)
{
$target = $im->getImageGeometry();
if ($target['width'] === $target['height']) {
// return early if already square
return;
}
if ($target['width'] > $target['height']) {
// landscape
$target['width'] = $target['height'];
} else {
// portrait
$target['height'] = $target['width'];
}
// how different entropy must be in order for it to matter
$threshold = 0.5;
// focus on this percentage of center of each slice
$focus = 0.5;
// the max size of each slice
$maxSlice = 10;
while (($diff = $im->getImageWidth() - $target['width']) > 0) {
$width = $im->getImageWidth();
$height = $im->getImageHeight();
$slice = min($diff, $maxSlice);
$left = clone $im;
$left->cropImage($slice, $height * $focus, 0, ($height - ($height * $focus)) / 2);
$right = clone $im;
$right->cropImage($slice, $height * $focus, $width - $slice, ($height - $height * $focus) / 2);
$diff = self::entropy($right) - self::entropy($left);
if ($diff > $threshold) {
$im->cropImage($width - $slice, $height, $slice, 0);
} elseif ($diff < -$threshold) {
$im->cropImage($width - $slice, $height, 0, 0);
} elseif (rand(0, 1)) {
$im->cropImage($width - $slice, $height, floor($slice / 2), 0);
} else {
$im->cropImage($width - $slice, $height, ceil($slice / 2), 0);
}
$im->setImagePage(0, 0, 0, 0);
}
while (($diff = $im->getImageHeight() - $target['height']) > 0) {
$width = $im->getImageWidth();
$height = $im->getImageHeight();
$slice = min($diff, $maxSlice);
$top = clone $im;
$top->cropImage($width * $focus, $slice, ($width - $width * $focus) / 2, 0);
$bottom = clone $im;
$bottom->cropImage($width * $focus, $slice, ($width - $width * $focus) / 2, $height - $slice);
$diff = self::entropy($top) - self::entropy($bottom);
if ($diff > $threshold) {
$im->cropImage($width, $height - $slice, 0, 0);
} elseif ($diff < -$threshold) {
$im->cropImage($width, $height - $slice, 0, $slice);
} elseif (rand(0, 1)) {
$im->cropImage($width, $height - $slice, 0, floor($slice / 2));
} else {
$im->cropImage($width, $height - $slice, 0, ceil($slice / 2));
}
$im->setImagePage(0, 0, 0, 0);
}
}
private static function entropy(\Imagick $im)
{
$im = clone $im;
// convert to grayscale
$im->setImageType(\Imagick::IMGTYPE_GRAYSCALE);
// calculate total number of pixels
$area = $im->getImageWidth() * $im->getImageHeight();
// use the histogram to calculate entropy
$entropy = 0;
foreach ($im->getImageHistogram() as $pixel) {
if ($count = $pixel->getColorCount()) {
$perc = $count / $area;
$entropy += $perc * log($perc, 2);
}
}
return $entropy * -1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment