Using Imagemagick to replicate photoshop's "expand selection" functionality - php

I'm looking to use Imagemagick (via PHP) to process PNGs with transparent backgrounds and some semi-transparent pixels (like gradients). The images will have to be altered like so:
1. make all non-transparent pixels the same, solid color (black is fine)
2. expand the edges of the shapes within the image by a 25 pixels
With this input (transparent BG, some pixels are semi-transparent):
I expect output like this:
This isn't just resizing the image. I'm looking for functionality similar to how photoshop expands a selection (for example, the gap in the top circle is negated because it's smaller than the 25 pixel expansion).
Currently, I have code that scans an image and finds the edges, but fails when there are multiple shapes that aren't connected within an image. Does Imagemagick have a way to do this or is there an algorithm I can use to scan an image and generate the output I need? Speed is a concern, but I can live with a slow solution as long as it works.

This answer is a work in progress, I think we can get you there though...
Basically, your step 1) means you want to set the RGB channels to black across the entire image, whilst leaving the transparency to determine the shapes - I think. Well, we can do that lots of ways, but let's use a threshold like this:
convert shapes.png -threshold 100% result.png
Now you want to expand the selection, but the selection is only really in the Alpha/transparency channel so we restrict our operations to that channel which will keep the speed up. In morphological terms, you are looking for a dilation, so you can do something like this:
convert shapes.png -threshold 100% -channel A -morphology dilate octagon:25 result.png
There are other shapes (disk, diamond, etc) and other degrees of dilation (I chose 25 dilations) - have a look at Anthony Thyssen's excellent ImageMagick Examples pages ... here.
Not sure what is going on with the gradations in transparency, so I have zapped them with an extra -threshold on the alpha channel:
convert shapes.png -threshold 100% -channel A -morphology dilate octagon:25 -threshold 99% result.png
I'll leave you to translate that into PHP - should be pretty easy.

Related

Color to Black and White from PDF to IMG to PDF again

Ok, so I am taking full color PDFs and trying to convert them to black and white images to then make them black and white PDF's.
Currently I am attempting to do this with imagemagick and i am for the most part successful with breaking them PDF into separate images. However once creating the separate images I want to make full size black and white copies next to the color copies as well as thumbnails thereof of both. Which that part I feel I'll be able to figure out on my own.
What I am having trouble figuring out is how to get them to become black and white. Not necessarily grayscale, since grayscale will still use colored ink in most printers to come up with the varying shades of gray. From which I am trying to avoid.
Is there a means I can do this with imagemagick or is my thought process all wrong?
Also worth noting is I am trying to do this through a browser and server side process with PHP
I think you are looking for "Two Colour Quamtisation" as described in Anthony Thyssen's excellent work here.
Basically, you use quantisation to pick the best two colours to represent your image, then force the darker one to black and the lighter one to white. So your processing becomes:
convert image.png -colors 2 -colorspace gray -normalize result.png
Btw, I presume you found and are using pdfimages (part of the poppler package) to extract the images in original quality.

How to specify an overlay image all four corners coordinates in php imagemagick

I am a beginner in Imagemagick / Imagick, so please bear with me. My issue looks rather simple, but I could not find a solution, or any help anywhere. Please take a look at the image below - I am trying to create an image, by specifying all four corners coordinates in pixels.
I looked at Affine Distortion but that function is too complicated for me, sometimes takes a while to be executed, and when it works - the results are very unpredictable (image rotation is wrong, there are parts of the image removed or misplaced, etc.) So if anyhow possible, I would not like to have to use Affine distortion.
Of course, if it is not possible to just specify each corner coordinates, a help in which direction I should be going is greatly appreciated. I would like to use Imagick, but if this is maybe better to do with GD or similar, I am open for that.
Without seeing source images, and what the expected result would be, I would assume you are attempting Perspective distortion. Meaning you want to take an image, and transform the MBR coordinates to a predefined set of points.
If I'm given a 400x400 checkerboard image, and want to distort it to the points listed above, the following ImageMagick command would work.
convert -size 400x400 pattern:checkerboard \
-virtual-pixel transparent \
-mattecolor transparent \
-distort Perspective '0,0 50,153 400,0 240,40 0,400 315,355 400,400 336,156' \
distorted_checkerboard.png
The -distort Perspective '... point matrix ...' reads as the following
Origin Point => Finial Point
0, 0 => 50,153 (top left)
400, 0 => 240, 40 (top right)
0,400 => 315,255 (bottom left)
400,400 => 336,155 (bottom right)

