WordPress - Blur Image on Upload - php

So I'm following the example given here (which I modified to only blur, no watermark), to make a blurred image in WordPress on upload. The problem is, that if the uploaded file is the exact same size, or smaller, than the set size, then WordPress will not generate an image, and hence no blurred one will be made.
I tried using a isst($meta['sizes']['background-image-blurred']['file']) to determine if one was made, and if not then copy() the source file, but then no WordPress "metadata" would be generated for the image (for non-WordPress people, the metadata is different than what you think), so it would give height/width undefined problems when displaying using wp_get_attachment_image.
So I'm convinced using wp_get_attachment_image hook as shown below is probably the wrong way to do this. It probably needs to happen earlier in the image upload process.
Any ideas on how to best get this working?
/**
* Several functions relatting to blurring images on uploaded.
* #see https://codeable.io/community/how-to-watermark-wordpress-images-with-imagemagick/
*/
add_image_size( 'background-image-blurred', 1920, 1080, true );
function generate_blurred_image( $meta ) {
$time = substr( $meta['file'], 0, 7); // Extract the date in form "2015/04"
$upload_dir = wp_upload_dir( $time ); // Get the "proper" upload dir
$filename = $meta['sizes']['background-image-blurred']['file'];
$meta['sizes']['background-image-blurred']['file'] = blur_image( $filename, $upload_dir );
return $meta;
}
add_filter( 'wp_generate_attachment_metadata', 'generate_blurred_image' );
function blur_image( $filename, $upload_dir ) {
$original_image_path = trailingslashit( $upload_dir['path'] ) . $filename;
$image_resource = new Imagick( $original_image_path );
$image_resource->gaussianBlurImage( 10, 100 ); // See: http://phpimagick.com/Imagick/gaussianBlurImage
return save_blurred_image( $image_resource, $original_image_path );
}
function save_blurred_image( $image_resource, $original_image_path ) {
$image_data = pathinfo( $original_image_path );
$new_filename = $image_data['filename'] . '-blurred.' . $image_data['extension'];
// Build path to new blurred image
$blurred_image_path = str_replace($image_data['basename'], $new_filename, $original_image_path);
if ( ! $image_resource->writeImage( $blurred_image_path ) ) {
return $image_data['basename'];
}
// Delete the placeholder image WordPress made now that it's been blurred
unlink( $original_image_path );
return $new_filename;
}

