Php financial XIRR not giving strange results - php

I'm using the XIRR function from the php financial library (http://www.phpclasses.org/package/892-PHP-Financial-functions-with-the-Excel-function-names-.html) but I get strange results with these values (dates are d/m/y):
(01/01/2014, -400) , (01/10/2014, 18)
MS Excel correctly returns 0.98, while the XIRR function returns -1.5714653207915E+40. The code is as follow:
$f->XIRR(array(-400,18), array(
mktime(0,0,0,1,1,2014),
mktime(0,0,0,10,1,2014),
), 0.1);
Can anyone explain me what am I doing wrong? Thanks in advance for any help.

I have written one code from PHP Excel Functions.
I have calculated XIRR and XNPV.
Here is code sample with some dummy data.
Main advantage is that, There is no dependency on any library in this
code.
<?php
$rate = 0.12;
$values = array(-5000,-3000,-8000,25000,-4000);
$dates = array('01-02-2015','05-05-2016','02-03-2018','03-03-2019','05-03-2019');
/** FINANCIAL_MAX_ITERATIONS */
define('FINANCIAL_MAX_ITERATIONS', 128);
/** FINANCIAL_PRECISION */
define('FINANCIAL_PRECISION', 1.0e-08);
$result = XIRR($values,$dates,0.1);
print_r($result);
function XIRR($values, $dates, $guess = 0.1) {
$x1 = 0.0;
$x2 = $guess;
$f1 = XNPV($x1, $values, $dates);
$f2 = XNPV($x2, $values, $dates);
for ($i = 0; $i < 128; ++$i) {
if (($f1 * $f2) < 0.0) break;
if (abs($f1) < abs($f2)) {
$f1 = XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
} else {
$f2 = XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
}
}
$f = XNPV($x1, $values, $dates);
if ($f < 0.0) {
$rtb = $x1;
$dx = $x2 - $x1;
} else {
$rtb = $x2;
$dx = $x1 - $x2;
}
for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; ++$i) {
$dx *= 0.5;
$x_mid = $rtb + $dx;
$f_mid = XNPV($x_mid, $values, $dates);
if ($f_mid <= 0.0) $rtb = $x_mid;
if ((abs($f_mid) < FINANCIAL_PRECISION) || (abs($dx) < FINANCIAL_PRECISION)) return $x_mid;
}
}
function XNPV($rate, $values, $dates) {
$valCount = count($values);
$xnpv = 0.0;
for ($i = 0; $i < $valCount; ++$i)
{
$datediff = strtotime($dates[$i]) - strtotime($dates[0]);
$datediff = round($datediff / (60 * 60 * 24));
$xnpv += $values[$i] / pow(1 + $rate,$datediff / 365);
}
return $xnpv;
}
?>

The correct XIRR value is -98.417% as shown below
-400 + 18(1+i)^-(273/365) = 0
18(1+i)^-(273/365) = 400
(1+i)^-(273/365) = 400/18
(1+i)^(273/365) = 18/400
(1+i) = (18/400)^(365/273)
1+i = (0.045)^(1.336996337)
i = (0.045)^(1.336996337) - 1
i = -0.984174769
i = -98.417%

Use the American mm/dd/yyyy :
mktime(0,0,0,1,1,2014),
mktime(0,0,0,1,10,2014)

Related

Pie chart problem does not show if data is missing

