PDFlib PHP place images next to each other - php

I have multiple Images coming from an array (could be 3, could be 10) and I want to place them next to each other and if they don't fit anymore, place them in a new line.
I have written a foreach statement and I got it to work, so that they place into a new line if they don't fit in the current one and they also place next to each other.
But my problem is the spacing between the images, because as of now it's different for each image. Some have really wide space between them while others are overlapping.
Here's the function I wrote:
function createAwardsTable(pdflib $p, int $textStartLeft, array $arrInput) {
$awardImages = $arrInput['awards'];
$boxHeight = 50;
$x = $textStartLeft;
$y = 275;
foreach($awardImages as $awardImage) {
$image = $p->load_image("auto", $awardImage, "");
if ($image == 0) {
echo("Couldn't load $image: " . $p->get_errmsg());
exit(1);
}
$imagewidth = $p->info_image($image, "imagewidth", "");
if ($x > (565 - 20)) {
$y = 215;
$x = $textStartLeft;
}
$buf = "boxsize={" . $imagewidth . " " . $boxHeight . "} fitmethod=auto matchbox={name=awardimage}";
// $buf = "scale=1 matchbox={name=awardimage}";
$p->fit_image($image, $x, $y, $buf);
$awardWidth = $p->info_matchbox("awardimage", 1, "x2");
$x = ($x - 20) + $awardWidth;
}
}
And here's a picture of the result I get in my pdf:

I think your logic is fine so far.
$awardWidth = $p->info_matchbox("awardimage", 1, "x2");
$x = ($x - 20) + $awardWidth;
i think the new calculation of $x is just not quite right. If I understand your description correctly, then you simply want to output the next award image 20 pixels next to the previously placed image. If that's the case, then it's enough to use the matchbox to get the position of the placed image and then just add 20 px on top of it.
$awardX2 = $p->info_matchbox("awardimage", 1, "x2");
$x = $awardX2 + 20;
Maybe the problem also comes from the wrong name of $awardWidth. Info_matchbox() Returns you the X-position and not the width. If you want the width, then you should also get the x1 position and then calculate the difference. $width = $x2-$x1)

Related

PHP get the newly added color from image at specific pixel range

I created a PHP code to add color at specific pixel range for example x=0,y=0 to x=24, y=0. This creates a straight line on top left corner of the image towards y axis. Now beofre closing the image if I try to read the color from those position it returns me the color I added. But if i reopen the image and try to read the color from those pixels it is not giving me the exact color I added instead it give a color close to it. I am adding the piece of code which I used:
`$canvas = imagecreatefromjpeg('first_frame.jpg');
//create a random color
$rand = str_pad(dechex(rand(0x000000, 0xFFFFFF)), 6, 0, STR_PAD_LEFT);
$dec_color= hexdec($rand);
for ($i=0; $i < 24; $i++) {
imagerectangle($canvas,$i,0,0,0, $dec_color);
}
//read the image pixels add
for ($x=0; $x < 24 ; $x++) {
echo $new_color= imagecolorat($canvas, $x, 0);
echo '<br>';
}
$filename = 'test/'.time().'.jpg';
//store the image in dir
imagejpeg($canvas, $filename);
//destroy the opened image
imagedestroy($canvas);`
The above code gives me tha random generated color, adds it to the image and then reads those added colors. So this code gives me the expected added colors.
If i add the following code after the function imagedestroy($canvas), it gives me some other colors close to the one added.
`$dimg = imagecreatefromjpeg($filename);
for ($x=0; $x < 24 ; $x++) {
echo $new_color= imagecolorat($dimg, $x, 0);
echo '<br>';
}
`
I need to extract the exact color added to the image after the image is stored.
Added color and extracted colors are here
Thanks a lot for your help CBroe. So here is the complete code for getting the exact pixel color added in an image and it is unchanged after shared and retrieved via multiple social media platforms. This may help someone in future.
<?php
// load the image
$canvas = imagecreatefromjpeg('first_frame.jpg');
// create a random color
$rand = str_pad(dechex(rand(0x000000, 0xFFFFFF)), 6, 0, STR_PAD_LEFT);
$dec_color= hexdec($rand);
echo 'color created and added: '.$dec_color;
// add the new color to image
for ($i=0; $i < 24; $i++) {
imagesetpixel($canvas,$i,0,$dec_color);
}
// store the image and close the file opened
$filename = 'test/output.png';
imagepng($canvas, $filename);
imagedestroy($canvas);
// now read the same file stored and get the color ranges to be stored
$dimg = imagecreatefrompng($filename);
$extracted_color = array();
for ($x=0; $x < 24 ; $x++) {
$extracted_color[]= imagecolorat($dimg, $x, 0);
}
echo "<br>Retrieved colors:<pre>".print_r($extracted_color,1)."</pre>";
imagedestroy($dimg);
?>

