PNG processing WITHOUT Image Magick and imagefrompng() - 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.

Related

How to reduce quality (filesize) for animated gif with PHP (ImageMagick)?

I want to reduce the quality of an animated GIF to reduce GIF filesize. Animation and dimensions should stay the same.
Is this even possible?
I've tried various Imagemagick functions, but with no luck. Maybe someone has done this?
This is just a logical thinking, but it would be cool, if something like this would work:
$gif = new Imagick($tempFilePath);
$gif = $gif->coalesceImages();
foreach ($gif as $frame) {
$frame->setImageCompression(8);
$frame->setImageCompressionQuality(10);
}
$gif = $gif->deconstructImages();
$gif->writeImages($tempFilePath, true);```
GIF does not use compression. So your compression arguments will not do anything. In ImageMagick command line, the simplest way is just to reduce the number of colors in the animation.
Input (Mount St. Helens colorized):
convert animation.gif -coalesce +dither -colors 64 -layers optimize animation2.gif
I am not an Imagick expert, but I think what you want is quantizeImages, which I believe will reduce colors for an animation or set of images. See also the example at quantizeImage to reduce the colors of one single image.
Note that I first use -coalesce to fill out the frames. I use +dither to avoid dithering when reducing colors. And finally I re-optimize the animation.
However, the better way is to use one common color map for all frames and use as few colors as possible. Then also do layer optimization on the animation. This can be done as follows on this simple (extremely small number) 3-color example.
Input (Mount St. Helens colorized):
3-color Color Table (enlarged for viewing):
convert xc:red xc:green1 xc:blue +append colortable.gif
convert animation.gif -coalesce +dither -remap colortable.gif -layers optimize new_animation.gif
More practically, you can create a reduced color table by collecting all the colors from all the combined animation frames, reduce the colors and then get the unique colors. For example:
convert animation.gif -coalesce +append +dither -colors 64 -unique-colors -depth 8 colortable2.gif
Then apply this color table to the animation
convert animation.gif -coalesce +dither -remap colortable2.gif -layers optimize new_animation2.gif
The file sizes are:
animation.gif --- 481 K
animation2.gif (64 colors) --- 479 K
new_animation (3 colors) --- 57 K
new_animation2 (64 colors) --- 393 K
For Imagick, see the following methods:
coalesce
remap
append
uniquecolors
quantizeimage for reduced colors

How to apply special filters to image using 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

PHP Imagick (ImageMagick) RGB > CMYK with Flat Black

I'm using PHP Imagick to convert PNG images generated in PhantomJS to TIF CMYK,
for print purposes I need a flat Black (cmyk - 0,0,0,100) - the conversion generates blacks like (cmyk - 58,49,44,89).
I'm converting the images using color profile (section of my code below) -> the code is based on Convert image from RGB to CMYK with Imagick
is it possible to force a flat black with Imagick ? do you know any other tools that might help ?
thanks,
if ($has_icc_profile === false) {
$icc_rgb = file_get_contents( '/srgb_profiles' . '/sRGB.icc');
$image->profileImage('icc', $icc_rgb);
unset($icc_rgb);
}
// then we add an CMYK profile
$icc_cmyk = file_get_contents( '/cmyk_profiles'.'/JapanColor2002Newspaper.icc');
$image->profileImage('icc', $icc_cmyk);
UPDATE :
after checking online I think I'm looking for a UCR en.wikipedia.org/wiki/Under_color_removal method for ImageMagick - I found that convert old versions supported under color removal
-undercolor <undercolor factor>x<black-generation factor>
control undercolor removal and black generation on CMYK images.
This option enables you to perform undercolor removal and black generation on CMYK images-- images to be printed on a four-color printing system. You can con- trol how much cyan, magenta, and yellow to remove from your image and how much black to add to it. The standard undercolor removal is 1.0x1.0. You'll frequently get better results, though, if the percentage of black you add to your image is slightly higher than the percentage of C, M, and Y you remove from it. For example you might try 0.5x0.7. (http://www.chemie.fu-berlin.de/chemnet/use/suppl/imagemagick/www/convert.html) -
apparently the option is not supported anymore, I'm interested if anyone knows if UCR is the solution I'm looking for and if anyone knows if it's supported or if I'm supposed to use a different method to get the same result.
If you use ImageMagick's convert at the command line like this to generate a grayscale ramp, 1 pixel wide and 256 pixels tall, going from white to black and convert it to CMYK colorspace and then show it as text, you get what you want:
convert -size 1x256 'gradient:rgb(255,255,255)-rgb(0,0,0)' -colorspace cmyk txt:
# ImageMagick pixel enumeration: 1,256,65535,cmyk
0,0: (0%,0%,0%,0%) #0000000000000000 cmyk(0,0,0,0)
0,1: (0%,0%,0%,0.392157%) #0000000000000101 cmyk(0,0,0,1)
0,2: (0%,0%,0%,0.784314%) #0000000000000202 cmyk(0,0,0,2)
0,3: (0%,0%,0%,1.17647%) #0000000000000303 cmyk(0,0,0,3)
0,4: (0%,0%,0%,1.56863%) #0000000000000404 cmyk(0,0,0,4)
0,5: (0%,0%,0%,1.96078%) #0000000000000505 cmyk(0,0,0,5)
0,6: (0%,0%,0%,2.35294%) #0000000000000606 cmyk(0,0,0,6)
0,7: (0%,0%,0%,2.7451%) #0000000000000707 cmyk(0,0,0,7)
0,8: (0%,0%,0%,3.13725%) #0000000000000808 cmyk(0,0,0,8)
0,9: (0%,0%,0%,3.52941%) #0000000000000909 cmyk(0,0,0,9)
0,10: (0%,0%,0%,3.92157%) #0000000000000A0A cmyk(0,0,0,10)
...
...
0,249: (0%,0%,0%,97.6471%) #000000000000F9F9 cmyk(0,0,0,249)
0,250: (0%,0%,0%,98.0392%) #000000000000FAFA cmyk(0,0,0,250)
0,251: (0%,0%,0%,98.4314%) #000000000000FBFB cmyk(0,0,0,251)
0,252: (0%,0%,0%,98.8235%) #000000000000FCFC cmyk(0,0,0,252)
0,253: (0%,0%,0%,99.2157%) #000000000000FDFD cmyk(0,0,0,253)
0,254: (0%,0%,0%,99.6078%) #000000000000FEFE cmyk(0,0,0,254)
0,255: (0%,0%,0%,100%) #000000000000FFFF cmyk(0,0,0,255)
You must be doing something different - maybe this will help you work it out. I am guessing it is your ICC profiles but you can experiment with the above command.
If you just want to experiment with spot values, you can just have IM translate a single pixel like this:
convert -size 1x1 xc:#000000 -colorspace cmyk txt:
# ImageMagick pixel enumeration: 1,1,65535,cmyk
0,0: (0%,0%,0%,100%) #000000000000FFFF cmyk(0,0,0,255)
or maybe more simply like this:
convert -size 1x1 xc:#000000 -depth 8 -colorspace cmyk txt:
# ImageMagick pixel enumeration: 1,1,255,cmyk
0,0: (0,0,0,255) #000000FF cmyk(0,0,0,255)
Note the following though:
You must put profiles between input image and output image names on the command line.
If your image has no embedded profile, the first profile you give is applied to the input image and the second to the output image. If your input image does have a profile, the first profile you give is applied to the output image.

72DPI PNG to a 300DPI PDF + Cutting Cross

I have a flash which outputs a PNG file in 72DPI, I need to execute the following steps (not necessarily in that order) in order to make it ready for print:
Convert to 300DPI
Resize to 9x5 cm
Put cross (for cutting later) on it
Save as PDF - 300DPI
With my little of tutorial reading + knowledge in IM, I got this far:
1. convert -units PixelsPerInch IMAGE.PNG -resample 300 IMAGE_300DPI.png
2. convert IMAGE_300DPI.png -resize 1111x639 IMAGE_300DPI_correct_size.png
3. composite -gravity center IMAGE_300DPI_correct_size.png cross.png card_new_with_cross.png
4. convert card_new_with_cross.png card_new.pdf
When I execute stage #4 everything changes and gets enlarged as far as I can tell, any Ideas?
by the way - 2 files (sideA.png & cross.png) can be found at:
http://www.bikur.co.il/sideA.png <-- the image
http://www.bikur.co.il/cross.png <-- the `cross-
It would be great if someone can help me with it
DPI is a conversion factor, not a measurement. It's used to convert an image's intrinsic pixels into some display medium's pixel.
e.g. a 300 dpi printer has "pixels" that are 1/300 inches in size, while a 72dpi monitor has 1/72 inch sized pixels. A 100x100#72dpi image on a monitor would have to be printed out at about 417x417 printer pixels to have the same apparent size (300/72 = 4.16666)
So, whatever size your image is initially, to get a 9x5cm image # 300dpi, it'd have to be converted as follows:
9 x 5cm = 3.543 x 1.968 inches # 300dpi = 1063 x 591

