Related
I’m creating an uploader that can upload jpg, giff and png images. Then converts them all too transparent PNG’s and then crops the image based on crop parameters send from client side. The crop can even supply negative axis coordinates, meaning the image is being cropped beyond image dimensions.
To ensure all supported formats can have transparency I first recreate the image into a transparent png, and this is working well.
//GET WIDTH AND HIEGHT OF UPLOADED JPG
list($imageWidth,$imageHeight)= getimagesize($originalDirectory.$file_name);
$image = imagecreatefromjpeg($originalDirectory.$file_name);
//CREATE NEW IMAGE BASED ON WIDTH AND HEIGHT OF SROUCE IMAGE
$bg = imagecreatetruecolor($imageWidth, $imageHeight);
//TRANSPARENCY SETTINGS FOR BOTH DESTINATION AND SOURCE IMAGES
$transparent2 = imagecolorallocatealpha($bg, 0, 0, 0, 127);
$transparent = imagecolorallocatealpha($image, 0,128,255,50); //ONLY TO ENSURE TRANSPARENCY IS WORKING
//SAVE TRANSPARENCY AMD FILL DESTINATION IMAGE
imagealphablending( $bg, false );
imagesavealpha($bg, true);
imagefill($bg, 0, 0, $transparent2);
//SAVE TRANSPARENCY AMD FILL SOURCE IMAGE
imagealphablending( $image, false );
imagesavealpha($image, true);
imagefill($image, 0, 0, $transparent); //ONLY TO ENSURE TRANSPARENCY IS WORKING
//CREATE AND SAVE AS PNG FILE WITH TRANSPARENCY
imagecopy($bg, $image, 0, 0, 0, 0, $imageWidth,$imageHeight);
header('Content-type: image/png');
imagepng($bg, $originalDirectory.$jpgFile);
imagedestroy($bg);
After the new png is created I use it to then only crop the image according to the parameters passed through from the client side scripting.
//GET NEWLY CREATED PNG
$src = imagecreatefrompng($originalSRC);
// NOT SURE IF NECESSARY BUT HAS NO EFFECT ON FINAL RESULT REGGARDLESS OF ANY SETTINGS DONE
imagealphablending( $image, false );
imagesavealpha($image, true);
//DEFINE DESTINATION CROPPED FILE
$thumbHighFilename = $thumbHighDirectory.'test.png';
//CREATE NEW IMAGE BASED ON FINAL CROP SIZE
$tmp = imagecreatetruecolor($cropWidth, $cropHeight);
//ENSURE DESTINATION HAS TRANSPARENT BACKGROUND
$transparent2 = imagecolorallocatealpha($tmp, 0, 0, 0, 127);
imagealphablending( $tmp, false );
imagesavealpha($tmp, true);
imagefill($tmp, 0, 0, $transparent2);
/* -------------------------------------------------
PROBLEM HERE
When I try to merge the two with the crop paramaters
send from client side. All transparencies work, except
where crop X and Y axis exceeds source image paramaters.
Currently 50px offset on destination image is to verify
transparency works.
The source coordinates are based on image not crop area.
Tried with both imagecopyresized & imagecopyresampled
-------------------------------------------------*/
imagecopyresized($tmp, $src, -50,-50, $xAxis,$yAxis,$cropWidth, $cropHeight, $pW, $pH);
//SAVE FINAL IMAGE
header('Content-type: image/png');
imagepng($tmp, $thumbHighFilename);
imagedestroy($tmp);
This is where the source and destination images still has there transparency; however the negative coordinates creates a black background around the source image. How can I get that to be transparent?
While I found a lot about transparencies, nothing has been a proper solution. For example imagefill afterwards will not work as source could use 100% black around the edges and will make that also transparent then, which it shouldn’t.
CLIENT SIDE CROP EXAMPLE WITH INDICATIONS
CURRENT FINAL IMAGE RESULT WITH ADDED INDICATIONS
From what I could find it seems that there is no way for the GD imagecopyresized and imagecopyresampled to inherit the default backgrounds of the images it is cropping. Thus it keeps adding the default black background to the source image.
The biggest problem I’ve had was actually the crop container being responsive, thus very difficult to determine crop parameters.
To get around the problem I asked my frontend developer to send me more parameters from the crop. Below are all parameters now being passed to php, and the variables in php that are linked to the parameters received:
$xAxisCropper & $yAxisCropper – The variables get the X and Y
coordinates of the container not the image being cropped.
$pW & $pH – Defines the width and height of the crop box.
$containerWidth & $containerheight – As the container is responsive
getting the height and width helps understand what size the
coordinates where calculated on.
$imResizeHeight & $imResizeWidth – Since the images in the container
are always set to be contained within the container, it was important
to get the width and height into which the image is being resized by
the CSS. Giving understanding of what is happening with the image
within the responsive container.
$originalWidth & $originalHeight – Defines the original size of the
image and could either be passed to php or retrieved from the
original image uploaded to the server.
With these parameters I could now recreate the container with the image in the centre, and crop the newly created image. Before I crop it’s important to get the right scaling of the image for the crop, in order to ensure the best quality image is cropped and not compressed before cropping.
To do this I started by determining if the image in the container is being scaled up or down within the container. If scaled up image needs to be scaled to container size, if scaled down the container needs to be increased to have the image fit in the container. Below is the code that currently determines this, and changes the necessary parameters accordingly:
//IF CSS CONTAIN RESIZES HEIGHT EQUAL TO CROP CONTAINER HEIGHT
if($imResizeHeight == $containerheight){
//IF IMAGE SIZE WAS INCREASED
if($imResizeHeight>$originalHeight){
//DEFINE NEW IMAGE SIZE TO SCALE TO CONTAINER
$new_height = $imResizeHeight;
$new_width = $originalWidth * ($new_height / $originalHeight);
$scale = 'image'; //DEFINE WHAT IS BEING INCREASED
//ESLSE INCREASE CONTAINER TO IMAGE HEIGHT DIMENSIONS
}else{
//RECALCULATE WIDTH & HEIGHT OF CONTAINER
$newContainerWidth = $containerWidth * ($originalHeight / $containerheight);
$newContainerheight = $originalHeight;
$scale = 'container'; //DEFINE WHAT IS BEING INCREASED
}
//IF CSS CONTAIN RESIZES WIDTH EQUAL TO CROP CONTAINER WIDTH
}elseif($imResizeWidth == $containerWidth) {
//IF IMAGE SIZE WAS INCREASED
if($imResizeWidth>$originalWidth){
//DEFINE NEW IMAGE SIZE TO SCALE TO CONTAINER
$new_width = $imResizeWidth;
$new_height = $originalHeight * ($new_width / $originalWidth);
$scale = 'image'; //DEFINE WHAT IS BEING INCREASED
//ESLSE INCREASE CONTAINER TO IMAGE WIDTH DIMENSIONS
}else{
//RECALCULATE WIDTH & HEIGHT OF CONTAINER
$newContainerheight = $containerheight * ($originalWidth / $containerWidth);
$newContainerWidth = $originalWidth;
$scale = 'container'; //DEFINE WHAT IS BEING INCREASED
}
}
//IF IMAGE WAS INCREASED
if($scale=='image'){
//SCALE IMAGE
$src = imagescale ( $src , $new_width , $new_height, IMG_BILINEAR_FIXED);
imagepng($src,$originalSRC,0);
//ADD CHANGES TO VARIABLES USED IN CROP
$pH = $pH * ($new_height / $originalHeight);
$pW = max(0, round($pW * ($new_width / $originalWidth)));
$originalWidth = $new_width;
$originalHeight = $new_height;
$newContainerWidth = $containerWidth;
$newContainerheight = $containerheight;
//ELSE CONTAINER WAS INCREASED
}else {
//RECALCULATE COORDINATES OF CONTAINER
$yAxisCropper = max(0, round($yAxisCropper * ($newContainerheight / $containerheight)));
$xAxisCropper = max(0, round($xAxisCropper * ($newContainerWidth / $containerWidth)));
}
Once the parameters have been redefined according to the scaling, I then create the transparent background according to the container size and add the image in the centre. Thus creating a proper version of the crop container as an image, below the code for creating the new image:
//CALCULATE CENTRE OF NEW CONTAINER
$centreX = max(0, round(($newContainerWidth-$originalWidth)/2));
$centreY = max(0, round(($newContainerheight-$originalHeight)/2));
//CREATE NEW IMAGE BASED ON WIDTH AND HEIGHT OF SROUCE IMAGE
$bg = imagecreatetruecolor($newContainerWidth, $newContainerheight);
//SAVE TRANSPARENCY AMD FILL DESTINATION IMAGE
$transparent = imagecolorallocatealpha($bg, 0,0,0,127);
imagealphablending( $bg, false);
imagesavealpha($bg, true);
imagefill($bg, 0, 0, $transparent);
//CREATE AND SAVE AS PNG FILE WITH TRANSPARENCY
imagecopy($bg, $src, $centreX, $centreY, 0, 0, $originalWidth,$originalHeight);
header('Content-type: image/png');
imagepng($bg, $originalSRC, 0);
imagedestroy($bg);
The result till thus far:
It is only at this point that I send the new image to be cropped according to the specified width and height. Code below:
$src = imagecreatefrompng($originalSRC);
$thumbHighFilename = $thumbHighDirectory.$new_image;
$tmp = imagecreatetruecolor($cropWidth, $cropHeight);
$transparent2 = imagecolorallocatealpha($tmp, 0, 0, 0, 127);
imagealphablending( $tmp, false );
imagesavealpha($tmp, true);
imagefill($tmp, 0, 0, $transparent2);
imagealphablending( $tmp, false );
imagesavealpha($tmp, true);
imagecopyresampled($tmp, $src, 0,0, $xAxisCropper,$yAxisCropper,$cropWidth, $cropHeight, $pW, $pH);
header('Content-type: image/png');
imagepng($tmp, $thumbHighFilename, 2);
Final result cropped 400x300
This has been the only way so far that I’ve managed to solve the problem. Code could probably still be optimised, but if someone has a more optimal solution please share.
I also wish to thank my frontend developer Salem for helping me to solve this irritating issue.
I also did have this annoying black Background Problem with png Files and the imagecropauto Function.
After "some" Tests, i have found a Solution as it seems. At least it works for me.
Here is my mofified Code:
$im=imagecreatefrompng("yourpicture.png");
$cropped=imagecropauto($im, IMG_CROP_SIDES);
imagesavealpha($cropped,true);
if($cropped !==false) {
imagedestroy($im);
$im=$cropped;
}
imagepng($im, "yourpicturecropped.png");
imagedestroy($im);
When i submit a new image to database which is extracted from pdf,it should be cropped image of original image. If the image already exists in database, than image should not be inserted and if its not inserted,i have to generate an identification value for that.
The identification value will also be inserted to the same table as of image.
There are keys involved. table page have the following identies
pid,qid,pidentifierval,image
$record = array('pid' => "NULL",
'qid' => $qid,
'pidentifierval' => $pid,
'image' => $crop,
'rotation' => 0);
function newquestionnaire($filename,$desc = "",$type="pngmono"){
global $db;
if ($desc == "") $desc = $filename;
//generate temp file
$tmp = tempnam(TEMPORARY_DIRECTORY, "FORM");
//print "Creating PNG files<br/>";
//use ghostscript to convert to PNG
exec(GS_BIN . " -sDEVICE=$type -r300 -sOutputFile=\"$tmp\"%d.png -dNOPAUSE -dBATCH \"$filename\"");
//add to questionnaire table
//
//create form entry in DB
//
$db->StartTrans();
$sql = "INSERT INTO questionnaires (qid,description,sheets)
VALUES (NULL,'$desc',0)";
$db->Execute($sql);
$qid = $db->Insert_Id();
//Number of imported pages
$pages = 0;
//read pages from 1 to n - stop when n does not exist
$n = 1;
$file = $tmp . $n . ".png";
while (file_exists($file))
{
$data = file_get_contents($file);
$images = split_scanning($data);
unset($image);
unset($data);
foreach($images as $data)
{
//header Cropped Function
// Original image
$filename = $data;
// Get dimensions of the original image
list($current_width, $current_height) = getimagesize($filename);
// The x and y coordinates on the original image where we
// will begin cropping the image
$left = 50;
$top = 50;
// This will be the final size of the image (e.g. how many pixels
// left and down we will be going)
$crop_width = 200;
$crop_height = 200;
// Resample the image
$canvas = imagecreatetruecolor($crop_width, $crop_height);
$current_image = imagecreatefromjpeg($filename);
imagecopy($canvas, $current_image, 0, 0, $left, $top, $current_width, $current_height);
$crop = imagepng($canvas, $filename, 100);
//check for header Cropped Image
what function should i made here ?
$pid = $pid.$pages;
if ($pid)
{
$pages++;
$record = defaultpage($qid,$pid,$crop);
$db->AutoExecute('pages',$record,'INSERT');
}
else
print T_("INVALID - IGNORING BLANK PAGE");
unset($data);
unset($crop);
i am confused here,that how should i check and compare if the image exists in the database or not. Please help
For cropping the image
i have found a solution
$data = '1.png';
list($current_width, $current_height) = getimagesize($data);
// The x and y co-ordinates on the original image where we will begin cropping the image
$left = 1100;
$top = 30;
// final size of image
$crop_width = 200;
$crop_height = 200;
//Resample the Image
$canvas = imagecreatefromtruecolor($crop_width, $crop_height);
$current_image = imagecreatefrompng($data);
imagecopy($canvas, $current_image, 0, 0, $left, $top, $current_width, $current_height);
imagepng($canvas, $data, 100);
For comparing two images
array image_compare(mixed $image1, mixed $image2, int $RTolerance, int $GTolerance, int $BTolerance, int $WarningTolerance, int $ErrorTolerance)
Parameters
image1
- A string containing the path to the image, or a resource of an image already created by GD Library. This is the first image to be compared.
image2
- A string containing the path to the second image, or a resource of the second image already created by GD Library. This will be the second image to be compared.
RTolerance, GTolerance, BTolerance (0-255)
- Specifies the maximum deviation for the red, green, or blue (respectively) channel before throwing a flag.
WarningTolerance (0-100)
- Percentage of channel differences before warning returned.
ErrorTolerance (0-100)
- Percentage of channel differences before error (flag) returned.
Return Values
An array will be returned with the following information:
PixelsByColors
- Pixel quantity * 3 (for each of R,G, and B channels).
PixelsOutOfSpec
- (If a pixel varies outside of xTolerance, for each red, green, and blue. Where x = R/G/B) If any channel exceeds the threshhold, this number is incremented.
PercentDifference
- The percentage of difference comparing PixelsOutOfSpec to PixelsByColors
WarningLevel and ErrorLevel
- If the percentage was large enough to trigger either the specified warning, or error level.
For comparison i have to work, i am thinking to use array.Using Select query for
getting images from the database,using while loop and fetching the result in array,calling array keys for the image to be stored in variable and using if else condition in the compare function above will work i think. What you people think ?
For image comparision, No Scanned image can be 100 percent accurate, so image comparision is not so easy. It is quite hectic task.
After a Lot of Research and work, I came to a point that if it is 100 percent necessary to do image comparision. I will have to use Php-OpenCV Library. If i have to allow some sort of error tolerance, the above class can work properly. My work can was achieved using Php-tesseract. I have simply used tesseract-OCR. I used ghostscript for converting the PDF into png, cropped the image, convert that particular part of image into text using Php-tesseract OCR Library that is placed on google Code website. Called that text into variable,using regexpression i was able to check that whether a particular text exists in the variable or not and used it in the condition as requried for me.
Consider this as end of my problem.
For convenience of the visitors i am pasting my Code Snippet so that it can be used.
// Cropping the image
// Get dimensions of the original image
list($current_width, $current_height) = getimagesize($file);
// The x and y co-ordinates on the original image where we will begin cropping the image
$left = 1100;
$top = 30;
// final size of image
$crop_width = 700;
$crop_height = 200;
//Resample the Image
$canvas = imagecreatetruecolor($crop_width,$crop_height);
$current_image = imagecreatefrompng($file);
imagecopy($canvas, $current_image, 0, 0, $left, $top, $current_width, $current_height);
imagepng($canvas, $file, 1);
// Note you will have to install Php tesseract Library before making the API Call.
$api= new TessBaseAPI;
$api->Init(".","eng",$mode_or_oem=OEM_DEFAULT);
$api->SetPageSegMode(PSM_AUTO);
$mImgFile = $file;
$handle=fopen($mImgFile,"rb");
$mBuffer=fread($handle,filesize($mImgFile));
//print strlen($mBuffer);
$result=ProcessPagesBuffer($mBuffer,strlen($mBuffer)*4,$api);
//print $result;
$result = ProcessPagesFileStream($mImgFile,$api);
//print "result(ProcessPagesFileStream)=";
print $result;
$txtchk = 'FORM';
if(preg_match("/$txtchk/i", $result)) {
echo true;
}
I hope it will be helpful for lot of people.
How do I change a rectangular image to a square-shaped avatar in php, such that no matter what is the resolution of the uploaded image, it is able to resize to a centralized 42 x 42 pixel avatar. This is the php code I am using. Anyone can advise.
<?php
//Name you want to save your file as
$save = 'myfile1.jpg';
$file = 'original1.jpg';
echo "Creating file: $save";
$size = 0.45;
header('Content-type: image/jpeg') ;
list($width, $height) = getimagesize($file) ;
$modwidth = $width * $size;
$modheight = $height * $size;
$tn = imagecreatetruecolor($modwidth, $modheight) ;
$image = imagecreatefromjpeg($file) ;
imagecopyresampled($tn, $image, 0, 0, 0, 0, $modwidth, $modheight, $width, $height) ;
// Here we are saving the .jpg, you can make this gif or png if you want
//the file name is set above, and the quality is set to 100%
imagejpeg($tn, $save, 100) ;
?>
First you need to be tell where is the central square in a rectangle with dimensions $x and $y.
// horizontal rectangle
if ($x > $y) {
$square = $y; // $square: square side length
$offsetX = ($x - $y) / 2; // x offset based on the rectangle
$offsetY = 0; // y offset based on the rectangle
}
// vertical rectangle
elseif ($y > $x) {
$square = $x;
$offsetX = 0;
$offsetY = ($y - $x) / 2;
}
// it's already a square
else {
$square = $x;
$offsetX = $offsetY = 0;
}
Now we can build a square from it, just need to resize it to 42x42. Something like this should work:
list($x, $y) = getimagesize($file);
// code snippet from above goes here
// so we get the square side and the offsets
$endSize = 42;
$tn = imagecreatetruecolor($endSize, $endSize);
imagecopyresampled($tn, $image, 0, 0, $offsetX, $offsetY, $endSize, $endSize, $square, $square);
So, if we have a rectangular image 100x80, the code will figure out that the big square size is 80, x offset is 10, y offset is 0. Roughly, it looks like this:
100
-----------
| |
| | 80
| |
-----------
|
V
80
--------- 42
| | -----
| | 80 ---> | | 42
| | -----
---------
After we crop the big square from the original rectangle, we just shrink it to the end size, which is 42 in your case.
Just tested and works perfectly, make sure you remove the echo line if you plan to output the image into the browser (combined with the header).
I don't know if someone was looking for this, but I needed to have a 600 x 600 squared image.
The source could be any rectangular image and I needed to mantain proportion of the original image and center the original rectangular image in a 600 x 600 squared white image.
Here it is
src_file: URL of Source Image
destination_file: Path where the destination file will be saved.
square_dimensions (pixels). I needed 600, but could be any value.
jpeg_quality: Quality (0, 100) . I used default 90
function square_thumbnail_with_proportion($src_file,$destination_file,$square_dimensions,$jpeg_quality=90)
{
// Step one: Rezise with proportion the src_file *** I found this in many places.
$src_img=imagecreatefromjpeg($src_file);
$old_x=imageSX($src_img);
$old_y=imageSY($src_img);
$ratio1=$old_x/$square_dimensions;
$ratio2=$old_y/$square_dimensions;
if($ratio1>$ratio2)
{
$thumb_w=$square_dimensions;
$thumb_h=$old_y/$ratio1;
}
else
{
$thumb_h=$square_dimensions;
$thumb_w=$old_x/$ratio2;
}
// we create a new image with the new dimmensions
$smaller_image_with_proportions=ImageCreateTrueColor($thumb_w,$thumb_h);
// resize the big image to the new created one
imagecopyresampled($smaller_image_with_proportions,$src_img,0,0,0,0,$thumb_w,$thumb_h,$old_x,$old_y);
// *** End of Step one ***
// Step Two (this is new): "Copy and Paste" the $smaller_image_with_proportions in the center of a white image of the desired square dimensions
// Create image of $square_dimensions x $square_dimensions in white color (white background)
$final_image = imagecreatetruecolor($square_dimensions, $square_dimensions);
$bg = imagecolorallocate ( $final_image, 255, 255, 255 );
imagefilledrectangle($final_image,0,0,$square_dimensions,$square_dimensions,$bg);
// need to center the small image in the squared new white image
if($thumb_w>$thumb_h)
{
// more width than height we have to center height
$dst_x=0;
$dst_y=($square_dimensions-$thumb_h)/2;
}
elseif($thumb_h>$thumb_w)
{
// more height than width we have to center width
$dst_x=($square_dimensions-$thumb_w)/2;
$dst_y=0;
}
else
{
$dst_x=0;
$dst_y=0;
}
$src_x=0; // we copy the src image complete
$src_y=0; // we copy the src image complete
$src_w=$thumb_w; // we copy the src image complete
$src_h=$thumb_h; // we copy the src image complete
$pct=100; // 100% over the white color ... here you can use transparency. 100 is no transparency.
imagecopymerge($final_image,$smaller_image_with_proportions,$dst_x,$dst_y,$src_x,$src_y,$src_w,$src_h,$pct);
imagejpeg($final_image,$destination_file,$jpeg_quality);
// destroy aux images (free memory)
imagedestroy($src_img);
imagedestroy($smaller_image_with_proportions);
imagedestroy($final_image);
}
Here is working snippet from my animal adoption project that cuts the middle of the image (horizontal or vertical) and makes it square, without adding white fields to the destination image:
/**
* Cuts a square image from the middle of rectangular image
* Requires: mbstring, exif, gd built-in extensions uncommented in php.ini
* Resamples rect img to square img and converts jpeg to wepp image
*
*/
function squarify($src_file,$dst_file,$square_side=600,$quality=90) {
// Deals only with jpeg
if(exif_imagetype($src_file) != IMAGETYPE_JPEG) { return 'src_not_jpeg'; }
// Convert old file into img
$src_img=imagecreatefromjpeg($src_file);
$src_side_x=imageSX($src_img);
$src_side_y=imageSY($src_img);
// Do not magnify image if its sides less than desired square side,
// issue false if image size is too small
// Remove, if you want src image be magnified
// if($src_side_x < $square_side || $src_side_y < $square_side) {
// return 'src_too_small';
// }
// Create new image
$dst_image=imagecreatetruecolor($square_side,$square_side);
// The image is square, just issue
// resampled image with adjusted square sides and image quality
if($src_side_x==$src_side_y) {
imagecopyresampled(
$dst_image,
$src_img,
0,
0,
0,
0,
$square_side,
$square_side,
$src_side_x,
$src_side_x
);
// The image is vertical, use x side as initial square side
} elseif($src_side_x<$src_side_y) {
$x1=0;
$y1=round(($src_side_y-$src_side_x)/2);
imagecopyresampled(
$dst_image,
$src_img,
0,
0,
$x1,
$y1,
$square_side,
$square_side,
$src_side_x,
$src_side_x
);
// The image is horizontal, use y side as initial square side
} else {
$x1=round(($src_side_x-$src_side_y)/2);
$y1=0;
imagecopyresampled(
$dst_image,
$src_img,
0,
0,
$x1,
$y1,
$square_side,
$square_side,
$src_side_y,
$src_side_y
);
}
// Save it to the filesystem
// imagewebp($dst_image,$dst_file,$quality);
// Or show it in the browser,
// dont forget about header('Content-type: image/webp')
imagewebp($dst_image,$dst_file,$quality);
}
And you can call this function on existing image:
if(!extension_loaded('mbstring')) { die('Err: mbstring extension not loaded.'); }
if(!extension_loaded('exif')) { die('Err: exif extension not loaded.'); }
if(!extension_loaded('gd')) { die('Err: gd extension not loaded.'); }
header('Content-type: image/webp');
squarify('images/src.jpg','images/res.webp',300,80);
A couple of years ago I authored a PHP (ZEND) module that I still use today in some of my projects. This module was built with a fairly rudimentary (i.e. copypasta) understanding of PHP image manipulation but works beautiful except in one case.
The module pulls blob data from a table, parses it into an image, uses imagcopyresampled() to resize it, and then sends the resulting .jpg to the browser, it is called as a standard controller action.
It seems to work in all cases except when the original image was saved by a user from facebook (i.e. right click on the facebook image viewer and download to desktop and then upload to client site). I've tested this a number of times myself and been able to replicate it. I've also been able to upload the same image when re-saved via photoshop without encountering the problem.
I suspect the facebook image display adds some sort of additional metadata inside the file that causes my system to break.
Is there a solution for this?
The code for the PHP image module is as follows:
private function _buildImage($mode) {
//Prepare the output image
//Currently CROP and RESIZE are using different output onstruction calls
//So $finalImage is initialized prior to entering the mode blocks.
$finalImage = imagecreatetruecolor($this->_width, $this->_height);
$backgroundFillColor = imagecolorallocate($finalImage, RED, BLUE, GREEN);
imageFill($finalImage, 0, 0, $backgroundFillColor);
$this->_table = $this->_getTable($mode);
$image = $this->_request->image;
$this->_imageData = $this->_table->fetchEntryAsRow($image);
//Set top and left to 0 to capture the top/left corner of the orignal image.
$top = 0;
$left = 0;
$inputImage = imagecreatefromstring( $this->_imageData->image);
list($inputWidth, $inputHeight) = $this->_getImageSize($this->_imageData->image);
//Ratio is the target ratio of $this->_width divided by $this->_height, as set in actions.
//For index thumbnails this ratio is .7
//For index large images this ratio is 2
$ratio = $this->_width / $this->_height;
//define offset width and offset height as being equal to input image width and height
$offsetWidth = $inputWidth;
$offsetHeight = $inputHeight;
//Define Original Ratio as found in the image in the table.
$inputRatio = $inputWidth / $inputHeight;
//Rendering maths for RESIZE and CROP modes.
//RESIZE forces the whole input image to appear within the frame of the output image.
//CROP forces the output image to contain only the relevantly sized piece of the input image, measured from the middle.
if($this->_mode == CROP) {
if($inputRatio > $ratio) {
//Original image is too wide, use $height as is. Modify $width;
//define $scale: input is scaled to output along height.
$scale = $inputHeight / $this->_height;
//Calculate $left: an integer calculated based on 1/2 of the input width * half of the difference in the rations.
$left = round(($inputWidth/2)*(($inputRatio-$ratio)/2), 0);
$inputWidth = round(($inputWidth - ($left*2)), 0);
$offset = $offsetWidth - $inputWidth;
} else {
//Original image is too high, use $width as is. Modify $height;
$scale = $inputWidth / $this->_width;
$inputHeight = round(($this->_height * $scale),0);
$offset = $offsetHeight - $inputHeight;
$top = $offset / 2;
}
imagecopyresampled($finalImage, //Destination Image
$inputImage, //Original Image
0, 0, //Destination top left Coord
$left, $top, //Source top left coord
$this->_width, $this->_height, //Final location Bottom Right Coord
$inputWidth, $inputHeight //Source bottom right coord.
);
} else {
if($inputRatio < $ratio) {
//Original image is too wide, use $height as is. Modify $width;
$scale = $inputHeight / $this->_height;
$calculatedWidth = round(($inputWidth / $scale), 0);
$calculatedHeight = $this->_height;
$offset = $this->_width - $calculatedWidth;
$left = round(($offset / 2), 0);
$top = 0;
} else {
//Original image is too high, use $width as is. Modify $height;
$scale = $inputWidth / $this->_width;
$calculatedHeight = round(($inputHeight / $scale),0);
$calculatedWidth = $this->_width;
$offset = $this->_height - $calculatedHeight;
$top = round(($offset / 2), 2);
}
imagecopyresampled($finalImage, //Destination Image
$inputImage, //Original Image
$left, $top, //Destination top left Coord
0, 0, //Source top left coord
$calculatedWidth, $calculatedHeight, //Final location Bottom Right Coord
$inputWidth, $inputHeight //Source bottom right coord.
);
}
imagejpeg($finalImage, null, 100);
imagedestroy($inputImage);
imagedestroy($finalImage);
}
I suspect that the problem may actually lay with the implementation of _getImageSize.
private function _getImageSize($data)
{
$soi = unpack('nmagic/nmarker', $data);
if ($soi['magic'] != 0xFFD8) return false;
$marker = $soi['marker'];
$data = substr($data, 4);
$done = false;
while(1) {
if (strlen($data) === 0) return false;
switch($marker) {
case 0xFFC0:
$info = unpack('nlength/Cprecision/nY/nX', $data);
return array($info['X'], $info['Y']);
break;
default:
$info = unpack('nlength', $data);
$data = substr($data, $info['length']);
$info = unpack('nmarker', $data);
$marker = $info['marker'];
$data = substr($data, 2);
break;
}
}
}
You can see another example of this problem at http://www.angelaryan.com/gallery/Image/22 which displays a blue square, rather than the image stored in the database.
Try automatically "re-saving" the image after the upload with
imagejpeg(imagecreatefromjpeg($filename),$filename,9);
This should re-create any malformed or unrecognised headers from the original Facebook image.
I think you should use the getimagesizefromstring function instead of parsing the raw data yourself:
list($width, $height, $type) = getimagesizefromstring($data);
Resaving the image will only degrade image quality.
Having a few teething problems watermarking a photo. It all works fine apart from the watermarked photo's colors become duller than they should be - very noticeable in-fact.
I'm using imagecopyresized to do my watermarking, as this specifically allows me to use PNG-24 watermarks, the others do not. I know the colors are usually OK, as I have just used readfile($url) as a test, and the photos are perfect.
Here is my script:
<?php
// get parent and watermark images & sizes
$image = imagecreatefromjpeg($url);
$imageSize = getimagesize($url);
$watermark = imagecreatefrompng('watermark.png');
$watermark_o_width = imagesx($watermark);
$watermark_o_height = imagesy($watermark);
// calculate new watermark width and position
if ($imageSize[0] > $imageSize[1] || $imageSize[0] == $imageSize[1]) {
$leftPercent = 23;
} else {
$leftPercent = 7;
}
$leftPixels = ($imageSize[0]/100)*$leftPercent;
$newWatermarkWidth = $imageSize[0]-$leftPixels;
$newWatermarkHeight = $watermark_o_height * ($newWatermarkWidth / $watermark_o_width);
// place watermark on parent image, centered and scaled
imagecopyresized(
$image,
$watermark,
$imageSize[0]/2 - $newWatermarkWidth/2,
$imageSize[1]/2 - $newWatermarkHeight/2,
0,
0,
$newWatermarkWidth,
$newWatermarkHeight,
imagesx($watermark),
imagesy($watermark)
);
// print
imagejpeg($image);
// destroy
imagedestroy($image);
imagedestroy($watermark);
?>
How can I stop this from happening? I'm reading about imagecreatetruecolor, does that solve the issue? I'm Googling "imagecreatetruecolor color loss photos" and variations but nobody really talks about this issue. If I do need this function, where would I add that to this script?
This has totally thrown a spanner in the works for me and would love for somebody to tell me where to stick it (not literally).
Here is an example of the color loss. The preview image should be exactly the same colors as the thumbnail. The thumbnails are created using readfile() whereas the previews are created using imagecreatefromjpeg and imagecopresized.
This example code works fine, by using the same characteristics as your images:
Original JPG: dark background; beautiful girl; red dress.
Watermark PNG: transparent background; text; gray color.
<?php
// Path the the requested file (clean up the value if needed)
$path = $url;
// Load image
$image = imagecreatefromjpeg($path);
$w = imagesx($image);
$h = imagesy($image);
// Load watermark
$watermark = imagecreatefrompng('watermark.png');
$ww = imagesx($watermark);
$wh = imagesy($watermark);
// Merge watermark upon the original image (center center)
imagecopy($image, $watermark, (($w/2)-($ww/2)), (($h/2)-($wh/2)), 0, 0, $ww, $wh);
// Output the image to the browser
header('Content-type: image/jpeg');
imagejpeg($image);
// destroy both images
imagedestroy($image);
imagedestroy($watermark);
// kill script
exit();
?>
Left: Output Image | Right: Original Image
Note:
The output image was compressed several times until: Original -> PHP Output -> GIMP -> Here.
After much testing, I came to the conclusion that PHP's GD Image does not support color profiles on the images that are being watermarked. I am now using Imagick and the colors are perfect.