Downscaling with php imagick - php

I'm having trouble limiting the output of a pdf-to-jpg converter using imagick, but I can't seem to find the right solution for only resizing images larger than 16 megapixel.
https://stackoverflow.com/a/6387086/3405380 has the solution I'm looking for, but I can't find the php equivalent of convert -resize '180000#>' screen.jpg (in my case that would be '16000000#>'). I tried $imagick->resizeImage(Imagick::RESOURCETYPE_AREA, 4000 * 4000);, but that just cuts off the conversion instead of resizing.
public static function storeFilePdfToJpg($file) {
$destinationPath = public_path() . Config::get('my.image_upload_path');
$filename = str_random(10) . '_' . str_replace('.pdf', '', $file->getClientOriginalName()) . '.jpg';
$imagick = new Imagick();
$imagick->setResolution(350,350);
$imagick->readImage($file->getPathName());
$imagick->setImageCompression(imagick::COMPRESSION_JPEG);
$imagick->setImageCompressionQuality(100);
$imagick->resizeImage(Imagick::RESOURCETYPE_AREA, 4000 * 4000);
$imagick->writeImages($destinationPath . '/' . $filename, false);
return Config::get('my.full_image_upload_path') . $filename;
}

The C api for Image Magick doesn't (afaik) expose this functionality, so it isn't possible for the PHP Imagick extension to implement this.
This is pretty easy to implement in PHP:
function openImageWithArea(int $desiredArea, string $filename) : Imagick
{
$image = new Imagick();
$dataRead = $image->pingImage($filename);
if (!$dataRead) {
throw new \Exception("Failed to ping image of filename $filename");
}
$width = $image->getImageWidth();
$height = $image->getImageHeight();
$area = $width * $height;
$ratio = $desiredArea / $area;
$desiredWidth = (int)($ratio * $width);
$desiredHeight = (int)($ratio * $height);
$imagick = new Imagick();
$imagick->setSize($desiredWidth, $desiredHeight);
$imagick->readImage($file);
return $imagick;
}
Should work.

Related

Cannot upload two of the same image files