How to convert text to (mutiple) images exactly?

I am trying to convert pages of content, images, text and links to solely images, this would be easily done if it weren't for the fact the links have to stay the same (still work) (I have toyed with the idea converting links to hard text so they can be read but that changes the way the content's layout).
Both the text and images will use Inconsolata because it is a "Monospaced font" which allows us to assume that there will be 66 characters on each line providing the font is set to 18px and the max container (div) is 595px.
My plan was to count the characters and to get the sizes for creating the images and "cutting up the links" related question: How to count characters on a single html line with PHP.
A small example of what I am talking about would be to convert text like below (pretend it's text currently):
And using PHP or any web language that will do the job fast, work out where the link(s) are and to create images that allow the new image version to contain working links and is exactly identical to the text version in looks and function (links).
I would change the link background and foreground colour to a colour that does not occur in your image and render the page using webkit2png. Then find the blocks of colour corresponding to your link colour using ImageMagick to make an image map.
So, in concrete terms, let's say you change your HTML to set the foreground and background colours of links to red (#ff0000) so your HTML looks something like this:
<p>
A link to Google follows:
<a style="color:#ff0000;background:#ff0000" href="www.google.com">Google</a> - a link to Google.
</p>
Then you do
webkit2png a.html
which gives you a PNG file like this:
You then use ImageMagick to colour everything not red to black, like this:
convert fileUsersmarktmpahtml-full.png -colorspace RGB -fill black +opaque "#ff0000" x.png
Then you tell ImageMagick to trim the background, leaving the red block only, and look at its coordinates with identify and you can see where the link is to get the coordinates for your image map.
convert out.png -trim y.png
identify y.png
y.png PNG 47x18 800x600+176+16 8-bit sRGB 2c 3.18KB 0.000u 0:00.000
You can see the red block is at offset +176+16 into the image.
You may want to do one link at a time, and re-render, or multiple links. If you do multiple links, either ask another question about finding multiple blobs in an image, or search for other answers (by me) with the words connected-components in them. If you get stuck, say, on choosing unused colours, or finding multiple blobs, just ask another question - they are free :-)
If you have multiple links, more like this:
<!DOCTYPE html>
<html>
<body>
<p>Here comes a link...
<a style="color:#ff0000;background:#ff0000" href="www.google.com">Google</a> - a link to Google.</p>
<p>And there will be another (longer) one along shortly...
<a style="color:#ff0000;background:#ff0000" href="www.google.com">Google, but longer</a> - a link to Google.</p>
</body>
</html>
You run the webkit2png as above and then anayse like this:
convert out-full.png -colorspace RGB -fuzz 10% \
-fill black +opaque red \
-fill white -opaque red \
-define connected-components:verbose=true \
-define connected-components:area-threshold=100 \
-connected-components 4 -auto-level \
output.png
Output
Objects (id: bounding-box centroid area mean-color):
0: 800x600+0+0 399.8,301.1 476976 rgba(0,0,0,1)
2: 121x18+357+50 417.0,58.5 2178 rgba(255,255,255,1)
1: 47x18+140+16 163.0,24.5 846 rgba(255,255,255,1)
Now you can see the blobs corresponding to the links in the second and third rows. You can crop those, and the pieces either side, out of the image using ImageMagick's crop tool like this:
convert input.png -crop 121x18+357+50 firstLink.png
convert input.png -crop 47x18+140+16 secondLink.png
Have you considered using image map? With an image map you can define clickable hot-spots in an image. This way there would be no need to cut the image into multiples. The calculation of the link's coordinates should be possible the way you detailed in your question. As long as the font is monospaced (refering to halfer's comment)
image map docs
Well it seems that you are doing slightly more than converting text as much as converting HTML (maybe text only HTML) since you have the ability to have a link. And since you are talking about a div with a width you also need to perform word-wrapping (which in itself is actually harder than it sounds). When you start getting into things like that then you will start looking more and more at a HTML parser.
Wouldn't a imagemap suffice ?
What do you plan on doing when a link spans multiple lines? or non-english languages (some languages are read right to left instead of left to right)?
A imagemap is my initial choice, then if that's not acceptable then I would suggest moving away from PHP to a different language (I would recommend Java).
Can you provide some more details about the broader project you are trying to put together?

