Well, just noticed my imagick is producing blurry / poor quality thumbnails. Couldn't find a piece of code that I need to correct in order to make thumbs better quality. Any suggestions? It all happens in a PHP function below. Also 2 image samples are below as well.
try {
$image = new Imagick($picfile);
if ($image->getImageColorspace() == \Imagick::COLORSPACE_CMYK) {
$image->transformimagecolorspace(\Imagick::COLORSPACE_SRGB);
}
Imagick::setResourceLimit (6, 1);
// hypothetical new h and w
$new_w = round($o_height * $maxwidth / $maxheight);
$new_h = round($o_width * $maxheight / $maxwidth);
$ratio=round(($o_width/$o_height), 2);
if($ratio < 1.25){$go_vertical=true;}
if($o_width > $o_height && !$go_vertical){ // horizontal
$image->cropThumbnailImage($maxwidth, $maxheight);
$image->writeImage($path); $pathe=1;
}
if($o_width <= $o_height || $go_vertical){ // vertical
$image2 = new Imagick();
$image2->newImage($new_w, $o_height+2, "white");
$x = round(abs(($new_w - $o_width) / 2))-1;
echo $mode." x=".$x." IMAGE (".$new_w.", ".$o_height.")<br />";
$image2->compositeImage($image, imagick::COMPOSITE_DEFAULT, $x, 0);
$image2->cropThumbnailImage($maxwidth, $maxheight);
// Strip out unneeded meta data
$image->stripImage();
$image2->writeImage($path);
$image2->destroy(); $pathe=2;
}
$image->destroy();
As images do not directly insereted here, here are links:
Good example: https://www.comfyco.com/temp/b/good.jpg
Bad example: https://www.comfyco.com/temp/b/bad.jpg
It doesn't look drastically different here but on actual website it's pretty bad sometimes. Any advice what parameter / line to add to make those thumbnails look better?
Thanks!
Related
I have made two GIFs to explain what I am trying to do. Where the grey border is the dimensions I am after (700*525). They are at the bottom of this question.
I want for all images that are larger than the given width and height to scale down to the border (from the centre) and then crop off the edges. Here is some code I have put together to attempt this:
if ($heightofimage => 700 && $widthofimage => 525){
if ($heightofimage > $widthofimage){
$widthofimage = 525;
$heightofimage = //scaled height.
//crop height to 700.
}
if ($heightofimage < $widthofimage){
$widthofimage = //scaled width.
$heightofimage = 700;
//crop width to 525.
}
}else{
echo "image too small";
}
Here are some GIFs that visually explain what I am trying to achieve:
GIF 1: Here the image proportions are too much in the x direction
GIF 2: Here the image proportions are too much in the y direction
image quality comparison for #timclutton
so I have used your method with PHP (click here to do your own test with the php) and then compared it to the original photo as you can see there is a big difference!:
Your PHP method:
(source: tragicclothing.co.uk)
The actual file:
(source: mujjo.com)
The below code should do what you want. I've not tested it extensively but it seems to work on the few test images I made. There's a niggling doubt at the back of mind that somewhere my math is wrong, but it's late and I can't see anything obvious.
Edit: It niggled enough I went through again and found the bug, which was that the crop wasn't in the middle of the image. Code replaced with working version.
In short: treat this as a starting point, not production-ready code!
<?php
// set image size constraints.
$target_w = 525;
$target_h = 700;
// get image.
$in = imagecreatefrompng('<path to your>.png');
// get image dimensions.
$w = imagesx($in);
$h = imagesy($in);
if ($w >= $target_w && $h >= $target_h) {
// get scales.
$x_scale = ($w / $target_w);
$y_scale = ($h / $target_h);
// create new image.
$out = imagecreatetruecolor($target_w, $target_h);
$new_w = $target_w;
$new_h = $target_h;
$src_x = 0;
$src_y = 0;
// compare scales to ensure we crop whichever is smaller: top/bottom or
// left/right.
if ($x_scale > $y_scale) {
$new_w = $w / $y_scale;
// see description of $src_y, below.
$src_x = (($new_w - $target_w) / 2) * $y_scale;
} else {
$new_h = $h / $x_scale;
// a bit tricky. crop is done by specifying coordinates to copy from in
// source image. so calculate how much to remove from new image and
// then scale that up to original. result is out by ~1px but good enough.
$src_y = (($new_h - $target_h) / 2) * $x_scale;
}
// given the right inputs, this takes care of crop and resize and gives
// back the new image. note that imagecopyresized() is possibly quicker, but
// imagecopyresampled() gives better quality.
imagecopyresampled($out, $in, 0, 0, $src_x, $src_y, $new_w, $new_h, $w, $h);
// output to browser.
header('Content-Type: image/png');
imagepng($out);
exit;
} else {
echo 'image too small';
}
?>
Using Imagick :
define('PHOTO_WIDTH_THUMB', 700);
define('PHOTO_HEIGHT_THUMB', 525);
$image = new Imagick();
$image->readImage($file_source);
$width = $image->getImageWidth();
$height = $image->getImageHeight();
if($width > $height){
$image->thumbnailImage(0, PHOTO_HEIGHT_THUMB);
}else{
$image->thumbnailImage(PHOTO_WIDTH_THUMB, 0);
}
$thumb_width = $image->getImageWidth();
$thumb_height = $image->getImageHeight();
$x = ($thumb_width - PHOTO_WIDTH_THUMB)/2;
$y = ($thumb_height - PHOTO_HEIGHT_THUMB)/2;
$image->cropImage(PHOTO_THUMB_WIDTH, PHOTO_THUMB_HEIGHT, $x, $y);
$image->writeImage($thumb_destination);
$image->clear();
$image->destroy();
unlink($file_source);
I have used GD library to accomplish the resize. Basically what I did is, I calculated the image dimension and then resized the image to dimension 700x525 from the center.
<?php
/*
* PHP GD
* resize an image using GD library
*/
//the image has 700X525 px ie 4:3 ratio
$src = 'demo_files/bobo.jpg';
// Get new sizes
list($width, $height) = getimagesize($src);
$x = 0;
$y = 0;
if($width < $height){
$newwidth = $width;
$newheight = 3/4 * $width;
$x = 0;
$y = $height/2 - $newheight/2;
}else{
$newheight = $height;
$newwidth = 4/3 * $height;
$x=$width/2 - $newwidth/2;
$y=0;
}
$targ_w = 700; //width of the image to be resized to
$targ_h = 525; ////height of the image to be resized to
$jpeg_quality = 90;
$img_r = imagecreatefromjpeg($src);
$dst_r = ImageCreateTrueColor( $targ_w, $targ_h );
imagecopyresampled($dst_r,$img_r,0,0,$x,$y,$targ_w,$targ_h,$newwidth,$newheight);
header('Content-type: image/jpeg');
imagejpeg($dst_r,null,$jpeg_quality);
exit;
?>
i used http://phpthumb.sourceforge.net to have a beutiful solution also with transparent curved edges.
this is an alternative route to solution, might suit someone's need with little configuration.
I am using imagine library to create thumbnails for images. It is as simple as this.
$size = new \Imagine\Image\Box(240, 180);
$imagine->open($source_path)->thumbnail($size, 'inset')->save($target_path);
the library provides two modes : inset and outbound. in inset mode the image is resized down but it does not fill the thumbnail size. So I need to pad it to fill the target size. Is there an easy way to do this using library functions ?
You have to crop your image if you don't want to "scale" thumbnails to fit. For cropping, you have to find exact starting points and it requires a little bit effort.
Writing a custom method to find exact cropping point, resize and return the new image is good idea. Imagine is really good library, it provides all methods we need.
Steps to follow:
Get the original image's dimensions using getSize()
Detect orientation of the image by comparing width and height.
Then find the exact crop point by orientation which you need to fit your new thumbnail without "scaling":
If it's landscape, find target width by using target box's width
Otherwise by using height.
Resize image using THUMBNAIL_OUTBOUND and create a "little big thumbnail".
Crop resized image using the cropping points which you find before.
Return the image instance.
Pseudo code:
function resizeToFit( $targetWidth, $targetHeight, $sourceFilename )
{
// Box is Imagine Box instance
// Point is Imagine Point instance
$target = new Box($targetWidth, $targetHeight );
$originalImage = imagine->open( $sourceFilename );
$orgSize = $originalImage->getSize();
if ($orgSize->width > $orgSize->height) {
// Landscaped.. We need to crop image by horizontally
$w = $orgSize->width * ( $target->height / $orgSize->height );
$h = $target->height;
$cropBy = new Point( ( max ( $w - $target->width, 0 ) ) / 2, 0);
} else {
// Portrait..
$w = $target->width; // Use target box's width and crop vertically
$h = $orgSize->height * ( $target->width / $orgSize->width );
$cropBy = new Point( 0, ( max( $h - $target->height , 0 ) ) / 2);
}
$tempBox = Box($w, $h);
$img = $originalImage->thumbnail($tempBox, ImageInterface::THUMBNAIL_OUTBOUND);
// Here is the magic..
return $img->crop($cropBy, $target); // Return "ready to save" final image instance
}
I have managed to pad thumbnails with following function.
function pad(\Imagine\Gd\Imagine $img, \Imagine\Image\Box $size, $fcolor='#000', $ftransparency = 100)
{
$tsize = $img->getSize();
$x = $y = 0;
if ($size->getWidth() > $tsize->getWidth()) {
$x = round(($size->getWidth() - $tsize->getWidth()) / 2);
} elseif ($size->getHeight() > $tsize->getHeight()) {
$y = round(($size->getHeight() - $tsize->getHeight()) / 2);
}
$pasteto = new \Imagine\Image\Point($x, $y);
$imagine = new \Imagine\Gd\Imagine();
$color = new \Imagine\Image\Color($fcolor, $ftransparency);
$image = $imagine->create($size, $color);
$image->paste($img, $pasteto);
return $image;
}
foozy, thanks for your comment, it helped me a lot!
But for me better worked this:
$img->resize($tempBox);
Instead of this:
$img = $originalImage->thumbnail($tempBox, ImageInterface::THUMBNAIL_OUTBOUND);
Maybe it will help somebody.
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.
I'm trying to downsize image uploaded by users at the time of upload.
This is no problem but I want to get specific with sizes now.
I'm looking for some advice on an algorithm im struggling to produce that will take any shape image - square or rectangle of any widths/heights and downsize it.
This image needs to be downsized to a target box size (this changes but is stored in a variable)..
So it needs to downsize the image so that both the width and height are larger than the width and height of the target maintaining aspect ratio. but only just..
The target size will be used to crop the image so there is no white space around the edges etc.
I'm looking for just the algorithm behind creating the correct resize based on different dimension images - I can handle the cropping, even resizing in most cases but it fails in a few so i'm reaching out.
I'm not really asking for PHP code more pseudo. Either is fine obviously.
Thanks Kindly.
Current code.. but I've gone through so many iterations this might not work at all anymore.. :P
$image = $image_orig->clone();
$wRatio = $imageprops['width'] / $dimensions[0]; // imageprops = original image dimens.
$hRatio = $imageprops['height'] / $dimensions[1]; // $dimensions[0] = new width
$maxRatio = max($wRatio,$hRatio); // $dimensions[1] = new height
var_dump('thumb');
$ratio = ($imageprops['width'] - $dimensions[0]) > ($imageprops['height'] - $dimensions[1]);
$shape = ($imageprops['width'] > $imageprops['height']);
$error = ($imageprops['width'] / $imageprops['height']);
if( $error < 0.95 || $error > 1.05 ) { // its NOT a square
if($shape){ // longer width
$scale = $imageprops['height'] / $dimensions[0];
var_dump('1');
$height = $imageprops['height'] / $scale;
$image->scaleImage(0, $height);
} else {
$scale = $imageprops['width'] / $dimensions[1];
var_dump('2');
$width = $imageprops['width'] / $scale;
$image->scaleImage($width, 0);
}
} elseif($ratio) { // is square
$scale = $imageprops['height'] / $dimensions[1];
$newWidth = $imageprops['width'] / $scale;
$extra = 0;
$height = $dimensions[1]+$extra;
$image->scaleImage(0, $height);
} else {
$scale = $imageprops['width'] / $dimensions[0];
$newHeight = $imageprops['height'] / $scale;
$extra = 0;
$width = $dimensions[0]+$extra;
$image->scaleImage($width, 0);
}
$image_size = $image->getImageGeometry();
$image->cropImage($dimensions[0], $dimensions[1],($image_size['width']-$dimensions[0])/2,($image_size['height']-$dimensions[1])/2);
Notice: I wrote this answer before the original poster edited his question to include things which clarified points which has since changed what I believed the original question was asking.
So, there are a few concepts and ideas that you can throw around to try and solve what it is you are intending to achieve. (Gravity Cropping, Content Aware Cropping, Content Aware Rescaling etc)
Because you are always decreasing the size of the original image, you are essentially just looking to "chop" out a section of the image. Very much like this:
The issue however, is that you frequently want to make sure that you select the best region of the image so that you don't crop on an irrelevant segment of the image. This is known as content-aware image cropping, and by just searching using "Content Aware Image Crop" you can find a wealth of information.
Anyway, moving on, depending on your exact use case, you might discover that actually you don't want to chop out anything form the image, and instead you want to "liquid scale and crop" the image, so that you do "Content Aware Resizing". The difference with content aware resizing is that the resizing ignores all aspect ratios of the image and instead looks at neighbouring edges and colors in order to resize the image in a fluidic way.
So, luckily enough, Imagick comes with it's very own [liquidRescaleImage][3] function.
You can use it like
$im->liquidRescaleImage(480, 260, 3, 18);
Using Liquid Rescale can rescale out quite nicely. Here is an example below which is anything but perfect, but I purposefully have created an example that isn't perfect so you can actually see that liquidRescale changes the composition of the image, rather than just adjusts the aspect ratio.
Original
Content Aware Liquid Rescale (450x350)
If however, you just want to scale an image, and keep the aspect ratio, you might want to do what sites such as Flickr do, where they make the images longestLength to be equal to a value. (Flickr for example has 6 or so different dimension sizes)
We resize your photos to more web-friendly dimensions. Each image has
a 75x75 and 150x150 square thumbnail and 100-, 240-, 320-, 500-, 640-
800*-, 1024-, 1600*-, and 2048*-pixel versions (that's the length of
the longest side), as well as your original file.
Basic Class that Replicates the Flickr Resize Policies...
<?php
class Scale {
public function getImageScale($x, $y, $longestLength, $allowDistort = false) {
//Set the default NEW values to be the old, in case it doesn't even need scaling
list($nx, $ny) = array($x, $y);
if ($x > $y) {
if ($longestLength > $x && !$allowDistort) {
return array($x, $y);
}
$r = $x / $longestLength;
return array($longestLength, $y / $r);
} else {
if ($longestLength > $x && !$allowDistort) {
return array($x, $y);
}
$r = $y / $longestLength;
return array($x / $r, $longestLength);
}
return array($nx, $ny);
}
}
And then if you were to pass in the Image Dimensions like this:
$originalImageX = 480;
$originalImageY = 260;
$scale = new Scale();
var_dump($scale->getImageScale($originalImageX,$originalImageY,120 ) );
/* Outputs
array(2) {
[0]=>
int(120)
[1]=>
int(65)
} */
Also, there is this Content Aware Cropping class on Git, that I have previously adapted the base class/Structure previously to use in my own project so I know that it works nicely. I'm not sure on the licencing of this class, but you can obviously take a look at it and slice out what works for you as I have done previously.
https://gist.github.com/2468935#file_content_aware_image.php
(And PS. Provide all the information in your questions straight up next time...)
Dunno if this is what you want, from my personal script library xD :
You only need to specify the desired $maxW $maxH and same for thumbnails, and it will downscale the image proportionally.
The script has two parts that are almost the same, one is for the thumbnails and one for the actual image.
list($w, $h) = getimagesize($file);
$maxW = 1024;
$maxH = 786;
$thumbMaxW = 138;
$thumbMaxH = 92;
$newW = $w;
$newH = $h;
if($w > $thumbMaxW OR $h > $thumbMaxH){
if($w>=$h){
$thumbW = $thumbMaxW;
$thumbRatio = $w/$thumbMaxW;
$thumbH = (int)($h/$thumbRatio);
} else {
$thumbH = $thumbMaxH;
$thumbRatio = $h/$thumbMaxH;
$thumbW = (int)($w/$thumbRatio);
}
} else {
$thumbW = $w;
$thumbH = $h;
}
--------------------------------------------
if($w > $maxW OR $h > $maxH){
if($w >= $h){
$newW = $maxW;
$reduction = $w/$newW;
$newH = (int) ($h/$reduction);
}
if($h > $w){
$newH = $maxH;
$reduction = $h/$newH;
$newW = (int)($w/$reduction);
}
} else {
$newW = $w;
$newH = $h;
}
I was trying to crop the animated gif and in the output i'm getting the same sized image, but cropped.
A lot of empty space is filled with canvas.
For example i had animated gif 600x100, but have requested 100x100 crop, on the output i'm getting 600x100 image with cropped image and empty space.
Someone knows the solution for this issue?
$gif = new Imagick($s['src']);
foreach($gif as $frame){
$frame->cropImage($s['params']['w'], $s['params']['h'], $s['params']['x'], $s['params']['y']);
}
$gif->writeImages($s['dest_path'] .'/'. $fullname,true);
I've been having the same problem as you, and I found the solution was using the coalesceimages function.
Here's a working example for crop and resize an animated gif in php with Imagick:
<?php
// $width and $height are the "big image"'s proportions
if($width > $height) {
$x = ceil(($width - $height) / 2 );
$width = $height;
} elseif($height > $width) {
$y = ceil(($height - $width) / 2);
$height = $width;
}
$image = new Imagick(HERE_YOU_PUT_BIG_IMAGE_PATH);
$image = $image->coalesceImages(); // the trick!
foreach ($image as $frame) {
$frame->cropImage($width, $height, $x, $y); // You crop the big image first
$frame->setImagePage(0, 0, 0, 0); // Remove canvas
}
$image = $image->coalesceImages(); // We do coalesceimages again because now we need to resize
foreach ($image as $frame) {
$frame->resizeImage($newWidth, $newHeight,Imagick::FILTER_LANCZOS,1); // $newWidth and $newHeight are the proportions for the new image
}
$image->writeImages(CROPPED_AND_RESIZED_IMAGE_PATH_HERE, true);
?>
Code above is being used for generating thumbnails with same with and height.
You might change it the way you want.
Notice that when using $frame->cropImage($width, $height, $x, $y); you should put there the values you might need.
IE $frame->cropImage($s['params']['w'], $s['params']['h'], $s['params']['x'], $s['params']['y']);
Of course that if you just want to crop instead of croping and resizing, just can do this:
$image = new Imagick(HERE_YOU_PUT_BIG_IMAGE_PATH);
$image = $image->coalesceImages(); // the trick!
foreach ($image as $frame) {
$frame->cropImage($s['params']['w'], $s['params']['h'], $s['params']['x'], $s['params']['y']);
$frame->setImagePage(0, 0, 0, 0); // Remove canvas
}
Hope it helps!
Ps: sorry for my english :)
Often ImageMagick has a 'page' or working area, something like a background layer. It sounds like this is remaining after cropping the image (I had a confusing time working out some compositing and resizing behavior with the command line tool before...).
Checking out the PHP manual page for cropImage, I saw this comment:
Christian Dehning - 09-Apr-2010 10:57
When cropping gif-images (I had no problems with jpg and png images), the canvas is not removed. Please run the following command on the cropped gif, to remove the blank space:
$im->setImagePage(0, 0, 0, 0);