How can I calculate the points along a line in PHP? - php

I apologize if this is overly simple, but I'm the furthest thing possible from a math major, and I have trouble reducing abstract formulas into workable code. I know the formula for a line, but turning it into code is making my head spin. And stressing out my code debugger.
Given a 2D grid coordinate system, I need to be able to specify a starting point X1,Y1 and an ending point X2,Y2 and calculate a list of all grid cells directly on a line between those two points.
So if...
X1,Y1 = 3,3
X2,Y2 = 0,5
I would want to calculate an array of points
3,3
2,4
1,4
0,5
Or something like that. It's also kind of important that I be able to list these points in order - so as above, I'm starting with the origin X,Y and moving toward the destination X,Y.
(And no, this isn't homework - I've seen that asked for a lot of other math questions here, so I'll get it out of the way up front. Maybe if I'd done this as homework 25 years ago I wouldn't need to ask now!)
I did find PHP Find Coordinates between two points which seems to talk around the solution, but the comments indicate that the "accepted" answer isn't complete.
Many thanks.

Sounds like your best bet might be Bresenham's algorithm for drawing a line, except instead of plotting the points you'll be capturing the x,y values.

I don't know that this is particularly elegant, but here's how I would do it:
function getLinePoints( $startPoint, $endPoint ){
$totalSteps = max( abs( $startPoint[0] - $endPoint[0] ), abs( $startPoint[1] - $endPoint[1] ) );
if ( $totalSteps == 0 ){
return $startPoint;
}
$xStep = ( $endPoint[0] - $startPoint[0] ) / $totalSteps;
$yStep = ( $endPoint[1] - $startPoint[1] ) / $totalSteps;
$points[] = $currentPoint = $startPoint;
for( $step = 0; $step < $totalSteps; $step++ ){
$currentPoint[0] += $xStep;
$currentPoint[1] += $yStep;
$points[] = array( round( $currentPoint[0], 0 ), round( $currentPoint[1], 0 ) );
}
return $points;
}
$pointA = array( 3, 3 );
$pointB = array( 0, 5 );
$points = getLinePoints( $pointA, $pointB );
This takes the following steps:
Get the total whole number of points you'll have to traverse horizontally or vertically.
Calculates how far you'll have to travel horizontally or vertically at each step.
Constructs an array of points with coordinates rounded to whole numbers.
Return value should be:
Array(
Array( 3, 3 ),
Array( 2, 4 ),
Array( 1, 4 ),
Array( 0, 5 )
)

I'm assuming you want whole-number coordinates, since that's what you listed in the example. But note that this is not necessarily the case for any arbitrary set of (x1,y1) and (x2,y2). My answer here will give you whole-number x-coordinates only.
I would use the two-point form of the linear equation.
yi = (y2-y1)/(x2-x1)*(xi-x1) + y1
where (xi,yi) are the points you are looking for.
for ($xi=$x1; $xi<$x2; $xi++) {
$yi = ($y2-$y1)/($x2-$x1)*($xi-$x1) + $y1
echo $xi + "," + $yi
}
Just make sure you have $x1 < $x2 before you run the above code.
A more complicated situation arises when you draw a line between two arbitrary points (x1,y1) and (x2,y2) and you want to output the list of squares (grid cells) intersected by that line.

So, for more specifics, here's a PHP function that will always return the points with the first array element as the origin and the last as the destination. This is tested and working, and is an adaptation of http://alex.moutonking.com/wordpress/?p=44.
function bresenham($x1, $y1, $x2, $y2, $guaranteeEndPoint=true) {
$xBegin = $x1;
$yBegin = $y1;
$xEnd = $x2;
$yEnd = $y2;
$dots = array();
$steep = abs($y2 - $y1) > abs($x2 - $x1);
if ($steep) {
$tmp = $x1;
$x1 = $y1;
$y1 = $tmp;
$tmp = $x2;
$x2 = $y2;
$y2 = $tmp;
}
if ($x1 > $x2) {
$tmp = $x1;
$x1 = $x2;
$x2 = $tmp;
$tmp = $y1;
$y1 = $y2;
$y2 = $tmp;
}
$deltax = floor($x2 - $x1);
$deltay = floor(abs($y2 - $y1));
$error = 0;
$deltaerr = $deltay / $deltax;
$y = $y1;
$ystep = ($y1 < $y2) ? 1 : -1;
for ($x = $x1; $x < $x2; $x++) {
$dots[] = $steep ? array($y, $x) : array($x, $y);
$error += $deltaerr;
if ($error >= 0.5) {
$y += $ystep;
$error -= 1;
}
}
if ($guaranteeEndPoint) {
if ((($xEnd - $x) * ($xEnd - $x) + ($yEnd - $y) * ($yEnd - $y)) < (($xBegin - $x) * ($xBegin - $x) + ($yBegin - $y) * ($yBegin - $y))) {
$dots[] = array($xEnd, $yEnd);
} else
$dots[] = array($xBegin, $yBegin);
}
if ($dots[0][0] != $xBegin and $dots[0][1] != $yBegin) {
return array_reverse($dots);
} else {
return $dots;
}
}

looks like you re looking for interpolation methods, check this : http://paulbourke.net/miscellaneous/interpolation/ (what u need is linear interpolation)

Related

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?

Incorrect saturation calculation in RGB to HSL function

Does anybody know the correct formula to get the saturation level from an RGB color?
I already have a function that does it. I've tried loads of them posted on the internet but only this one seemed to work (first-time) for me, apart from the saturation level being occasionally slightly out.
rgb(204,153,51) should equal hsl(40,60,50), instead I have hsl(40,75,50). As you can see my hue and lightness are correct, in-fact, the saturation is mostly correct too, but sometimes it's not and I need to correct that if I can.
This is what I've built so far, so I can check all the color values are correct for my images, before storing them in a database for my search engine.
And this is the function in question where I believe the saturation is being calculated incorrectly:
function RGBtoHSL($red, $green, $blue)
{
$r = $red / 255.0;
$g = $green / 255.0;
$b = $blue / 255.0;
$H = 0;
$S = 0;
$V = 0;
$min = min($r,$g,$b);
$max = max($r,$g,$b);
$delta = ($max - $min);
$L = ($max + $min) / 2.0;
if($delta == 0) {
$H = 0;
$S = 0;
} else {
$S = $delta / $max;
$dR = ((($max - $r) / 6) + ($delta / 2)) / $delta;
$dG = ((($max - $g) / 6) + ($delta / 2)) / $delta;
$dB = ((($max - $b) / 6) + ($delta / 2)) / $delta;
if ($r == $max)
$H = $dB - $dG;
else if($g == $max)
$H = (1/3) + $dR - $dB;
else
$H = (2/3) + $dG - $dR;
if ($H < 0)
$H += 1;
if ($H > 1)
$H -= 1;
}
$HSL = ($H*360).', '.($S*100).', '.round(($L*100),0);
return $HSL;
}
One clue I have, as to why this doesn't work 100%, is that I'm first converting a HEX color to an RGB, then the RGB to an HSL. Would this be a problem due to web-safe colors or can you spot anything else that may cause this in the function? Or is this just how it is?
UPDATE 1
Trying other images, it appears to be mostly 'beige' (approx.) colors that are slightly out on saturation. Using a color picker, if I move the saturation bar to where it should be there isn't a huge difference, so maybe my search feature won't pick up on this too much. It would be nice to solve it though, before I run it on 500,000 photos.
THE FIX
Thanks to OmnipotentEntity, below, he noticed I missing piece to my function. I changed:
$S = $delta / $max;
to:
$S = $L > 0.5 ? $delta / (2 - $max - $min) : $delta / ($max + $min);
and now produces 100% correct results.
FRIENDLY NOTE
If anybody would like the code to produce this color table, just ask.
It looks like you’re missing a piece of the calculation for saturation if your luma is > .5 as shown here in this JavaScript HSL code.
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;

Why is NaN returned from the euclidean method

I have a problem with this function:
function encludean_method($lat1,$lon1,$lat2,$lon2)
{
$x1 = $lat1;
$y1 = $lon1;
$x2 = $lat2;
$y2 = $lon2;
$x = ( pow($x2,2) - pow($x1,2));
$y = ( pow($y2,2) - pow($y1,2));
$distance = (sqrt($x + $y));
return $distance;
}
The problem when I call the above function with some values such as
(1.57454123333,103.6200516333,1.57483825,103.619484475)
it returns NaN. Can anyone tell me why that NaN is returned and how to solve it?
$x and $y can assume negative values (which in fact does happen in your example), yielding in total a negative expression for $x + $y. I'm not sure whether what you're doing is right at all, wouldn't the correct version be more like:
$x1 = $lat1;
$y1 = $lon1;
$x2 = $lat2;
$y2 = $lon2;
$x = $x2 - $x1;
$y = $y2 - $y1;
$distance = (sqrt(pow($x, 2) + pow($y, 2)));
return $distance;
By squaring before calculating the square root, you're guaranteed to operate on positive values only.
Try this
function euclidean_method($lat1,$lon1,$lat2,$lon2) {
$x = $lat1-$lat2;
$y = $lon1-$lon2;
return sqrt($x*$x+$y*$y)
}
The problem is that your x value is smaller than the negativity of your y and thus it will become a negative value you're trying to take the root of.
Because of a wrong algorithm.
function encludean_method($lat1,$lon1,$lat2,$lon2)
{
$x1 = doubleval($lat1);
$y1 = doubleval($lon1);
$x2 = doubleval($lat2);
$y2 = doubleval($lon2);
$x = doubleval( pow($x2 - $x1, 2.0));
$y = doubleval( pow($y2 - $y1, 2.0));
$distance = doubleval(sqrt($x + $y));
return ($distance);
}
Note: (x12 - x22) < (x1 - x2)2.
That's why your code gave x = float(0.00093541820670495) and y = float(-0.11753762299304). So, sqrt(x + y) is too close to zero.
But if you are trying to get the distance between that point over the Earth surface, you should use another formula - the one you are using is applicable for Cartesian coordinate system only.
Notice, that latitude and longitude are measured in degrees. So the number you get is a bit misterious =)
So, after googling a bit, i've noticed some interesting services and formulas. Here's the code you are possibly trying to invoke:
function encludean_method($lat1, $lon1, $lat2, $lon2)
{
$R = 6372.8; // km
$lat1 = deg2rad($lat1);
$lat2 = deg2rad($lat2);
$lon1 = deg2rad($lon1);
$lon2 = deg2rad($lon2);
$a = (cos($lat2) * cos($lon2)) - (cos($lat1) * cos($lon1));
$b = (cos($lat2) * sin($lon2)) - (cos($lat1) * sin($lon1));
$c = sin($lat2) - sin($lat1);
$ch = sqrt(pow($a, 2.0) + pow($b, 2.0) + pow($c, 2.0));
$phi = 2.0 * asin($ch / 2.0);
return $R * $phi;
}
Now, encludean_method(1.5745, 103.6200, 1.5748, 103.6195) returns 0.0648375378577 value, which is possibly the distance you are searching for; measured in kilometers.
Here's the example and one more (based on this answer). And here is the service i used to verify that code.
Hope this will help you! =)

