PHP/GD Gaussian Blur Effect - php

I need to obfuscate a certain area of an image using PHP and GD, currently I'm using the following code:
for ($x = $_GET['x1']; $x < $_GET['x2']; $x += $pixel)
{
for ($y = $_GET['y1']; $y < $_GET['y2']; $y += $pixel)
{
ImageFilledRectangle($image, $x, $y, $x + $pixel - 1, $y + $pixel - 1, ImageColorAt($image, $x, $y));
}
}
This basically replaces the selected area with squares of $pixel pixels. I want to accomplish some kind of blur (gaussian preferably) effect, I know I can use the ImageFilter() function:
ImageFilter($image, IMG_FILTER_GAUSSIAN_BLUR);
But it blurs the entire canvas, my problem is that I just want to blur a specific area.

You can copy a specific part of the image into a new image, apply the blur on the new image and copy the result back.
Sort of like this:
$image2 = imagecreate($width, $height);
imagecopy ( $image2 , $image , 0 , 0 , $x , $y , $width , $height);
imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR);
imagecopy ($image, $image2, $x, $y, 0, 0, $width, $height);

I did not check the documentation for imagefilter and I don't know if this is impossible or if there is an equivalent to applying this to (a part) of an image. But assuming there isn't, why not:
Copy the part you want to blur to a new (temporary) GD image (no need to write it to disk, just assign it to a new temp variable).
Apply gaussian blur filter to this temporary image.
Copy the resulting (filtered) image right back where it came from (functionality to do this is definitely in the GD library)

Related

php - Best way to blur images

I'm trying to blur first image like third one,But I cant do it ! :(
header('Content-Type: image/jpeg');
$image = imagecreatefromjpeg('1.jpg');
for ($x=1; $x<=40; $x++){
imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR,999);
}
imagefilter($image, IMG_FILTER_SMOOTH,99);
imagefilter($image, IMG_FILTER_BRIGHTNESS, 10);
imagejpeg($image);
imagedestroy($image);
Try to scale down the image and apply the loop with gaussian blur on the resized image. That way you will save some performance on large images:
header('Content-Type: image/jpeg');
$file = '1.jpg';
$image = imagecreatefromjpeg($file);
/* Get original image size */
list($w, $h) = getimagesize($file);
/* Create array with width and height of down sized images */
$size = array('sm'=>array('w'=>intval($w/4), 'h'=>intval($h/4)),
'md'=>array('w'=>intval($w/2), 'h'=>intval($h/2))
);
/* Scale by 25% and apply Gaussian blur */
$sm = imagecreatetruecolor($size['sm']['w'],$size['sm']['h']);
imagecopyresampled($sm, $image, 0, 0, 0, 0, $size['sm']['w'], $size['sm']['h'], $w, $h);
for ($x=1; $x <=40; $x++){
imagefilter($sm, IMG_FILTER_GAUSSIAN_BLUR, 999);
}
imagefilter($sm, IMG_FILTER_SMOOTH,99);
imagefilter($sm, IMG_FILTER_BRIGHTNESS, 10);
/* Scale result by 200% and blur again */
$md = imagecreatetruecolor($size['md']['w'], $size['md']['h']);
imagecopyresampled($md, $sm, 0, 0, 0, 0, $size['md']['w'], $size['md']['h'], $size['sm']['w'], $size['sm']['h']);
imagedestroy($sm);
for ($x=1; $x <=25; $x++){
imagefilter($md, IMG_FILTER_GAUSSIAN_BLUR, 999);
}
imagefilter($md, IMG_FILTER_SMOOTH,99);
imagefilter($md, IMG_FILTER_BRIGHTNESS, 10);
/* Scale result back to original size */
imagecopyresampled($image, $md, 0, 0, 0, 0, $w, $h, $size['md']['w'], $size['md']['h']);
imagedestroy($md);
// Apply filters of upsized image if you wish, but probably not needed
//imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR);
//imagefilter($image, IMG_FILTER_SMOOTH,99);
//imagefilter($image, IMG_FILTER_BRIGHTNESS, 10);
imagejpeg($image);
imagedestroy($image);
I have borrowed the downsizing idea and some of the code form this answer
I have tested the solution with your image, and the result:
You can elaborate and increase/decrease the blur by changing the number of loops for the small image. If you change for ($x=1; $x <=40; $x++){ to for ($x=1; $x <=75; $x++){ you will get more blur.
The downside to this solution is that you will get som visible pixelation because of the resizing going on. The upside is better performance, less server load and execution time compared to if you would apply the blur loop on the original sized image.
i also faced this problem in my project that time i use some long code but i think that, that code is not proper and create my own small code, here is that code
header("content-type: image/png");
$image = imagecreatefromjpeg("abc.jpg");
for ($x=1; $x<=50; $x++)
{
imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR);
}
imagepng($image);
imagedestroy($image);
A note on the IMG_FILTER_SMOOTH steps in Michael K's answer:
imagefilter($sm, IMG_FILTER_SMOOTH,99);
According to PHP:imagefilter
IMG_FILTER_SMOOTH
Applies a 9-cell convolution matrix where center pixel has the weight arg1 and others weight of 1.0. The result is normalized by dividing the sum with arg1 + 8.0 (sum of the matrix).
any float is accepted, large value (in practice: 2048 or more) = no change
So the arg1 in imagefilter() weighs the center pixel of the 3x3-kernel in relation to the other pixels. A value of 99 will not change anything and can be skipped. For more blur, you want to use a small arg1 (below 8).
I had similar issue coding with Laravel, I used following solution:
$image = \Image::make($path);
$width = $image->getWidth();
$height = $image->getHeight();
$image->resize(round($width / 5), round($height / 5));
$image->blur(10);
$image->resize($width, $height);
$image->blur(70);
$image->save($path, 60, 'jpg');
Laravel uses http://image.intervention.io/, it can be used standalone.
You can change how much times to resize the image, how much blur to apply on small and enlarged image afterwards.
easy
header('Content-Type: image/jpeg');
$image = imagecreatefromjpeg('1.jpg');
$image = imagescale($image , $YOU_IMG_WIDTH/40, $YOU_IMG_WIDTH/40);
imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR);
$image = imagescale($image , $YOU_IMG_WIDTH, $YOU_IMG_HEIGHT);
imagejpeg($image);
imagedestroy($image);

