Convert 32-bit TGA to PNG - php

I am working from this code and taking into account the comments to "fix" it for 32-bit, but it seems to still not work. I am pretty sure it has something to do with the TGA descriptor. This will have bits 0-3 as the alpha channel depth which will always be 8 for 32 bit, and the code doesn't account for that.
I tried to understand how to piece it together using this C code as a guide, but no luck.
It seems the once you take into account the pixel being of length of 4 (as per the patch in comments) his dwordize only accounts for 3 of the 4 bytes, the 4th byte being the alpha bits I think.
I tried changing the function from
function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
return $c*256*256 + $b*256 + $a;
}
to
function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
$d = ord($str[3]);
return $d*256*256*256 + $c*256*256 + $b*256 + $a;
}
which didn't work, and then tried
function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
$d = ord($str[3]);
return $c*256*256 + $b*256 + $a + $d*256*256*256;
}
All of which I am trying to go off the C code which goes from like RBGA to BGRA and then the indices are all weird. I really don't understand the C code enough to apply it to the PHP code.
I also found this site which may help, I am reading it over now and if I come up with anything I will update.

I have found your solution. There were missing adjustments that needed to be made in the RLE decode function to support the 32 bits pixel format: i.e: AARRGGBB
So first of all we need to bypass the color depth check of your library as suggested in the comments, so we change the check to this on line 99
if ($header['pixel_size'] != 24 && $header['pixel_size'] != 32) {
die('Unsupported TGA color depth');
}
Then we need to change some variables that are color depth dependent. Most of them will be used for data formatting so we need them to have the correct value regardless of the color depth.
So that would be:
line 104 : $bytes = $header['pixel_size'] / 8;
line 117 : $size = $header['width'] * $header['height'] * $bytes;
line 154 : $pixels = str_split($data, $bytes);
and remove the line 153: $num_bytes = $header['pixel_size']/8; We do not need it anymore.
Then we will need the rle_decode() function to know the pixel format size, so int the for loop when we call it we need to pass this parameter, so we need to change as well the following line:
line 141: $data = rle_decode($data, $size, $bytes);
Therefore the function prototypes changes into:
line 9: function rle_decode($data, $datalen, $pixel_size)
Now you can see on this function there are multiple 3 magic numbers (i.e: for ($j = 0; $j<3*$value; $j++) on line 29).
All of those must be replaced by the pixel size parameter. (3 for 24 bits and 4 for 32 bits)
Moreover sometimes it will write only 3 bytes instead of 4 when creating the pixels so we must adjust that as well.
So finally this gives us the following algorithm:
function rle_decode($data, $datalen, $pixel_size)
{
$len = strlen($data);
$out = '';
$i = 0;
$k = 0;
while ($i<$len)
{
dec_bits(ord($data[$i]), $type, $value);
if ($k >= $datalen)
{
break;
}
$i++;
if ($type == 0) //raw
{
for ($j=0; $j<$pixel_size*$value; $j++)
{
$out .= $data[$j+$i];
$k++;
}
$i += $value*$pixel_size;
}
else //rle
{
for ($j=0; $j<$value; $j++)
{
$out .= $data[$i] . $data[$i+1] . $data[$i+2];
if ($pixel_size == 4) $out .= $data[$i+3];
$k++;
}
$i += $pixel_size;
}
}
return $out;
}
That's it, you will now get your TGA images perfectly converted. I'm giving you the full tga.php code so you can directly paste it and test it by yourself:
<?php
// Author: de77
// Licence: MIT
// First-version: 9.02.2010
// Version: 24.08.2010
// http://de77.com
function rle_decode($data, $datalen, $pixel_size)
{
$len = strlen($data);
$out = '';
$i = 0;
$k = 0;
while ($i<$len)
{
dec_bits(ord($data[$i]), $type, $value);
if ($k >= $datalen)
{
break;
}
$i++;
if ($type == 0) //raw
{
for ($j=0; $j<$pixel_size*$value; $j++)
{
$out .= $data[$j+$i];
$k++;
}
$i += $value*$pixel_size;
}
else //rle
{
for ($j=0; $j<$value; $j++)
{
$out .= $data[$i] . $data[$i+1] . $data[$i+2];
if ($pixel_size == 4) $out .= $data[$i+3];
$k++;
}
$i += $pixel_size;
}
}
return $out;
}
function dec_bits($byte, &$type, &$value)
{
$type = ($byte & 0x80) >> 7;
$value = 1 + ($byte & 0x7F);
}
function getimagesizetga($filename)
{
$f = fopen($filename, 'rb');
$header = fread($f, 18);
$header = #unpack( "cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
"ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
"cpixel_size/cdescriptor", $header);
fclose($f);
$types = array(0,1,2,3,9,10,11,32,33);
if (!in_array($header['image_type'], $types))
{
return array(0, 0, 0, 0, 0);
}
if ($header['pixel_size'] > 32)
{
return array(0, 0, 0, 0, 0);
}
return array($header['width'], $header['height'], 'tga', $header['pixel_size'], $header['image_type']);
}
function imagecreatefromtga($filename)
{
$f = fopen($filename, 'rb');
if (!$f)
{
return false;
}
$header = fread($f, 18);
$header = unpack( "cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
"ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
"cpixel_size/cdescriptor", $header);
switch ($header['image_type'])
{
case 2: echo "Image is not compressed\n";//no palette, uncompressed
case 10: //no palette, rle
break;
default: die('Unsupported TGA format');
}
if ($header['pixel_size'] != 24 && $header['pixel_size'] != 32)
{
die('Unsupported TGA color depth');
}
$bytes = $header['pixel_size'] / 8;
if ($header['image_id_len'] > 0)
{
$header['image_id'] = fread($f, $header['image_id_len']);
}
else
{
$header['image_id'] = '';
}
$im = imagecreatetruecolor($header['width'], $header['height']);
$size = $header['width'] * $header['height'] * $bytes;
//-- check whether this is NEW TGA or not
$pos = ftell($f);
fseek($f, -26, SEEK_END);
$newtga = fread($f, 26);
if (substr($newtga, 8, 16) != 'TRUEVISION-XFILE')
{
$newtga = false;
}
fseek($f, 0, SEEK_END);
$datasize = ftell($f) - $pos;
if ($newtga)
{
$datasize -= 26;
}
fseek($f, $pos, SEEK_SET);
//-- end of check
$data = fread($f, $datasize);
if ($header['image_type'] == 10)
{
$data = rle_decode($data, $size, $bytes);
}
if (bit5($header['descriptor']) == 1)
{
$reverse = true;
}
else
{
$reverse = false;
}
$i = 0;
$pixels = str_split($data, $bytes);
//read pixels
if ($reverse)
{
for ($y=0; $y<$header['height']; $y++)
{
for ($x=0; $x<$header['width']; $x++)
{
imagesetpixel($im, $x, $y, dwordize($pixels[$i]));
$i++;
}
}
}
else
{
for ($y=$header['height']-1; $y>=0; $y--)
{
for ($x=0; $x<$header['width']; $x++)
{
imagesetpixel($im, $x, $y, dwordize($pixels[$i]));
$i++;
}
}
}
fclose($f);
return $im;
}
function dwordize($str)
{
$a = ord($str[0]);
$b = ord($str[1]);
$c = ord($str[2]);
return $c*256*256 + $b*256 + $a;
}
function bit5($x)
{
return ($x & 32) >> 5;
}
And here is the output PNG image directly created from the script:
And if you would prefer a direct download link rather than pasting the fixed code I give you here the full php file: https://www.sendspace.com/file/92uir9
At the end of the day, just change the tga.php file and everything will automagically work.