Unfortunately wp hasn't got a filter to force a size so what you can do is hook in after and resize your image if not created and pop it into the metadata.
I haven't got imagick so you will have to try these functions yourself, but you have the correct process above, you just need to update the filename and type in the array below. PS don't output anything within the filter!
function custom_img_size(){
add_image_size( 'background-image-blurred', 1920, 1080, true );
}
add_action( 'after_setup_theme', 'custom_img_size' );
add_filter('wp_generate_attachment_metadata', 'force_add_size', 100);
function force_add_size( $metadata ) {
if(!isset($metadata['sizes']['background-image-blurred'])){
//not set so initiate our custom size...
//I dont have imagick installed so just use your functions here to duplicate
//note original file = $filename update the $newfilename below...
//sample resize code ...
$upload_dir = wp_upload_dir();
$filename= $upload_dir['basedir'].'/'.$metadata['file'];
$extension = strtolower(strrchr($metadata['file'], '.'));
$newfilename= str_replace($extension, '-1200x1080', $filename).$extension;
copy($filename, $newfilename );
//end sample resize code.....
$filetype= 'image/jpeg';
$metadata['sizes']['background-image-blurred']= array(
"file"=> $newfilename,
"width"=> 1920,
"height"=> 1080,
"mime-type"=> $filetype
);
}
return $metadata;
}
Updates
This is designed to only catch where your existing filter has failed to create your blurred custom size otherwise it does nothing. You should still include your original filters. You may have an issue in the original code: You are deleting the original file in your filters and this will cause issues as there is a postmeta field called '_wp_attached_file' that will need updating. I have included a note below on this.
The filter catches the metadata before saving so any changes are also going to be saved once you return the $metadata. If you look at the source code: https://core.trac.wordpress.org/browser/tags/3.8.1/src/wp-admin/includes/image.php#L72 here you can see exactly how it works. I've also confirmed using wp4.3
I have attempted to insert the imagick functions you need below. I havent tested as i dont actually have it installed anywhere. (imagemagick is actually a wonderful opensource program but very server intensive). Try this function in place of the one above:
add_filter('wp_generate_attachment_metadata', 'force_add_size', 100, 2);
function force_add_size( $metadata, $id ){
$upload_dir = wp_upload_dir();
$filename= $upload_dir['basedir'].'/'.$metadata['file'];
$extension = strtolower(strrchr($metadata['file'], '.'));
$newfilename= str_replace($extension, '-blurred', $filename).$extension;
$image_resource = new Imagick( $filename);
$image_resource->resizeImage(1920,1080,Imagick::FILTER_LANCZOS,.3);
$image_resource->writeImage( $newfilename );
//http://www.dylanbeattie.net/magick/filters/result.html
unlink( $filename );//delete original image altogether ---> you might want to update the post meta on this '_wp_attached_file' , you can actually get the attachment id from the filter, i have added it above. It would be best to have a actual image url in there! something like $sfile= str_replace($upload_dir['basedir'],'', $newfilename); update_post_meta($id, '_wp_attached_file', $sfile );
switch($extension){
case '.jpg':
case '.jpeg':
$type = 'image/jpeg';
break;
case '.gif':
$type = 'image/gif';
break;
case '.png':
$type = 'image/png';
break;
default:
$type = 'image/jpeg'; // you might want a conditional to check its actually a image...imagick will do this for you as well....it shouldnt get this far if not a image.
break;
}
$metadata['sizes']['background-image-blurred']= array(
"file"=> $newfilename,
"width"=> 1920,//your custom image size, has to be this! you could get the global var and check for sizes but its this size in particular we want?
"height"=> 1080,
"mime-type"=> $type
);
return $metadata;
}
update
to prevent the image stretching out smaller images replace the imagick code with this.
$upload_dir = wp_upload_dir();
$filename= $upload_dir['basedir'].'/'.$metadata['file'];
$extension = strtolower(strrchr($metadata['file'], '.'));
$newfilename= str_replace($extension, '-blurred', $filename).$extension;
$image_resource = new Imagick( $filename);
if($image_resource->getImageWidth() <= 1920 || $image_resource->getImageHeight() > <= 1020) {
return $metadata;
}
$image_resource->resizeImage(1920,1080,Imagick::FILTER_LANCZOS,.3);
$image_resource->writeImage( $newfilename );
//http://www.dylanbeattie.net/magick/filters/result.html

When I was first reading your question I was unsure if you were having problems setting the image or generating the image or both. I thought the issue with setting the image might be that you needed to update the post image with wp_update_attachment_metadata after using wp_generate_attachment_metadata to have the post use the new image instead.
When I reread your question I realized it had to be an issue with the add_image_size() because it happens with an image the same size or smaller than the one uploaded. I had once run into this issue as well with (re)generating an alternate size image for a new theme. In my case it was that either the width or the height parameter was not being met on add_image_size().
I wanted to verify this in the Wordpress Code Reference and found a comment by a contributor near the bottom that is exactly the same issue you face, albeit without a solution posted.
If you upload an image whose dimensions match the add_image_size()
when crop is set to true, in the $meta object accessed by the
wp_generate_attachment_metadata filter, that matching image size will
not be available. Also, image sizes that have larger dimensions than
an uploaded photo will not be available either.
(Thus if you are using a technique to create something like a
monochrome derivative image, you won’t be able to get it to work if
the uploaded image is exactly the same size as the image size you’re
using for your black and white version).
I think in your case there is a workaround solution since you are using Imagick, in the fact that you can do some image math. Using the borderImage function simply add a border to each image up to 1 pixel larger than the image you have uploaded. Since the add_image_size() crops from center by default that should trigger the resize to the size you need and solve the issue.
For example you want dimensions of 1920 x 1080. Using Imagick getSize you can check the size of the image. Lets say it is exactly 1920 x 1080, but as we know this will not trigger the blur. So using borderImage we add a 1 pixel white border to the image. This should trigger wordpress to resize the image to 1920 x 1080 as long as crop is set to true.
If the image is smaller but relatively close to the needed size we just make the border lets say 10 pixels and so on to fill in the size, and let wordpress crop from center. This will work with proportional images only.
If the image is a good bit smaller, lets say 200 x 800, we should consider adding a different add_image_size() option to handle smaller images. If you need to forge ahead then we can either use Imagick to "expand" our image to the needed proportions OR we can create a new white Imagick canvas the size we need and overlay the image to top left such that we have whitespace below and to the right of our new image. You would then use the add_image_size crop parameter to cutoff the excess white. For example, add_image_size('background-image-blurred', 1920, 1080, array('left','top')) to set the crop starting from the top left of the image.

