Calculating background position with face detection co-ordinates - php

I maintain a site that has a database of roughly 30,000 images associated with a listing. The images are many different sizes and aspect ratios, so in order to present them in a uniform manner on the site I've written a function that allows me to present the image responsively at a specific aspect ratio.
function image($src,$aspect) {
$html = '<img class="img-responsive" src="furniture/blank-'.$aspect.'.png" style="background:url(\''.$src.'\');background-position:center center;background-size:cover;" >';
return $html;
}
In my 'furniture' folder I have a set of transparent PNG files in the desired aspect ratio to force the size of the container that the actual image becomes the background to. So, there's a 4x3.png file for instance, which is just a 4px wide by 3px high empty graphic that I can call with image('path/to/image.jpg','4x3') - it works pretty well.
However, the problem comes when the image has someone's face in it, as the default 'center center' positioning can sometimes cut off heads.
I've already done the step of face detection, and each record in the database has a flag as to whether a face was found in the image and if so, the X, Y and Width values. The issue is then trying to figure out the background position because I'm dealing with a responsive layout, so don't know necessarily know what size the image is displayed.
I'm assuming the logic I'll need to follow is;
IF image has face in it
THEN set the background-position with a vertical offset calculated based on the Y coordinate of the face
ELSE
set the background-position to center center
How do I figure out the value of the vertical offset bearing in mind the size of the image is unknown?

Ok, so figured this one out but posting here just in case anyone else is struggling with it.
The basic logic is to work out the height of the aspect window based on the width of the actual image (no need to worry about the responsive nature at this stage). You then find the ideal central point of the image to put in the frame and work out whether that point is above or below the limits of how your image can slide within your aspect window.
I put this into a function I call and then drop the result directly into the background-position CSS.
In the below, the variables are:
$img = path to the image
$aspect1 = e.g. 16 (if 16x9)
$aspect2 = e.g. 9 (if 16x9)
$faceD = Set to 1 if the image has a face detected in it
$faceY = Y pixel of top of face
$faceW = Width (and Height) of face area
function calcImagePosition($img,$aspect1,$aspect2,$faceD,$faceY,$faceW) {
if($faceD==1) { // Only do this if there's a face in the image
list($width, $height) = getimagesize($_SERVER['DOCUMENT_ROOT']."/".$img); // Get the image dimensions
$aspectHeight = ($width/$aspect1)*$aspect2; // Get the height of the aspect window based on the actual width of the image
$idealCentre = $faceY + ($faceW/2); // The best part of the image to appear in the aspect window is halfway down the face bounds
if($idealCentre<($aspectHeight/2)) { // if the ideal centre is higher than half the height of the aspect window, just set it to top
$position = "center top";
} elseif(($height-$idealCentre)<($aspectHeight/2)) { // Also, if there's less than half the height of the aspect window at the bottom, set it to bottom
$position = "center bottom";
} else { // This will slide the position to best match the face
$pixel = $idealCentre-($aspectHeight/2); // Get the absolute pixel value
$percent = intval(($pixel/$height)*100); // Convert this to a percentage so it doesn't matter about responsive sizing
$position = "center ".$percent."%";
}
} else {
$position = "center center"; // default for no face detection
}
return $position;
}
I'm using this in conjunction with Karthik Tharavaad's php port of the Face Detector which does a reasonable job of creating the data needed for $faceY and $faceW.

Related

How to get width and height PDF page by Imagick?

