I'm using Intervention in a laravel app (as everyone does) and have come across an issue where black borders are being applied around an image when cropped, like so:
Seems odd that it's preserving the transparency of the image file, and then filling the rest of the image with black.
My method for cropping and saving these images is as follows:
/**
* Accept parameter array (
'_token' => 'E1seBDvsEBj1aNpLmenIKkAoNSKct878tqnIwOQO',
'x' => 55,
'y' => '30',
'width' => '200',
'height' => '200',
'filename' => '1_somerandomhash.jpg',
)
*
* Move the given filename from TMP_ORG_LOGOS to ORGANIZATION_LOGOS_DIRECTORY, apply
* cropped dimensions, rename to organization slug name plus small random hash, return
* true or false
*
* #param array $params
* #return bool
*/
public function saveLogo(int $userId, array $params) : bool {
try{
$org = $this->userService->getUserDefaultOrg($userId);
$newImageName = $org->slug . '-' . uniqid() . '.png';
Image::make(getenv('TMP_ORG_LOGOS') . $params['filename'])
->crop(round($params['width']), round($params['height']),
round($params['x']), round($params['y']))
->save(getenv('ORGANIZATION_LOGOS_DIRECTORY') . $newImageName, 100);
$org->logo = $newImageName;
$org->save();
return true;
} catch (\Exception $e){
return false;
}
}
Has anyone run across this before? Is there a way within intervention to make these black borders transparent?
EDIT
I should also mention that intervention is using php's default GD library for image manipulation.
Was able to resolve this by creating a separate image using intervention's canvas() method. I then used trim() on the cropped image to remove the surrounding borders, and inserted that image into the center of the new canvas image. A little convoluted, and slow (3-4 seconds on the server) but it solves the problem. Still open to better solutions (if any exist).
public function saveLogo(int $userId, array $params) : bool {
try{
$org = $this->userService->getUserDefaultOrg($userId);
$newImageName = $org->slug . '-' . uniqid() . '.png';
$cropped = Image::make(getenv('TMP_ORG_LOGOS') . $params['filename'])
->crop(round($params['width']), round($params['height']),
round($params['x']), round($params['y']))
->encode('png', 100)->trim();
$canvas = \Image::canvas(round($params['width']), round($params['height']));
$canvas->insert($cropped, 'center');
$canvas->save(getenv('ORGANIZATION_LOGOS_DIRECTORY') . $newImageName, 100);
$org->logo = $newImageName;
$org->save();
return true;
} catch (\Exception $e){
return false;
}
}
#Importing modules opencv + numpy
import cv2
import numpy as np
#Reading an image (you can use PNG or JPG)
img = cv2.imread('Robo.jpg')
## 0.5 means 50% of image to be scaling
img = cv2.resize(img,None,fx=0.5,fy=0.5)
# black
img1 = np.zeros((600,600,3))
#Getting the bigger side of the image
s = max(img1.shape[0:2])
#Creating a dark square with NUMPY
f = np.zeros((s,s,3),np.uint8)
#Getting the centering position
ax,ay = (s - img.shape[1])//2,(s - img.shape[0])//2
#Pasting the 'image' in a centering position
f[ay:img.shape[0]+ay,ax:ax+img.shape[1]] = img
#Showing results (just in case)
cv2.imshow("IMG",f)
cv2.imshow("IMG2",img)
#A pause, waiting for any press in keyboard
cv2.waitKey(0)
#Saving the image
# cv2.imwrite("img2square.png",f)
cv2.destroyAllWindows()
Related
I need to create PDF's thumbnail every time it gets loaded via POST method.
Once I upload the file inside Controller, it runs getThumb function that uses Imagick to create thumbnail. The problem is that everytime I do that, this request breaks and shows this error - The "/tmp/phpY14gRo" file does not exist or is not readable..
Imagick is properly installed. I use php-7.2-apache docker image.
But if I run shell_excec script that does absolutely the same thing, it works! That eliminates all suspicions of the wrong dependency installation
Here's the function from my controller:
public function createThumb($source, $target, $size = 256, $page = 1)
{
if (file_exists($source) && !is_dir($source)): // source path must be available and not be a directory
if (mime_content_type($source) != 'application/pdf'):
return FALSE; // source is not a pdf file returns a failure
endif;
$sepa = '/'; // using '/' as file separation for nfs on linux.
$target = dirname($source) . $sepa . $target;
$size = intval($size); // only use as integer, default is 256
$page = intval($page); // only use as integer, default is 1
$page--; // default page 1, must be treated as 0 hereafter
if ($page < 0) {
$page = 0;
} // we cannot have negative values
//It breaks exactly right here
$img = new Imagick($source . "[$page]"); // [0] = first page, [1] = second page
$imH = $img->getImageHeight();
$imW = $img->getImageWidth();
if ($imH == 0) {
$imH = 1;
} // if the pdf page has no height use 1 instead
if ($imW == 0) {
$imW = 1;
} // if the pdf page has no width use 1 instead
$sizR = round($size * (min($imW, $imH) / max($imW, $imH))); // relative pixels of the shorter side
$img->setImageColorspace(255); // prevent image colors from inverting
$img->setImageBackgroundColor('white'); // set background color and flatten
$img = $img->flattenImages(); // prevents black zones on transparency in pdf
$img->setimageformat('jpeg');
if ($imH == $imW) {
$img->thumbnailimage($size, $size);
} // square page
if ($imH < $imW) {
$img->thumbnailimage($size, $sizR);
} // landscape page orientation
if ($imH > $imW) {
$img->thumbnailimage($sizR, $size);
} // portrait page orientation
if (!is_dir(dirname($target))) {
mkdir(dirname($target), 0777, true);
} // if not there make target directory
$img->writeimage($target);
$img->clear();
$img->destroy();
if (file_exists($target)) {
return $target;
} // return the path to the new file for further processing
endif;
return FALSE; // the source file was not available, or Imagick didn't create a file, so returns a failure
}
I thought that it was permission problems but found out that it's not.
Update:
If I initialize Imagick without parameters it won't throw errors and thus won't create thumbnail as it doesn't get file path. So, whenever I add file path and PHP starts searching for that file, and the error occurs. Inside the log, I noticed that the InvalidArgumentException exception was thrown by a Symfony framework.
Here's an image of the error:
After debugging I found out that Imagick was not imported into the project.
So, I just added use Imagick at the top of my Controller.
In my case i call $validator->fails() two times.
In my case, the first call $validator->fails() in my Controller action, after checking deleted the file. The second call could not find this file.
I'm using the PHPPowerPoint lib to generate some powerpoint files. The problem is that the images that are coming from the server are too big, and are breaking the limits of the slide.
Here is some code that I'm using for the images:
if(file_exists($dir.$image)){
// Add logo
$shape = $slide->createDrawingShape();
$shape->setName('PHPPowerPoint logo');
$shape->setDescription('PHPPowerPoint logo');
$shape->setPath($dir.$image);
$shape->setWidth(310);
$shape->setHeight(608);
$shape->setOffsetX(330);
$shape->setOffsetY(70);
//$shape->setOffsetY(720 - 10 - 40);
}
Notice that Im already setting the width and height, but the image is not respecting those configurations. Is there anything that I can do to make the images respect the size that I need or get a responsive behavior?
Edit:
This the code for the function createTemplateSlide:
function createTemplatedSlide(PHPPowerPoint $objPHPPowerPoint, $dir, $image, $razao, $nome, $data, $contador)
{
// Create slide
$slide = $objPHPPowerPoint->createSlide();
if(file_exists($dir.$image)){
// echo("$contador) o arquivo: '".$dir.$image."' existe <br>");
// Add logo
$shape = $slide->createDrawingShape();
$shape->setName('PHPPowerPoint logo');
$shape->setDescription('PHPPowerPoint logo');
$shape->setPath($dir.$image);
$shape->setWidth(310);
$shape->setHeight(608);
$shape->setOffsetX(330);
$shape->setOffsetY(70);
//$shape->setOffsetY(720 - 10 - 40);
}
// Create a shape (text)
// echo date('H:i:s') . " Create a shape (rich text)<br>";
$shape = $slide->createRichTextShape();
$shape->setHeight(60);
$shape->setWidth(960);
$shape->setOffsetX(0);
$shape->setOffsetY(20);
$shape->getAlignment()->setHorizontal( PHPPowerPoint_Style_Alignment::HORIZONTAL_CENTER );
$textRun = $shape->createTextRun($razao." ".$data);
$textRun->getFont()->setBold(true);
$textRun->getFont()->setSize(16);
$textRun->getFont()->setColor( new PHPPowerPoint_Style_Color( '00000000' ) );
for ($i=0; $i <23 ; $i++) {
$shape->createBreak();
}
$textRun = $shape->createTextRun(utf8_encode($nome));
$textRun->getFont()->setBold(true);
$textRun->getFont()->setSize(16);
$textRun->getFont()->setColor( new PHPPowerPoint_Style_Color( '00000000' ) );
// Return slide
return $slide;
}
Following the suggestion given by #MadeInDreams, I found this topic:
Resize image in PHP
I used the solution there and the function imagejpeg to copy the new generated image to some folder of my project.
I'm using the storage facade to store an avatar which works fine, but I want to resize my image like I did in previous versions of Laravel.
How can I go about doing this?
Here is what I have so far (doesn't work)
$path = $request->file('createcommunityavatar');
$resize = Image::make($path)->fit(300);
$store = Storage::putFile('public/image', $resize);
$url = Storage::url($store);
Error Message:
Command (hashName) is not available for driver (Gd).
You're trying to pass into putFile wrong object. That method expects File object (not Image).
$path = $request->file('createcommunityavatar');
// returns \Intervention\Image\Image - OK
$resize = Image::make($path)->fit(300);
// expects 2nd arg - \Illuminate\Http\UploadedFile - ERROR, because Image does not have hashName method
$store = Storage::putFile('public/image', $resize);
$url = Storage::url($store);
Ok, now when we understand the main reason, let's fix the code
// returns Intervention\Image\Image
$resize = Image::make($path)->fit(300)->encode('jpg');
// calculate md5 hash of encoded image
$hash = md5($resize->__toString());
// use hash as a name
$path = "images/{$hash}.jpg";
// save it locally to ~/public/images/{$hash}.jpg
$resize->save(public_path($path));
// $url = "/images/{$hash}.jpg"
$url = "/" . $path;
Let's imagine that you want to use Storage facade:
// does not work - Storage::putFile('public/image', $resize);
// Storage::put($path, $contents, $visibility = null)
Storage::put('public/image/myUniqueFileNameHere.jpg', $resize->__toString());
The put method works with the Image intervention output.
The putFile method accepts either an Illuminate\Http\File or Illuminate\Http\UploadedFile instance.
$photo = Image::make($request->file('photo'))
->resize(400, null, function ($constraint) { $constraint->aspectRatio(); } )
->encode('jpg',80);
Storage::disk('public')->put( 'photo.jpg', $photo);
The above code resizes the uploaded file to 400px width while holding the aspect ratio. Then encodes to jpg at 80% quality.
The file is then stored to the public disc.
Note you must provide a filename, not just the directory.
Using Laravel 5.8
I had a similar issue when trying to read an image file with Image when this one was saved and loaded with Storage.
Beside all the answers I wasn't sure why it wasn't working.
Exception when Image was trying to read the file
Intervention\Image\Exception\NotReadableException : Unable to init from given binary data.
Short answer
Adding ->encode()solved the issue
http://image.intervention.io/api/encode
Scenario
Basically I had a test like this
Storage::fake();
$photo = factory(Photo::class)->create();
$file = \Image::make(
UploadedFile::fake()->image($photo->file_name, 300, 300)
);
Storage::disk($photo->disk)
->put(
$photo->fullPath(),
$file
);
And in the controller I had something like this
return \Image::make(
Storage::disk($photo->disk)
->get(
$photo->fullPath()
)
)->response();
Solution
After investigation I realized that any file created by Image and saved by the Storage had a size of 0 octets.
After looking at all the solutions from this post and few hours after, I noticed everyone was using encode() but no one did mention it was that. So I tried and it worked.
Investigating a bit more, Image does, in fact, encode under the hood before saving.
https://github.com/Intervention/image/blob/master/src/Intervention/Image/Image.php#L146
So, my solution was to simple doing this
$file = \Image::make(
\Illuminate\Http\UploadedFile::fake()->image('filename.jpg', 300, 300)
)->encode();
\Storage::put('photos/test.jpg', $file);
testable in Tinker, It will create a black image
The cleanest solution I could find—using the native Storage facade—is the following. I can confirm that this works in Laravel 5.7, using intervention/image version 2.4.2.
$file = $request->file('avatar');
$path = $file->hashName('public/avatars');
$image = Image::make($file)->fit(300);
Storage::put($path, (string) $image->encode());
$url = Storage::url($path);
I do it this way:
Resize and save image somewhere (such as in the public folder).
Create a new File and pass it to Laravel filesystem functions (such as putFileAs).
Delete temporary intervention file
Note: Of course you can modify it according to your needs.
$file = $request->file('portfolio_thumb_image');
$image = Image::make($file);
$image->resize(570, 326, function ($constraint) {
$constraint->aspectRatio();
});
$thumbnail_image_name = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME).'.'.$file->getClientOriginalExtension();
$image->save(public_path('images/'.$thumbnail_image_name));
$saved_image_uri = $image->dirname.'/'.$image->basename;
//Now use laravel filesystem.
$uploaded_thumbnail_image = Storage::putFileAs('public/thumbnails/'.$portfolio_returned->id, new File($saved_image_uri), $thumbnail_image_name);
//Now delete temporary intervention image as we have moved it to Storage folder with Laravel filesystem.
$image->destroy();
unlink($saved_image_uri);
Make sure to add use Illuminate\Http\File; to top of your file for this to work, and read through the documentation section Automatic Streaming.
This assumes you want all jpegs
$path = $request->file('createcommunityavatar');
$resize = Image::make($path)->fit(300)->encode('jpg');
$filePath = $resize->getRealPath() . '.jpg';
$resize->save($filePath);
$store = Storage::putFile('public/image', new File($resize));
$url = Storage::url($store);
This is how I am doing it in my application with comments to help
// Get the file from the request
$requestImage = request()->file('image');
// Get the filepath of the request file (.tmp) and append .jpg
$requestImagePath = $requestImage->getRealPath() . '.jpg';
// Modify the image using intervention
$interventionImage = Image::make($requestImage)->resize(125, 125)->encode('jpg');
// Save the intervention image over the request image
$interventionImage->save($requestImagePath);
// Send the image to file storage
$url = Storage::putFileAs('photos', new File($requestImagePath), 'thumbnail.jpg');
return response()->json(['url' => $url]);
I've done it with following way, its simple and without any path confusion :
//Get file
$path= $request->file('createcommunityavatar');
// Resize and encode to required type
$img = Image::make($file)->fit(300)->encode('jpg');
//Provide own name
$name = time() . '.jpg';
//Put file with own name
Storage::put($name, $img);
//Move file to your location
Storage::move($name, 'public/image/' . $name);
Try updating the GD extension for the current php version.
If that doesn't help, try saving the resized image on local disk and using Storage::putFile.
You may delete the file once it has been uploaded to your storage path.
The second parameter to your putFile method is an instance of the Image Intervention class. You need to pass this as the second parameter to the putFile method.
$resize->save($absolutePath . 'small/' . $imageName);
You can't store an \Intervention\Image\Image object directly with the Laravel 5 filesystem. What you can do is resize the image from your request, and save it under the same tmp path. Then just store the uploaded (overwritten) file to the filesystem.
Code:
$image = $request->file('createcommunityavatar');
//resize and save under same tmp path
$resize = Image::make($image)->fit(300)->save();
// store in the filesystem with a generated filename
$store = $image->store('image', 'public');
// get url from storage
$url = Storage::url($store);
In my case the error was caused by calling the hashName method on an image instance.
//initially
$image = Image::make(request('image')->getRealPath());
$filename = $image->hashName();
//to fix the error
$image = Image::make(request('image')->getRealPath());
$filename = request('image')->hashName();
I am using array_walk in a script where I can call one specific function; now, I would like to call a function randomly from a pool of similar functions and manipulate the array values. It could be a loop construct. To be more specific, they are all image functions, generating images dynamically. Another requirement is that I need to save images in a particular order in a folder such as 001.jpg, 002.jpg, 003.jpg and so on. Currently, I am saving images using the code imagejpeg($im, "savedimages/" . time() . "-" . rand() . ".jpg", 90); As I call a function randomly, is it possible to maintain a similar order in saving images. I need an idea how to do that.
$fontFace = 'AmsiPro-Ultra.ttf';
$sentences = preg_split('/(?<=[.?!])\s+(?=[a-z])/i', $html);
array_walk($sentences, 'dynamicImage', $fontFace);
function dynamicImage($sentence, $key, $fontFace)
{
$img = 'green.jpg';
$client = new Client;
$image = $client->loadJpeg($img);
$palette = $image->extract();
imagickResize($img);
// create a transparent base image that we will merge the square image into.
$img = new Img();
$img->create(640, 720, true);
// first image; merge with base.
$img2 = new Img('small_square_img.jpg');
$img->merge($img2);
$img->save();
$color = 'D2F57D';
pngcolorizealpha('second_img.png', $color);
stringFunction($sentence, $palette[0], $fontFace);
$im = mergeImages(array(
'first.image.jpg',
'second.image.jpg'
));
# header('Content-type: image/jpg');
imagejpeg($im, "savedimages/" . time() . "-" . rand() . ".jpg", 90);
}
something like this
$func_1 = function(){
echo '1';
};
$func_2 = function(){
echo '2';
};
$functions = array(
$func_1,
$func_2,
..
);
array_shuffle( $functions );
$functions[1]( );
or
foreach( $functions as $index => $function ){
$function();
}
On the last one array_shuffle, if I remember right will reset the keys, if not you can do array_values($functions ) to reset them. Also note I've not tested this, but essentially that should work.
I am wanting to alter the following php code so that it resizes and displays images one at a time, with a 'next' and 'previous' button, in order to browse through the photos. I don't want any image gallery or lightbox solutions, rather the photos to just show on the page. I'm new to php so If someone can help out or point me in the right direction all help is appreciated.
$sql = "select * from people";
$result = mysql_query($sql) or die ("Could not access DB: " . mysql_error());
while ($row = mysql_fetch_assoc($result))
{
echo "<div class=\"picture\">";
echo "<p>";
// Note that we are building our src string using the filename from the database
echo "<img src=\"images/" . $row['filename'] . "\" alt=\"\" /><br />";
echo $row['fname'] . " " . $row['lname'] . "<br />";
echo "</p>";
echo "</div>";
source of above code
You can scale them in the browser using width and height attributes (or just one will maintain aspect ratio) however this is bad for many reasons including bandwidth, page performance and image quality.
You can re-size the images using libraries such as GD or Imagick
A quick sample with IMagick:
$hThumb = new Imagick($path_to_file); // Source file
$hThumb->thumbnailImage($x, $y); // You can use 300, 0 to do 300 width and maintain aspect ratio.
$hThumb->stripImage(); // remove meta data
$hThumb->writeImage($path_to_thumb); // write the image to disk
Note
Be sure to have permissions to read/write. You can verify this permissions by using is_readable and is_writable.
Loading
It's recommended to load the images using AJAX which is quite easy if using JQuery or a similar library.
$('#nextBtn').click(function() {
var index = 0; // Store this in your image ID tag perhaps
// e.g. $('#theImage').attr('id').replace('image', '')
// where ID is imageX
$.ajax({
url: 'getImages.php?index=' + index,
type: "GET",
success: function(data) {
var result = $.parseJSON(data);
if (result.success) {
// Set your image src with the new path.
// Use result.image_data.src etc...
}
}
});
});
The PHP would be relatively simple too, a structure similar to this:
<?php
$return = array('success' => false, 'image_data' => array());
if (isset($_GET['index']) && is_numeric($_GET['index')) {
// Fetch your image
$return = array(
'success' => true,
'image_data' => array(
'src' => $src,
'title' => $title,
'index' => $index
)
);
}
echo json_encode($return);
?>
** Another note **
As stated by kgb you should resize these on upload, however, they may not be user submitted so you can also check if the thumbs exist on output and generate any as required. Certainly don't generate them for every view though.
You should resize images on upload, not on output. Store both the original and the resized images, show small images in the list and full size when user wants it...
A sample code from imagecopyresampled() docs:
// Get new dimensions
list($width, $height) = getimagesize($filename);
$widthNew = 320; // You decide
$heightNew = 240; // You decide
// Resample
$imageNew = imagecreatetruecolor($widthNew, $heightNew);
$imageOld = imagecreatefromjpeg($filename);
imagecopyresampled($imageNew, $imageOld, 0, 0, 0, 0, $widthNew, $heightNew, $width, $height);
// Output
imagejpeg($imageNew, $newFilename, 100);
This example expects gd extension to be included with php. Imagick extension mentioned by Martin is more powerful and provides nicer interface, but is rarely included on web hostings.
Also googled this for you: http://www.9lessons.info/2009/03/upload-and-resize-image-with-php.html