i think you need to trick a bit why you are not trying to have image of the size 1 x 1 or 5 X 5 so every time regardless what ever the image would be it will generate the Thumbnail for that image.
and upon the 'wp_generate_attachment_metadata' Hack the Stuff and generate the image from the original image and replace the 1 x 1 image with the Blured image which you want there off cource you need to trick over the filter in case you are using 'background-image-blurred' at any places for display or other computation purpose.
Hope you got the idea i think it's a little bit hack but should work proper.

What about focusing on the wp_handle_upload() function?
Here is an example of how it was used to blur images.

An outside-the-box suggestion: since you are only blurring the image and not making any other changes to the image, why not let CSS do the legwork instead of creating another image on the server?
.heroBackgroundImage {
background: url('uploadedimage.jpg');
-webkit-filter: blur(8px);
-moz-filter: blur(8px);
-o-filter: blur(8px);
-ms-filter: blur(8px);
filter: blur(8px);
-webkit-transform: translate3d(0,0,0);
}
Save the server effort; let it be handled on the client side.
EDIT: Just saw your comment about it can't be CSS. Added webkit-transform to move effect to the GPU, making it render more efficiently. Otherwise, still saving for posterity.

The example you are referring to makes use of wordpress filters.
The problem is: A wordpress filter is always bound to a certain image resolution and therefore you cannot have the image processed regardless of its resolution.
My recommendation is that you avoid filters. Instead pre-process the image at an earlier point in time. Please find below a sample code that will blur the image initially before handing over control to wordpress:
// perform new upload
include_once( ABSPATH . 'wp-admin/includes/image.php' );
$imagetype = end(explode('/', getimagesize($imageurl)['mime']));
$uniq_name = date('dmY').''.(int) microtime(true);
$filename = $uniq_name.'.'.$imagetype;
$uploaddir = wp_upload_dir();
$uploadfile = $uploaddir['path'] . '/' . $filename;
$contents= file_get_contents($imageurl);
$savefile = fopen($uploadfile, 'w');
fwrite($savefile, $contents);
fclose($savefile);
// apply blur
$image_resource = new Imagick( $uploadfile );
$image_resource->resizeImage(500, 500, null, 1);
$image_resource->blurImage( 40, 20 );
$image_data = pathinfo( $uploadfile );
$new_filename = $image_data['filename'] . '-blur.' . $image_data['extension'];
$blur_image_path = str_replace($image_data['basename'], $new_filename, $uploadfile);
if ( ! $image_resource->writeImage( $blur_image_path ) ) {
unlink( $uploadfile );
return ""; // fixme
}
unlink( $uploadfile );
$wp_filetype = wp_check_filetype(basename($filename), null );
$attachment = array(
'post_mime_type' => $wp_filetype['type'],
'post_title' => $imageurlHash,
'post_content' => '',
'post_status' => 'inherit'
);
$attach_id = wp_insert_attachment( $attachment, $blur_image_path );
$imagenew = get_post( $attach_id );
$fullsizepath = get_attached_file( $imagenew->ID );
$attach_data = wp_generate_attachment_metadata( $attach_id, $fullsizepath );
wp_update_attachment_metadata( $attach_id, $attach_data );

Related

Trying to resize uploaded files as they are saved to server