Based on the de77.com code, but with some additions:
<?php
// References:
// https://stackoverflow.com/questions/24709142/convert-32-bit-tga-to-png
// https://github.com/nothings/stb/blob/master/stb_image.h
// http://tfc.duke.free.fr/coding/tga_specs.pdf
// http://www.paulbourke.net/dataformats/tga/
//
// This class development started with the following code:
// http://de77.com/tga-imagetga-imagecreatefromtga
// Author: de77
// Licence: MIT
// First-version: 9.02.2010
// Version: 24.08.2010
// http://de77.com
//
// C.. oct 2022
// I put the code in a class,
// I added the color-mapped formats,
// I added the 15b & 16b pixel-size formats.
//
// The following is supported:
//
// Pixel-depths: 8b, 15b, 16b, 24b, 32b
//
// Image Type Description
// 0 No Image Data Included
// 1 Uncompressed, Color-mapped Image
// 2 Uncompressed, True-color Image
// 3 Uncompressed, Grayscale Image
// 9 Run-length encoded, Color-mapped Image
// 10 Run-length encoded, True-color Image
// 11 Run-length encoded, Grayscale Image
//
// NOTE: i commented out 16b-alpha code using: //!
// It does not work, and images come out all transparent.
class uje_tga_class {
private function rle_decode(&$data, $bytes) {
$out = '';
$i = 0;
$len = strlen($data);
while ($i<$len) {
$b = ord($data[$i]);
$type = ($b & 0x80);
$count = 1 + ($b & 0x7F);
$i++;
// raw or RLE
if ($type == 0) { //raw
$size = $bytes*$count;
$out .= substr($data, $i, $size);
$i += $size;
} else { //rle
$s = substr($data, $i, $bytes);
$out .= str_repeat($s, $count);
$i += $bytes;
}
}
return $out;
}
private function unmapcolors(&$data, $npixels, $colormapentrybytes, &$palette, $pixelbytes) {
$out = '';
for ($i=0, $i2=0; $i<$npixels; $i++, $i2+=$colormapentrybytes) {
$idx = ord($data[$i2]); // $colormapentrybytes == 1 or 2
if ($colormapentrybytes == 2) $idx += (ord($data[$i2+1]) << 8);
$idx *= $pixelbytes;
$out .= substr($palette, $idx, $pixelbytes);
}
return $out;
}
public function getimagesizetga($filename) {
$f = fopen($filename, 'rb');
$header = fread($f, 18);
$header = #unpack("cimage_id_len/ccolor_map_type/cimage_type/vcolor_map_origin/vcolor_map_len/" .
"ccolor_map_entry_size/vx_origin/vy_origin/vwidth/vheight/" .
"cpixel_size/cdescriptor", $header);
fclose($f);
$types = array(0,1,2,3,9,10,11,32,33);
if (!in_array($header['image_type'], $types)) return array(0, 0, 0, 0, 0);
if ($header['pixel_size'] > 32) return array(0, 0, 0, 0, 0);
return array($header['width'], $header['height'], 'tga', $header['pixel_size'], $header['image_type']);
}
public function imagecreatefromtga($filename) {
// open the TGA file for binary reading
$f = fopen($filename, 'rb');
if (!$f) return false;
// read the file
try {
// read the TGA header
$header = fread($f, 18);
$header = unpack("cimage_id_len/ccolor_map_type/cimage_type/" .
"vcolor_map_origin/vcolor_map_len/ccolor_map_entry_size/" .
"vx_origin/vy_origin/vwidth/vheight/" .
"cpixel_size/cdescriptor", $header);
// check for supported tga formats
switch ($header['image_type']) {
case 1: // color-mapped uncompressed
case 2: // truecolor uncompressed
case 3: // grayscale uncompressed
case 9: // color-mapped run-length encoded
case 10: // truecolor run-length encoded
case 11: // grayscale run-length encoded
break;
default:
throw new RuntimeException('Unsupported format: '. $header['image_type']);
}
$iscolormapped = ($header['image_type'] == 9 || $header['image_type'] == 1);
$isrle = ($header['image_type'] == 9 || $header['image_type'] == 10 || $header['image_type'] == 11);
// check for supported pixel sizes. ($header['pixel_size'])
// But if this is a colormapped image, that "pixel_size" is in fact the width of each entry in the colormap.
// For a colormapped image, the pixelsize is stored in $header['color_map_entry_size'].
if ($iscolormapped) {
if ($header['color_map_type'] == 0) {
throw new RuntimeException('Missing colormap');
}
$pixelbits = $header['color_map_entry_size'];
// the entries in the colormap can be 8 or 16 bit wide
$colormapentrybytes = $header['pixel_size']; // this is really the number of bits..
if (($colormapentrybytes != 8) && ($colormapentrybytes != 16)) {
throw new RuntimeException('Unsupported colormap entry bits: '. $colormapentrybytes);
}
$colormapentrybytes /= 8; // ..now it's bytes
} else {
$pixelbits = $header['pixel_size'];
}
switch ($pixelbits) {
case 8: // grayscale
$pixelbytes = 1;
break;
case 15: // truecolor 5b blue + 5b green + 5b red + 1b dummy
case 16: // truecolor 5b blue + 5b green + 5b red + 1b alpha
$pixelbytes = 2;
break;
case 24: // truecolor
$pixelbytes = 3;
break;
case 32: // truecolor
$pixelbytes = 4;
break;
default:
throw new RuntimeException('Unsupported pixel bits: '. $pixelbits);
}
// skip the image_id
$header['image_id'] = ($header['image_id_len'] > 0)? fread($f, $header['image_id_len']) : '';
// check whether this is a TGA v2.0
$tgav2 = true;
$pos = ftell($f); // store the current filepointer
fseek($f, -26, SEEK_END); // (possibly) read the file footer
$footer = fread($f, 26);
if (substr($footer, 8, 16) != 'TRUEVISION-XFILE') $tgav2 = false;
fseek($f, 0, SEEK_END);
$datasize = ftell($f) - $pos;
if ($tgav2) $datasize -= 26; // pixeldata does not include any footer
fseek($f, $pos, SEEK_SET); // restore filepointer
// if color-mapped then read the palette.
// The palette starts at the file-location where the image-data starts for the other formats.
// So first read the palette, and then correct the final datasize.
if ($iscolormapped) {
$palettesize = $header['color_map_len'] * $pixelbytes;
$pos = ftell($f) + $header['color_map_origin'];
fseek($f, $pos, SEEK_SET); // set filepointer to palette
$palette = fread($f, $palettesize);
$datasize -= $palettesize;
}
// Read the image data.
// If this is a colormapped image then this is not the pixeldata, but the indexes into the colormap.
$data = fread($f, $datasize);
} catch (Exception $e) {
//echo 'Exception: ', $e->getMessage(), "\n";
return false;
} finally {
fclose($f);
}
// get the pixeldata
if ($iscolormapped) {
$npixels = $header['width'] * $header['height'];
// colormapped images have the color-indexes encoded (pixeldata must be looked-up after RL decoding)
if ($isrle) $data = $this->rle_decode($data, $colormapentrybytes);
$pixeldata = $this->unmapcolors($data, $npixels, $colormapentrybytes, $palette, $pixelbytes);
} else
if ($isrle) { // possibly Run Length decode
$pixeldata = $this->rle_decode($data, $pixelbytes);
} else // uncompressed
$pixeldata = $data;
// create the image
$im = imagecreatetruecolor($header['width'], $header['height']);
// if the image has alpha data, then prepare for it
imagealphablending($im, false); // no blending. Just copy the pixel
//! if (!$iscolormapped && ($header['pixel_size'] == 32 || $header['pixel_size'] == 16)) {
if (!$iscolormapped && ($header['pixel_size'] == 32)) {
imagesavealpha($im, true); // be sure to save the alpha data
} else {
imagesavealpha($im, false);
}
// read pixel-ordering
$toptobottom = (($header['descriptor'] & 32) != 0);
$righttoleft = (($header['descriptor'] & 16) != 0);
// process the pixels
$i = 0;
for ($y=0; $y<$header['height']; $y++)
for ($x=0; $x<$header['width']; $x++) {
switch($pixelbytes) {
case 1:
$r = $g = $b = ord($pixeldata[$i]);
$col = imagecolorallocate($im, $r,$g,$b);
break;
case 2:
$r = (ord($pixeldata[$i+1]) & 0x7C) >> 2;
$g = ((ord($pixeldata[$i]) & 0xE0) >> 5) + ((ord($pixeldata[$i+1]) & 0x03) << 3);
$b = ord($pixeldata[$i]) & 0x1F;
$r <<= 3;
$g <<= 3;
$b <<= 3;
//! if ($header['pixel_size'] == 16) {
//! $a = ((ord($pixeldata[$i+1]) & 0x80) == 0)? 127 : 0; // 1b alpha means? transparent : opaque
//! $col = imagecolorallocatealpha($im, $r,$g,$b,$a);
//! } else {
$col = imagecolorallocate($im, $r,$g,$b);
//! }
break;
case 3:
$r = ord($pixeldata[$i+2]);
$g = ord($pixeldata[$i+1]);
$b = ord($pixeldata[$i]);
$col = imagecolorallocate($im, $r,$g,$b);
break;
case 4:
$r = ord($pixeldata[$i+2]);
$g = ord($pixeldata[$i+1]);
$b = ord($pixeldata[$i]);
$a = 127 - (ord($pixeldata[$i+3]) >> 1); // 128 alpha values with png transulency (where 127 = transparent, 0 = opaque)
$col = imagecolorallocatealpha($im, $r,$g,$b,$a);
break;
}
// set the pixel in the image
$xp = ($righttoleft)? $header['width'] - 1 - $x : $x;
$yp = ($toptobottom)? $y : $header['height'] - 1 - $y;
imagesetpixel($im, $xp, $yp, $col);
// next pixel in the pixeldata
$i += $pixelbytes;
}
return $im;
}
public function imagetga($im, $filename) {
list($width, $height) = array(imagesx($im), imagesy($im));
$tga = "\0\0\2\0\0\0\0\0\0\0\0\0" . pack('vv', $width, $height) . ' ';
for ($y=0; $y<$height; $y++)
for ($x=0; $x<$width; $x++) {
$rgb = imagecolorat($im, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$a = ($rgb >> 24) & 0x7F;
$tga .= chr($b).chr($g).chr($r).chr((127-$a)*2);
}
file_put_contents($filename, $tga);
}
public function tga2png($tga_filename, $png_filename) {
$im = $this->imagecreatefromtga($tga_filename);
if (!$im) return false;
//header('Content-Disposition: Attachment;filename=image.png');
//header("Content-type: image/png");
imagepng($im, $png_filename);
imagedestroy($im);
return true;
}
}
?>