I have a PHP pie chart which uses data from Mysql to show the chart. However if one data is missing the whole chart turns to one color. For example for grading if the input is A, B, D and F(pay attention C grade input is missing) then the whole pie chart is in one color like Orange or red.
Can you please help me with this? Thanks
<?php
$show_label = true; // true = show label, false = don't show label.
$show_percent = true; // true = show percentage, false = don't show percentage.
$show_text = true; // true = show text, false = don't show text.
$show_parts = false; // true = show parts, false = don't show parts.
$label_form = 'square'; // 'square' or 'round' label.
$width = 199;
$background_color = 'FFFFFF'; // background-color of the chart...
$text_color = '000000'; // text-color.
$colors = array('0000ff', '006600', 'ffff00','DD7C1D', 'FF3300', 'CC6600','990000','520000','BFBFC1','808080'); // colors of the slices.
$shadow_height = 16; // Height on shadown.
$shadow_dark = true; // true = darker shadow, false = lighter shadow...
// DON'T CHANGE ANYTHING BELOW THIS LINE...
$data = $_GET["data"];
$label = $_GET["label"];
$height = $width/2;
$data = array_filter(explode('*',$data));
if ($label != '') $label = explode('*',$label);
for ($i = 0; $i < count($label); $i++)
{
if ($data[$i]/array_sum($data) < 0.1) $number[$i] = ' '.number_format(($data[$i]/array_sum($data))*100,1,',','.').'%';
else $number[$i] = number_format(($data[$i]/array_sum($data))*100,1,',','.').'%';
if (strlen($label[$i]) > $text_length) $text_length = strlen($label[$i]);
}
if (is_array($label))
{
$antal_label = count($label);
$xtra = (5+15*$antal_label)-($height+ceil($shadow_height));
if ($xtra > 0) $xtra_height = (5+15*$antal_label)-($height+ceil($shadow_height));
$xtra_width = 5;
if ($show_label) $xtra_width += 20;
if ($show_percent) $xtra_width += 45;
if ($show_text) $xtra_width += $text_length*8;
if ($show_parts) $xtra_width += 35;
}
$img = ImageCreateTrueColor($width+$xtra_width, $height+ceil($shadow_height)+$xtra_height);
ImageFill($img, 0, 0, colorHex($img, $background_color));
foreach ($colors as $colorkode)
{
$fill_color[] = colorHex($img, $colorkode);
$shadow_color[] = colorHexshadow($img, $colorkode, $shadow_dark);
}
$label_place = 5;
if (is_array($label))
{
for ($i = 0; $i < count($label); $i++)
{
if ($label_form == 'round' && $show_label)
{
imagefilledellipse($img,$width+11,$label_place+5,10,10,colorHex($img, $colors[$i % count($colors)]));
imageellipse($img,$width+11,$label_place+5,10,10,colorHex($img, $text_color));
}
else if ($label_form == 'square' && $show_label)
{
imagefilledrectangle($img,$width+6,$label_place,$width+16,$label_place+10,colorHex($img, $colors[$i % count($colors)]));
imagerectangle($img,$width+6,$label_place,$width+16,$label_place+10,colorHex($img, $text_color));
}
if ($show_percent) $label_output = $number[$i].' ';
if ($show_text) $label_output = $label_output.$label[$i].' ';
if ($show_parts) $label_output = $label_output.$data[$i];
imagestring($img,'2',$width+20,$label_place,$label_output,colorHex($img, $text_color));
$label_output = '';
$label_place = $label_place + 15;
}
}
$centerX = round($width/2);
$centerY = round($height/2);
$diameterX = $width-4;
$diameterY = $height-4;
$data_sum = array_sum($data);
$start = 270;
for ($i = 0; $i < count($data); $i++)
{
$value += $data[$i];
$end = ceil(($value/$data_sum)*360) + 270;
$slice[] = array($start, $end, $shadow_color[$value_counter % count($shadow_color)], $fill_color[$value_counter % count($fill_color)]);
$start = $end;
$value_counter++;
}
for ($i=$centerY+$shadow_height; $i>$centerY; $i--)
{
for ($j = 0; $j < count($slice); $j++)
{
ImageFilledArc($img, $centerX, $i, $diameterX, $diameterY, $slice[$j][0], $slice[$j][1], $slice[$j][2], IMG_ARC_PIE);
}
}
for ($j = 0; $j < count($slice); $j++)
{
ImageFilledArc($img, $centerX, $centerY, $diameterX, $diameterY, $slice[$j][0], $slice[$j][1], $slice[$j][3], IMG_ARC_PIE);
}
OutputImage($img);
ImageDestroy($img);
function colorHex($img, $HexColorString)
{
$R = hexdec(substr($HexColorString, 0, 2));
$G = hexdec(substr($HexColorString, 2, 2));
$B = hexdec(substr($HexColorString, 4, 2));
return ImageColorAllocate($img, $R, $G, $B);
}
function colorHexshadow($img, $HexColorString, $mork)
{
$R = hexdec(substr($HexColorString, 0, 2));
$G = hexdec(substr($HexColorString, 2, 2));
$B = hexdec(substr($HexColorString, 4, 2));
if ($mork)
{
($R > 99) ? $R -= 100 : $R = 0;
($G > 99) ? $G -= 100 : $G = 0;
($B > 99) ? $B -= 100 : $B = 0;
}
else
{
($R < 220) ? $R += 35 : $R = 255;
($G < 220) ? $G += 35 : $G = 255;
($B < 220) ? $B += 35 : $B = 255;
}
return ImageColorAllocate($img, $R, $G, $B);
}
function OutputImage($img)
{
header('Content-type: image/jpg');
ImageJPEG($img,NULL,100);
}
?>

how to do XIRR calculation in php?