I have an issue with uploading the same image twice, but in different sizes. What I thought I could do was having one file input, where I choose the image. In the upload php file, I have two variables:
$img = $_POST["file"];
$imgThumbnail = $_POST["file"];
I have then created a function to upload images to my webpage in php. I call the functions twice after each other, but the second time the function is called, I get the error "No such file or directory.." - the first image is uploaded correctly, but the second is not because of this error. What am I doing wrong, when the file is actually the same?
// First time, normal size img
uploadNewsIMG($imgFile, $pathToUpload, $img_author, $img_text, $img_tags, 950, "false");
// Second time, thumbnail size img - ERROR IS HERE "No such file or directory.. etc."
uploadNewsIMG($imgFileThumbnail, $pathToUpload, $img_author, $img_text, $img_tags, 400, "true");
When uploading the second image, this is the line that gives me the "No such.." error:
$fileName = $fileIMG['tmp_name']; // <- THIS IS THE VARIABLE "$imgFileThumbnail"
$image_size = getimagesize($fileName); // <- ERROR HERE
EDIT
This is the function uploadNewsIMG():
function uploadNewsIMG($fileIMG, $path, $author, $alt_text, $img_tags, $max_width, $thumbnail) {
global $con;
$realFileName = $fileIMG['name'];
$fileName = $fileIMG['tmp_name'];
$realFileName = str_replace("(", "", $realFileName);
$realFileName = str_replace(")", "", $realFileName);
$fileName = str_replace("(", "", $fileName);
$fileName = str_replace(")", "", $fileName);
$realFileName = strtolower($realFileName);
$now = strtotime("now");
if ($thumbnail != "true") {
$targetFilePath = "../../" . $path . $now . "-" . $realFileName;
$img_source_link = $path . $now . "-" . $realFileName;
} else {
$targetFilePath = "../../" . $path . "thumb-" . $now . "-" . $realFileName;
$img_source_link = $path . "thumb-" . $now . "-" . $realFileName;
}
$targetFilePath = str_replace(" ","", $targetFilePath);
$img_source_link = str_replace(" ","", $img_source_link);
// Resize billedet
$dimension = $max_width;
$image_size = getimagesize($fileName);
$mime = $image_size['mime'];
$width = $image_size[0];
$height = $image_size[1];
$ratio = $width / $height;
if ($ratio > 1) {
$new_width = $dimension;
$new_height = $dimension / $ratio;
} else {
$new_height = $dimension;
$new_width = $dimension * $ratio;
}
$src = imagecreatefromstring(file_get_contents($fileName));
$destination = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($destination, $src, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
if ($mime == "image/jpeg") {
imagejpeg($destination, $fileName, 100);
} else if ($mime == "image/png") {
imagepng($destination, $fileName, 9);
}
imagedestroy($src);
imagedestroy($destination);
if (move_uploaded_file($fileName, $targetFilePath)) {
}
}
The problem here is that once you call:
move_uploaded_file
The file is gone, and you can't access it on the second try because no such file exists anymore.
What I would recommend you to do instead is just copy the image instead of moving it. This way you will be able to run this function as many times as you like.
Here is the link for this function: PHP copy.

Convert ICO to PNG using PHP Imagick

I am currently trying to convert an ICO file to a 16x16 px PNG, using PHP-Imagick. What I've tried so far:
<?php
if (empty(\Imagick::queryFormats("ICO"))) {
throw new \Exception('Unsupported format');
}
$sourceFile = __DIR__ . '/favicon.ico';
$targetFile = __DIR__ . '/favicon.png';
$im = new \Imagick($sourceFile);
$im->writeImages($targetFile, true);
This works partially. The problem is, that an ICO file may contain multiple images, so the code above creates multiple PNG files
favicon-0.png
favicon-1.png
...
for every size. This is okay, but then, I need the possibility to find the one that is close to 16x16 px, scale it down (if needed) and delete all others. For this, I already tried some things and this is where I'm stuck currently:
<?php
if (empty(\Imagick::queryFormats("ICO"))) {
throw new \Exception('Unsupported format');
}
$sourceFile = __DIR__ . '/favicon.ico';
$targetFile = __DIR__ . '/favicon.png';
$im = new \Imagick($sourceFile);
$count = $im->getNumberImages();
if ($count > 1) {
for ($x = 1; $x <= $count; $x++) {
$im->previousImage();
$tmpImageWidth = $im->getImageWidth();
$tmpImageHeight = $im->getImageHeight();
// ???
}
}
$im->writeImages($targetFile, true);
I guess, i would find a working way with some trial and error. But I would like to know, if there's an easier way to achieve this.
TL;DR: I need a simple way to convert an ICO file of any size to a 16x16 px PNG, using PHP-Imagick (using GD isn't an option).
Update:
My (currently working but maybe suboptimal) solution:
<?php
if (empty(\Imagick::queryFormats("ICO"))) {
throw new \Exception('Unsupported format');
}
$sourceFile = __DIR__ . '/favicon.ico';
$targetFile = __DIR__ . '/favicon.png';
$im = new \Imagick($sourceFile);
$count = $im->getNumberImages();
$targetWidth = $targetHeight = 16;
if ($count > 1) {
$images = [];
for ($x = 1; $x <= $count; $x++) {
$im->previousImage();
$images[] = [
'object' => $im,
'size' => $im->getImageWidth() + $im->getImageHeight()
];
}
$minSize = min(array_column($images, 'size'));
$image = array_values(array_filter($images, function($image) use ($minSize) {
return $minSize === $image['size'];
}))[0];
$im = $image['object'];
if ($image['size'] <> $targetWidth + $targetHeight) {
$im->cropThumbnailImage($targetWidth, $targetHeight);
}
}
else {
if ($im->getImageWidth() <> $targetWidth || $im->getImageHeight() <> $targetHeight) {
$im->cropThumbnailImage($targetWidth, $targetHeight);
}
}
$im->writeImage($targetFile);
Updated Answer
On re-reading your question, it seems you actually want to make a PNG file from an ICO file. I have read the Wikipedia entry for ICO files and, as usual, it is a poorly specified complicated mess of closed source Microsoft stuff. I can't tell if they come in some order.. smallest first, or largest first, so I think my recommendation would be to simply iterate through all the images in your ICO file, as you planned, and get the one with the largest number of pixels and resize that to 16x16.
Original Answer
Too much for a comment, maybe not enough for an answer... I don't use PHP Imagick much at alll, but if you use ImageMagick at the command-line in the Terminal, you can set the ICO sizes like this:
magick INPUT -define icon:auto-resize="256,128,96,64,16" output.ico
to say what resolutions you want embedded in the output file. As I said, I don't use PHP, but I believe the equivalent is something like:
$imagick->setOption('icon:auto-resize', "16");
Sorry I can't help better, I am just not set up to use PHP and Imagick, but hopefully you can work it out from here.
My final solution:
<?php
if (empty(\Imagick::queryFormats("ICO"))) {
throw new \Exception('Unsupported format');
}
$sourceFile = __DIR__ . '/favicon.ico';
$targetFile = __DIR__ . '/favicon.png';
$im = new \Imagick($sourceFile);
$count = $im->getNumberImages();
$targetWidth = $targetHeight = 16;
if ($count > 1) {
$images = [];
for ($x = 1; $x <= $count; $x++) {
$im->previousImage();
$images[] = [
'object' => $im,
'size' => $im->getImageWidth() + $im->getImageHeight()
];
}
$minSize = min(array_column($images, 'size'));
$image = array_values(array_filter($images, function($image) use ($minSize) {
return $minSize === $image['size'];
}))[0];
$im = $image['object'];
if ($image['size'] <> $targetWidth + $targetHeight) {
$im->cropThumbnailImage($targetWidth, $targetHeight);
}
}
else {
if ($im->getImageWidth() <> $targetWidth || $im->getImageHeight() <> $targetHeight) {
$im->cropThumbnailImage($targetWidth, $targetHeight);
}
}
$im->writeImage($targetFile);

Laravel show error getimagesize on image update

I am having a strange issue here on image update. After add a new product I need the option of update image and get this error while inserting it everything is ok.
getimagesize(2.png): failed to open stream: No such file or directory
Here is my code
$file = $request->image;
if (isset($file))
{
list($width, $height) = getimagesize($file);
$extension = $file->getClientOriginalExtension();
$final_filename = $file->getFilename() . '.' . $extension;
$new_height_75 = (75 * $height) / $width;
$thumb_img_75 = Image::make($file->getRealPath())->resize(75, $new_height_75);
$thumb_img_75->save(public_path('attachments/product_images/thumbs_75').'/'.$final_filename,80);
$new_height_250 = (250 * $height) / $width;
$thumb_img_250 = Image::make($file->getRealPath())->resize(250, $new_height_250);
$thumb_img_250->save(public_path('attachments/product_images/thumbs_250').'/'.$final_filename,80);
$new_height_150 = (150 * $height) / $width;
$thumb_img_150 = Image::make($file->getRealPath())->resize(150, $new_height_150);
$thumb_img_150->save(public_path('attachments/product_images/thumbs_150').'/'.$final_filename,80);
Storage::disk('product_images')->put($final_filename, File::get($file));
$product->image = $final_filename;
}
If i dd($file ); it gets the file I want to upload.
Thanks
why not you try laravel validator to validate image type as well as image file size https://laravel.com/docs/5.4/validation#rule-image

Resize copied image on upload as thumbnail

I am working on an uploader and slowly getting it working, I am uploading 3 images at once, and setting arrays for each one as keys, with an increment of ++1. I am wanting to resize the image before it gets copied to the thumbnail folder.
I have this code.
Everything works with it.
As you see, I started on getting the file info, but after that I am totally stuck on what to do after to resize the image proportionally with a maximum width of xpx and height to match it without looking distorted.
Any help would be really appreciated. Thank You.
EDIT --- I started working on it myself and wondering if this is the right approach to what i am doing.
<?php
if (isset($_POST['addpart'])) {
$image = $_FILES['images']['tmp_name'];
$name = $_POST['username'];
$i = 0;
foreach ($image as $key) {
$fileData = pathinfo(basename($_FILES["images"]["name"][$i]));
$fileName[] = $name . '_' . uniqid() . '.' . $fileData['extension'];
move_uploaded_file($key, "image/" . end($fileName));
copy("image/" . end($fileName), "image_thumbnail/" . end($fileName));
// START -- THE RESIZER THAT IS BEING WORKED ON
$source = "image_thumb/" . end($fileName);
$dest = "image_thumb/" . end($fileName);
$quality = 100;
$scale = 1 / 2;
$imsize = getimagesize($source);
$x = $scale * $imsize[0];
$y = $scale * $imsize[1];
$im = imagecreatefromjpeg($source);
$newim = imagecreatetruecolor($x, $y);
imagecopyresampled($newim, $im, 0, 0, 0, 0, $x, $y, $imsize[0], $imsize[1]);
imagejpeg($newim, $dest, $quality);
// END -- THE RESIZER THAT IS BEING WORKED ON
$i++;
}
echo 'Uploaded<br>';
echo 'Main Image - ' . $fileName[0] . '<br>';
echo 'Extra Image 1 - ' . $fileName[1] . '<br>';
echo 'Extra Image 2 - ' . $fileName[2] . '<br>';
echo '<hr>';
}
?>
thanks
Use GD library.
Create input image object using imagecreatefromstring() for example: imagecreatefromstring(file_get_contents($_FILES['images']['tmp_name'][$i]))
It's the simplest way.
Another option is to detect file type and use functions like imagecreatefromjpeg (), imagecreatefrompng(), etc.
Create output empty image using imagecreate()
Use imagecopyresampled() or imagecopyresized() to resize image and copy+paste it from input image to output image
Save output image using function like imagejpeg()
Clean memory using imagedestroy()
The built-in image manipulation commands of PHP makes your code difficult to understand and to maintain. I suggest you to use a library which wraps it into a more productive way.
If you use Intervention/image, for example, your code will look like this:
<?php
// target file to manipulate
$filename = $_FILES['images']['tmp_name'];
// create image instance
$img = Image::make($filename);
// resize to width, height
$img->resize(320, 240);
// save it!
$img->save('thumbs/'. uniqid() . '.' . pathinfo($filename, PATHINFO_EXTENSION));
Read the full documentation here: http://image.intervention.io/use/uploads

php good idea to create different image size versions on the fly?

Recently I heard of the possibility to get different versions of an uploaded image on the fly via php's header('Content-Type: image/jpeg');.
So instead of:
<img src="files/myimage-thumb.jpg">
<img src="files/myimage-midsized.jpg">
<img src="files/myimage-original.jpg">
you could do:
<img src="script/getimage.php?v=thumb">
<img src="script/getimage.php?v=midsized">
<img src="script/getimage.php?v=original">
then the script runs and via header('Content-Type: image/jpeg'); … it returns an image which was generated on the server (client?) .. temporarily..
IS THIS A GOOD IDEA?
I'm a bit sceptic, because I could imagine that it takes lot more time and performance!?
I do that creating a new image and storing on a cache folder then next time if the image already exists i retrieve it or create it.
Something like this:
function resize_image($filename, $width, $height)
{
if (!file_exists($filename) || !is_file($filename))
{
return false;
}
$info = pathinfo($filename);
$extension = $info['extension'];
$new_image = 'cache/' . utf8_substr($filename, 0, strrpos($filename, '.')) . '-' . $width . 'x' . $height . '.' . $extension;
if (file_exists($new_image))
{
return $new_image;
}
return new_iamge($filename, $width, $height);
}

Categories