Rendering a coloured background with transparent foreground in PHP

I am rendering audio waveforms directly from MP3's on the fly as they are uploaded to the server. My upload script currently saves both the original mp3 and renders the waveform to a png.
Currently I render the background first to a rectangle. This code produces either a transparent or coloured background dependant upon the value of $background:
I am trying to create a transparent png overlay in PHP.
if (!$img) {
// create original image width based on amount of detail
// each waveform to be processed with be $height high, but will be condensed
// and resized later (if specified)
$img = imagecreatetruecolor($data_size / DETAIL, $height * sizeof($wavs_to_process));
// fill background of image
if ($background == "") {
// transparent background specified
imagesavealpha($img, true);
$transparentColor = imagecolorallocatealpha($img, 0, 0, 0, 127);
imagefill($img, 0, 0, $transparentColor);
} else {
list($br, $bg, $bb) = html2rgb($background);
imagefilledrectangle($img, 0, 0, (int) ($data_size / DETAIL), $height * sizeof($wavs_to_process), imagecolorallocate($img, $br, $bg, $bb));
}
}
I then loop through the data points of a dynamically resampled MP3 and for each point I draw a line onto this background which renders the waveform image.
I use this code to produce the waveform image:
// don't print flat values on the canvas if not necessary
if (!($v / $height == 0.5 && !$draw_flat))
// draw the line on the image using the $v value and centering it vertically on the canvas
imageline(
$img,
// x1
(int) ($data_point / DETAIL),
// y1: height of the image minus $v as a percentage of the height for the wave amplitude
$height * $wav - $v,
// x2
(int) ($data_point / DETAIL),
// y2: same as y1, but from the bottom of the image
$height * $wav - ($height - $v),
imagecolorallocate($img, $r, $g, $b)
);
} else {
// skip this one due to lack of detail
fseek($handle, $ratio + $byte, SEEK_CUR);
}
}
This works perfectly, however I need to create the waveform as a transparent overlay in order to place it over a div with a CSS gradient. So, I need to render the background as #ffffff and the actual waveform itself needs to be transparent. In essence I need the transparency inverted on the produced png's.
I have tried using:
imagecolorallocatealpha($img, 0, 0, 0, 127)
On the waveform rendering portion but always seem to just end up with a coloured rectangle with no visible waveform and I'm not sure where I am going wrong.
Any help would be greatly appreciated.
Please try the following. Disable blending mode­Docs for the image:
imagealphablending($img, FALSE);
This will enable that you can set pixels with alpha information directly. To do so, you need to allocate the color with the alpha­Docs set to 100% transparent as well:
imagecolorallocatealpha($img ,$r, $g, $b, $alpha = 127);
BTW, you can allocate the color once and then re-use it, so you don't need to call the imagecolorallocatealpha function that often.
Let me know if this works.