FPDF - Determining height of MultiCell before placing?

The basic question: Is it possible to determine the height of a MultiCell before placing it in the document?
The reason: I've been tasked with creating a PDF version of a form. This form allows text input, with a resulting variable length. One person my not enter anything, another person may write a few paragraphs. "The Powers That Be" do not want this text breaking between pages.
Currently, after placing each block, I check the position on the page, and if I'm near the end, I create a new page.
if($this->getY() >= 250) {
$this->AddPage('P');
}
For the most part, this works. But there are the few sneaky ones that come in at, say 249, and then have boatloads of text. It seems it would make more sense to determine the height of a block before placing it to see if it actually fits on the page.
Surely there must be a way to do this, but Googling around hasn't proven very helpful.
Edit to clarify: The PDF is being generated after the form is submitted. Not an active, editable PDF form...
Earned the tumbleweed badge for this question. :( Anyway, in the off chance someone has the same issue, I figured I'd answer with what I did. I doubt this is the most efficient method, but it got the job done.
First, I create two FPDF objects; a temporary one that never gets output and the final, complete pdf that the end user will see.
I get my current Y coordinate in both objects. I then place the block of data using Cell() or MultiCell() (for this form, usually a combination of the two) into the temporary object, and then check the new Y coordinate. I can then do the math to determine the height of the block. -Note, the math can get a bit funky if the block breaks across pages. Remember to take your top and bottom margins into account.
Now that I know the height of a block, I can take the current Y coordinate of the destination object, subtract it from the total height of a page(minus bottom margin) to get my available space. If the block fits, place it, if not, add a page and place it at the top.
Repeat with each block.
Only caveat I see is if any single block is longer then an entire page, but with this form, that never happens. (famous last words...)
See also https://gist.github.com/johnballantyne/4089627
function GetMultiCellHeight($w, $h, $txt, $border=null, $align='J') {
// Calculate MultiCell with automatic or explicit line breaks height
// $border is un-used, but I kept it in the parameters to keep the call
// to this function consistent with MultiCell()
$cw = &$this->CurrentFont['cw'];
if($w==0)
$w = $this->w-$this->rMargin-$this->x;
$wmax = ($w-2*$this->cMargin)*1000/$this->FontSize;
$s = str_replace("\r",'',$txt);
$nb = strlen($s);
if($nb>0 && $s[$nb-1]=="\n")
$nb--;
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$height = 0;
while($i<$nb)
{
// Get next character
$c = $s[$i];
if($c=="\n")
{
// Explicit line break
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
//Increase Height
$height += $h;
$i++;
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
continue;
}
if($c==' ')
{
$sep = $i;
$ls = $l;
$ns++;
}
$l += $cw[$c];
if($l>$wmax)
{
// Automatic line break
if($sep==-1)
{
if($i==$j)
$i++;
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
//Increase Height
$height += $h;
}
else
{
if($align=='J')
{
$this->ws = ($ns>1) ? ($wmax-$ls)/1000*$this->FontSize/($ns-1) : 0;
$this->_out(sprintf('%.3F Tw',$this->ws*$this->k));
}
//Increase Height
$height += $h;
$i = $sep+1;
}
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
}
else
$i++;
}
// Last chunk
if($this->ws>0)
{
$this->ws = 0;
$this->_out('0 Tw');
}
//Increase Height
$height += $h;
return $height;
}
In addition to JLuc answer which is working as one would imagine it, too. Implementing this functionality however is not trivial and requires you to extend the main fpdf class.
First off if you have an extended fpdf already you could just copy JLuc's answer and skip this step. If not, go ahead like this:
create a File and Class that extends FPDF (name the file as your class name):
<?php
namespace YourNameSpace\Library;
class FpdfExtended extends \FPDF {
...
}
If you are not using namespaces you could just use require. Once that file is created insert the GetMultiCellHeight from the gist or this answer.
Now you'd need to instantiate the extended Class a simple constructor most likely suffices.
use YourNameSpace\Library\FpdfExtended
...
$pdf = new FpdfExtended();
...
now you could simply use the GetMultiCellHeight method to decide the next cells height like so:
$col1 = 'col 1 text without line break';
$col2 = 'col 2 text with line break \n and some more text';
// get next cell height
$nextHeight = $pdf->GetMultiCellHeight($width, $height, $col2);
$pdf->MultiCell($width, $nextHeight, $col1, 0, 'L', 1);
Good luck and happy coding you poor souls still using FPDF.
I have done it by this way. We are passing height in multicell. That is $h. Total height would be (number of lines * $h). Because $h is height of one line. And we want to know total height so total height is $totalLines * $h.

How to calculate thickness of an object in PNG file using PHP GD library

I have a PNG frame and I want to know its thickness. I am able to calculate the width/height of the image itself.
$frame = imagecreatefrompng('frame.png');
// get frame dimentions
$frame_width = imagesx($frame);
$frame_height = imagesy($frame);
But can't figure out a way to calculate thickness of frame, please see image below so see what I mean.
Any suggestions?
From the last answer it shows that there's no objects in a raster image file. However, you can do it by searching the first occurrence of transparent colour and the first occurrence of the non-transparent colour and calculate the distance of them (assumes that your image's blank area are all transparent).
Example code:
<?php
$img = imagecreatefrompng('./frame.png');//open the image
$w = imagesx($img);//the width
$h = imagesy($img);//the height
$nonTransparentPos = null;//the first non-transparent pixel's position
$transparentPos = null;//the first transparent pixel's position
//loop through each pixel
for($x = 0; $x < $w; $x++){
for($y = 0; $y < $h; $y++){
$color = imagecolorsforindex($img,imagecolorat($img,$x,$y));
if($color['alpha'] < 127 && $nonTransparentPos === null){
$nonTransparentPos = array($x,$y);
}
if($color['alpha'] === 127 && $transparentPos === null){
$transparentPos = array($x,$y);
}
}
//leave the loop if we have finished finding the two values.
if($transparentPos !== null && $nonTransparentPos !== null){
break;
}
}
$length = $transparentPos[0]-$nonTransparentPos[0];//calculate the two point's x-axis distance
echo $length;
?>
There aren't any objects in a PNG file. You can only get a color (with a transparency) by coordinates with imagecolorat() & imagecolorsforindex().