I am using Glide to deliver image content from one of my sites. This is working well and I have now built a file upload so that admins can upload images to the site for subsequent download.
Some of the images that admins will upload will be much larger than I need (or want the overhead of storing on the server), so I want to downsize them, preferably during the upload routine or failing that, just after they have been saved to their new location (storage/app/images)
So, I've been hacking around with intervention for instance without much success because of my poor understanding of the file names and paths available from getClientOriginalName/Extension etc.
Could anyone show me a pattern for this which would work well. Ideally I'd love to include something like I've seen on others' examples like...
$img = Image::make('foo.jpg')->resize(300, 200);
... in the correct place in my code
foreach($files as $file) {
$fileExtension = $file->getClientOriginalExtension();
$fileMimeType = $file->getMimeType();
if(in_array($fileExtension, $allowableExtensions)) {
if(in_array($fileMimeType, $allowableMimes)) {
array_push($dbFileList, $file->getClientOriginalName());
$newImage = '/images/' . $propertyCode . '/' . $file->getClientOriginalName();
Storage::put('/images/' . $propertyCode . '/' . $file->getClientOriginalName(), file_get_contents($file));
}else{
$errorMessage = 'At least one file was not an image, check your results...';
}
}else{
$errorMessage = 'At least one file was not an image, check your results...';
}
}
Update 1:
Storage::put('/images/' . $propertyCode . '/' . $file->getClientOriginalName(), file_get_contents($file));
$img = Image::make($file);
Storage::put('/images/new/' . $file->getClientOriginalName(), $img);
This updated code outputs the files to the /new directory and all looks fine, but the output files have 'zero bytes'. What am I missing?
Update 2: Final code
The final answer (after using the proper code provided by contributors) was that:
I had to move my app from virtual box on to the dev machine (iMac) to prevent extra confusion with paths
The path for the images must exist prior to making the ->save()
The path variable must be set in advance of the ->save()
I don't need the Storage::put at all, so the larger file never ends up on the server.
Then this final code started to work.
$path = storage_path('app/smallpics/')."/".$file->getClientOriginalName();
$img = Image::make($file)->resize(300,200)->save($path);
Much thanks to all of you. You make my Laravel learning curve a bit less terrifiying!!
You can use Intervention to manipulate your image (resize etc.) as
$new_image = Image::make($file)->resize(300,200)->save('/path/to/save');
The image upload and resize work flow is like:
Upload the image from tmp to your directory.
Make a copy of that image by setting the height, width, quality and save it in the same or some other directory.
Delete the original image.
So as per your code flow:
Storage::put('/images/' . $propertyCode . '/' . $file->getClientOriginalName(), file_get_contents($file));
after this code, put the image compress code and after that delete the original image.
you can use Intervention or just use imagemagick convert command line command for resize or convert.
Pay attention to comments :
public function saveUploadPic(Request $request)
{
$pic = $request->file('<NAME_OF_FILE_INPUT_IN_HTML_FORM>');
#check for upload correctly
if(!$pic->isValid())
{
throw new Exception("IMAGE NOT UPLOADED CORRECTLY");
}
#check for mime type and extention
$ext = $pic->getClientOriginalExtension();
$mime = $pic->getMimeType();
if(!in_array($mime, $allowedMimeTypeArray) || !in_array($ext, $allowedExtArray))
{
throw new Exception("This Image Not Support");
}
#check for size
$size = $pic->getClientSize() / 1024 / 1024;
if($size > $allowedSize)
{
throw new Exception("Size Of Image Is More Than Support Size");
}
########################YOU HAVE TWO OPTION HERE###################
#1- save image in a temporary location with random hash for name if u need orginal image for other process
#below code save image in <LARAVEL_APP_PATH>/storage/app/tmp/pics/
$hash = md5(date("YmdHis").rand(1,10000));
$pic->storeAs('tmp/pics', $hash.'.'.$ext);
#Then resize or convert it
$img = Image::make(storage_path('app/tmp/pics/'.$hash.'.'.$ext))->resize(300, 200);
#save new image whatever u want
$img->save('<PATH_TO_SAVE_IMAGE>');
#after u finish with orginal image delete it
Storage::delete(storage_path('app/tmp/pics/'.$hash.'.'.$ext);
#2- Or just use below for resize and save image witout need to save in temporary location
$img = Image::make($pic->getRealPath())->resize(300,200);
$img->save('<PATH_TO_SAVE_IMAGE>');
}
if you want to use convert see this link.

Automatically resize Wordpress images to a maximum width and height upon uploading?

I created a blog where some users can upload images through the Wordpress dashboard. The site gets bogged down quickly because the original images are so big. Some users don't have the knowledge to resize the images themselves before uploading them, and I don't want to have to resize them manually.
Is there any way I can set a maximum width and height for uploaded images? I don't even want the original to remain on the website. I want the largest version of the image on the website to match the width and height restrictions I set.
add this code in your theme's functions.php it will replace the original image with the the re-sized version.
function replace_uploaded_image($image_data) {
// if there is no large image : return
if (!isset($image_data['sizes']['large'])) return $image_data;
// paths to the uploaded image and the large image
$upload_dir = wp_upload_dir();
$uploaded_image_location = $upload_dir['basedir'] . '/' .$image_data['file'];
$large_image_location = $upload_dir['path'] . '/'.$image_data['sizes']['large']['file'];
// delete the uploaded image
unlink($uploaded_image_location);
// rename the large image
rename($large_image_location,$uploaded_image_location);
// update image metadata and return them
$image_data['width'] = $image_data['sizes']['large']['width'];
$image_data['height'] = $image_data['sizes']['large']['height'];
unset($image_data['sizes']['large']);
return $image_data;
}
add_filter('wp_generate_attachment_metadata','replace_uploaded_image');
Article Source: http://goo.gl/nkszUn
Well why dont you create a new image size?
http://codex.wordpress.org/Function_Reference/add_image_size
And use that image on your templates.
This will work for new image uploads as well as older ones replacing user uploaded large images automatically with your defined Large size from admin panel's media settings:
add_filter('wp_generate_attachment_metadata','replace_uploaded_image');
function replace_uploaded_image($image_data) {
// if there is no large image : return
if (!isset($image_data['sizes']['large'])) return $image_data;
// paths to the uploaded image and the large image
$upload_dir = wp_upload_dir();
$uploaded_image_location = $upload_dir['basedir'] . '/' .$image_data['file'];
// $large_image_location = $upload_dir['path'] . '/'.$image_data['sizes']['large']['file']; // ** This only works for new image uploads - fixed for older images below.
$current_subdir = substr($image_data['file'],0,strrpos($image_data['file'],"/"));
$large_image_location = $upload_dir['basedir'] . '/'.$current_subdir.'/'.$image_data['sizes']['large']['file'];
// delete the uploaded image
unlink($uploaded_image_location);
// rename the large image
rename($large_image_location,$uploaded_image_location);
// update image metadata and return them
$image_data['width'] = $image_data['sizes']['large']['width'];
$image_data['height'] = $image_data['sizes']['large']['height'];
unset($image_data['sizes']['large']);
return $image_data;
}

How to get the local path of a file in wordpress

Since in wordpress, the files/images uploaded are stored in 3 different sizes thus taking up the memory. I have a code that re sizes the image given the URL of that image. The code to resize is:
$img = wp_get_image_editor( $image_url );
if ( ! is_wp_error( $img ) ) {
$img->resize( 200, 200, false );
$filename = $img->generate_filename(
'final',
ABSPATH.'wp-content/uploads',
NULL
);
$img->save($filename);
}
So I want to use this code to resize the image from the local path of the user so that I don't use up too much of my memory.
Can anyone tell me how to get the local path and url of the file uploaded by url?
How about this for getting the local path of an image from its URL? :
function ContentUrlToLocalPath($url){
preg_match('/.*(\/wp\-content\/uploads\/\d+\/\d+\/.*)/', $url, $mat);
if(count($mat) > 0) return ABSPATH . $mat[1];
return '';
}
It assumes the file is located in the uploads folder.
we could use get_attached_file() for retrieve attached file path based on attachment ID
try:
<?php
get_attached_file( $attachment_id);
?>

Joomla 3 Component - Upload Image and Convert with Imagick

I am currently creating a component, where a user creates a new image and there's an upload button and a name input, all the basic gallery fields for an Image..
But I have come across two issues, the one is how would i write the following code so that it exports one image b/w and the other color scaled to amax width of 160, Here's the code I have:
function save(){
if(!defined('DS')) define('DS', DIRECTORY_SEPARATOR);
$input=JFactory::getApplication()->input;
$input->get('jform', NULL, NULL);
$file = JRequest::getVar('jform', null, 'files', 'array');
$data = JRequest::getVar( 'jform', null, 'post', 'array' );
$path = JPATH_ROOT;
//
//
// Make the file name safe.
jimport('joomla.filesystem.file');
$file['name']['logo'] = JFile::makeSafe($file['name']['logo']);
// Move the uploaded file into a permanent location.
if (isset($file['name']['logo'])) {
// Make sure that the full file path is safe.
$filepath = JPath::clean($path. DS ."images". DS ."associations". DS . strtolower($file['name']['logo']));
// Move the uploaded file.
JFile::upload( $file['tmp_name']['logo'], $filepath );
$data['logo'] = strtolower( $file['name']['logo'] );
//convert image
$image = $filepath;
$im = new Imagick();
$im->pingImage($image);
$im->readImage( $image );
$im->setImageResolution(72,72);
$im->resampleImage(72,72,imagick::FILTER_UNDEFINED,1);
$im->scaleImage(160,0);
$im->setImageFormat('jpeg');
$im->setImageCompression(imagick::COMPRESSION_JPEG);
$im->setImageCompressionQuality(60);
$im->modulateImage(100, 0, 100);
$im->writeImage($image);
$im->destroy();
}
$input->post->set('jform',$data);
return parent::save();
}
And Finally, How Would I manage this? it saves the image name in the database but once i return to this item it just has an upload field, where it would be rather useful to have the image name or the actual image with a delete function showing and a reupload...
Am I going the right way about this?... Any Help Greatly Appreciated... Thank you :)
How to produce 2 images
You have to create 2 resources out from the uploaded original, transform each and save in the separate file. For example I'be using the JImage package (note there's no limitcolors filter available)
$image_one = new JImage($filepath);
$image_one
->filter('grayscale')
->toFile($path_one, IMAGETYPE_JPEG, array('quality' => 60));
$image_two = new JImage($filePath);
$image_two
->filter('limitcolors', array('limit' => 160))
->toFile($path_two, IMAGETYPE_GIF);
Managing images
Depends on the reason, why there"s functionality being implemented and so relation of the uploaded image to other objects/ events.
If the uploaded images are used for user profiles, you should store a user id in the database along with image location. Next time check if current users has any images related to his/ her account and display.

