Imagick how to remove blur when saving pdf? - php

I have a task, there are many PDF files, they have a document in the upper left corner, to save sheets of paper, you need to combine 4 such documents onto one sheet. I haven't found anything better than Imagick. My task is to copy a rectangle from a PDF document, and paste it into a new one created at certain coordinates so that there are 4 pieces in a sheet.
const SCAN_DIR = "E:\pdf";
const OUT_FILE = 'E:/result/result.pdf';
const A4_COLS = 595;
const A4_ROWS = 842;
const COMPRESSIONQUALITY = 100;
try {
$imagick = new Imagick();
$imagick->newImage(A4_COLS, A4_ROWS, new ImagickPixel('white'), 'pdf');
$imagick->setImageCompressionQuality(COMPRESSIONQUALITY);
$imagickTemp = $imagick;
$i = $j = 0;
$files = scandir(SCAN_DIR);
foreach ($files as $file) {
if ($file === '.' or $file === '..') continue;
$file = SCAN_DIR . '/' . $file;
$imagickPDF = new Imagick();
$imagickPDF->readImage($file);
$imagickPDF->setImageCompressionQuality(COMPRESSIONQUALITY);
$imagickPDF->cropImage(290, 420, 0, 0);
$x = $y = 0;
switch ($i) {
case 0:
$x = 0;
$y = 0;
break;
case 1:
$x = 300;
$y = 0;
break;
case 2:
$x = 0;
$y = 430;
break;
case 3:
$x = 300;
$y = 430;
break;
}
$imagickTemp->compositeImage($imagickPDF, Imagick::COMPOSITE_ATOP, $x, $y);
$imagickPDF->clear();
$imagickPDF->destroy();
if ($i !== 0 && ($i+1) % 4 === 0 ) {
if ($j > 0) {
$imagick->addImage($imagickTemp);
$imagickTemp->clear();
$imagickTemp->destroy();
}
$imagickTemp = new Imagick();
$imagickTemp->newImage(A4_COLS, A4_ROWS, new ImagickPixel('white'), 'pdf');
$imagickTemp->setImageCompressionQuality(COMPRESSIONQUALITY);
$j++;
$i = 0;
} else $i++;
}
$imagickTemp->setImageCompressionQuality(COMPRESSIONQUALITY);
$imagick->writeImages(OUT_FILE, true);
$imagick->clear();
$imagick->destroy();
} catch (ImagickException $e) {
print_r($e);
}
Original image:
result image:
How can I fix it?

Related

How can I optimize this image "edge detection" algorithm?