I have used PHP class from, http://www.phpclasses.org/ but it gives -1571465320791500131898188521929991589462016.00 % this as answer many times(you can take this example, date1= 04/14/2015, date2=08/19/2015(mm/dd/yyyy format), value1=-110,value2=5) Is there any solution for this, any other better go?
This question is asked previously, but there is no satisfactory answer..
-110 14-Apr-15
5 19-Aug-15
--------------------
-0.99986137
Above is what Excel shows,,,
Below is the code from php class
///////////for xirr calc////////////////
function DATEDIFF($datepart, $startdate, $enddate)
{
switch (strtolower($datepart)) {
case 'yy':
case 'yyyy':
case 'year':
$di = getdate($startdate);
$df = getdate($enddate);
return $df['year'] - $di['year'];
break;
case 'q':
case 'qq':
case 'quarter':
die("Unsupported operation");
break;
case 'n':
case 'mi':
case 'minute':
return ceil(($enddate - $startdate) / 60);
break;
case 'hh':
case 'hour':
return ceil(($enddate - $startdate) / 3600);
break;
case 'd':
case 'dd':
case 'day':
return ceil(($enddate - $startdate) / 86400);
break;
case 'wk':
case 'ww':
case 'week':
return ceil(($enddate - $startdate) / 604800);
break;
case 'm':
case 'mm':
case 'month':
$di = getdate($startdate);
$df = getdate($enddate);
return ($df['year'] - $di['year']) * 12 + ($df['mon'] - $di['mon']);
break;
default:
die("Unsupported operation");
}
}
function XNPV($rate, $values, $dates)
{
if ((!is_array($values)) || (!is_array($dates))) return null;
if (count($values) != count($dates)) return null;
$xnpv = 0.0;
for ($i = 0; $i < count($values); $i++)
{
$xnpv += $values[$i] / pow(1 + $rate, $this->DATEDIFF('day', $dates[0], $dates[$i]) / 365);
}
return (is_finite($xnpv) ? $xnpv: null);
}
function XIRR($values, $dates, $guess = 0.1)
{
if ((!is_array($values)) && (!is_array($dates))) return null;
if (count($values) != count($dates)) return null;
// create an initial bracket, with a root somewhere between bot and top
$x1 = 0.0;
$x2 = $guess;
$f1 = $this->XNPV($x1, $values, $dates);
$f2 = $this->XNPV($x2, $values, $dates);
for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; $i++)
{
if (($f1 * $f2) < 0.0) break;
if (abs($f1) < abs($f2)) {
$f1 = $this->XNPV($x1 += 1.6 * ($x1 - $x2), $values, $dates);
} else {
$f2 = $this->XNPV($x2 += 1.6 * ($x2 - $x1), $values, $dates);
}
}
if (($f1 * $f2) > 0.0) return null;
$f = $this->XNPV($x1, $values, $dates);
if ($f < 0.0) {
$rtb = $x1;
$dx = $x2 - $x1;
} else {
$rtb = $x2;
$dx = $x1 - $x2;
}
for ($i = 0; $i < FINANCIAL_MAX_ITERATIONS; $i++)
{
$dx *= 0.5;
$x_mid = $rtb + $dx;
$f_mid = $this->XNPV($x_mid, $values, $dates);
if ($f_mid <= 0.0) $rtb = $x_mid;
if ((abs($f_mid) < FINANCIAL_ACCURACY) || (abs($dx) < FINANCIAL_ACCURACY)) return $x_mid;
}
return null;
}
And We have to call it like this
echo 'XIRR: ' . $f->XIRR(array(-10000,2750,4250,3250,2750), array(
mktime(0,0,0,1,1,2008),
mktime(0,0,0,3,1,2008),
mktime(0,0,0,10,30,2008),
mktime(0,0,0,2,15,2009),
mktime(0,0,0,4,1,2009),
), 0.1) . "\n";

Mandelbrot set won't render correctly

I am currently trying to make an animated Mandelbrot set visualisation. But right now it won't even render a single frame correctly.
I don't know where I made the mistake. I guess there is an error in the math.Will you please have a look at it?
Here is a how it looks right now:
Here is my mandelbrot function:
function mandelbrot($a, $b, $limit) {
$a_orig = $a;
$b_orig = $b;
$count = 0;
while(($count < $limit) && (sqrt(($a * $a) + ($b * $b)) <= 2)) {
$a = ($a * $a) - ($b * $b) + $a_orig;
$b = (2 * $a * $b) + $b_orig;
$count++;
}
return $count;
}
And here is the entire code:
<?php
function HSVtoRGB(array $hsv) {
list($H,$S,$V) = $hsv;
//1
$H *= 6;
//2
$I = floor($H);
$F = $H - $I;
//3
$M = $V * (1 - $S);
$N = $V * (1 - $S * $F);
$K = $V * (1 - $S * (1 - $F));
//4
switch ($I) {
case 0:
list($R,$G,$B) = array($V,$K,$M);
break;
case 1:
list($R,$G,$B) = array($N,$V,$M);
break;
case 2:
list($R,$G,$B) = array($M,$V,$K);
break;
case 3:
list($R,$G,$B) = array($M,$N,$V);
break;
case 4:
list($R,$G,$B) = array($K,$M,$V);
break;
case 5:
case 6: //for when $H=1 is given
list($R,$G,$B) = array($V,$M,$N);
break;
}
return array($R, $G, $B);
}
function mandelbrot($a, $b, $limit) {
$a_orig = $a;
$b_orig = $b;
$count = 0;
while(($count < $limit) && (sqrt(($a * $a) + ($b * $b)) <= 2)) {
$a = ($a * $a) - ($b * $b) + $a_orig;
$b = (2 * $a * $b) + $b_orig;
$count++;
}
return $count;
}
ini_set("max_execution_time", 0);
header ("Content-Type: image/gif");
$num_frames = 60;
$size = 1024;
$points = array($size);
$image = imagecreate($size, $size);
for($j = 0; $j <= $num_frames; $j++) {
$tmp_color = HSVtoRGB(array(($j + 1) / ($num_frames + 1), 1, 1));
$color[$j] = imagecolorallocate($image, $tmp_color[0] * 255, $tmp_color[1] * 255, $tmp_color[2] * 255);
}
for($x = 0; $x < $size; $x++) {
for($y = 0; $y < $size; $y++) {
imagesetpixel($image, $x, $y, $color[mandelbrot(-2 + ($x * 2.7 / ($size - 1)), -1.35 + ($y * 2.7 / ($size - 1)), $num_frames)]);
}
}
imagegif($image);
imagedestroy($image);
?>
Your complex number square is wrong. You are overwriting the old value of a where it is needed again in the computation of b. So save it in a temporary variable.
Also, the bailout value of 60 iterations is rather small, 200 would be more appropriate for this scale, for more detailed images it should be reasonably rapidly increase.
Use a*a+b*b < 4 instead of the unnecessary square root. One could re-use the values of a*a and b*b which would also solve the problem of the temporary variable.
norm=10
while ... and norm < 4
a2=a*a
b2=b*b
norm=a2+b2
b=2*a*b+b_orig
a=a2-b2+a_orig
end