PHP GD poor thumb quality

$tmp_im = imagecreatetruecolor($width, $height);
$x = $this->getX();
$y = $this->getY();
$w = floor($resize_height * ($x / $y));
$h = $resize_height;
$this->tmp_im = imagecreatetruecolor($w, $h);
imagealphablending($this->tmp_im, false);
imagesavealpha($this->tmp_im, true);
imagecopyresampled($this->tmp_im, $this->im, 0, 0, 0, 0, $w, $h, $this->getX(), $this->getY());
$this->im = $this->tmp_im;
$hs = floor(($width - $this->getX())/2);
$vs = floor(($height - $this->getY())/2);
imagecopy($tmp_im, $this->im, $hs, $vs, 0, 0, $this->getX(), $this->getY());
$this->im = $tmp_im;
the results is a poor quality resized image, what im doing wrong? i also tried to use a imagejpeg with quality = 100
Use imagecopytruecolor() instead of
imagecopy($tmp_im, $this->im, $hs, $vs, 0, 0, $this->getX(), $this->getY());
This is a Notice on the official PHP documentation page:
There is a problem due to palette image limitations (255+1 colors).
Resampling or filtering an image commonly needs more colors than 255,
a kind of approximation is used to calculate the new resampled pixel
and its color. With a palette image we try to allocate a new color, if
that failed, we choose the closest (in theory) computed color. This is
not always the closest visual color. That may produce a weird result,
like blank (or visually blank) images. To skip this problem, please
use a truecolor image as a destination image, such as one created by
imagecreatetruecolor().

PHP imagettftext baseline workaround

I am writing to print text to an image using PHP. However, the function imagettftext() uses the baseline, whereas I need the text vertically centered.
So, I either need a method to print text with y not the distance from top to baseline, but from top to top of bounding box OR I need a method using which I could determine the distance between top of bounding box and baseline.
Apparently, I am confusing you. So, to make it clear: I am aware of the function imagettfbbox(). Using that function I can determine height and width of resulting text box. Its height, however, is utterly useless for vertical alignment when printing with imagettftext(), because the Y parameter is not the distance to the top of the box (or even the bottom, but at least something I could have used having the height) but the distance to the baseline of the text within.
EDIT: Why am I not accepting the latest answer?
See my latest comment below the answer, and use this image as a reference.
I do not know if the answer still interested.However, the imagettfbbox() function give you more information than simply the height and the width of the bounding box. It's designed exactly to return information needed by the imagettftext() to manage the text as you want.
The trick lies in the fact that the coordinates returned from imagettfbbox() are not related to the absolute top left corner, but to the baseline of the font for the particular text. This is the reason because the box is specified in point coordinates, and these are often negative.
In short:
$dims = imagettfbbox($fontsize, 0, $font, $text);
$ascent = abs($dims[7]);
$descent = abs($dims[1]);
$width = abs($dims[0])+abs($dims[2]);
$height = $ascent+$descent;
...
// In the example code, for the vertical centering of the text, consider
// the simple following formula
$y = (($imageHeight/2) - ($height/2)) + $ascent;
This works perfectly for my projects.
Hope this help.
Sorry for english.
Marco.
Not entirely sure what your asking...can you give an example? Perhaps imagettfbbox is what you need?
// get bounding box dims
$dims = imagettfbbox($fontsize, 0, $font, $quote);
// do some math to find out the actual width and height
$width = $dims[4] - $dims[6]; // upper-right x minus upper-left x
$height = $dims[3] - $dims[5]; // lower-right y minus upper-right y
edit: Here is an example of vertically centered text
<?php
$font = 'arial.ttf';
$fontsize = 100;
$imageX = 500;
$imageY = 500;
// text
$text = "FOOBAR";
// create a bounding box for the text
$dims = imagettfbbox($fontsize, 0, $font, $text);
// height of bounding box (your text)
$bbox_height = $dims[3] - $dims[5]; // lower-right y minus upper-right y
// Create image
$image = imagecreatetruecolor($imageX,$imageY);
// background color
$bgcolor = imagecolorallocate($image, 0, 0, 0);
// text color
$fontcolor = imagecolorallocate($image, 255, 255, 255);
// fill in the background with the background color
imagefilledrectangle($image, 0, 0, $imageX, $imageY, $bgcolor);
$x = 0;
$y = (($imageY/2) - ($bbox_height/2)) + $fontsize;
imagettftext($image, $fontsize, 0, $x, $y , $fontcolor, $font, $text);
// tell the browser that the content is an image
header('Content-type: image/png');
// output image to the browser
imagepng($image);
// delete the image resource
imagedestroy($image);
?>