Related

Thumbnails with Face Detection in PHP

My objective with this script is to make smart thumbnails. In my demo package, I am using two scripts from different sources.
To crop thumbnails (Source) - It totally works like native wordpress thumbnails
Face Detection in PHP (Source)
I am using the Face Detection to get the desired coordinates (where the face is) and then feed the coordinates to the crop script to make a thumbnail.
The problem is, if the Face Detection script does not find a face, it'd just time-out with the time out error
Fatal error: Maximum execution time of 30 seconds exceeded in...
I do not know how to come around this issue.
Is there any way to limit the amount of time the face detector takes to detect? I mean, if not found in like 15 seconds, return null.
Here's the Face Detection code:
<?php
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// 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
namespace svay;
use Exception;
class FaceDetector
{
protected $detection_data;
protected $canvas;
protected $face;
private $reduced_canvas;
/**
* Creates a face-detector with the given configuration
*
* Configuration can be either passed as an array or as
* a filepath to a serialized array file-dump
*
* #param string|array $detection_data
*/
public function __construct($detection_data = 'detection.dat')
{
if (is_array($detection_data)) {
$this->detection_data = $detection_data;
return;
}
if (!is_file($detection_data)) {
// fallback to same file in this class's directory
$detection_data = dirname(__FILE__) . DIRECTORY_SEPARATOR . $detection_data;
if (!is_file($detection_data)) {
throw new \Exception("Couldn't load detection data");
}
}
$this->detection_data = unserialize(file_get_contents($detection_data));
}
public function faceDetect($file)
{
if (is_resource($file)) {
$this->canvas = $file;
} elseif (is_file($file)) {
//getting extension type (jpg, png, etc)
$type = explode(".", $file);
$ext = strtolower($type[sizeof($type)-1]);
$ext = (!in_array($ext, array("jpeg","png","gif"))) ? "jpeg" : $ext;
if ($ext == 'jpeg') {
$this->canvas = imagecreatefromjpeg($file);
} else if ($ext == 'png') {
$this->canvas = imagecreatefrompng($file);
} else if ($ext == 'gif') {
$this->canvas = imagecreatefromgif($file);
}
} else {
throw new Exception("Can not load $file");
}
$im_width = imagesx($this->canvas);
$im_height = imagesy($this->canvas);
//Resample before detection?
$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->getImgStats($this->reduced_canvas);
$this->face = $this->doDetectGreedyBigToSmall(
$stats['ii'],
$stats['ii2'],
$stats['width'],
$stats['height']
);
if ($this->face['w'] > 0) {
$this->face['x'] *= $ratio;
$this->face['y'] *= $ratio;
$this->face['w'] *= $ratio;
}
} else {
$stats = $this->getImgStats($this->canvas);
$this->face = $this->doDetectGreedyBigToSmall(
$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 json_encode($this->face);
}
public function getFace()
{
return $this->face;
}
protected function getImgStats($canvas)
{
$image_width = imagesx($canvas);
$image_height = imagesy($canvas);
$iis = $this->computeII($canvas, $image_width, $image_height);
return array(
'width' => $image_width,
'height' => $image_height,
'ii' => $iis['ii'],
'ii2' => $iis['ii2']
);
}
protected function computeII($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_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;
}
}
return array('ii'=>$ii, 'ii2' => $ii2);
}
protected function doDetectGreedyBigToSmall($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->detectOnSubImage($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 detectOnSubImage($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;
$count_data = count($this->detection_data);
for ($i_stage = 0; $i_stage < $count_data; $i_stage++) {
$stage = $this->detection_data[$i_stage];
$trees = $stage[0];
$stage_thresh = $stage[1];
$stage_sum = 0;
$count_trees = count($trees);
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;
$count_rects = count($rects);
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;
}
}
Here's an example use:
include "facedetection/FaceDetector.php";
$detector = new svay\FaceDetector('detection.dat');
$detector->faceDetect($path);
$coord = $detector->getFace();
Any help or suggest other php thumbnails with face detection script.
Can you increase the time limit? You can use set_time_limit() or change your php.ini if you have access to it.
Also, how big is the detection.dat file? Loading the entire file in memory with file_get_contents() can take some time if the file is large.
Unless you mess up with the FaceDetection code (which is not recommended, unless you will never want to upgrade the library), you won't be able to stop the execution after 15 seconds. They don't provide any hooks where you could tell the script to stop.

imagecreatefrombmp BMP to JPG

Im using the the online code (https://github.com/hm2k/hm2k-php/blob/master/functions/imagecreatefrombmp.php) for converting "BMP" to "JPG"; I put in a separate file (in the includes library) as following:
class bmp2jpg {
/**
* #convert BMP to GD
* #param string $src
* #param string|bool $dest
* #return bool
*/
public static function bmp2gd($src, $dest = false) {
// open source file for reading
if (!($srch = fopen($src, 'rb'))) {
user_error('Unable to open source for reading.');
return false;
}
// open the destination file for writing
if (!($desth = fopen($dest, 'wb'))) {
user_error('Unable to open destination for writing.');
return false;
}
// get the headers
$header = unpack('vtype/Vsize/v2reserved/Voffset', fread($srch, 14));
// get the rest of the image
$info = unpack('Vsize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vncolor/Vimportant', fread($srch, 40));
// extract the header and info into varibles
extract($info);
extract($header);
// check for BMP signature
if ($type != 0x4D42) {
user_error('Source image is not a BMP.');
return false;
}
// set the pallete
$palette_size = $offset - 54;
$ncolor = $palette_size / 4;
// true-color vs. palette
$gd_header = '';
$gd_header .= ($palette_size == 0) ? "\xFF\xFE" : "\xFF\xFF";
$gd_header .= pack('n2', $width, $height);
$gd_header .= ($palette_size == 0) ? "\x01" : "\x00";
if ($palette_size) {
$gd_header .= pack('n', $ncolor);
}
// do not allow transparency
$gd_header .= "\xFF\xFF\xFF\xFF";
// write the destination headers
fwrite($desth, $gd_header);
unset($gd_header);
// if we have a palette
if ($palette_size) {
// read the palette
$palette = fread($srch, $palette_size);
// begin the gd palette
$gd_palette = '';
$j = 0;
// loop of the palette
while ( $j < $palette_size ) {
$b = $palette{$j++};
$g = $palette{$j++};
$r = $palette{$j++};
$a = $palette{$j++};
// assemble the gd palette
$gd_palette .= $r . $g . $b . $a;
}
// finish the palette
$gd_palette .= str_repeat("\x00\x00\x00\x00", 256 - $ncolor);
// write the gd palette
fwrite($desth, $gd_palette);
unset($gd_palette);
}
// scan line size and alignment
$scan_line_size = (($bits * $width) + 7) >> 3;
$scan_line_align = ($scan_line_size & 0x03) ? 4 - ($scan_line_size & 0x03) : 0;
// main loop
for($i = 0, $l = $height - 1; $i < $height; $i++, $l--) {
// create scan lines starting from bottom
fseek($srch, $offset + (($scan_line_size + $scan_line_align) * $l));
$scan_line = fread($srch, $scan_line_size);
$gd_scan_line = '';
if ($bits == 24) {
$j = 0;
while ( $j < $scan_line_size ) {
$b = $scan_line{$j++};
$g = $scan_line{$j++};
$r = $scan_line{$j++};
$gd_scan_line .= "\x00" . $r . $g . $b;
}
}
else if ($bits == 8) {
$gd_scan_line = $scan_line;
}
else if ($bits == 4) {
$j = 0;
while ( $j < $scan_line_size ) {
$byte = ord($scan_line{$j++});
$p1 = chr($byte >> 4);
$p2 = chr($byte & 0x0F);
$gd_scan_line .= $p1 . $p2;
}
$gd_scan_line = substr($gd_scan_line, 0, $width);
}
else if ($bits == 1) {
$j = 0;
while ( $j < $scan_line_size ) {
$byte = ord($scan_line{$j++});
$p1 = chr((int)(($byte & 0x80) != 0));
$p2 = chr((int)(($byte & 0x40) != 0));
$p3 = chr((int)(($byte & 0x20) != 0));
$p4 = chr((int)(($byte & 0x10) != 0));
$p5 = chr((int)(($byte & 0x08) != 0));
$p6 = chr((int)(($byte & 0x04) != 0));
$p7 = chr((int)(($byte & 0x02) != 0));
$p8 = chr((int)(($byte & 0x01) != 0));
$gd_scan_line .= $p1 . $p2 . $p3 . $p4 . $p5 . $p6 . $p7 . $p8;
}
// put the gd scan lines together
$gd_scan_line = substr($gd_scan_line, 0, $width);
}
// write the gd scan lines
fwrite($desth, $gd_scan_line);
unset($gd_scan_line);
}
// close the source file
fclose($srch);
// close the destination file
fclose($desth);
// return destination file
return $dest;
}
/**
* #ceate a BMP image
* #param string $filename
* #return bin string on success
* #return bool false on failure
*/
public static function imagecreatefrombmp($filename) {
// create a temp file
$tmpfile = tempnam(sys_get_temp_dir(), 'gd');
// convert to gd
if (bmp2gd($filename, $tmpfile)) {
// create image resource
$img = imagecreatefromgd($tmpfile);
}
// remove temp file
#unlink($tmpfile);
return isset($img) ? $img : false;
}
}
and Im calling the function this way:
require_once ("..\includes\imagecreatefrombmp.php");
$GD_Employee = bmp2jpg::bmp2gd("..\img\Rep.bmp");
$dest=bmp2jpg::imagecreatefrombmp($GD_Employee);
but it gives me the following errors:
Warning: require_once(..\includes\imagecreatefrombmp.php): failed to open stream
Fatal error: require_once(): Failed opening required '..\includes\imagecreatefrombmp.php'
Could you please let me know why? and am I supposed to pass the image name like "..\img\Rep.bmp"
Thanks
Because it can't find imagecreatefrombmp.php ... check if it is in right directory, it counts from the first run php file.

Using PHP to grab a simplified colour palette from an image?

So far I have found examples on how to grab all the RGB values of each pixel in an image but I want something that will break down an image and give me a simplified color palette.
Is there a way to use imagetruecolortopalette to somehow spit out the reduced palette colours, or to break an image into 25 x 25 blocks and then grab the average value of that block?
Maybe there is an alternative I'm missing? I basically just want to be able to find the most common colors within an image.
Thanks in advance.
Hmm, well I have had created something like this for a client. The screenshot is below
The complete code is as follows
$microTime = microtime(true);
function textColor($R1, $G1, $B1) {
$a = (($R1 * 299) + ($G1 * 587 ) + ($B1 * 114 )) / 1000;
if ($a < 128)
return 'white';
else
return 'black';
}
function rgb2html($r, $g = -1, $b = -1) {
$hex = "#";
$hex.= str_pad(dechex($r), 2, "0", STR_PAD_LEFT);
$hex.= str_pad(dechex($g), 2, "0", STR_PAD_LEFT);
$hex.= str_pad(dechex($b), 2, "0", STR_PAD_LEFT);
return $hex;
if (is_array($r) && sizeof($r) == 3)
list($r, $g, $b) = $r;
$r = intval($r);
$g = intval($g);
$b = intval($b);
$r = dechex($r < 0 ? 0 : ($r > 255 ? 255 : $r));
$g = dechex($g < 0 ? 0 : ($g > 255 ? 255 : $g));
$b = dechex($b < 0 ? 0 : ($b > 255 ? 255 : $b));
$color = (strlen($r) < 2 ? '0' : '') . $r;
$color .= (strlen($g) < 2 ? '0' : '') . $g;
$color .= (strlen($b) < 2 ? '0' : '') . $b;
return '#' . $color;
}
function colorPalette($imageFile, $colorJump, $granularity = 5) {
$granularity = max(1, abs((int) $granularity));
$colors = array();
$ratio = array();
$wastageCount = array();
$occurrenceSCount = array();
$occurrenceMCount = array();
$size = #getimagesize($imageFile);
if ($size === false) {
return false;
}
$img = #imagecreatefromstring(file_get_contents($imageFile));
if (!$img) {
user_error("Unable to open image file");
return false;
}
for ($y = 0; $y < $size[1]; $y += $granularity) {
$lastColor = NULL;
$lastX = -1;
for ($x = 0; $x < $size[0]; $x += $granularity) {
$thisColor = imagecolorat($img, $x, $y);
$rgb = imagecolorsforindex($img, $thisColor);
$red = round(round(($rgb['red'] / $colorJump)) * $colorJump);
$green = round(round(($rgb['green'] / $colorJump)) * $colorJump);
$blue = round(round(($rgb['blue'] / $colorJump)) * $colorJump);
$thisRGB = $red . ',' . $green . ',' . $blue;
if ($lastColor != $thisRGB) {
if (array_key_exists($thisRGB, $wastageCount)) {
$wastageCount[$thisRGB]++;
} else {
$wastageCount[$thisRGB] = 1;
}
if ($lastX + 1 == $x) {
if (array_key_exists($lastColor, $occurrenceSCount)) {
$occurrenceSCount[$lastColor]++;
} else {
$occurrenceSCount[$lastColor] = 1;
}
}
if ($lastX + 1 != $x) {
if (array_key_exists($lastColor, $occurrenceMCount)) {
$occurrenceMCount[$lastColor]++;
} else {
$occurrenceMCount[$lastColor] = 1;
}
}
$lastColor = $thisRGB;
$lastX = $x;
}
if (array_key_exists($thisRGB, $colors)) {
$colors[$thisRGB]++;
} else {
$colors[$thisRGB] = 1;
}
}
}
$totalPixels = array_sum($colors);
foreach ($colors as $k => $v) {
$ratio[$k] = round(($v / $totalPixels ) * 100, 2);
}
return array($ratio, $wastageCount, $colors, $occurrenceSCount, $occurrenceMCount);
}
usage
$colorJump = 1;
$pixelJump = 1;
$paletteR = colorPalette($dbImgFile_dir, $colorJump, $pixelJump);
$palette = $paletteR[0];
$wastage = $paletteR[1];
$colorsFound = $paletteR[2];
$occSArray = $paletteR[3];
$occMArray = $paletteR[4];
$totalPixels = array_sum($colorsFound);
$totalTime = abs(microtime(true) - $microTime);
the looping around is more complex, as I have to get the pallet from the DB and to match the colors with them, and also the template parser is used which is full custom code, and will not help you.
Just ignore the Required Weight column from this, the single occurrences and multiple occurrences are calculated, if there is a single pixel, for example RED, RED, BLUE, RED, RED it will be 1 single occurrence and 2 multiple occurrence

Get Image From PHP using Cocoa-Touch

I got this php code.. that creates a pie Chart
when you go to the site: http://localhost/social/Test.php?data=100*200*100
How do i get this image from the php file to my Xcode project?
When you enter the site the image will download
But when you enter the site on the iPhone the image will only show
<?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('003366', 'CCD6E0', '7F99B2','F7EFC6', 'C6BE8C', '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 = 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 && $data[$i] > 0)
{
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 && $data[$i] > 0)
{
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 ($data[$i] > 0)
{
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++)
{
if ($slice[$j][0] != $slice[$j][1]) 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++)
{
if ($slice[$j][0] != $slice[$j][1]) 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);
}
?>
please do a post request form php server to your app, convert the image to base64 encoding and pass this as a string, then save this file as .jpg or .png etc as required.
Thanks
djrecker
Retrieve the image data via dataWithContentsOfUrl and then either
Use imageWithData to get your UIImage; or
Write the NSData to a file with writeToFile and then you can later retrieve the image via imageWithContentsOfFile
Note, you'll have to put that PHP code on a web server accessible by the iPhone (as obviously localhost won't work). So, it might look like (I've replaced your localhost reference with host.com ... you'll have to replace that with the URL of the web site you put your PHP code on):
NSString *urlString = #"http://host.com/social/Test.php?data=100*200*100";
NSURL *url = [NSURL URLWithString:urlString];
NSError *err;
NSData *imageData = [NSData dataWithContentsOfURL:url options:0 error:&err];
NSAssert(imageData, #"Unable to retrieve image");
Now you can use that image:
self.imageView.image = [UIImage imageWithData:imageData];
Or, alternatively, now you can write it to a file on your iPhone, too, if you want:
NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *documentFullPath = [documentsFolder stringByAppendingPathComponent:#"pie.jpg"]; // I noticed from the php source that this is a jpg
BOOL success = [imageData writeToFile:documentFullPath options:0 error:&err];
NSAssert(success, #"Unable to write file");
Which you can retrieve later:
self.imageView.image = [UIImage imageWithContentsOfFile:documentFullPath];
Note, this is an inefficient way to draw a pie chart in iOS (to have php server generate jpg that you then download, requiring Internet connection and download at 3G speeds if you're not on wifi, etc.). I'd probably do something with Core Graphics or grab one of the various charting/graphing APIs out there. If you google "ios 3d pie chart" you'll get a bunch of hits. This one looks pretty cool, though I've never tried it.

How to check pixels near specified pixel in php: color

Here i try to check image, but it has some "ellipses" of another color, and i try to smooth them by my color. But my code searches only for specific color, how to say him that for example 5% of difference is bad to?
How to find near my pixel all the same pixel and color them in another color?
<?php
function LoadJpeg($imgname)
{
$count = 0;
/* Attempt to open */
$im = #imagecreatefrompng($imgname);
$imagedata = getimagesize($imgname);
for($i=0; $i<$imagedata[0]; $i++){
for($j=0; $j<$imagedata[1]; $j++){
$rgb[$i][$j] = imagecolorat($im, $i, $j);
//echo $rgb[$i][$j];
//echo "<br>";
}
}
for($i=0; $i<$imagedata[0]-5; $i++){
for($j=0; $j<$imagedata[1]-5; $j++){
if (($rgb[$i][$j] == $rgb[$i+3][$j]) || ($rgb[$i][$j] == $rgb[$i][$j+3]))
{
#echo "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa";
$count = $count + 1;
//echo "<br> <br>";
//echo $count;
//$red = imagecolorallocate($im, 255, 255, 255);
imagesetpixel($im, $i, $j, 13437229);
}
}
}
return $im;
}
header('Content-Type: image/jpeg');
$img = LoadJpeg('1.png');
// echo "Image width is: " . $imagedata[0];
// echo "Image height is: " . $imagedata[1];
imagejpeg($img,null, 100);
?>
Main trouble is with strong ==, but some difference of this color must be catched by my if case.
What about calculating distance between each color component? If one of the components differ by more than 5 percents, return false:
// Returns RGB components of the color represented by an integer
function components($color) {
return array(($color >> 16) & 0xFF, ($color >> 8) & 0xFF, $color & 0xFF);
}
// Performs "similarity test" of 2 colors
function isSimilar($color1, $color2) {
$c1 = components($color1);
$c2 = components($color2);
for ($i = 0; $i < 3; $i++) {
$k = ($c1[$i] > $c2[$i]) ? ($c1[$i] - $c2[$i]) / $c2[$i] : ($c2[$i] - $c1[$i]) / $c1[$i];
if ($k > 0.05) return false;
}
return true;
}
// ...
if (isSimilar($rgb[$i][$j], $rgb[$i][$j + 3]) or isSimilar($rgb[$i][$j], $rgb[$i + 3][$j])) {
// ...
}
The code may require additional testing and tweaking, but I think you've got an idea.

Categories