There are images I want to remove the background of (or set it to transparent rather). For that reason, I have tested a bash imagick command that looks like this:
convert test.jpg -alpha set -channel RGBA -bordercolor white -border 1x1 -fuzz 2% -fill none -floodfill +0+0 white -shave 1x1 test.png
Because I need to use this in my php script, I need to translate this over now. What I've come up with is this:
$imagick->readImage($path);
$imagick->setImageAlphaChannel(Imagick::ALPHACHANNEL_SET);
$imagick->borderImage('white', 1, 1);
$imagick->floodFillPaintImage('transparent', 20, 'white', 0, 0, false);
$imagick->shaveImage(1, 1);
$imagick->writeImage(str_replace('.jpg', '.png', $path));
From what I can tell, it generates the image and it removes big parts of the background. But the fuzziness setting seems to be ignored.
The results of this script are always the same as when I use -fuzz 0% in the command prompt, no matter what fuzziness value I pass. Am I doing something wrong or is there a bug (which would leave me searching for another script capable of doing this)?
Am I doing something wrong or is there a bug
Let's call it a documentation error.
Imagick::floodFillPaintImage (and most of the other functions that take a fuzz parameter) need to have the fuzz scaled to the quantum range that ImageMagick was compiled with. e.g. for ImageMagick compiled with 16 bits of depth, the quantum range would be (2^16 - 1) = 65535
There is an example at http://phpimagick.com/Imagick/floodFillPaintImage
$imagick->floodFillPaintImage(
$fillColor,
$fuzz * \Imagick::getQuantum(),
$targetColor,
$x, $y,
$inverse,
$channel
);
So the reason that you're seeing the output image be the same as if you had passed in 0 fuzz, is that ImageMagick is interpreting the value of 2 that you are passing in as 2 / 65535 .... which is approximately zero.
Related
I want use OCR. But the images can't read perfectly, so i'm converting image to delete noise background, Original Images.
then, i'm run this command :
convert -colorspace gray -modulate 120 -contrast-stretch 10%x80% -modulate 140 -gaussian-blur 1 -contrast-stretch 5%x50% +repage -negate -gaussian-blur 4 -negate -modulate 130 original.jpeg clean.jpeg
Images Result
The problem is, how to convert above command to php?
Well, i'm very confusing using imagick in php.
mycode (this what i know) :
$image = new Imagick('captcha.png');
$image->modulateImage(450, 0, 500);
$image->writeImage("output.jpg");
Result from PHP Imagick : HERE
I know, it's diffrent configuration number, but result not to far.
Any suggestions how?
==== answare (thank you fmw42)
$image = new Imagick('captcha.png');
$image->thresholdimage(0.1 * \Imagick::getQuantum(), 134217727);
$image->shaveImage(2, 1);
$image->writeImage("output.jpg");
To remove the black border and threshold your image in ImageMagick, do
Input:
convert img.png -shave 1x1 -threshold 0 result.png
Because the 8 and 7 touch, I would be surprised if OCR worked.
For Imagick, see
https://www.php.net/manual/en/imagick.thresholdimage.php
https://www.php.net/manual/en/imagick.shaveimage.php
I was working on image filters using PHP, but I am unable to find some filters like those at https://pinetools.com/. In particular, the ones listed below:
Clip Image
Adjust channels
Change Image exposure
Vibrance
I couldn't find these filters' solution in PHP GD filters or in PHP Imagick, based upon ImageMagick.
Is there any solution for this. Are there any of these filters there that I could not find?
How do I apply these filters to an image in PHP Imagick?
I know the question is broad please don't devote it, I thought it strange to post a separate question for each filter?
I have worked out the equivalent of Pinetools clip image in ImageMagick commands for values between 0 and 50.
Input:
For example at 50, clip image produces the following:
The following ImageMagick command reproduces that:
convert lena.jpg -black-threshold 50% -white-threshold 50% clip_image_imagemagick.png
The functions to use in Imagick are:
http://us3.php.net/manual/en/imagick.blackthresholdimage.php
http://us3.php.net/manual/en/imagick.whitethresholdimage.php
I do not know Imagick, but looking at the documentation, it appears that it wants a threshold value as a color, so try "gray(50%)" when the clip image value is 50.
Similarly, I have worked out the equivalent of Pinetools Adjust colors.
Here is the result for Pinetools red adjust 50.
In ImageMagick, that would be:
convert lena.jpg -channel r -level 0x50% +channel adjust_red_50.png
And the Imagick command is:
http://us3.php.net/manual/en/imagick.levelimage.php
But I am not sure what the values are. I believe they may be number in the range of 0 to quantum range. So if your IM version is Q16, then 0 to 65535 and if Q8, then 0 to 255. So 50% in Q16 would be 65535/2=32767.5. So
levelImage (0, 1.0, 32767.5, $channel = Imagick::CHANNEL_RED );
For Pinetools exposure 50, I can come close using the ImageMagick command -evaluate add.
Pinetools Exposure 50:
The ImageMagick command would be:
convert lena.jpg -evaluate add 40% lena_add_40%.png
And for Pinetools Exposure 100:
And the ImageMagick command would be twice that:
convert lena.jpg -evaluate add 80% lena_add_80%.png
The Imagick command for 50 would likely be:
http://us3.php.net/manual/en/imagick.evaluateimage.php
evaluateImage(Imagick::EVALUATE_ADD, 26214);
where 65535*40/100=26214
For Pinetools Vibrance 50:
You can do that in ImageMagick by changing colorspace to HSL (or HCL or similar), then applying sigmoidal-contrast to the Saturation/Chroma channel.
A close equivalent would be:
convert lena.jpg -colorspace HSL -channel g -sigmoidal-contrast 3,0% +channel -colorspace sRGB tmp.jpg
In Imagick, you would change colorspace using:
http://us3.php.net/manual/en/imagick.transformimagecolorspace.php
Then apply sigmoidalcontrastImage to the Saturation channel which in HSL would be the green channel:
http://us3.php.net/manual/en/imagick.sigmoidalcontrastimage.php
Then convert the colorspace back to (s)RGB.
Note that sigmoidal contrast is non-linear. You want to set the mid-point to 0% so that the straight part of the curve is at 0 and the curved part that curves over to near flat is at the top right. So it is like a non-linear brightness control on the Saturation.
You won't find one-to-one matching of those filters. But you can get similar effects in Imagick (or ImageMagick directly) with the following, though argument control is different.
clip image is http://us3.php.net/manual/en/imagick.contraststretchimage.php
adjust channels is
http://us3.php.net/manual/en/imagick.levelimage.php
change exposure is also
http://us3.php.net/manual/en/imagick.levelimage.php
There is no vibrance exact equivalence, but you can change saturation using:
http://us3.php.net/manual/en/imagick.modulateimage.php
If you are on Unix-like systems, you can use PHP exec() and run some of my bash ImageMagick shell scripts. I have several for vibrance and exposure (called xposure) and one for brightness-contrast adjustment. See http://www.fmwconcepts.com/imagemagick/index.php
I need to change each hue of yellow to blue, and each hue of dark gray to light gray in PNG images with transparency.
The problem is:
I can't use Photoshop, because I have 100 images, and I need to change hues many time.
I can't use Image Magick, because I need more sophisticated calculations, than '-fx' can do.
I can't use PHP imagefrompng(), because this nasty crap not works with a lot of my images,
even with all suggested fixes like:
$background = imagecolorallocate($png, 255, 255, 255);
// removing the black from the placeholder
imagecolortransparent($png, $background);
// turning off alpha blending (to ensure alpha channel information is preserved, rather than removed (blending with the rest of the image in the form of black))
imagealphablending($png, true);
// turning on alpha channel information saving (to ensure the full range of transparency is preserved)
imagesavealpha($png, true);
and so on. It works with some images, but not with others.
All I need is a PNG library (maybe not in PHP), that can give me red, green, blue and alpha component of a pixel at coordinates x, y, and then set this pixel after my calculations, eg:
$rgba = getrgba($image, $x, $y);
$rgba = my_function($rgba);
setrgba($image, $x, $y, $rgba);
Maybe you can suggest libraries in other languages, not only PHP?
If you don't mind using Python check out Pillow, specifically its PixelAccess class. These threads (1, 2) should be helpful and have some code examples.
Method 1
If you just want to get at the raw pixel values of R,G,B and Alpha without worrying about compression and encoding, use ImageMagick to convert your image to plain, uncompressed, unencoded binary and read it and process it to your heart's content.
So, if we make a 1x1 pixel PNG file with RGBA(0,64,255,0.5) to test with:
convert -size 1x1 xc:"rgba(0,64,255,0.5)" a.png
Now we can get ImageMagick to make a raw, RGBA file that you can read and process as you wish with whatever language you wish at whatever level of complexity that you wish:
convert a.png rgba:data.bin
and now we can look in that file:
xxd data.bin
Result
0000000: 0040 ff80 .#..
There you can see and read all the RGBA pixels. When you are finished, just do the opposite to get back a PNG - note that you must tell ImageMagick the size first since it cannot know this:
convert -size 1x1 rgba:data.bin new.png
Note that ImageMagick is quite a large package to install, and you can achieve much the same as the above with the much lighter-weight vips package:
vips VipsForeignSaveRaw a.png data.rgb
Method 2
Alternatively, if you want your data as uncompressed, human-readable ASCII, use the venerable NetPBM formats of PPM (Portable Pixmap) and PGM (Portable Greymap) - see NetPBM on Wikipedia.
Make a 4x1 image and write as PPM:
convert -size 4x1 xc:"rgba(0,64,255,0.1)" -compress none ppm:-
P3
4 1
65535
0 16448 65535 0 16448 65535 0 16448 65535 0 16448 65535
You can see the 4 repeated RGB values there hopefully. If you want it as a file, just change the ppm:- at the end with someFile.ppm.
Make same image again and extract Alpha channel to separate file:
convert -size 4x1 xc:"rgba(0,64,255,0.1)" -alpha extract -compress none pgm:-
P2
4 1
65535
6554 6554 6554 6554
Hopefully you can see that 6554 is 0.1 on a scale of 0-65535.
If you just want 8-bit data on any of the above, add in -depth 8.
Method 3
As Glenn suggests in the comments, another option is to omit the -compress none on Option 2 which will give you a very similar file format except the pixel data will be in binary, after the header which remains in ASCII. This is generally faster and smaller.
I'm attempting to try and assign a value to an image based on its 'saturation level', to see if the image is black and white or color. I'm using Imagick, and have found what seems to be the perfect code for the command line and am trying to replicate it using the PHP library.
I think I understand the concept:
Convert image to HSL.
Extract the 'g' channel (which is the S channel in HSL).
Calculate the mean of this channel.
Command line code
convert '$image_path' -colorspace HSL -channel g -separate +channel -format '%[fx:mean]' info:
My PHP code
$imagick = new Imagick($image_path);
$imagick->setColorspace(imagick::COLORSPACE_HSL);
print_r($imagick->getImageChannelMean(imagick::CHANNEL_GREEN));
Output
My PHP code isn't outputting the same sort of values as the command line code, though. For example, a grayscale image gives 0 for the command line code, but the PHP code gives [mean] => 10845.392051182 [standardDeviation] => 7367.5888849872.
Similarly, another grayscale image gives 0 vs. [mean] => 31380.528443457 [standardDeviation] => 19703.501101904.
A colorful image gives 0.565309 vs. [mean] => 33991.552881892 [standardDeviation] => 16254.018540044.
There just doesn't seem to be any kind of pattern between the different values. Am I doing something obviously wrong?
Thanks.
Just to add, I've also tried this PHP code
$imagick = new Imagick($image_path);
$imagick->setColorspace(imagick::COLORSPACE_HSL);
$imagick->separateImageChannel(imagick::CHANNEL_GREEN);
$imagick->setFormat('%[fx:mean]');
But I get an Unable to set format error when I try and set the format. I've also tried setFormat('%[fx:mean] info:'), setFormat('%[mean]'), setFormat('%mean'), etc.
Update — FIXED!
Thanks to #danack for figuring out I needed to use transformImageColorspace() and not setColorspace(). The working code is below.
$imagick = new Imagick($image_path);
$imagick->transformImageColorspace(imagick::COLORSPACE_HSL);
$saturation_channel = $imagick->getImageChannelMean(imagick::CHANNEL_GREEN);
$saturation_level = $saturation_channel['mean']/65535;
setFormat doesn't replicate the command line option -format - the one in Imagick tries to set the image format, which should be png, jpg etc. The one in the command line is setting the format for info - the closest match for which in Imagick is calling $imagick->identifyImage(true) and parsing the results of that.
Also you're just calling the wrong function - it should be transformImageColorspace not setColorSpace. If you use that you can use the statistics from getImageChannelMean.
There are other ways to test for greyness which may be more appropriate under certain circumstances. The first is to convert the a clone of the image to grayscale, and then compare it to the original image:
$imagick = new Imagick($image_path);
$imagickGrey = clone $imagick;
$imagickGrey->setimagetype(\Imagick::IMGTYPE_GRAYSCALE);
$differenceInfo = $imagick->compareimages($imagickGrey, \Imagick::METRIC_MEANABSOLUTEERROR);
if ($differenceInfo['mean'] <= 0.0000001) {
echo "Grey enough";
}
That would probably be appropriate if you image had areas of color that were also transparent - so they theoretically have color, but not visibly.
Or if you only care about whether the image is of a grayscale format:
$imageType = $imagick->getImageType();
if ($imageType === \Imagick::IMGTYPE_GRAYSCALE ||
$imageType === Imagick::IMGTYPE_GRAYSCALEMATTE) {
//This is grayscale
}
I found a command line here:
convert image.png -colorspace HSL -channel g -separate +channel -format "%[fx:mean]" info:
It prints a number between 0 and 1, where zero means grayscale.
If your images have tint. (from scanner as example) you should do auto color for them before detecting gray scale.
You should normalizeImage(imagick::CHANNEL_ALL) for all separate channels of image. separateImageChannel()
But
What is the Imagick equivalent of following Imagemagick command?
convert i.jpg -set colorspace RGB ( -clone 0 -fill black -colorize 100% ) ( -clone 0 colorspace gray -negate ) -compose blend -define compose:args=70,30 -composite o.jpg
I did the following Imagick commands, but it doesnt seem to be the same
$img = new Imagick("i.jpg");
$img->setImageColorspace(Imagick::COLORSPACE_RGB);
$clone1 = $img->clone();
$clone1->colorizeImage('black', 1.0);
$clone2 = $img->clone();
$clone2->setImageColorspace(Imagick::COLORSPACE_GRAY);
$clone2->negateImage(0);
$img->setOption('compose:args', '70x30');
$img->compositeImage($clone1, Imagick::COMPOSITE_BLEND, 0, 0);
$img->compositeImage($clone2, Imagick::COMPOSITE_BLEND, 0, 0);
$img->writeImage("o.jpg");
Where have I made mistakes?
For the most part, what you have is correct. Two minor issues will need to be addressed to match the CLI result.
First
Move the setOption line before reading any images in the Imagick object.
$img = new Imagick();
$img->setOption('compose:args', '70x30');
$img->readImage("i.jpg");
// ...
Secound
Colorizing the black color to 100% usually results in a solid black image. For whatever reason, there's no effect with MagickColorizeImage method. There's some work-arounds using ImagickDraw & background-color assignment listed within the comments on PHP.net's documentation. I'd recommend revisiting your first $clone1 image.