I have a function that, given an image with a transparent background and an unknown object in it, finds the top, left, right and bottom boundaries of the object. The purpose is so that I can simply draw a box around the boundaries of the object. I'm not trying to detect the actual edges of the object - just the top most, bottom most, etc.
My function works well, but is slow because it scans every single pixel in the image.
My question is: Is there a faster, more efficient way to detected the upper-most, left-most, right-most, and bottom-most non-transparent pixel in an image, using stock PHP/GD functionality?
There's a catch that affects the options: the object in the image may have transparent parts. For example, if it's an image of a non-filled shape.
public static function getObjectBoundaries($image)
{
// this code looks for the first non white/transparent pixel
// from the top, left, right and bottom
$imageInfo = array();
$imageInfo['width'] = imagesx($image);
$imageInfo['height'] = imagesy($image);
$imageInfo['topBoundary'] = $imageInfo['height'];
$imageInfo['bottomBoundary'] = 0;
$imageInfo['leftBoundary'] = $imageInfo['width'];
$imageInfo['rightBoundary'] = 0;
for ($x = 0; $x <= $imageInfo['width'] - 1; $x++) {
for ($y = 0; $y <= $imageInfo['height'] - 1; $y++) {
$pixelColor = imagecolorat($image, $x, $y);
if ($pixelColor != 2130706432) { // if not white/transparent
$imageInfo['topBoundary'] = min($y, $imageInfo['topBoundary']);
$imageInfo['bottomBoundary'] = max($y, $imageInfo['bottomBoundary']);
$imageInfo['leftBoundary'] = min($x, $imageInfo['leftBoundary']);
$imageInfo['rightBoundary'] = max($x, $imageInfo['rightBoundary']);
}
}
}
return $imageInfo;
}
Function calls in PHP are expensive. Calling imagecolorat() per pixel will absolutely ruin performance. Efficient coding in PHP means finding a built-in function that can somehow do the job. The following code makes use of the palette GD functions. At a glance it might not be intuitive but the logic is actually pretty simple: the code keeps copying the image a line of pixels at a time until it notices that it requires more than one colors to represent them.
function getObjectBoundaries2($image) {
$width = imagesx($image);
$height = imagesy($image);
// create a one-pixel high image that uses a PALETTE
$line = imagecreate($width, 1);
for($y = 0; $y < $height; $y++) {
// copy a row of pixels into $line
imagecopy($line, $image, 0, 0, 0, $y, $width, 1);
// count the number of colors in $line
// if it's one, then assume it's the transparent color
$count = imagecolorstotal($line);
if($count > 1) {
// okay, $line has employed more than one color so something's there
// look at the first color in the palette to ensure that our initial
// assumption was correct
$firstColor = imagecolorsforindex($line, 0);
if($firstColor['alpha'] == 127) {
$top = $y;
} else {
// it was not--the first color encountered was opaque
$top = 0;
}
break;
}
}
if(!isset($top)) {
// image is completely empty
return array('width' => $width, 'height' => $height);
}
// do the same thing from the bottom
$line = imagecreate($width, 1);
for($y = $height - 1; $y > $top; $y--) {
imagecopy($line, $image, 0, 0, 0, $y, $width, 1);
$count = imagecolorstotal($line);
if($count > 1) {
$firstColor = imagecolorsforindex($line, 0);
if($firstColor['alpha'] == 127) {
$bottom = $y;
} else {
$bottom = $height - 1;
}
break;
}
}
$nonTransparentHeight = $bottom - $top + 1;
// scan from the left, ignoring top and bottom parts known to be transparent
$line = imagecreate(1, $nonTransparentHeight);
for($x = 0; $x < $width; $x++) {
imagecopy($line, $image, 0, 0, $x, $top, 1, $nonTransparentHeight);
$count = imagecolorstotal($line);
if($count > 1) {
$firstColor = imagecolorsforindex($line, 0);
if($firstColor['alpha'] == 127) {
$left = $x;
} else {
$left = 0;
}
break;
}
}
// scan from the right
$line = imagecreate(1, $nonTransparentHeight);
for($x = $width - 1; $x > $left; $x--) {
imagecopy($line, $image, 0, 0, $x, $top, 1, $nonTransparentHeight);
$count = imagecolorstotal($line);
if($count > 1) {
$firstColor = imagecolorsforindex($line, 0);
if($firstColor['alpha'] == 127) {
$right = $x;
} else {
$right = $width - 1;
}
break;
}
}
return array('width' => $width, 'height' => $height, 'topBoundary' => $top, 'bottomBoundary' => $bottom, 'leftBoundary' => $left, 'rightBoundary' => $right);
}
I think you could test the 4 sides one after an other, stopping as soon as a pixel is found.
For the top boundary (untested code) :
// false so we can test it's value
$bound_top = false;
// The 2 loops have 2 end conditions, if end of row/line, or pixel found
// Loop from top to bottom
for ($y = 0; $y < $img_height && $bound_top === false; $y++) {
// Loop from left to right (right to left would work to)
for ($x = 0; $x < $img_width && $bound_top === false; $x++) {
if (imageColorAt($img, $x, $y) != 2130706432) {
$bound_top = $y;
}
}
}
After the loops, if $bound_top is still false, don't bother checking the other sides, you checked all pixels, the image is empty. If not, just do the same for the other sides.
Not every pixel needs to be examined. The following code checks columns from left to right to get leftBoundary, right to left to get rightBoundary, rows from top to bottom (while excluding pixels we've already checked) to get topBoundary, and similarly for bottomBoundary.
function get_boundary($image)
{
$imageInfo = array();
$imageInfo['width'] = imagesx($image);
$imageInfo['height'] = imagesy($image);
for ($x = 0; $x < $imageInfo['width']; $x++) {
if (!is_box_empty($image, $x, 0, 1, $imageInfo['height'])) {
$imageInfo['leftBoundary'] = $x;
break;
}
}
for ($x = $imageInfo['width']-1; $x >= 0; $x--) {
if (!is_box_empty($image, $x, 0, 1, $imageInfo['height'])) {
$imageInfo['rightBoundary'] = $x;
break;
}
}
for ($y = 0; $y < $imageInfo['height']; $y++) {
if (!is_box_empty($image, $imageInfo['leftBoundary'], $y, $imageInfo['rightBoundary']-$imageInfo['leftBoundary']+1, 1)) {
$imageInfo['topBoundary'] = $y;
break;
}
}
for ($y = $imageInfo['height']-1; $y >= 0; $y--) {
if (!is_box_empty($image, $imageInfo['leftBoundary'], $y, $imageInfo['rightBoundary']-$imageInfo['leftBoundary']+1, 1)) {
$imageInfo['bottomBoundary'] = $y;
break;
}
}
return $imageInfo;
}
function is_box_empty($image, $x, $y, $w, $h)
{
for ($i = $x; $i < $x+$w; $i++) {
for ($j = $y; $j < $y+$h; $j++) {
$pixelColor = imagecolorat($image, $i, $j);
if ($pixelColor != 2130706432) { // if not white/transparent
return false;
}
}
}
return true;
}

Convert 32-bit TGA to PNG

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;
}
}
?>