I use package pdf-to-image for Yii2 based on Imagick library to convert each page of PDF to image. Also I need to get width and height or format of particular PDF page. Is there any way to do that?
Imagick is a native php extension to create and modify images using the ImageMagick API. So doesn't retry any PDF's info but image's info:
Imagick::getNumberImages — Returns the number of images in the
object.
$pdf->getNumberOfPages(); //returns number of images that are equal to number of PDF's pages. This is a method from pdf-to-image package.
A PDF describes the content and appearance of one or more pages. It also contains a definition of the physical size of those pages. That page size definition is not as straightforward as you might think. There can in fact be up to 5 different definitions in a PDF that relate to the size of its pages. These are called the boundary boxes or page boxes.
The MediaBox is used to specify the width and height of the page. For the average user, this probably equals the actual page size.
Each page in a PDF can have different sizes for the various page boxes.
A PDF always has a MediaBox definition. All the other page boxes do not necessarily have to be present in regular PDF files.
The MediaBox is the largest page box in a PDF. The other page boxes can equal the size of the MediaBox but they are not expected to be larger (The latter is explicitly required in the PDF/X-4 requirements). If they are larger, the PDF viewer will use the values of the MediaBox.
You should be able to retrieve the exact "HiResBoundingBox" value (which is the MediaBox value in PDF).
A test document is A4 (210mm x 297mm) which is 595.28pt x 841.89pt and has four(4) pages.
The unit of these values is PostScript points (where 72 pt == 1 inch).
$pdf = "1.pdf";
$output = shell_exec("identify -format \"%[pdf:HiResBoundingBox]\" $pdf");
echo $output;
prints this String:
595.28x841.89+0+0595.28x841.89+0+0595.28x841.89+0+0595.28x841.89+0+0
with some REGEX you could get width:595.28pt and height:841.89pt for each page and convert them to millimeters.
How about this approach?
By Imagick I can easily get image from pdf file
$RESOLUTION = 300;
$myurl = 'filename.pdf['.$pagenumber.'];'
$image = new Imagick($myurl);
$image->setResolution( $RESOLUTION , $RESOLUTION );
$image->setImageFormat( "png" );
$image->writeImage('newfilename.png');
Now I have image from page of PDF file. I know resolution (number pixels per inch) and I can get width and height of image in pixels. So don't need to have deep knowledge in math to calculate width and height of page of PDF in inch:
$pdfPageWidth = $imageWidth / $RESOLUTION;
$pdfPageHeight = $imageHeght/ $RESOLUTION;

ImageMagick crop Image with a surrounding background