Resizing images with filling

I am trying to use this class: http://www.verot.net/php_class_upload_samples.htm
If anyone is familiar with resizing and filling the rest of the image, please help me.
The idea is, if you upload an image that is 600x600, I want to save two images: 1-600x600 and 1-300x300. So far it's working great. However, if the user chose to upload picture with size 749x1202, I want the script to make it look good in 300x300 and 600x600 where the useless space is filled with white so the image is still 600x600. The same should happen for a smaller image, e.g. 249x400.
Here is my code so far:
// Set the upload directory
$uploadDir = '../../htdocs/public/product_images/'.$id.'/';
$thumbDir = '../../htdocs/public/product_images/'.$id.'/thumbs/';
#mkdir($uploadDir, 0755, true);
#mkdir($thumbDir, 0755, true);
// Store the file content in a variable
$file = file_get_contents('php://input');
// Save the file to the server
file_put_contents($uploadDir . $filename, $file);
$targetFile = str_replace('//','/',$uploadDir) . $filename;
$targetThumb = str_replace('//','/',$thumbDir) . $filename;
copy ($targetFile,$targetThumb);
$image = new upload($targetFile);
if ($image->uploaded) {
$image->image_resize = true;
$image->image_ratio_fill = true;
$image->image_x = 600;
$image->image_y = 600;
$image->file_overwrite = true;
$image->process($uploadDir);
}
$image = new upload($targetThumb);
if ($image->uploaded) {
$image->image_resize = true;
$image->image_ratio_fill = true;
$image->image_x = 300;
$image->image_y = 300;
$image->file_overwrite = true;
$image->process($thumbDir);
}
I can use another way doing it, but if someone can walk me through a bit, I would be very grateful.
What you can do is to scale your image with aspect ratio kept, so at least one dimension matches your requirements (i.e. you get 128x300) then create empty image (canvas) and merge these two centering the image on canvas. But I'd simply write this 128x300 px image down on disk and enforce 300x300 px container size in HTML/CSS - this is better as your files are smaller and you can easily change i.e. bg color of the canvas just by changing your CSS - with former technique, it will not be that easy if bg is not transparent (and if it is, then why not use HTML anyway)?

Categories