PHP - Create one image from images

i have n-images and want to create one with php code. I use imagecopymerge(), but can't make it. Some example please?
Code:
$numberOfImages = 3;
$x = 940;
$y = 420;
$background = imagecreatetruecolor($x, $y*3);
$firstUrl = '/images/upload/photoalbum/photo/1.jpg';
$secondUrl = '/images/upload/photoalbum/photo/2.jpg';
$thirdUrl = '/images/upload/photoalbum/photo/3.jpg';
$outputImage = $background;
$first = imagecreatefromjpeg($firstUrl);
$second = imagecreatefromjpeg($secondUrl);
$third = imagecreatefromjpeg($thirdUrl);
imagecopymerge($outputImage,$first,0,0,0,0, $x, $y,100);
imagecopymerge($outputImage,$second,0,$y,0,0, $x, $y,100);
imagecopymerge($outputImage,$third,0,$y*2,0,0, $x, $y,100);
imagejpeg($outputImage, APPLICATION_PATH .'/images/upload/photoalbum/photo/test.jpg');
imagedestroy($outputImage);
Thanks kruksmail,
I adapted your answer for a specific project in which the images could be unknown. So I made your answer work with an array of images.
It also gives the ability to specify how many rows or columns you want. I added some comments to help also.
$images = array('/images/upload/photoalbum/photo/1.jpg','/images/upload/photoalbum/photo/2.jpg','/images/upload/photoalbum/photo/3.jpg');
$number_of_images = count($images);
$priority = "columns"; // also "rows"
if($priority == "rows"){
$rows = 3;
$columns = $number_of_images/$rows;
$columns = (int) $columns; // typecast to int. and makes sure grid is even
}else if($priority == "columns"){
$columns = 3;
$rows = $number_of_images/$columns;
$rows = (int) $rows; // typecast to int. and makes sure grid is even
}
$width = 150; // image width
$height = 150; // image height
$background = imagecreatetruecolor(($width*$columns), ($height*$rows)); // setting canvas size
$output_image = $background;
// Creating image objects
$image_objects = array();
for($i = 0; $i < ($rows * $columns); $i++){
$image_objects[$i] = imagecreatefromjpeg($images[$i]);
}
// Merge Images
$step = 0;
for($x = 0; $x < $columns; $x++){
for($y = 0; $y < $rows; $y++){
imagecopymerge($output_image, $image_objects[$step], ($width * $x), ($height * $y), 0, 0, $width, $height, 100);
$step++; // steps through the $image_objects array
}
}
imagejpeg($output_image, 'test.jpg');
imagedestroy($output_image);
print "<div><img src='test.jpg' /></div>";

Codeigniter Trim Image