I am using imagemagick to crop an image (using the PHP interface, but i don't think that matters too much).
I wish to crop an image, but if the crop portion goes over the image, I want it to show a background colour.
Here is the code I have so far:
$newImg = new Imagick($imgUrl);
$newImg->cropImage($cropW, $cropH, $x, $y);
$newImg->resizeImage($resizedW, $resizedH, Imagick::FILTER_CATROM, 1);
$newImg->writeImage($output_filename);
However for some reason, imagemagick refuses to show any portion of the image that is further than boundary of the image (i.e. if x and y is larger than the original image width and height, it pushes it down into view of the image).
e.g.
I want it so that if x and y is beyond the image portion, it shows a background color instead. Thanks!
UPDATE
Thanks to namelivia suggestion I decided to use the "extent" tool.However I am unable to set a background colour using this tool through PHP. For example, the following produces a larger image but with a black background, NOT purple.
$newImg = new Imagick($imgUrl);
$newImg->setImageBackgroundColor('#e7609a'); //Doesn't return an error (i.e. returns true) but also does not work!
$newImg->setImageExtent(2000, 2000);
$newImg->writeImage($output_filename);
UPDATE 2
Seems like you should use extentImage (NOT setImageExtent) if you wish to use a background color.
I think you should use the extent option first, using extent you can also pick a background color for the area "behind" then you can crop the extended image.

PHP Imagick::resampleImage resizes image instead of resampling

I'm trying to downsample every image that I consider large for my site on uploading, so when a user tries to upload an image I firstly check if it has an acceptable resolution, otherwise I want to drop this resolution. The code I use for this is:
if ($image->isValid()){
$imagick = new \Imagick();
$imagick->readImage($image);
$resolution = $imagick->getImageResolution();
$resolution_x = $resolution['x'];
$resolution_y = $resolution['y'];
if ($resolution_x > 30 && $resolution_y > 30){
$imagick->setImageResolution($resolution_x,$resolution_x);
$imagick->resampleImage($resolution_x/2,$resolution_x/2,\imagick::FILTER_CATROM,1);
}
$imagick->writeImage($uploadDir.$path);
}
This code was supposed to set the resolution of an image with resolution 300 dpi for example to 150dpi. Instead of this, the resolution remains 300 dpi and the image dimensions drop to the half of their previous values (e.g an image (1200x800) turns into (600x400)). Am I missing something about the functionality of Imagick::resampleImage or is there any error in my code? I've done a lot of search before post this question and tried lot of different ways to succeed my goal using Imagick but I cannot get it done!
The 'resolution' in the setImageResolution and getImageResolution functions refer to the dots per inch setting of the image, which is a hint to printers of what size to print the image i.e. how many dots per inch it should be printed at.
It does not affect the pixel dimensions of the image, and so does not have a noticeable effect on the image on a computer, which does not use the DPI setting to render an image.
You want to use either just $imagick->getImageWidth() and $imagick->getImageHeight() or $imagick->getImageGeometry() to get the pixel size of the image, and then resample it based on those pixel dimension, rather than the printer hint setting.
It sounds like the resolution values should be the same in both setImageResolution and resampleImage. Have you tried this yet?
$imagick->setImageResolution($resolution_x/2,$resolution_x/2);

PHP Imagick outline/contour and sketch

I have a problem with getting fine contour of the image.
How to do it with PHP Imagick?
Input image: Imagick wizard
Plan #1 Outline
Get image with (more/less) clear, consistent background (for example: white, red or transparent)
Remove background if it is set
Add outline (specific color)
Remove image inside
Result: http://i57.tinypic.com/2wg91qx.png
Plan #2 Sketch
Get image with (more/less) clear, consistent background (for example: white, red or transparent)
Remove background if it is set
Add sketch effect
Remove image inside
Result: http://i60.tinypic.com/az9vr5.png
PS:
borders and/or shadows didnt' work for me well
There are many ways to outline a picture. Here's one of them that does more or less what you wanted. Note that wizard's picture requires some extra processing. First background isn't fully white (it has some #FEFEFE or alike pixels). Also what is more troubling the upper part of the desk is filled with pure white. So you can either use white pixels after blurring as background (my way) or try to flood fill from the corner with matteFloodfillImage(). However this may leave space between desk legs not transparent.
function drawImage(Imagick $i)
{
$i->setImageFormat("png");
header("Content-Type: image/" . $i->getImageFormat());
echo $i;
exit;
}
$o = new Imagick('wizard.png');
$o->setImageBackgroundColor('white'); // handle tranparent images
$o = $o->flattenImages(); // flatten after setting background
$o->blurImage(5, 30);
$o->whiteThresholdImage( "#F8F8F8" );
$o->blackThresholdImage( "#FFFFFF" );
$o->edgeImage(5);
$o->negateImage(false);
$o->paintTransparentImage($o->getImagePixelColor(0, 0), 0, 2000);
$o->colorizeImage("red", 1);
drawImage($o);
Sketching is a little more complex and I would recommend further reading on IM capabilities http://www.imagemagick.org/Usage/photos/#color-in

PHP uploading images in the correct dimensions

I know this question is very common but the main point in my question is that i want to know how facebook or some other websites upload their pictures in the correct size. for example. if we upload a picture of the dimension : width : 1000px , height : 20px. then facebook updates the status updates with that pic but makes the image size small in the correct ratio. its jus scales down the picture but we can know that the image is very widthy (my own word for very high width :P) where as long heighty pictures are also posted in the correct proportion.
I have included the image examples above. how does facebook calculate the size n ratio of the pics and echo out the pic by keeping the right dimensions but scaling it down at the same time.
This code is fairly verbose, but might give you an idea on how to calculate image dimensions.
Parameters are your source width and your target maximum resize width and heights
function image_resize_dimensions($source_width,$source_height,$thumb_width,$thumb_height)
{
$source_ratio = $source_width / $source_height;
$thumb_ratio = $thumb_width / $thumb_height;
// Ratio is Taller
if ($thumb_ratio > $source_ratio)
{
$result_height = $thumb_height;
$result_width = $thumb_height * $source_ratio;
}
// Ratio is Wider
elseif ($thumb_ratio < $source_ratio)
{
$result_width = $thumb_width;
$result_height = $thumb_width / $source_ratio;
}
// Ratio the Same
elseif($thumb_ratio == $source_ratio)
{
$result_height = $thumb_height;
$result_width = $thumb_width;
}
return array('x'=>$result_width,'y'=>$result_height);
}
I think there are a few factors.
the main factor is the width of the content the image is displayed in
the original width of the image
whether you want to fill the whole content with the image or not (Facebook doesn't)
Let's say you have an image which has a width of 1000px. Your content is 400px. You want to fill the content a half with your image, so you have an end width of 200px for your image. The height is just ca percental calculation (rule of three).
In the end, you will have the correct ratio and it fits well in your content.
Of course you could also check whether the image is 16:9 or 4:3 to determine the max width for images like your first example.

Categories