PHP Imagick API: How to change the Hue of an Image to known hexadecimal value

I create template websites. If a client choose a blue or green or purple heading, I don't want to have to store all those different color variations of an image. I want to programmatically change the hue. I do not want to 'flood fill' it because that would remove any textures or bevels.
For example page you see I have accomplished exactly what I want.
http://sta.oursitesbetter.com/test/index.php
I have done this using Imagick modulateImage function.
HOWEVER, I am just throwing random 'Hue' values and not RGB values. I want to accomplish this same thing feeding RGB values. I need a function similar to modulateImage however it must take RGB as value and set the image to that hue.
I have studied for the past 5 hours and cannot figure out how to do it. I need help.
Has any of the gurus of StackOverflow got a PHP Imagick solution to this color quandary?
From Wikipedia: Hue #Computing hue from RGB:
$hex=(sqrt(3)*($green-$blue)) /
(2*($red-$green-$blue));//$red, $green and $blue are each a value in the range 0->255
$img->modulateImage(100, 100, intval($hex*100/256));
//probably 256->100 value above will work, if didn't , try with the following insead.
//$img->modulateImage(100, 100, $hex);
Another option might be to use ImageMagick to create a single pixel image in the RGB colour you want and then see what ImageMagick makes that in HSL colorspace. Say your RGB values are 25,126,200, you could do this:
convert -size 1x1! xc:rgb\(25,126,200\) -colorspace HSL txt:-
# ImageMagick pixel enumeration: 1,1,65535,hsl
0,0: (57.0474%,77.7783%,44.1184%) #920AC71C70F1 hsl(57.0474%,77.7783%,44.1184%)
The following equivalent methods are maybe a little more succinct:
convert xc:rgb\(25,126,200\) -format "%[fx:100*p{0,0}.hue]" info:
57.0476
convert xc:rgb\(25,126,200\) -format "%[fx:100*hue]" info:
57.0476
So, IM makes that a Hue of 57.0474%. Of course you don't need to run that and parse the text output, I am only doing that to demonstrate the concept - you can access the pixel directly in PHP.

Categories