How to pick a color to make transparent in images?

First question, please be gentle ;-)
I've written an image class that makes simple things (rectangles, text) a bit easier, basically a bunch of wrapper methods for PHP's image functions.
What I'm trying to do now is to allow the user to define a selection, and have the following image operations only affect the selected area. I figured I'd do this by copying the image to imgTwo and deleting the selected area from it, do the following image operations on the original as usual, then when $img->deselect() is called, copy imgTwo back to the original, and destroy the copy.
is this the best way? obviously it will be tricky to define deselected areas within a selected area but I can live with that for now :)
Then, the way I'm erasing the selection from the copy is by drawing a rectangle in a transparent color, which is working - but I can't figure out how to choose that color while being sure it doesn't occur in the rest of the image. The input images in this application are true color PNGs, so no palette with color indexes (I think?).
There has to be a better way than to collect the colours of each individual pixel and then finding a color that doesn't appear in the $existing_colours array .. right?
PNG transparency works differently to GIF transparency - you don't need to define a particular colour as transparent.
Just use imagecolorallocatealpha() and make sure you've set imagealphablending() to false:
// "0, 0, 0" can be anything; 127 = completely transparent
$c = imagecolorallocatealpha($img, 0, 0, 0, 127);
// Set this to be false to overwrite the rectangle instead of drawing on top of it
imagealphablending($img, false);
imagefilledrectangle($img, $x, $y, $width - 1, $height - 1, $c);
The code ended up looking like this:
# -- select($x, $y, $x2, $y2)
function select($x, $y, $x2, $y2) {
if (! $this->selected) { // first selection. create new image resource, copy current image to it, set transparent color
$this->copy = new MyImage($this->x, $this->y); // tmp image resource
imagecopymerge($this->copy->img, $this->img, 0, 0, 0, 0, $this->x, $this->y, 100); // copy the original to it
$this->copy->trans = imagecolorallocatealpha($this->copy->img, 0, 0, 0, 127); // yep, it's see-through black
imagealphablending($this->copy->img, false); // (with alphablending on, drawing transparent areas won't really do much..)
imagecolortransparent($this->copy->img, $this->copy->trans); // somehow this doesn't seem to affect actual black areas that were already in the image (phew!)
$this->selected = true;
}
$this->copy->rect($x, $y, $x2, $y2, $this->copy->trans, 1); // Finally erase the defined area from the copy
}
# -- deselect()
function deselect() {
if (! $this->selected) return false;
if (func_num_args() == 4) { // deselect an area from the current selection
list($x, $y, $x2, $y2) = func_get_args();
imagecopymerge($this->copy->img, $this->img, $x, $y, $x, $y, $x2-$x, $y2-$y, 100);
}else{ // deselect everything, draw the perforated copy back over the original
imagealphablending($this->img, true);
imagecopymerge($this->img, $this->copy->img, 0, 0, 0, 0, $this->x, $this->y, 100); // copy the copy back
$this->copy->__destruct();
$this->selected = false;
}
}
For those curious, here are the two classes:
http://dev.expocom.nl/functions.php?id=104 (image.class.php)
http://dev.expocom.nl/functions.php?id=171 (MyImage.class.php extends image.class.php)

Categories