Imagemagick - Center an object in a given picture

I have a task to write a PHP script that makes objects in the center of their pictures, For example
Some of the pictures are horizontally oriented and some are vertically oriented based on that and the size of the white space I have to Crop/Add white space to the original picture.
The first method I had is by detecting the borders of the object within the picture but some picture doesn't have clear white background e.g:
This Picture has extra white space on the borders and has gray gradient as the background which makes it harder to detect the object's borders, So I tried to apply The Sobel operator by Imagemagick
exec("convert 1.jpg -define convolve:scale='50%!' -bias 50% -morphology Convolve Sobel -solarize 50% -level 50,0% ssc1.jpg");
The result was ok
And Now I have to find the borders in the filtered image, And the question is
What's the best way to find the coordinates of the borders (the output should be X1,X2,Y1,Y2)?
I have read some similar problems like this one that converts the image to text and remove white (black in my case) pixels but I'm not sure what's the best approach to solve this (I'm newbie in Image Processing).
I don't have time today to do this in ImageMagick, but I did an experiment in Photoshop and it seems to work nicely - at least for your lady on a gradated background.
I took the image and duplicated its layer in Photoshop (Cmd+J).
I set the Blending Mode of the upper layer to Hard Mix
I did Image->Trim from the menu at top of screen.
There is a good explanation of how Hard Mix works here at the bottom of the page.
You could emulate the Hard Mix effect using ImageMagick's fx operator like this here
Hope the approach helps.

Convert white parts in image to transparent to simulate printing

I am currently developing an AJAX application using PHP and Javascript that allows people to upload images that will be printed on foil. Since I will be using a standard CMYK printer, it won't print any white parts, but rather just leave those spots blank - a thing that usually does not make a difference on white paper, but here it does, since I'm printing on foil. It gets more complicated when you consider that a grey dot will become semi-transparent black, and I'm not even talking of colors yet.
Yet I would like to create a PNG file with an alpha channel that will simulate the printing process, so I can give a preview of how the printed foil would look when being hold against different backgrounds.
Now I do understand the basic theory of subtractive and additive color models and also of RGBA and CMYK, but then again it's only the basics and here I'm kind of at a loss of how to proceed. I guess in theory you would convert every pixel into CMYK and interpret each channel as a scale from transparent to color instead of from white to color, but how would you translate that back into RGBA?
The nicest thing would, of course, be if ImageMagick would provide such a feature .... does it, or do I have to loop through the pixels manually? If the latter, how would I do the calculations?
I don't know if this has been an issue before. I couldn't find anything on either Stackoverflow or Google, but maybe I just missed the right keywords. Any further reading, food for thoughts or hyperlinks with a note "we discussed this a million times, idiot!" would be warmly welcome.
Thanks
What you basically want is to add an alpha channel to the uploaded image and create a PNG file.
The alpha channel should represent the opacity of the printed color. To generate it, you have several options:
Take a copy of the image, convert it to grayscale, invert it and use it as alpha channel.
Create an alpha channel compute the alpha value of each pixel derived from the original image as: alpha = 1.0 - min(red, green, blue) (use 255 instead of 1.0 depending on whether you're using integer or floating point numbers).
Convert the original image to CMYK (using a color profile and not the poor formulas youn find all over the internet) and use it as the basis to create the alpha channel for the original RGB image: alpha = max(cyan, magenta, yellow, black).
Come up with an even better formula to compute the transparency of each pixel such as: alpha = min(0.2 * cyan + 0.5 * magenta + 0.1 * yellow + 0.7 * black, 1.0)
The last one is just a guess of the relative opacity of each color. You can certainly improve it.

Categories