219 lines
7.5 KiB
PHP
219 lines
7.5 KiB
PHP
<?php
|
|
|
|
namespace Shikiryu\WebGobbler\Assembler;
|
|
|
|
use Imagick;
|
|
use Shikiryu\WebGobbler\Assembler;
|
|
use Shikiryu\WebGobbler\Config;
|
|
use Shikiryu\WebGobbler\Pool;
|
|
|
|
class Superpose extends Assembler
|
|
{
|
|
/**
|
|
* @var Imagick
|
|
*/
|
|
private $current_image;
|
|
|
|
private $base_image;
|
|
|
|
/**
|
|
* Superpose constructor.
|
|
*
|
|
* @param \Shikiryu\WebGobbler\Pool $pool
|
|
* @param \Shikiryu\WebGobbler\Config $config
|
|
* @param string $from_file
|
|
*/
|
|
public function __construct(Pool $pool, Config $config, $from_file = null)
|
|
{
|
|
$this->base_image = $from_file;
|
|
|
|
parent::__construct($pool, $config);
|
|
}
|
|
|
|
/**
|
|
* @param string $file
|
|
*
|
|
* @return void
|
|
* @throws \ImagickException
|
|
* @throws \Exception
|
|
*/
|
|
public function saveTo($file)
|
|
{
|
|
$this->prepareImage();
|
|
$this->current_image->writeImage($file);
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
public function display()
|
|
{
|
|
try {
|
|
$this->prepareImage();
|
|
|
|
header('Content-type: image/jpeg');
|
|
echo $this->current_image;
|
|
} catch (\ImagickException $e) {
|
|
echo $e->getMessage();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return \Imagick
|
|
* @throws \ImagickException
|
|
*/
|
|
public function superpose()
|
|
{
|
|
$file = $this->pool->getImage();
|
|
$this->files[] = $file;
|
|
|
|
return $this->superposeOneImage($this->current_image, new \Imagick($file));
|
|
}
|
|
|
|
/**
|
|
* Superposes one image in the current image.
|
|
* This method must only be called by the assembler_superpose thread !
|
|
*
|
|
* @param \Imagick $current_image
|
|
* @param \Imagick $image_to_superpose
|
|
*
|
|
* @return \Imagick
|
|
* @throws \Exception
|
|
*/
|
|
private function superposeOneImage($current_image, $image_to_superpose)
|
|
{
|
|
if ($this->config->get('assembler.superpose.variante', 0) === 1) {
|
|
$current_image->brightnessContrastImage(0.99, 1);
|
|
}
|
|
if ($image_to_superpose->getColorspace() !== \Imagick::COLORSPACE_RGB) {
|
|
$image_to_superpose->setColorspace(\Imagick::COLORSPACE_RGB);
|
|
}
|
|
|
|
# If the image is bigger than current image, scale it down to 1/2 of final picture dimensions
|
|
# (while keeping its ratio)
|
|
$image_x = $image_to_superpose->getImageWidth();
|
|
$image_y = $image_to_superpose->getImageHeight();
|
|
if ($image_x < 32 || $image_y < 32) {
|
|
throw new \Exception('Image too small.');
|
|
}
|
|
|
|
if ($image_x > $this->config->get('assembler.sizex') || $image_y > $this->config->get('assembler.sizey')) {
|
|
try {
|
|
$image_to_superpose->scaleImage($this->config->get('assembler.sizex') / 2, $this->config->get('assembler.sizey') / 2, true);
|
|
} catch (\ImagickException $e) {
|
|
}
|
|
}
|
|
|
|
# Scale down/up image if required.
|
|
$scale_value = $this->config->get('assembler.superpose.scale', 1);
|
|
if ($scale_value !== 1) {
|
|
$image_to_superpose->thumbnailImage($image_x * $scale_value, $image_y * $scale_value);
|
|
}
|
|
$image_x = $image_to_superpose->getImageWidth();
|
|
$image_y = $image_to_superpose->getImageHeight();
|
|
|
|
# Some image are too white.
|
|
# For example, the photo of a coin on a white background.
|
|
# These picture degrad the quality of the final image.
|
|
# We try to dectect them by summing the value of the pixels
|
|
# on the borders.
|
|
# If the image is considered "white", we invert it.
|
|
$pixel_count = 1; // to prevent divide by zero error.
|
|
$value_count = 0;
|
|
for ($x = 0; $x < $image_x; $x += 20) {
|
|
$pixel = $image_to_superpose->getImagePixelColor($x, 5);
|
|
$value_count += array_sum($pixel->getColor());
|
|
$pixel = $image_to_superpose->getImagePixelColor($x, $image_y - 5);
|
|
$value_count += array_sum($pixel->getColor());
|
|
$pixel_count += 2;
|
|
}
|
|
for ($y = 0; $y < $image_y; $y += 20) {
|
|
$pixel = $image_to_superpose->getImagePixelColor(5, $y);
|
|
$value_count += array_sum($pixel->getColor());
|
|
$pixel = $image_to_superpose->getImagePixelColor($image_x - 5, $y);
|
|
$value_count += array_sum($pixel->getColor());
|
|
$pixel_count += 2;
|
|
}
|
|
# If the average r+g+b of the border pixels exceed this value,
|
|
# we consider the image is too white, and we invert it.
|
|
if (100 * $value_count / (255 * 3) / $pixel_count > 60) {
|
|
$image_to_superpose->negateImage(false);
|
|
}
|
|
|
|
$paste_coords_x = random_int(-($image_x/2), max(0, $this->config->get('assembler.sizex')-$image_x));
|
|
$paste_coords_y = random_int(-($image_y/2), max(0, $this->config->get('assembler.sizey')-$image_y));
|
|
|
|
# Darken image borders
|
|
$image_to_superpose = $this->darkenImageBorder($image_to_superpose);
|
|
|
|
if ($this->config->get('assembler.superpose.randomrotation', false)) {
|
|
$image_to_superpose->rotateImage('none', random_int(0, 359));
|
|
$image_to_superpose = $this->darkenImageBorder($image_to_superpose);
|
|
}
|
|
|
|
// help for mask : https://phpimagick.com/Tutorial/backgroundMasking
|
|
$image_to_superpose->setImageMatte(true);
|
|
$mask_image = clone $image_to_superpose;
|
|
$mask_image->setImageType(Imagick::IMGTYPE_GRAYSCALE);
|
|
$mask_image->transformImageColorSpace(Imagick::COLORSPACE_GRAY);
|
|
|
|
# Invert the transparency of 5% of the images (Except if we are in variante 1 mode)
|
|
if ($this->config->get('assembler.superpose.variante') === 1 && random_int(0, 100) < 5) {
|
|
$mask_image->negateImage(true);
|
|
}
|
|
|
|
$image_to_superpose->compositeImage($mask_image, \Imagick::COMPOSITE_COPYOPACITY, 0, 0);
|
|
$current_image->compositeImage($image_to_superpose, \Imagick::COMPOSITE_DEFAULT, $paste_coords_x, $paste_coords_y);
|
|
|
|
if ($this->config->get('assembler.superpose.variante') === 0) {
|
|
$current_image->equalizeImage();
|
|
} else {
|
|
$current_image->autoLevelImage();
|
|
}
|
|
|
|
return $current_image;
|
|
}
|
|
|
|
/**
|
|
* @param \Imagick $image
|
|
* @param int|null $border_size
|
|
*
|
|
* @return \Imagick
|
|
*/
|
|
private function darkenImageBorder(Imagick $image, $border_size = null)
|
|
{
|
|
if (null === $border_size) {
|
|
$border_size = $this->config->get('assembler.superpose.bordersmooth');
|
|
}
|
|
|
|
$image_x = $image->getImageWidth();
|
|
$image_y = $image->getImageHeight();
|
|
|
|
for ($i = 0; $i < $border_size; $i++) {
|
|
$draw = new \ImagickDraw();
|
|
$draw->setStrokeOpacity(($border_size-$i)/$border_size);
|
|
$draw->setStrokeWidth(1);
|
|
$draw->setStrokeColor('black');
|
|
$draw->setFillColor('none');
|
|
$draw->rectangle($i, $i, $image_x - $i, $image_y - $i);
|
|
$image->drawImage($draw);
|
|
}
|
|
|
|
return $image;
|
|
}
|
|
|
|
/**
|
|
* @throws \ImagickException
|
|
*/
|
|
private function prepareImage()
|
|
{
|
|
$this->current_image = null === $this->base_image ? new \Imagick() : new \Imagick($this->base_image);
|
|
$this->current_image->setSize($this->config->get('assembler.sizex'), $this->config->get('assembler.sizey'));
|
|
$this->current_image->newImage($this->config->get('assembler.sizex'), $this->config->get('assembler.sizey'), 'none');
|
|
|
|
$nb_images = $this->config->get('assembler.superpose.min_num_images', 5);
|
|
for ($i = 0; $i < $nb_images; $i++) {
|
|
$this->current_image = $this->superpose();
|
|
}
|
|
}
|
|
} |