i have this function that transforms image to trapezoid using PHP GD:
function perspective($i,$gradient=0.9,$rightdown=true,$background=0xFFFFFF) {
$mult=3;
$w=imagesx($i);
$h=imagesy($i);
$image=imagecreatetruecolor($w*$mult,$h*$mult);
imagecopyresized($image,$i,0,0,0,0,$w*$mult,$h*$mult,$w,$h);
imagedestroy($i);
$w*=$mult;
$h*=$mult;
$im=imagecreatetruecolor($w,$h);
$background=imagecolorallocate($im,($background>>16)&0xFF,($background>>8)&0xFF,$background&0xFF);
imagefill($im,0,0,$background);
imageantialias($im,true);
$nh=$h-($h*$gradient);
for ($x=0; $x<$w; $x++) {
$ni=(($rightdown) ? $x : $w-$x);
$p=intval($h-(($ni/$w)*$nh));
if (($p%2)<>0)
$p-=1;
$nx=intval(($p-$h)/2);
imagecopyresampled($im,$image,$x,0,$x,$nx,1,$p,1,$h-1);
imageline($im,$x,$h-1,$x,$h+$nx,$background);
imageline($im,$x,0,$x,-$nx-1,$background);
}
imagedestroy($image);
imagefilter($im,IMG_FILTER_SMOOTH,10);
$i=imagecreatetruecolor($w/$mult,$h/$mult);
imageantialias($i,true);
imagecopyresampled($i,$im,0,0,0,0,$w,$h,$w*$mult,$h*$mult);
imagedestroy($im);
return $i;
}
But i cant modify it to produce isosceles trapezoid, i think there needs just one small modification, but i cant figure it outh (i tried lot of things).
Can someone help?
Right, basically that code should generate the right values, but due to a bug has a lot of cludges in place to get a trapezium shape. The bug is that the copy of each line has the destination-y and the source-y values transposed. The source-y should always be 0, the destination-y should change.
There were also a few other small numerical bugs and double rounding at unnecessary points throwing off the results.
Also, the variable naming was atrocious, so I have rewritten it so that the entire function is clear.
Try the following:
function makeTrapeziumImage($image, $gradient, $rightToLeft = false, $background = 0xFFFFFF, $supersampleScale = 3) {
$originalWidth = imagesx($image);
$originalHeight = imagesy($image);
$supersampledWidth = $originalWidth * $supersampleScale;
$supersampledHeight = $originalHeight * $supersampleScale;
$supersampledImage = imagecreatetruecolor($supersampledWidth, $supersampledHeight);
imagecopyresized($supersampledImage, $image,
0, 0, 0, 0,
$supersampledWidth, $supersampledHeight, $originalWidth, $originalHeight);
$workingImage = imagecreatetruecolor($supersampledWidth, $supersampledHeight);
$backgroundColour = imagecolorallocate($workingImage, ($background >> 16) & 0xFF, ($background >> 8) & 0xFF, $background & 0xFF);
imagefill($workingImage, 0, 0, $backgroundColour);
imageantialias($workingImage,true);
$endHeight = $supersampledHeight - ($supersampledHeight * $gradient);
for ($x = 0; $x < $supersampledWidth; $x++) {
$cX = ($rightToLeft ? $supersampledWidth - $x : $x);
$dstHeight = $supersampledHeight - ((($cX + 1) / $supersampledWidth) * $endHeight);
$dstY = intval(($supersampledHeight - $dstHeight) / 2) - 1; // -1 required as zero-indexed
$dstY = ($dstY < 0 ? 0 : $dstY); // Rounding can make $dstY = -1
$dstHeight = intval($dstHeight); // Round the height after calculating $dstY
imagecopyresampled($workingImage, $supersampledImage,
$cX, $dstY, $cX, 0,
1, $dstHeight, 1, $supersampledHeight);
}
imagedestroy($supersampledImage);
imagefilter($workingImage, IMG_FILTER_SMOOTH, 10);
$resizedImage = imagecreatetruecolor($originalWidth, $originalHeight);
imageantialias($resizedImage, true);
imagecopyresampled($resizedImage, $workingImage,
0, 0, 0, 0,
$originalWidth, $originalHeight, $supersampledWidth, $supersampledHeight);
imagedestroy($workingImage);
return $resizedImage;
}
The essential mechanism of the inner loop, as before, is to take each column of pixels, along the x-axis and resize them over a gradient. It naturally creates an isosceles trapezium. In order to create a non-isosceles trapezoid, another gradient would have to be specified. Alternatively, a set of start and end y-values could be specified and the gradients calculated from them.
Whilst this example works along the x-axis, in either direction as before, it could just as easily work along the y-axis (or the image could be rotated 90 degrees, processed, then rotated back).
Related
EDIT: please forget about this question! I made a stupid error in the original code.
The example code works as expected!
I'm trying to rotate AND crop images.
I have this so far:
$w = 100;
$h = 400;
$img1 = imagecreatefromjpeg('image1.jpg');
$img2 = imagecreatefromjpeg('image2.jpg');
for ($i = 0; $i < 2; $i++) {
${'img'.$i + 3} = imagecreatetruecolor($h, $h);
imagecopy(${'img'.$i + 3}, ${'img'.$i + 1}, 0, 0, 0, 0, $w, $h);
${'img'.$i + 3} = imagerotate(${'img'.$i + 3}, 90, 0);
${'img'.$i + 3} = imagecrop(${'img'.$i + 3}, array(0, 0, $h, $w));
imagejpeg(${'img'.$i + 3});
imagedestroy(${'img'.$i + 3});
imagedestroy(${'img'.$i + 1});
}
So what I essentially do is open some JPGs, create new images, copy the JPGs into the new images and then crop the images.
Alas this results in empty images ...
What am I doing wrong?
No idea if this will make any difference to the lack of output - but what do $img1 & $img2 do - they don't get used as far as i can see?
#error_reporting( E_ALL );
$w = 100;
$h = 400;
$img1 = imagecreatefromjpeg('image1.jpg');
$img2 = imagecreatefromjpeg('image2.jpg');
for ($i = 0; $i < 3; $i++) {
$new=${'img'.$i + 3};
$src=${'img'.$i + 1};
$new = imagecreatetruecolor($h, $h);
imagecopy( $new, $src, 0, 0, 0, 0, $w, $h);
$new = imagerotate($new, 90, 0);
$new = imagecrop($new, array(0, 0, $h, $w));
imagejpeg($new);
imagedestroy($new);
imagedestroy($src);
break;/* just to see if it gets this far*/
}
Firstly, you should consider using image copy resampled for a better quality of result from the process.
Then, you need to correctly use the imagejpeg function, currently you are simply loading the JPGs and outputting them directly, which may be what you want but you're in a loop and you can't loop load multiple images directly to the same file. It also means that the image you're seeing (or not) is the final image in the set.
Your Problem is that your for loop runs THREE times but your only have data associated with the first two instances, but the third instance is empty, and as this is the most recent this is the only instance output to the browser.
So:
1) Save the images you have generated with imagejpeg($data,$filename);. You can define a filename as $filename = $img+3.".jpg"; or similar.
2) It would also be much easier to debug and read your code if you used arrays instead of numerically incremented variables, that's a very messy way of writing code!
3) If you do want to output the image directly to the browser you need PHP to supply a header such as header('Content-Type: image/jpeg'); before outputting the contents of imagejpeg.
A Rehash of your code:
$w = 100;
$h = 400;
$img[1] = imagecreatefromjpeg('image1.jpg');
$img[2] = imagecreatefromjpeg('image2.jpg');
for ($i = 3; $i < 5; $i++) {
$img[$i] = imagecreatetruecolor($h, $h);
$j = $i -2;
imagecopyresampled($img[$i], $img[$j], 0, 0, 0, 0, $h, $h, $w, $h,);
// this function also contains destination width and destination
// height, which are equal to the size of the destination image so
// are set as $h, $h in the function above.
$img[$i] = imagerotate($img[$i], 90, 0);
$img[$i] = imagecrop($img[$i], array(0, 0, $h, $w));
$filename = "image-".$i.".jpg";
imagejpeg($img[$i], $filename);
imagedestroy($img[$i]);
imagedestroy($img[$j]);
}
We have a script that generates MP3 waveform images, and it currently uses a specified foreground and background color. What I'd like is to have the background completely transparent.
I've messed with the below script a bit but I have no idea what I'm doing. Any ideas on how to get it to where it only generates the foreground color and not the background?
/**
* Image generation
*/
// how much detail we want. Larger number means less detail
// (basically, how many bytes/frames to skip processing)
// the lower the number means longer processing time
define("DETAIL", 5);
// get user vars from form
$width = isset($_POST["width"]) ? (int) $_POST["width"] : 775;
$height = isset($_POST["height"]) ? (int) $_POST["height"] : 122;
$background = isset($_POST["background"]) ? $_POST["background"] : $background_color;
$foreground = isset($_POST["foreground"]) ? $_POST["foreground"] : $foreground_color;
// create original image width based on amount of detail
$img = imagecreatetruecolor(sizeof($data) / DETAIL, $height);
// fill background of image
list($r, $g, $b) = html2rgb($background);
imagefilledrectangle($img, 0, 0, sizeof($data) / DETAIL, $height, imagecolorallocate($img, $r, $g, $b));
// generate background color
list($r, $g, $b) = html2rgb($foreground);
// loop through frames/bytes of wav data as genearted above
for($d = 0; $d < sizeof($data); $d += DETAIL) {
// relative value based on height of image being generated
// data values can range between 0 and 255
$v = (int) ($data[$d] / 255 * $height);
// draw the line on the image using the $v value and centering it vertically on the canvas
imageline($img, $d / DETAIL, 0 + ($height - $v), $d / DETAIL, $height - ($height - $v), imagecolorallocate($img, $r, $g, $b));
}
$outPngName= $outputName.".png";
This is what you are looking for.
// fill background with transparent color / white
$transColor = imagecolortransparent($img, imagecolorallocatealpha($img, 255, 255, 255, 127));
imagefill($img, 0, 0, $transColor);
Whenever I am trying to use the function imageflip(), it shows me the following message
Fatal error: Call to undefined function imageflip() in D:\xampp\htdocs\temp1\image_flip.php on line 6
Once I have called the imap_open function, even though, I already installed the imap extension and configured all. However, it still shows the same message.
imageflip() is only available after PHP 5.5. However, you can still define it yourself, as explained here (although if you plan to upgrade to PHP 5.5, it is not recommended to implement yours, or at least change the name to avoid duplication problems). For the sake of stackoverflow, I'll paste the code here:
<?php
/**
* Flip (mirror) an image left to right.
*
* #param image resource
* #param x int
* #param y int
* #param width int
* #param height int
* #return bool
* #require PHP 3.0.7 (function_exists), GD1
*/
function imageflip(&$image, $x = 0, $y = 0, $width = null, $height = null)
{
if ($width < 1) $width = imagesx($image);
if ($height < 1) $height = imagesy($image);
// Truecolor provides better results, if possible.
if (function_exists('imageistruecolor') && imageistruecolor($image))
{
$tmp = imagecreatetruecolor(1, $height);
}
else
{
$tmp = imagecreate(1, $height);
}
$x2 = $x + $width - 1;
for ($i = (int) floor(($width - 1) / 2); $i >= 0; $i--)
{
// Backup right stripe.
imagecopy($tmp, $image, 0, 0, $x2 - $i, $y, 1, $height);
// Copy left stripe to the right.
imagecopy($image, $image, $x2 - $i, $y, $x + $i, $y, 1, $height);
// Copy backuped right stripe to the left.
imagecopy($image, $tmp, $x + $i, $y, 0, 0, 1, $height);
}
imagedestroy($tmp);
return true;
}
And to use it:
<?php
$image = imagecreate(190, 60);
$background = imagecolorallocate($image, 100, 0, 0);
$color = imagecolorallocate($image, 200, 100, 0);
imagestring($image, 5, 10, 20, "imageflip() example", $color);
imageflip($image);
header("Content-Type: image/jpeg");
imagejpeg($image);
I haven't tried it, and the code isn't mine at all, but with some tricks you could adapt it to your needs.
I need to make this effect with php. I know that there is IMG_FILTER_PIXELATE in PHP image filter. But I need it to be smoother and embossed? like in this image:
This effect will make any image uploaded by user become pixelated and the edge of the picture become red (I know IMG_FILTER_EDGEDETECT but I don't know how to use it to change edge color).
I have no idea how to do this.
As the last answer was theoretical and seemed to be not enough, I've created a practical example:
Note: This is far from the "ideal" and perfect pixelate effect function, but it does it's job. Feel free to edit it according to your own needs.
<?php
/* Function to make pixelated images
* Supported input: .png .jpg .jpeg .gif
*
*
* Created on 24.01.2011 by Henrik Peinar
*/
/*
* image - the location of the image to pixelate
* pixelate_x - the size of "pixelate" effect on X axis (default 10)
* pixelate_y - the size of "pixelate" effect on Y axis (default 10)
* output - the name of the output file (extension will be added)
*/
function pixelate($image, $output, $pixelate_x = 20, $pixelate_y = 20)
{
// check if the input file exists
if(!file_exists($image))
echo 'File "'. $image .'" not found';
// get the input file extension and create a GD resource from it
$ext = pathinfo($image, PATHINFO_EXTENSION);
if($ext == "jpg" || $ext == "jpeg")
$img = imagecreatefromjpeg($image);
elseif($ext == "png")
$img = imagecreatefrompng($image);
elseif($ext == "gif")
$img = imagecreatefromgif($image);
else
echo 'Unsupported file extension';
// now we have the image loaded up and ready for the effect to be applied
// get the image size
$size = getimagesize($image);
$height = $size[1];
$width = $size[0];
// start from the top-left pixel and keep looping until we have the desired effect
for($y = 0;$y < $height;$y += $pixelate_y+1)
{
for($x = 0;$x < $width;$x += $pixelate_x+1)
{
// get the color for current pixel
$rgb = imagecolorsforindex($img, imagecolorat($img, $x, $y));
// get the closest color from palette
$color = imagecolorclosest($img, $rgb['red'], $rgb['green'], $rgb['blue']);
imagefilledrectangle($img, $x, $y, $x+$pixelate_x, $y+$pixelate_y, $color);
}
}
// save the image
$output_name = $output .'_'. time() .'.jpg';
imagejpeg($img, $output_name);
imagedestroy($img);
}
pixelate("test.jpg", "testing");
?>
This is the example function to create pixelated effect on images.
Here's an example results of using this function:
Original:
Pixelated 5px:
Pixelated 10px:
Pixelated 20px:
Thank you for your answer. I used your function and added another loop to change color of the outer pixel of the squares using a function called imagelinethick in http://www.php.net/manual/en/function.imageline.php. So it became:
<?php
$image = imagecreatefromjpeg('Penguins.jpg');
$imagex = imagesx($image);
$imagey = imagesy($image);
$pixelate_y=10;
$pixelate_x=10;
$height=$imagey;
$width=$imagex;
for($y = 0;$y < $height;$y += $pixelate_y+1)
{
for($x = 0;$x < $width;$x += $pixelate_x+1)
{
// get the color for current pixel
$rgb = imagecolorsforindex($image, imagecolorat($image, $x, $y));
// get the closest color from palette
$color = imagecolorclosest($image, $rgb['red'], $rgb['green'], $rgb['blue']);
imagefilledrectangle($image, $x, $y, $x+$pixelate_x, $y+$pixelate_y, $color);
}
}
for($y = 0;$y < $height;$y += $pixelate_y+1)
{
for($x = 0;$x < $width;$x += $pixelate_x+1)
{
//make a border line for each square
$rgb = imagecolorsforindex($image, imagecolorat($image, $x, $y));
$color = imagecolorclosest($image, 123, 123, 123);
imagelinethick($image, $x, $y, $x, $y+$pixelate_y, $color, 1);
imagelinethick($image, $x, $y, $x+$pixelate_x, $y, $color, 2);
}
}
function imagelinethick($image, $x1, $y1, $x2, $y2, $color, $thick = 1)
{
/* this way it works well only for orthogonal lines
imagesetthickness($image, $thick);
return imageline($image, $x1, $y1, $x2, $y2, $color);
*/
if ($thick == 1) {
return imageline($image, $x1, $y1, $x2, $y2, $color);
}
$t = $thick / 2 - 0.5;
if ($x1 == $x2 || $y1 == $y2) {
return imagefilledrectangle($image, round(min($x1, $x2) - $t), round(min($y1, $y2) - $t), round(max($x1, $x2) + $t), round(max($y1, $y2) + $t), $color);
}
$k = ($y2 - $y1) / ($x2 - $x1); //y = kx + q
$a = $t / sqrt(1 + pow($k, 2));
$points = array(
round($x1 - (1+$k)*$a), round($y1 + (1-$k)*$a),
round($x1 - (1-$k)*$a), round($y1 - (1+$k)*$a),
round($x2 + (1+$k)*$a), round($y2 - (1-$k)*$a),
round($x2 + (1-$k)*$a), round($y2 + (1+$k)*$a),
);
imagefilledpolygon($image, $points, 4, $color);
return imagepolygon($image, $points, 4, $color);
}
header("Content-Type: image/JPEG");
imageJPEG($image, "", 75);
?>
The result is like this: http://www.flickr.com/photos/52700219#N06/6759029339/
But I think this still need some improvement to make it smoother.
Here goes theoretically:
You have a image:
RGBRGBRGBRGB
GBRGBRGBRGBR
GBRGBRGBRRGB
BGRGBGRGGRBG
Take the color of the first pixel and set the same color for a square of next pixels (both down and right). Then take the color of a 5th pixel (as 4 ones in the start have already the same color). If you are done for the first row, go +3 rows down and start again.
So you get:
RRRRGGGBBBB
RRRRGGGBBBB
RRRRGGGBBBB
RRRRGGGBBBB
In PHP you can use the following functions to make this:
http://php.net/manual/en/function.imagecolorat.php to select the color of a pixel
http://php.net/manual/en/function.imagecolorset.php to set the color of a pixel
http://php.net/manual/en/function.imagesx.php get image width
http://php.net/manual/en/function.imagesy.php get image height
use for loops thru the pixels of a image
This is my attempt at the problem.
You can alter the pixelate block size and you can apply a blur which softens the effect on high contrast images. Can be a slow on large images with small pixelate block sizes though.
The scripts stores the colours of relevant pixels in an array. It then embosses the image, alters the contrast as required, pixelates the image using the imagefilter() function and then (if tile enhance is set) embosses it again (this increases the 3D effect on the final tiles). If blur is required the script the applies the Gaussian blur. The script then draws filled squares using the colour array to create the colourful pixelated effect within the embossed tile borders.
function pixelatemboss($image,$blockwidth=10,$blur=5,$tileenhance="true",$contrast=0,$negate="true")
{
if($blockwidth>1)
{
imagefilter($image,IMG_FILTER_CONTRAST,$contrast);
for($x=1;$x<imagesx($image);$x=$x+$blockwidth)
{
for($y=1;$y<imagesy($image);$y=$y+$blockwidth)
{
$color[$x][$y]=imagecolorat($image,$x,$y);
}
}
imagefilter($image,IMG_FILTER_EMBOSS);
imagefilter($image,IMG_FILTER_CONTRAST,$contrast);
imagefilter($image,IMG_FILTER_PIXELATE,$blockwidth,false);
if($tileenhance=="true")
{
imagefilter($image,IMG_FILTER_EMBOSS);
}
for($b=0;$b<$blur;$b++)
{
imagefilter($image,IMG_FILTER_GAUSSIAN_BLUR);
}
for($x=1;$x<imagesx($image);$x=$x+$blockwidth)
{
for($y=1;$y<imagesy($image);$y=$y+$blockwidth)
{
$rgb=$color[$x][$y];
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$col=imagecolorallocate($image,$r,$g,$b);
imagefilledrectangle($image,$x,$y,$x+($blockwidth-2),$y+($blockwidth-2),$col);
}
}
}
return $image;
}
Note for php 5.4 and up you need to use:
imageJPEG($image, NULL, 75);
You can no longer specify NULL by using a double quote (like this example):
imageJPEG($image, "", 75);
I want to skew an image into a trapezoidal shape. The left and right edges need to be straight up and down; the top and left edges need to be angular. I have no idea what the best way to do this is.
I'm using GD Library and PHP. Can anyone point me in the right direction?
Thanks,
Jason
Try this:
<?
// Set it up
$img_name = "grid.jpg";
$src_img = imagecreatefromjpeg($img_name);
$magnify = 4;
// Magnify the size
$w = imagesx($src_img);
$h = imagesy($src_img);
$dst_img = imagecreatetruecolor($w * $magnify, $h * $magnify);
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $w * $magnify, $h * $magnify, $w, $h);
$src_img = $dst_img;
// Skew it
$w *= $magnify;
$h *= $magnify;
$new_lh = abs($h * 0.66);
$new_rh = $h ;
$step = abs((($new_rh - $new_lh) / 2) / $w);
$from_top = ($new_rh - $new_lh) / 2 ;
$dst_img = imagecreatetruecolor($w, $new_rh);
$bg_colour = imagecolorallocate($dst_img, 255, 255, 255);
imagefill($dst_img, 0, 0, $bg_colour);
for ($i = 0 ; $i < $w ; $i ++)
{
imagecopyresampled($dst_img, $src_img, $i, $from_top - $step * $i, $i, 0, 1, $new_lh + $step * $i * 2, 1, $h);
}
// Reduce the size to "anti-alias" it
$src_img = $dst_img;
$dst_img = imagecreatetruecolor($w / $magnify * 0.85, $new_rh / $magnify);
imagecopyresampled ($dst_img, $src_img, 0, 0, 0, 0, $w / $magnify * 0.85, $h / $magnify, $w, $h);
header("Content-Type: image/jpg");
imagejpeg($dst_img);
?>
I found this thread (translated Dutch -> English) discussing the same thing. Looks like it might be what you're after. I think it's clear that you can't skew with GD without writing your own function to do so. If you have ImageMagick available, you might find this to be easier to achieve.
Good luck.
Make a nested loop for x = 0 to width and inside y = 0 to height, get pixel (rgba) at that coordinate, calculate new xy for skew rotation (using sine cosine, you have to have skew origin for angle calculation) and then copy pixels to empty image. Just high school math