Procedurally generating a texture

I'm trying to figure out a script that will generate a texture (which can then be multiplied by a grayscale image to "apply" it). So far my method involves seeding the RNG, then randomly generating a 8x8 matrix of integers in the range [0,3], then scaling up that matrix to a 256x256 image using some level of interpolation.
Here's an example output (seed value 24):
(source: adamhaskell.net)
On the left is the matrix scaled with nearest-neighbor interpolation. On the right is my attempt at bilinear interpolation. For the most part it seems okay, but then you get structures like near the middle-left where there are two diagonally-adjoining orange squares faced with two diagonally-adjoining red squares, andthe result is no interpolation for that area. Additionally, it's being treated more like a heatmap (as shown by the abundance of orange in the top-left corner) and that's causing more problems.
Here's the code I have for my "bilinear interpolation":
<?php
$matrix = Array();
srand(24);
$dim = 256;
$scale = 32;
for($y=0;$y<=$dim/$scale;$y++) for($x=0;$x<=$dim/$scale;$x++) $matrix[$y][$x] = rand(0,3);
$img = imagecreate($dim,$dim);
imagecolorallocate($img,255,255,255);
$cols = Array(
imagecolorallocate($img,128,0,0),
imagecolorallocate($img,128,64,32),
imagecolorallocate($img,128,128,0),
imagecolorallocate($img,64,64,64)
);
for($y=0;$y<$dim;$y++) {
for($x=0;$x<$dim;$x++) {
$xx = floor($x/$scale); $yy = floor($y/$scale);
$x2 = $x%$scale; $y2 = $y%$scale;
$col = $cols[round((
$matrix[$yy][$xx]*($scale-$x2)*($scale-$y2)
+ $matrix[$yy][$xx+1]*$x2*($scale-$y2)
+ $matrix[$yy+1][$xx]*($scale-$x2)*$y2
+ $matrix[$yy+1][$xx+1]*$x2*$y2
)/($scale*$scale))];
imagesetpixel($img,$x,$y,$col);
}
}
header("Content-Type: image/png");
imagepng($img);
exit;
In reality, this may be a bit of an XY Problem. What I'm specifically trying to do is generate "fur patterns" for creatures in a game I'm planning. In particular I want to be able to have it so that breeding mixes elements from the two parents (be it colour or elements of the pattern), so just having a random seed won't really cut it. Ideally I need some kind of vector-based approach, but I'm way out of my depth there so any help would be very much appreciated.
A couple things come to mind:
You are not interpolating the color values. To expand on zakinster's comment, you are interpolating the color indices, and then rounding to the nearest one. One effect of this is that you wind up with a swath of yellow (index 2) in between orange (index 1) and gray (index 3) areas. If you interpolated the color values instead, you would wind up with, perhaps, grayish orange?
You have more yellow and orange, and less red and gray in the final image. This is because of using round() to snap to a color index. Your calculation (before round()) may produce floats evenly distributed between 0 to 3, but rounding doesn't preserve it.
So, here are some suggestions:
If you are not limited to 4 colors, use more. Interpolate the color values (i.e. (128,0,0) mixed with (64,64,64) produces (91,32,32)) rather than the indices.
If you are limited to just those 4 colors, try some kind of dithering. A simple approach, with minimal changes to your code, would be to add some randomness to the color index that is chosen. So, instead of round(...), do something like this: say your calculation produces the value 1.7. Then, round to up to 2 with a 70% probability, and down to 1 the other 30%. This will blend the colors, but it may produce a very noisy image. If you are prepared to change your code more substantially, check out Floyd-Steinberg dithering.
I know it is old question, and answer from #markku-k is correct, anyway I have similar problem and here is my modified code for the question
several notices:
it produces 2 images in one, to show "original matrix" and result
it uses 8x8 matrix to produce result, but actual matrix is 10x10 to cover borders
it uses color to color index algorithm base on simple delta, it works ok for me
here is the code:
<?php
$matrix = array();
$dim = 256;
$scale = 32;
for($y=0; $y<=9; $y++)
{
$matrix[$y] = array();
for($x=0; $x<=9; $x++)
{
$same = false;
do
{
$matrix[$y][$x] = mt_rand(0, 3); // do not use rand function, mt_rand provide better results
if ( ($x>0) && ($y>0) ) // check for checkers siatuion, where no colors are preferable and produce 90 degree angles
{
$c1 = $matrix[$y-1][$x-1];
$c2 = $matrix[$y][$x];
$c3 = $matrix[$y-1][$x];
$c4 = $matrix[$y][$x-1];
$same = ( ($c1==$c2) && ($c3==$c4) );
}
} while ($same);
}
}
$img = imagecreate($dim*2 + 32*4, $dim + 32*2);
$colorsRGB = array(0x800000, 0x804020, 0x808000, 0x404040);
$cols = Array(
imagecolorallocate($img,128,0,0), // red
imagecolorallocate($img,128,64,32), // orange
imagecolorallocate($img,128,128,0), // yellow
imagecolorallocate($img,64,64,64), // gray
imagecolorallocate($img,0,0,0), // black, just to fill background
);
imagefilledrectangle($img, 0, 0, $dim*2 + 32*4 - 1, $dim + 32*2 - 1, $cols[4]);
function mulclr($color, $multiplicator)
{
return array(($color>>16) * $multiplicator, (($color>>8)&0xff) * $multiplicator, ($color&0xff) * $multiplicator);
}
function addclr($colorArray1, $colorArray2)
{
return array($colorArray1[0]+$colorArray2[0], $colorArray1[1]+$colorArray2[1], $colorArray1[2]+$colorArray2[2]);
}
function divclr($colorArray, $div)
{
return array($colorArray[0] / $div, $colorArray[1] / $div, $colorArray[2] / $div);
}
function findclridx($colorArray, $usedColors)
{
global $colorsRGB;
$minidx = $usedColors[0];
$mindelta = 255*3;
foreach ($colorsRGB as $idx => $rgb)
{
if (in_array($idx, $usedColors))
{
$delta = abs($colorArray[0] - ($rgb>>16)) + abs($colorArray[1] - (($rgb>>8)&0xff)) + abs($colorArray[2] - ($rgb&0xff));
if ($delta < $mindelta)
{
$minidx = $idx;
$mindelta = $delta;
}
}
}
return $minidx;
}
for($y=0; $y<($dim+64); $y++)
{
for($x=0; $x<($dim+64); $x++)
{
$xx = $x>>5;
$yy = $y>>5;
$x2 = ($x - ($xx<<5));
$y2 = ($y - ($yy<<5));
imagesetpixel($img, $x, $y, $cols[$matrix[$yy][$xx]]);
if ( ($xx>0) && ($yy>0) && ($xx<=8) && ($yy<=8) )
{
$color1 = $colorsRGB[$matrix[$yy][$xx]];
$color2 = $colorsRGB[$matrix[$yy][ ($xx+1) ]];
$color3 = $colorsRGB[$matrix[ ($yy+1) ][$xx]];
$color4 = $colorsRGB[$matrix[ ($yy+1) ][ ($xx+1) ]];
$usedColors = array_unique(array($matrix[$yy][$xx], $matrix[$yy][ ($xx+1) ], $matrix[ ($yy+1) ][$xx], $matrix[ ($yy+1) ][ ($xx+1) ]));
$a1 = mulclr($color1, ($scale-$x2)*($scale-$y2));
$a1 = addclr($a1, mulclr($color2, $x2*($scale-$y2)));
$a1 = addclr($a1, mulclr($color3, ($scale-$x2)*$y2));
$a1 = addclr($a1, mulclr($color4, $x2*$y2));
$a1 = divclr($a1, $scale*$scale);
$clrIdx = findclridx($a1, $usedColors);
$col = $cols[$clrIdx];
imagesetpixel($img, $dim+$x+32*2, $y, $col);
}
}
}
header("Content-Type: image/png");
imagepng($img);
exit;
here is sample result:

Determine position of one image in another with PHP

I have two images(small and big). One of them contains another one. Something like one image is a photo and another one is a picture of the page of the photoalbum where this photo is situated. I hope you understood what I said.
So how do I get coordinates (x,y) of a small image on the big one using PHP?
It is quite easy to do on your own, without relying on external libs other than gd.
What you need to be aware of, is that you most likely cannot do a simple pixel per pixel check, as filtering and compression might slightly modify the value of each pixel.
The code I am proposing here will most likely be slow, if performance is a concern, you could optimize it or take shortcuts. Hopefully, the code puts you on the right track!
First, lets iterate on our pictures
$small = imagecreatefrompng("small.png");
$large = imagecreatefrompng("large.png");
$smallwidth = imagesx($small);
$smallheight = imagesy($small);
$largewidth = imagesx($large);
$largeheight = imagesy($large);
$foundX = -1;
$foundY = -1;
$keepThreshold = 20;
$potentialPositions = array();
for($x = 0; $x <= $largewidth - $smallwidth; ++$x)
{
for($y = 0; $y <= $largeheight - $smallheight; ++$y)
{
// Scan the whole picture
$error = GetImageErrorAt($large, $small, $x, $y);
if($error["avg"] < $keepThreshold)
{
array_push($potentialPositions, array("x" => $x, "y" => $y, "error" => $error));
}
}
}
imagedestroy($small);
imagedestroy($large);
echo "Found " . count($potentialPositions) . " potential positions\n";
The goal here is to find how similar the pixels are, and if they are somewhat similar, keep the potential position. Here, I iterate each and every pixel of the large picture, this could be a point of optimization.
Now, where does this error come from?
Getting the likeliness
What I did here is iterate over the small picture and a "window" in the large picture checking how much difference there was on the red, green and blue channel:
function GetImageErrorAt($haystack, $needle, $startX, $startY)
{
$error = array("red" => 0, "green" => 0, "blue" => 0, "avg" => 0);
$needleWidth = imagesx($needle);
$needleHeight = imagesy($needle);
for($x = 0; $x < $needleWidth; ++$x)
{
for($y = 0; $y < $needleHeight; ++$y)
{
$nrgb = imagecolorat($needle, $x, $y);
$hrgb = imagecolorat($haystack, $x + $startX, $y + $startY);
$nr = $nrgb & 0xFF;
$hr = $hrgb & 0xFF;
$error["red"] += abs($hr - $nr);
$ng = ($nrgb >> 8) & 0xFF;
$hg = ($hrgb >> 8) & 0xFF;
$error["green"] += abs($hg - $ng);
$nb = ($nrgb >> 16) & 0xFF;
$hb = ($hrgb >> 16) & 0xFF;
$error["blue"] += abs($hb - $nb);
}
}
$error["avg"] = ($error["red"] + $error["green"] + $error["blue"]) / ($needleWidth * $needleHeight);
return $error;
}
So far, we've established a potential error value for every "window" in the large picture that could contain the small picture, and store them in an array if they seem "good enough".
Sorting
Now, we simply need to sort our best matches and keep the best one, it is most likely where our small picture is located:
function SortOnAvgError($a, $b)
{
if($a["error"]["avg"] == $b["error"]["avg"])
{
return 0;
}
return ($a["error"]["avg"] < $b["error"]["avg"]) ? -1 : 1;
}
if(count($potentialPositions) > 0)
{
usort($potentialPositions, "SortOnAvgError");
$mostLikely = $potentialPositions[0];
echo "Most likely at " . $mostLikely["x"] . "," . $mostLikely["y"];
}
Example
Given the two following pictures:
and
You should have the following result:
Found 5 potential positions
Most likely at 288,235
Which corresponds exactly with the position of our duck. The 4 other positions are 1 pixel up, down, left and right.
I am going to edit this entry after I'm done working on some optimizations for you, as this code is way too slow for big images (PHP performed even worse than I expected).
Edit
First, before doing anything to "optimize" the code, we need numbers, so I added
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
$time_start = microtime_float();
and
$time_end = microtime_float();
echo "in " . ($time_end - $time_start) . " seconds\n";
at the end to have a specific idea of how much time is taken during the algorithm. This way, I can know if my changes improve or make the code worse. Given that the current code with these pictures takes ~45 minutes to execute, we should be able to improve this time quite a lot.
A tentative that was not succesful, was to cache the RGB from the $needle to try to accelerate the GetImageErrorAt function, but it worsened the time.
Given that our computation is on a geometric scale, the more pixels we explore, the longer it will take... so a solution is to skip many pixels to try to locate as fast as possible our picture, and then more accurately zone in on our position.
I modified the error function to take as a parameter how to increment the x and y
function GetImageErrorAt($haystack, $needle, $startX, $startY, $increment)
{
$needleWidth = imagesx($needle);
$needleHeight = imagesy($needle);
$error = array("red" => 0, "green" => 0, "blue" => 0, "avg" => 0, "complete" => true);
for($x = 0; $x < $needleWidth; $x = $x + $increment)
{
for($y = 0; $y < $needleHeight; $y = $y + $increment)
{
$hrgb = imagecolorat($haystack, $x + $startX, $y + $startY);
$nrgb = imagecolorat($needle, $x, $y);
$nr = $nrgb & 0xFF;
$hr = $hrgb & 0xFF;
$ng = ($nrgb >> 8) & 0xFF;
$hg = ($hrgb >> 8) & 0xFF;
$nb = ($nrgb >> 16) & 0xFF;
$hb = ($hrgb >> 16) & 0xFF;
$error["red"] += abs($hr - $nr);
$error["green"] += abs($hg - $ng);
$error["blue"] += abs($hb - $nb);
}
}
$error["avg"] = ($error["red"] + $error["green"] + $error["blue"]) / ($needleWidth * $needleHeight);
return $error;
}
For example, passing 2 will make the function return 4 times faster, as we skip both x and y values.
I also added a stepSize for the main loop:
$stepSize = 10;
for($x = 0; $x <= $largewidth - $smallwidth; $x = $x + $stepSize)
{
for($y = 0; $y <= $largeheight - $smallheight; $y = $y + $stepSize)
{
// Scan the whole picture
$error = GetImageErrorAt($large, $small, $x, $y, 2);
if($error["complete"] == true && $error["avg"] < $keepThreshold)
{
array_push($potentialPositions, array("x" => $x, "y" => $y, "error" => $error));
}
}
}
Doing this, I was able to reduce the execution time from 2657 seconds to 7 seconds at a price of precision. I increased the keepThreshold to have more "potential results".
Now that I wasn't checking each pixels, my best answer is:
Found 8 potential positions
Most likely at 290,240
As you can see, we're near our desired position, but it's not quite right.
What I'm going to do next is define a rectangle around this "pretty close" position to explore every pixel inside the stepSize we added.
I'm now changing the lower part of the script for:
if(count($potentialPositions) > 0)
{
usort($potentialPositions, "SortOnAvgError");
$mostLikely = $potentialPositions[0];
echo "Most probably around " . $mostLikely["x"] . "," . $mostLikely["y"] . "\n";
$startX = $mostLikely["x"] - $stepSize + 1; // - $stepSize was already explored
$startY = $mostLikely["y"] - $stepSize + 1; // - $stepSize was already explored
$endX = $mostLikely["x"] + $stepSize - 1;
$endY = $mostLikely["y"] + $stepSize - 1;
$refinedPositions = array();
for($x = $startX; $x <= $endX; ++$x)
{
for($y = $startY; $y <= $endY; ++$y)
{
// Scan the whole picture
$error = GetImageErrorAt($large, $small, $x, $y, 1); // now check every pixel!
if($error["avg"] < $keepThreshold) // make the threshold smaller
{
array_push($refinedPositions, array("x" => $x, "y" => $y, "error" => $error));
}
}
}
echo "Found " . count($refinedPositions) . " refined positions\n";
if(count($refinedPositions))
{
usort($refinedPositions, "SortOnAvgError");
$mostLikely = $refinedPositions[0];
echo "Most likely at " . $mostLikely["x"] . "," . $mostLikely["y"] . "\n";
}
}
Which now gives me an output like:
Found 8 potential positions
Most probably around 290,240
Checking between X 281 and 299
Checking between Y 231 and 249
Found 23 refined positions
Most likely at 288,235
in 13.960182189941 seconds
Which is indeed the right answer, roughly 200 times faster than the initial script.
Edit 2
Now, my test case was a bit too simple... I changed it to a google image search:
Looking for this picture (it's located at 718,432)
Considering the bigger picture sizes, we can expect a longer processing time, but the algorithm did find the picture at the right position:
Found 123 potential positions
Most probably around 720,430
Found 17 refined positions
Most likely at 718,432
in 43.224536895752 seconds
Edit 3
I decided to try the option I told you in the comment, to scale down the pictures before executing the find, and I had great results with it.
I added this code before the first loop:
$smallresizedwidth = $smallwidth / 2;
$smallresizedheight = $smallheight / 2;
$largeresizedwidth = $largewidth / 2;
$largeresizedheight = $largeheight / 2;
$smallresized = imagecreatetruecolor($smallresizedwidth, $smallresizedheight);
$largeresized = imagecreatetruecolor($largeresizedwidth, $largeresizedheight);
imagecopyresized($smallresized, $small, 0, 0, 0, 0, $smallresizedwidth, $smallresizedheight, $smallwidth, $smallheight);
imagecopyresized($largeresized, $large, 0, 0, 0, 0, $largeresizedwidth, $largeresizedheight, $largewidth, $largeheight);
And for them main loop I iterated on the resized assets with the resized width and height. Then, when adding to the array, I double the x and y, giving the following:
array_push($potentialPositions, array("x" => $x * 2, "y" => $y * 2, "error" => $error));
The rest of the code remains the same, as we want to do the precise location on the real size pictures. All you have to do is add at the end:
imagedestroy($smallresized);
imagedestroy($largeresized);
Using this version of the code, with the google image result, I had:
Found 18 potential positions
Most around 720,440
Found 17 refined positions
Most likely at 718,432
in 11.499078989029 seconds
A 4 times performance increase!
Hope this helps
Use ImageMagick.
This page will give you answer: How can I detect / calculate if a small pictures is present inside a bigger picture?

Categories