I am using the upload class that codeigniter comes with:
$config['upload_path'] = getcwd() . '/public/images';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = '100';
$config['max_width'] = '1024';
$config['max_height'] = '768';
$config['encrypt_name'] = true;
$this->load->library('upload', $config);
if ( ! $this->upload->do_upload())
{
echo $error = array('error' => $this->upload->display_errors());
}
else
{
echo $data = array('upload_data' => $this->upload->data());
}
It works fine uploading the file but now I would like to trim all the extra white space from the image. I looked at the image manipulation class but it does not seem to do it. So I looked around and found this Crop whitespace from image in PHP. I am unsure though how to put the two together. Any ideas?
You're right, the image manipulation class doesn't support doing that.
You can, however, extend the library (bottom of http://codeigniter.com/user_guide/general/creating_libraries.html) and add a new method based on the code from the link you found already.
Here, I was bored at work, extend CI's image manipulation lib and add this method:
public function trim_whitespace($color = 'FFFFFF')
{
//load the image
$img = $this->image_create_gd();
//find the size of the borders
$b_top = 0;
$b_btm = 0;
$b_lft = 0;
$b_rt = 0;
//top
for(; $b_top < imagesy($img); ++$b_top) {
for($x = 0; $x < imagesx($img); ++$x) {
if(imagecolorat($img, $x, $b_top) != '0x'.$color) {
break 2; //out of the 'top' loop
}
}
}
//bottom
for(; $b_btm < imagesy($img); ++$b_btm) {
for($x = 0; $x < imagesx($img); ++$x) {
if(imagecolorat($img, $x, imagesy($img) - $b_btm-1) != '0x'.$color) {
break 2; //out of the 'bottom' loop
}
}
}
//left
for(; $b_lft < imagesx($img); ++$b_lft) {
for($y = 0; $y < imagesy($img); ++$y) {
if(imagecolorat($img, $b_lft, $y) != '0x'.$color) {
break 2; //out of the 'left' loop
}
}
}
//right
for(; $b_rt < imagesx($img); ++$b_rt) {
for($y = 0; $y < imagesy($img); ++$y) {
if(imagecolorat($img, imagesx($img) - $b_rt-1, $y) != '0x'.$color) {
break 2; //out of the 'right' loop
}
}
}
//copy the contents, excluding the border
$newimg = imagecreatetruecolor(
imagesx($img)-($b_lft+$b_rt), imagesy($img)-($b_top+$b_btm));
imagecopy($newimg, $img, 0, 0, $b_lft, $b_top, imagesx($newimg), imagesy($newimg));
// Output the image
if ($this->dynamic_output == TRUE)
{
$this->image_display_gd($newimg);
}
else
{
// Or save it
if ( ! $this->image_save_gd($newimg))
{
return FALSE;
}
}
}
Usage:
// load extended image lib
$this->load->library('image_lib');
// configure image lib
$config['image_library'] = 'gd2';
$config['source_image'] = 'path/to/source.img';
$config['new_image'] = 'path/to/output.img';
$this->image_lib->initialize($config);
$this->image_lib->trim_whitespace('38ff7e'); // set colour to trim, defaults to white (ffffff)
$this->image_lib->clear();
It's worth noting that even though it will work, you really shouldn't use this as a dynamic_output, do the trim on save otherwise it will just slow everything down. Also, it is looking for just 1 colour value (although, that is a limitation from the code you posted, there may be better functions for this out there) so if it's a compressed jpg you might have trouble getting all of the whitespace.
To use the ImageMagick you should add the function to the image_lib.php:
public function trimimg()
{
$protocol = 'image_process_'.$this->image_library;
return $this->$protocol('trimimg');
}
and then add to the function
public function image_process_imagemagick($action = 'resize')
{
// Do we have a vaild library path?
if ($this->library_path === '')
{
$this->set_error('imglib_libpath_invalid');
return FALSE;
}
if ( ! preg_match('/convert$/i', $this->library_path))
{
$this->library_path = rtrim($this->library_path, '/').'/convert';
}
// Execute the command
$cmd = $this->library_path.' -quality '.$this->quality;
if ($action === 'crop')
{
$cmd .= ' -crop '.$this->width.'x'.$this->height.'+'.$this->x_axis.'+'.$this->y_axis;
}
elseif ($action === 'rotate')
{
$cmd .= ($this->rotation_angle === 'hor' OR $this->rotation_angle === 'vrt')
? ' -flop'
: ' -rotate '.$this->rotation_angle;
}
elseif ($action === 'trimimg')
{
$cmd .= ' -trim';
}
the last elseif.
Then
$this->image_lib->trimimg())

Cut 1 big image into small images with the same size

Given a JPG image, cut off it in horizontal direction so that the size of spic is around 100K
Any tips?
Have used this function in the past works well for me...
This will take an input file and create tiles of the specified size...
function makeTiles($name, $imageFileName, $crop_width, $crop_height)
{
$dir = "source dir";
$slicesDir = "target dir";
// might be good to check if $slicesDir exists etc if not create it.
$inputFile = $dir . $imageFileName;
$img = new Imagick($inputFile);
$imgHeight = $img->getImageHeight();
$imgWidth = $img->getImageWidth();
$cropWidthTimes = ceil($imgWidth/$crop_width);
$cropHeightTimes = ceil($imgHeight/$crop_height);
for($i = 0; $i < $cropWidthTimes; $i++)
{
for($j = 0; $j < $cropHeightTimes; $j++)
{
$img = new Imagick($inputFile);
$x = ($i * $crop_width);
$y = ($j * $crop_height);
$img->cropImage($crop_width, $crop_height, $x, $y);
$data = $img->getImageBlob();
$newFileName = $slicesDir . $name . "_" . $x . "_" . $y . ".jpg";
$result = file_put_contents ($newFileName, $data);
}
}
}

Categories