PHP function to convert HSL to RGB or Hex

Does anyone know a PHP function (for >5.3) which can convert an HSL color to either RGB or Hex? I've tried a dozen Google searches and none of the functions I have found work as expected.
It doesn't matter whether the function converts to RGB or hex because converting between those two is trivial. The inputs are HSL values for CSS (Hue: 0–360, Saturation: 0–100, Lightness: 0–100).
Edit: Specifying the input and output format would be a bonus :)
Taking the code from one of the answers in the link of Jim's comment (PHP HSV to RGB formula comprehension), we can compute it as follows:
<?php
$hue = 209;
$sat = 75;
$lum = 60;
$hue /= 360;
$sat /= 100;
$lum /= 100;
$result = ColorHSLToRGB($hue, $sat, $lum);
var_dump($result); echo '<br>';
printf("rgb = %d,%d,%d<br>", $result['r'], $result['g'], $result['b']);
function ColorHSLToRGB($h, $s, $l){
$r = $l;
$g = $l;
$b = $l;
$v = ($l <= 0.5) ? ($l * (1.0 + $s)) : ($l + $s - $l * $s);
if ($v > 0){
$m;
$sv;
$sextant;
$fract;
$vsf;
$mid1;
$mid2;
$m = $l + $l - $v;
$sv = ($v - $m ) / $v;
$h *= 6.0;
$sextant = floor($h);
$fract = $h - $sextant;
$vsf = $v * $sv * $fract;
$mid1 = $m + $vsf;
$mid2 = $v - $vsf;
switch ($sextant)
{
case 0:
$r = $v;
$g = $mid1;
$b = $m;
break;
case 1:
$r = $mid2;
$g = $v;
$b = $m;
break;
case 2:
$r = $m;
$g = $v;
$b = $mid1;
break;
case 3:
$r = $m;
$g = $mid2;
$b = $v;
break;
case 4:
$r = $mid1;
$g = $m;
$b = $v;
break;
case 5:
$r = $v;
$g = $m;
$b = $mid2;
break;
}
}
return array('r' => $r * 255.0, 'g' => $g * 255.0, 'b' => $b * 255.0);
}
?>
Output:
array(3) { ["r"]=> float(76.5) ["g"]=> float(155.55) ["b"]=> float(229.5) }
rgb = 76,155,229
Putting this together (which helped me produce this chart)
/**
* convert a HSL colorscheme to either Hexadecimal (default) or RGB.
*
* We want a method where we can programmatically generate a series of colors
* between two values (eg. red to green) which is easy to do with HSL because
* you just change the hue. (0 = red, 120 = green). You can use this function
* to convert those hsl color values to either the rgb or hexadecimal color scheme.
* e.g. You have
* hsl(50, 100%, 50%)
* To convert,
* $hex = convertHSL(50,100,50); // returns #ffd500
* or
* $rgb = convertHSL(50,100,50, false); // returns rgb(255, 213, 0)
*
* see https://coderwall.com/p/dvsxwg/smoothly-transition-from-green-to-red
* #param int $h the hue
* #param int $s the saturation
* #param int $l the luminance
* #param bool $toHex whether you want hexadecimal equivalent or rgb equivalent
* #return string usable in HTML or CSS
*/
function convertHSL($h, $s, $l, $toHex=true){
$h /= 360;
$s /=100;
$l /=100;
$r = $l;
$g = $l;
$b = $l;
$v = ($l <= 0.5) ? ($l * (1.0 + $s)) : ($l + $s - $l * $s);
if ($v > 0){
$m;
$sv;
$sextant;
$fract;
$vsf;
$mid1;
$mid2;
$m = $l + $l - $v;
$sv = ($v - $m ) / $v;
$h *= 6.0;
$sextant = floor($h);
$fract = $h - $sextant;
$vsf = $v * $sv * $fract;
$mid1 = $m + $vsf;
$mid2 = $v - $vsf;
switch ($sextant)
{
case 0:
$r = $v;
$g = $mid1;
$b = $m;
break;
case 1:
$r = $mid2;
$g = $v;
$b = $m;
break;
case 2:
$r = $m;
$g = $v;
$b = $mid1;
break;
case 3:
$r = $m;
$g = $mid2;
$b = $v;
break;
case 4:
$r = $mid1;
$g = $m;
$b = $v;
break;
case 5:
$r = $v;
$g = $m;
$b = $mid2;
break;
}
}
$r = round($r * 255, 0);
$g = round($g * 255, 0);
$b = round($b * 255, 0);
if ($toHex) {
$r = ($r < 15)? '0' . dechex($r) : dechex($r);
$g = ($g < 15)? '0' . dechex($g) : dechex($g);
$b = ($b < 15)? '0' . dechex($b) : dechex($b);
return "#$r$g$b";
} else {
return "rgb($r, $g, $b)";
}
My tests of all other implementations showed only weird and possibly unplausible results. Here is a PHP implementation of #Mohsen's code from https://stackoverflow.com/a/9493060/1598477. Plus a test to show the full beauty...
Sorry to cross-post this. But I really haven't seen any other implementation that gives the quality I needed.
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param {number} h The hue
* #param {number} s The saturation
* #param {number} l The lightness
* #return {Array} The RGB representation
*/
function hue2rgb($p, $q, $t){
if($t < 0) $t += 1;
if($t > 1) $t -= 1;
if($t < 1/6) return $p + ($q - $p) * 6 * $t;
if($t < 1/2) return $q;
if($t < 2/3) return $p + ($q - $p) * (2/3 - $t) * 6;
return $p;
}
function hslToRgb($h, $s, $l){
if($s == 0){
$r = $l;
$g = $l;
$b = $l; // achromatic
}else{
$q = $l < 0.5 ? $l * (1 + $s) : $l + $s - $l * $s;
$p = 2 * $l - $q;
$r = hue2rgb($p, $q, $h + 1/3);
$g = hue2rgb($p, $q, $h);
$b = hue2rgb($p, $q, $h - 1/3);
}
return array(round($r * 255), round($g * 255), round($b * 255));
}
/* Uncomment to test * /
for ($i=0;$i<360;$i++) {
$rgb=hslToRgb($i/360, 1, .9);
echo '<div style="background-color:rgb(' .$rgb[0] . ', ' . $rgb[1] . ', ' . $rgb[2] . ');padding:2px;"></div>';
}
/* End Test */
The PEAR package Image_Color2 has methods to transform between color models - see convertTo.
Here is my solution
HSV value restrictions: $H [0-359], $S [0-100], $V [0-100]
function hsv_to_rgb($iH, $iS, $iV) {
if($iH < 0) $iH = 0;
if($iH > 360) $iH = 360;
if($iS < 0) $iS = 0;
if($iS > 100) $iS = 100;
if($iV < 0) $iV = 0;
if($iV > 100) $iV = 100;
$dS = $iS/100.0;
$dV = $iV/100.0;
$dC = $dV*$dS;
$dH = $iH/60.0;
$dT = $dH;
while($dT >= 2.0) $dT -= 2.0; // php modulus does not work with float
$dX = $dC*(1-abs($dT-1)); // as used in the Wikipedia link
switch($dH) {
case($dH >= 0.0 && $dH < 1.0):
$dR = $dC; $dG = $dX; $dB = 0.0; break;
case($dH >= 1.0 && $dH < 2.0):
$dR = $dX; $dG = $dC; $dB = 0.0; break;
case($dH >= 2.0 && $dH < 3.0):
$dR = 0.0; $dG = $dC; $dB = $dX; break;
case($dH >= 3.0 && $dH < 4.0):
$dR = 0.0; $dG = $dX; $dB = $dC; break;
case($dH >= 4.0 && $dH < 5.0):
$dR = $dX; $dG = 0.0; $dB = $dC; break;
case($dH >= 5.0 && $dH < 6.0):
$dR = $dC; $dG = 0.0; $dB = $dX; break;
default:
$dR = 0.0; $dG = 0.0; $dB = 0.0; break;
}
$dM = $dV - $dC;
$dR += $dM; $dG += $dM; $dB += $dM;
$dR *= 255; $dG *= 255; $dB *= 255;
return array(round($dR), round($dG), round($dB));
}
Copied from this SO Answer by Cullub.
This code worked perfectly to convert a user email to user avatar colour. After long hours of searching, I got the correct hex value similar to the one generated using javascript on the front end.
TL;DR: The full code can be found here on Pastebin.
/**
* convert user email to hsl for user avatar
* #param string $string
* #return string HEX color code
*/
function stringToColor($string)
{
$hash = 0;
$l = 70;
$s = 60;
for ($i = 0; $i < strlen($string); $i++) {
$hash = ord($string[$i]) + (($hash << 5) - $hash);
}
$h = fmod($hash, 360);
return $this->hslToHex($h, $s, $l, true);
}
/**
* Converts HSL to Hex by converting it to
* RGB, then converting that to hex.
*
* string hslToHex($h, $s, $l[, $prependPound = true]
*
* $h is the Degrees value of the Hue
* $s is the Percentage value of the Saturation
* $l is the Percentage value of the Lightness
* $prependPound is a bool, whether you want a pound
* sign prepended. (optional - default=true)
*
* Calls:
* hslToRgb
*
* Output: Hex in the format: #00ff88 (with
* pound sign). Rounded to the nearest whole
* number.
*/
function hslToHex($h, $s, $l, $prependPound = true)
{
//convert hsl to rgb
$rgb = $this->hslToRgb($h, $s, $l);
//convert rgb to hex
$hexR = $rgb['r'];
$hexG = $rgb['g'];
$hexB = $rgb['b'];
//round to the nearest whole number
$hexR = round($hexR);
$hexG = round($hexG);
$hexB = round($hexB);
//convert to hex
$hexR = dechex($hexR);
$hexG = dechex($hexG);
$hexB = dechex($hexB);
//check for a non-two string length
//if it's 1, we can just prepend a
//0, but if it is anything else non-2,
//it must return false, as we don't
//know what format it is in.
if (strlen($hexR) != 2) {
if (strlen($hexR) == 1) {
//probably in format #0f4, etc.
$hexR = "0" . $hexR;
} else {
//unknown format
return false;
}
}
if (strlen($hexG) != 2) {
if (strlen($hexG) == 1) {
$hexG = "0" . $hexG;
} else {
return false;
}
}
if (strlen($hexB) != 2) {
if (strlen($hexB) == 1) {
$hexB = "0" . $hexB;
} else {
return false;
}
}
//if prependPound is set, will prepend a
//# sign to the beginning of the hex code.
//(default = true)
$hex = "";
if ($prependPound) {
$hex = "#";
}
$hex = $hex . $hexR . $hexG . $hexB;
return $hex;
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/.
* Assumes h, s, and l are in the format Degrees,
* Percent, Percent, and returns r, g, and b in
* the range [0 - 255].
*
* Called by hslToHex by default.
*
* Calls:
* degPercPercToHsl
* hueToRgb
*
* #param Number h The hue value
* #param Number s The saturation level
* #param Number l The luminence
* #return Array The RGB representation
*/
function hslToRgb($h, $s, $l)
{
//convert the hue's 360 degrees in a circle to 1
$h /= 360;
//convert the saturation and lightness to the 0-1
//range by multiplying by 100
$s /= 100;
$l /= 100;
//If there's no saturation, the color is a greyscale,
//so all three RGB values can be set to the lightness.
//(Hue doesn't matter, because it's grey, not color)
if ($s == 0) {
$r = $l * 255;
$g = $l * 255;
$b = $l * 255;
} else {
//calculate some temperary variables to make the
//calculation eaisier.
if ($l < 0.5) {
$temp2 = $l * (1 + $s);
} else {
$temp2 = ($l + $s) - ($s * $l);
}
$temp1 = 2 * $l - $temp2;
//run the calculated vars through hueToRgb to
//calculate the RGB value. Note that for the Red
//value, we add a third (120 degrees), to adjust
//the hue to the correct section of the circle for
//red. Simalarly, for blue, we subtract 1/3.
$r = 255 * $this->hueToRgb($temp1, $temp2, $h + (1 / 3));
$g = 255 * $this->hueToRgb($temp1, $temp2, $h);
$b = 255 * $this->hueToRgb($temp1, $temp2, $h - (1 / 3));
}
$rgb['r'] = $r;
$rgb['g'] = $g;
$rgb['b'] = $b;
return $rgb;
// return "rgb($r, $g, $b)";
}
/**
* Converts an HSL hue to it's RGB value.
*
* Input: $temp1 and $temp2 - temperary vars based on
* whether the lumanence is less than 0.5, and
* calculated using the saturation and luminence
* values.
* $hue - the hue (to be converted to an RGB
* value) For red, add 1/3 to the hue, green
* leave it alone, and blue you subtract 1/3
* from the hue.
*
* Output: One RGB value.
*
* Thanks to Easy RGB for this function (Hue_2_RGB).
* http://www.easyrgb.com/index.php?X=MATH&$h=19#text19
*
*/
function hueToRgb($temp1, $temp2, $hue)
{
if ($hue < 0) {
$hue += 1;
}
if ($hue > 1) {
$hue -= 1;
}
if ((6 * $hue) < 1) {
return ($temp1 + ($temp2 - $temp1) * 6 * $hue);
} elseif ((2 * $hue) < 1) {
return $temp2;
} elseif ((3 * $hue) < 2) {
return ($temp1 + ($temp2 - $temp1) * ((2 / 3) - $hue) * 6);
}
return $temp1;
}
If you have decimal RGB values (enhzflep showed how to get them), you can easily get the #ab01cd hex web string:
$rgb['r'] = ($t = round($rgb['r'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$rgb['g'] = ($t = round($rgb['g'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$rgb['b'] = ($t = round($rgb['b'] * 255, 0)) < 15 ? '0'.dechex($t) : dechex($t);
$hexweb = "#".$rgb['r'].$rgb['g'].$rgb['b'];

Face detection in PHP

Does anybody know of a good way to do face detection in PHP? I came across some code here that claims to do this, but I can't seem to get it to work properly. I'd like to make this work (even though it will be slow) and any help you can give me would be really appreciated.
Here's the code from the link:
<?php
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
// #Author Karthik Tharavaad
// karthik_tharavaad#yahoo.com
// #Contributor Maurice Svay
// maurice#svay.Com
class Face_Detector {
protected $detection_data;
protected $canvas;
protected $face;
private $reduced_canvas;
public function __construct($detection_file = 'detection.dat') {
if (is_file($detection_file)) {
$this->detection_data = unserialize(file_get_contents($detection_file));
} else {
throw new Exception("Couldn't load detection data");
}
//$this->detection_data = json_decode(file_get_contents('data.js'));
}
public function face_detect($file) {
if (!is_file($file)) {
throw new Exception("Can not load $file");
}
$this->canvas = imagecreatefromjpeg($file);
$im_width = imagesx($this->canvas);
$im_height = imagesy($this->canvas);
//Resample before detection?
$ratio = 0;
$diff_width = 320 - $im_width;
$diff_height = 240 - $im_height;
if ($diff_width > $diff_height) {
$ratio = $im_width / 320;
} else {
$ratio = $im_height / 240;
}
if ($ratio != 0) {
$this->reduced_canvas = imagecreatetruecolor($im_width / $ratio, $im_height / $ratio);
imagecopyresampled($this->reduced_canvas, $this->canvas, 0, 0, 0, 0, $im_width / $ratio, $im_height / $ratio, $im_width, $im_height);
$stats = $this->get_img_stats($this->reduced_canvas);
$this->face = $this->do_detect_greedy_big_to_small($stats['ii'], $stats['ii2'], $stats['width'], $stats['height']);
$this->face['x'] *= $ratio;
$this->face['y'] *= $ratio;
$this->face['w'] *= $ratio;
} else {
$stats = $this->get_img_stats($this->canvas);
$this->face = $this->do_detect_greedy_big_to_small($stats['ii'], $stats['ii2'], $stats['width'], $stats['height']);
}
return ($this->face['w'] > 0);
}
public function toJpeg() {
$color = imagecolorallocate($this->canvas, 255, 0, 0); //red
imagerectangle($this->canvas, $this->face['x'], $this->face['y'], $this->face['x']+$this->face['w'], $this->face['y']+ $this->face['w'], $color);
header('Content-type: image/jpeg');
imagejpeg($this->canvas);
}
public function toJson() {
return "{'x':" . $this->face['x'] . ", 'y':" . $this->face['y'] . ", 'w':" . $this->face['w'] . "}";
}
public function getFace() {
return $this->face;
}
protected function get_img_stats($canvas){
$image_width = imagesx($canvas);
$image_height = imagesy($canvas);
$iis = $this->compute_ii($canvas, $image_width, $image_height);
return array(
'width' => $image_width,
'height' => $image_height,
'ii' => $iis['ii'],
'ii2' => $iis['ii2']
);
}
protected function compute_ii($canvas, $image_width, $image_height ){
$ii_w = $image_width+1;
$ii_h = $image_height+1;
$ii = array();
$ii2 = array();
for($i=0; $i<$ii_w; $i++ ){
$ii[$i] = 0;
$ii2[$i] = 0;
}
for($i=1; $i<$ii_w; $i++ ){
$ii[$i*$ii_w] = 0;
$ii2[$i*$ii_w] = 0;
$rowsum = 0;
$rowsum2 = 0;
for($j=1; $j<$ii_h; $j++ ){
$rgb = ImageColorAt($canvas, $j, $i);
$red = ($rgb >> 16) & 0xFF;
$green = ($rgb >> 8) & 0xFF;
$blue = $rgb & 0xFF;
$grey = ( 0.2989*$red + 0.587*$green + 0.114*$blue )>>0; // this is what matlab uses
$rowsum += $grey;
$rowsum2 += $grey*$grey;
$ii_above = ($i-1)*$ii_w + $j;
$ii_this = $i*$ii_w + $j;
$ii[$ii_this] = $ii[$ii_above] + $rowsum;
$ii2[$ii_this] = $ii2[$ii_above] + $rowsum2;
}
}
return array('ii'=>$ii, 'ii2' => $ii2);
}
protected function do_detect_greedy_big_to_small( $ii, $ii2, $width, $height ){
$s_w = $width/20.0;
$s_h = $height/20.0;
$start_scale = $s_h < $s_w ? $s_h : $s_w;
$scale_update = 1 / 1.2;
for($scale = $start_scale; $scale > 1; $scale *= $scale_update ){
$w = (20*$scale) >> 0;
$endx = $width - $w - 1;
$endy = $height - $w - 1;
$step = max( $scale, 2 ) >> 0;
$inv_area = 1 / ($w*$w);
for($y = 0; $y < $endy ; $y += $step ){
for($x = 0; $x < $endx ; $x += $step ){
$passed = $this->detect_on_sub_image( $x, $y, $scale, $ii, $ii2, $w, $width+1, $inv_area);
if( $passed ) {
return array('x'=>$x, 'y'=>$y, 'w'=>$w);
}
} // end x
} // end y
} // end scale
return null;
}
protected function detect_on_sub_image( $x, $y, $scale, $ii, $ii2, $w, $iiw, $inv_area){
$mean = ( $ii[($y+$w)*$iiw + $x + $w] + $ii[$y*$iiw+$x] - $ii[($y+$w)*$iiw+$x] - $ii[$y*$iiw+$x+$w] )*$inv_area;
$vnorm = ( $ii2[($y+$w)*$iiw + $x + $w] + $ii2[$y*$iiw+$x] - $ii2[($y+$w)*$iiw+$x] - $ii2[$y*$iiw+$x+$w] )*$inv_area - ($mean*$mean);
$vnorm = $vnorm > 1 ? sqrt($vnorm) : 1;
$passed = true;
for($i_stage = 0; $i_stage < count($this->detection_data); $i_stage++ ){
$stage = $this->detection_data[$i_stage];
$trees = $stage[0];
$stage_thresh = $stage[1];
$stage_sum = 0;
for($i_tree = 0; $i_tree < count($trees); $i_tree++ ){
$tree = $trees[$i_tree];
$current_node = $tree[0];
$tree_sum = 0;
while( $current_node != null ){
$vals = $current_node[0];
$node_thresh = $vals[0];
$leftval = $vals[1];
$rightval = $vals[2];
$leftidx = $vals[3];
$rightidx = $vals[4];
$rects = $current_node[1];
$rect_sum = 0;
for( $i_rect = 0; $i_rect < count($rects); $i_rect++ ){
$s = $scale;
$rect = $rects[$i_rect];
$rx = ($rect[0]*$s+$x)>>0;
$ry = ($rect[1]*$s+$y)>>0;
$rw = ($rect[2]*$s)>>0;
$rh = ($rect[3]*$s)>>0;
$wt = $rect[4];
$r_sum = ( $ii[($ry+$rh)*$iiw + $rx + $rw] + $ii[$ry*$iiw+$rx] - $ii[($ry+$rh)*$iiw+$rx] - $ii[$ry*$iiw+$rx+$rw] )*$wt;
$rect_sum += $r_sum;
}
$rect_sum *= $inv_area;
$current_node = null;
if( $rect_sum >= $node_thresh*$vnorm ){
if( $rightidx == -1 )
$tree_sum = $rightval;
else
$current_node = $tree[$rightidx];
} else {
if( $leftidx == -1 )
$tree_sum = $leftval;
else
$current_node = $tree[$leftidx];
}
}
$stage_sum += $tree_sum;
}
if( $stage_sum < $stage_thresh ){
return false;
}
}
return true;
}
}
Usage:
$detector = new Face_Detector('detection.dat');
$detector->face_detect('maurice_svay_150.jpg');
$detector->toJpeg();
The problem I am running into, seems to be coming up in the comments on that page as well. "imagecolorat() [function.imagecolorat]: 320,1 is out of bounds." So, I added a error_reporting(0) to the top of the file (not really the solution), and it seems to work sometimes while other times it just doesn't do anything.
Any thoughts?
It would probably be easier/safer to do this with OpenCV, which is written in lower-level code. PHP is interpreted, so it's likely going to be hella slow when doing the job.
Hope this helps!
You need to turn off error reporting
<?php
ini_set( 'display_errors', 1 );
error_reporting( E_ALL ^ E_NOTICE );
require_once('face_detector.php');
$detector = new Face_Detector('detection.dat');
$detector->face_detect('img/8.jpg');
$detector->toJpeg();
?>
The project has been upgraded on github repository by following this link Face detection
The problem was on the loop, this code works fine :
for ($i=1; $i<$ii_h-1; $i++) {
$ii[$i*$ii_w] = 0;
$ii2[$i*$ii_w] = 0;
$rowsum = 0;
$rowsum2 = 0;
for ($j=1; $j<$ii_w-1; $j++) {
$rgb = ImageColorAt($canvas, $j, $i);
$red = ($rgb >> 16) & 0xFF;
$green = ($rgb >> 8) & 0xFF;
$blue = $rgb & 0xFF;
$grey = (0.2989*$red + 0.587*$green + 0.114*$blue)>>0; // this is what matlab uses
$rowsum += $grey;
$rowsum2 += $grey*$grey;
$ii_above = ($i-1)*$ii_w + $j;
$ii_this = $i*$ii_w + $j;
$ii[$ii_this] = $ii[$ii_above] + $rowsum;
$ii2[$ii_this] = $ii2[$ii_above] + $rowsum2;
}
}
Good luck
Try removing the +1 from these lines:
$ii_w = $image_width+1;
$ii_h = $image_height+1;
This code is trying to check the colors from positions 1 to 320 instead of 0 to 319 in the 320 pixel image.
This is an old topic but still this fix is better than any I saw so far so, it might help someone
// Turn off error reporting...
$ctl = error_reporting();
error_reporting(0);
$detector = new Face_Detector('detection.dat');
$detector->face_detect('img/8.jpg');
$detector->toJpeg();
// Turn on reporting...if you wish
error_reporting($ctl);
Quick fix: in the compute_ii function
Replace:
$rgb = ImageColorAt($canvas, $j, $i);
With:
$rgb = ImageColorAt($canvas, $j-1, $i-1);

Categories