How can I calculate a trend line in PHP?

So I've read the two related questions for calculating a trend line for a graph, but I'm still lost.
I have an array of xy coordinates, and I want to come up with another array of xy coordinates (can be fewer coordinates) that represent a logarithmic trend line using PHP.
I'm passing these arrays to javascript to plot graphs on the client side.
Logarithmic Least Squares
Since we can convert a logarithmic function into a line by taking the log of the x values, we can perform a linear least squares curve fitting. In fact, the work has been done for us and a solution is presented at Math World.
In brief, we're given $X and $Y values that are from a distribution like y = a + b * log(x). The least squares method will give some values aFit and bFit that minimize the distance from the parametric curve to the data points given.
Here is an example implementation in PHP:
First I'll generate some random data with known underlying distribution given by $a and $b
// True parameter valaues
$a = 10;
$b = 5;
// Range of x values to generate
$x_min = 1;
$x_max = 10;
$nPoints = 50;
// Generate some random points on y = a * log(x) + b
$X = array();
$Y = array();
for($p = 0; $p < $nPoints; $p++){
$x = $p / $nPoints * ($x_max - $x_min) + $x_min;
$y = $a + $b * log($x);
$X[] = $x + rand(0, 200) / ($nPoints * $x_max);
$Y[] = $y + rand(0, 200) / ($nPoints * $x_max);
}
Now, here's how to use the equations given to estimate $a and $b.
// Now convert to log-scale for X
$logX = array_map('log', $X);
// Now estimate $a and $b using equations from Math World
$n = count($X);
$square = create_function('$x', 'return pow($x,2);');
$x_squared = array_sum(array_map($square, $logX));
$xy = array_sum(array_map(create_function('$x,$y', 'return $x*$y;'), $logX, $Y));
$bFit = ($n * $xy - array_sum($Y) * array_sum($logX)) /
($n * $x_squared - pow(array_sum($logX), 2));
$aFit = (array_sum($Y) - $bFit * array_sum($logX)) / $n;
You may then generate points for your Javascript as densely as you like:
$Yfit = array();
foreach($X as $x) {
$Yfit[] = $aFit + $bFit * log($x);
}
In this case, the code estimates bFit = 5.17 and aFit = 9.7, which is quite close for only 50 data points.
For the example data given in the comment below, a logarithmic function does not fit well.
The least squares solution is y = -514.734835478 + 2180.51562281 * log(x) which is essentially a line in this domain.
I would recommend using library: http://www.drque.net/Projects/PolynomialRegression/
Available by Composer: https://packagist.org/packages/dr-que/polynomial-regression.
In case anyone is having problems with the create_function, here is how I edited it. (Though I wasn't using logs, so I did take those out.)
I also reduced the number of calculations and added an R2. It seems to work so far.
function lsq(){
$X = array(1,2,3,4,5);
$Y = array(.3,.2,.7,.9,.8);
// Now estimate $a and $b using equations from Math World
$n = count($X);
$mult_elem = function($x,$y){ //anon function mult array elements
$output=$x*$y; //will be called on each element
return $output;
};
$sumX2 = array_sum(array_map($mult_elem, $X, $X));
$sumXY = array_sum(array_map($mult_elem, $X, $Y));
$sumY = array_sum($Y);
$sumX = array_sum($X);
$bFit = ($n * $sumXY - $sumY * $sumX) /
($n * $sumX2 - pow($sumX, 2));
$aFit = ($sumY - $bFit * $sumX) / $n;
echo ' intercept ',$aFit,' ';
echo ' slope ',$bFit,' ' ;
//r2
$sumY2 = array_sum(array_map($mult_elem, $Y, $Y));
$top=($n*$sumXY-$sumY*$sumX);
$bottom=($n*$sumX2-$sumX*$sumX)*($n*$sumY2-$sumY*$sumY);
$r2=pow($top/sqrt($bottom),2);
echo ' r2 ',$r2;
}

Categories