RMagick torn edges - imagemagick

I'm trying to create a torn edge effect in RMagick. Is there a filter similar to photoshop's crystallize?
Also, I found this ImageMagick code that does it here http://www.imagemagick.org/Usage/thumbnails/#torn:
convert thumbnail.gif \
\( +clone -alpha extract -virtual-pixel black \
-spread 10 -blur 0x3 -threshold 50% -spread 1 -blur 0x.7 \) \
-alpha off -compose Copy_Opacity -composite torn_paper.png
However, I don't understand any of it. Can anyone provide some advice?

This command does two main things: create a mask with that torn-paper effect, apply the mask to the image. It does them, fancily, in one line, by using +clone and the parentheses. It's less confusing to do it as two commmands though:
convert thumbnail.gif \
-alpha extract \
-virtual-pixel black \
-spread 10 \
-blur 0x3 \
-threshold 50% \
-spread 1 \
-blur 0x.7 \
mask.png
convert thumbnail.gif mask.png \
-alpha off \
-compose Copy_Opacity \
-composite torn_paper.png
The first command is fairly complex. But you can find decent explanations of each of the component commands in the ImageMagick docs:
http://www.imagemagick.org/Usage/masking/#alpha_extract
http://www.imagemagick.org/Usage/misc/#virtual_examples
http://www.imagemagick.org/script/command-line-options.php#blur
Also, by splitting the command into these two pieces, you can see what the mask looks like on its own. It's basically the inverse of the paper effect. White throughout the middle of the images, fading to black around the "torn edges".
The second command is a lot more straightforward. Copy_Opacity, as described in the ImageMagick docs, is a way of making parts of an image transparent or not. Anything that's black in the mask will be made transparent in the resulting image. In effect, the second command uses the mask to "erase" the edges of the original thumbnail in a stylistically interesting way.

Related

Is there a way to darken an image except for a rounded rectangle in ImageMagick?

To demonstrate.
Original:
After ImageMagick:
I have some ideas:
Duplicate the image, crop the duplicate, then flatten the images together
Reverse mask?
Does anyone have any suggestions?
Although this has been successfully answered, I'll offer another simple approach using IMv6...
convert pasta.jpg -fill white \
\( +clone -evaluate set 25% -draw "roundrectangle 20,20 80,80 15,15" \) \
-compose multiply -composite result.png
After reading in the input image, in parentheses it clones the input and uses -evaluate to set the entire clone to 25% gray. A white "roundrectangle" is drawn on that gray image in the area you want to preserve. The mask looks like this...
Then after the parentheses, that mask and the input image are composited with -compose multiply. That leaves the white rectangle area as it was in the original input, and the rest of the image is multiplied by 0.25, leaving it darkened. The result...
This command should work just the same in IMv7 by changing "convert" to "magick". And it should work as well on Windows CLI by changing the continued-line backslashes "\" to carets "^", and removing any backslashes before the parentheses, so this "\(...\)" to this "(...)".
You can do that in Imagemagick by darkening the whole image, creating a round rectangle mask image, then composite the original with the darkened using the mask image.
Input:
convert pasta.jpg \
\( -clone 0 -brightness-contrast -75,0 \) \
\( -clone 0 -fill white -colorize 100 -fill black -draw "roundrectangle 20,20 80,80 15,15" -alpha off \) \
-compose over -composite \
pasta_rect.jpg
Result:

How to pass Imagemagick montage output and image properties to the convert command without using any temporary files?

I have a set of images, and I can use the Imagemagick montage command on them to produce a montage image file with transparent background (let's call this fgimg). Now I have another existing image (let's call this bgimg) that I'd like to use (after some special processing with the convert command) as the background for fgimg, which can be achieved within the same convert command. At this point it seems trivial to avoid writing the temporary fgimg to disk, simply by piping the standard output of montage to the standard input of convert.
My problem is that the special processing I'm applying to bgimg will require some knowledge of the image properties of fgimg (e.g., resizing bgimg to have the same size as fgimg), which I don't know in advance. How can this information be retrieved and used in the convert command?
Note: I'm using Imagemagick version 6.9.7-4 on Linux.
I'll include some commands below to further illustrate the problem in detail.
The following command produces the montage image fgimg from a set of input images. The output is in the 'special' miff format (which seems best for temporary output to be worked on later), and has transparent background so that the actual background can be applied later. Most of the other options here are not important, but the point is that the output size (dimensions) cannot be determined in advance.
montage input_*.jpg -tile 5x -border 2 -geometry '200x200>+20+20' \
-gravity center -set label '%f\n%G' -background none -fill white \
-title 'Sample Title' miff:fgimg
Next, I have another input image bgimg.jpg. I want to perform some processing on it before using it as background to fgimg. The processing can be quite complex in general, but in this example, I want to:
resize bgimg.jpg to fit inside the dimensions of fgimg without any cropping;
apply a fade-to-black effect around the edges;
make it the same size as fgimg, with a black background;
combine this with fgimg to produce the final output.
Notice that I need the size of fgimg in two places. I can first extract this into a shell variable:
size=$(identify -format '%G' miff:fgimg)
Then I can do all the steps above in one convert command (note that $size is used twice):
convert "bgimg.jpg[$size]" -gravity center \
\( +clone -fill white -colorize 100% -bordercolor black \
-shave 20 -border 20 -blur 0x20 \) -compose multiply -composite \
-background black -compose copy -extent $size \
miff:fgimg -compose over -composite final_out.jpg
Now here is the problem: I want to avoid writing the temporary file fgimg to disk.
I could replace miff:fgimg with miff:- in both the montage and convert commands and then just pipe one to the other: montage ... | convert .... But how do I deal with the $size?
I tried to use file descriptors (miff:fd:3) but this does not seem to work, which is confirmed by the comments to this question.
Is there a way to do this (in Imagemagick v6) without creating a temporary file?
This example command uses ImageMagick v6 on a bash shell. Instead of "montage" it starts by using "convert" to create a "logo:", one of IM's built-in sample images, then pipes it out as a MIFF and into the "convert" command that follows. You can pipe the output of "montage" just as easily. And it uses another IM built-in image "rose:" as your "bgimg.jpg"...
convert logo: miff:- | convert - rose: \
+distort SRT "%[fx:t?min(u.w/v.w,u.h/v.h):1] 0" \
-shave 1 +repage -gravity center -bordercolor black \
\( -clone 1 -fill white -colorize 100 -shave 6 -border 6 \
-blur 0x6 -clone 1 -compose multiply -composite \) -delete 1 \
\( -clone 0 -alpha off -fill black -colorize 100 \
-clone 1 -compose over -composite \) -delete 1 \
+swap -composite final_out.jpg
That reads the piped image "-" and the background image "rose:".
Then it uses "+distort" with an FX expression to scale "rose:" to the maximum dimensions that still fit within the original piped input image. That operation adds a pixel all around so we use "-shave 1" to get rid of that.
Next inside parentheses it clones that re-scaled background image, makes an edge blur mask, and composites them to make the fade-to-black edge on the background image. Right after the parentheses it deletes the non-edged background image.
In the next parentheses it clones the input image, makes it black, clones the modified background image, and composites it centered over the black one. Again the non-extended background image is discarded after the parentheses with "-delete 1".
Finally the modified background and the input image are put in the proper order with "+swap" and composited for the final output. Run this command without the last "-composite" to see the two images that result from the prior parts of the command.
Both the main input image and background image can be any size, any dimensions, and any aspect ratio. This works for me on v6.8.9 on a bash shell. It should work on any ImageMagick newer. It should work on Windows by removing all the backslashes that escape parentheses and changing the continued-line backslashes "\" to carets "^".
EDITED TO ADD:
You can use that FX expression to find the scaling amount, save it as a variable, then isolate the background image inside parentheses and use that variable to do the scaling and shaving there. That way it only affects the background image. There may be a rounding error with that, but the main image, which determines the exact final output dimensions, will be unaffected. Note the difference in the first few lines of this command...
convert logo: miff:- | convert - rose: \
-set option:v1 "%[fx:min(u.w/v.w,u.h/v.h)]" \
\( -clone 1 +distort SRT "%[v1] 0" -shave 1 \) -delete 1 \
+repage -gravity center -bordercolor black \
\( -clone 1 -fill white -colorize 100 -shave 6 -border 6 \
-blur 0x6 -clone 1 -compose multiply -composite \) -delete 1 \
\( -clone 0 -alpha off -fill black -colorize 100 \
-clone 1 -compose over -composite \) -delete 1 \
+swap final_out.jpg

Trying to add a stroke around a PNG, can it be improved?

I'm trying to find a good way to add a 3px white stroke to a ton of png files, effectively to make them look like "stickers." I've got some sample code that does a decent job, but I can't seem to get the cropping right. Also, the stroke looks a bit pixelated and I wanted to know if it's possible to get cleaner edges!
I did a bunch of internet scouring, found some sample code, tweaked it around, and came to something that almost resembles what I'm looking for. The images are always going to be PNGs, so I looked into things like inkscape/gimp from the command line but realized I should be able to do this just using convert from the terminal.
convert in.png \
\( -clone 0 -alpha extract -threshold 0 \) \
\( -clone 1 -blur 10x65000 -threshold 0 \) \
\( -clone 2 -fill red -opaque white \) \
\( -clone 3 -clone 0 -clone 1 -alpha off -compose over -composite \) \
-delete 0,1,3 +swap -alpha off -compose copy_opacity -composite \
out.png
in:
out:
ideally:
Your main problem is it is that you do not have enough space bettween your object and the sides of the image. You just need to add your image with transparency and then remove any excess later.
In ImageMagick 6, this should do what you want.
1) read the input
2) add a larger border than you need to add
3) extract the alpha channel from the input and dilate it by the amount of border (in this case 10)
4) copy the previous image and color the white as red and the black as transparent
5) composite the original over the red/transparent image
6) delete the original and the red/transparent image
7) swap the composite with the dilated alpha channel and put the dilated alpha channel into the alpha channel of the previous image
8) trim the excess transparency from the border padding
9) save to output
convert img.png \
-bordercolor none -border 20 \
\( -clone 0 -alpha extract -morphology dilate diamond:10 \) \
\( -clone 1 -fuzz 30% -fill red -opaque white -fill none -opaque black \) \
\( -clone 2,0 -compose over -composite \) \
-delete 0,2 \
+swap -alpha off -compose copy_opacity -composite \
-trim +repage \
result.png
For ImageMagick 7, replace convert with magick.
If on a Unix-like system, you might be interested in my bash ImageMagick script, contour, at http://www.fmwconcepts.com/imagemagick/index.php
A better result can be achieved by replacing diamond:10 with disk:10

"Diff" an image using ImageMagick with emphasis

Follow up on “Diff” an image using ImageMagick
When you are working with forms (as opposed to images), the changes are very hard to see with this technique. I wanted to show some type of yellow highlight maybe 10-20 pixels "bigger" (padding) around the actual pixels that changed.
So instead of just this
Something more like this
It seems like I am just missing something here in the stack that can make this work.
convert '(' file1.png -flatten -grayscale Rec709Luminance ')' \
'(' file2.png -flatten -grayscale Rec709Luminance ')' \
... here ? ...
'(' -clone 0-1 -compose darken -composite ')' \
... or here ? ...
-channel RGB -combine diff.png
I can also run this as separate commands and does not need to be fast, most of this is going to be run offline.
I also tried studying the technique here (specifically how the thumbnail scaling gives you the effect I want as the pixels get expanded) but this code is using the library instead of the ImageMagick command line tools. Line 248 => "make the red as visible as possible"
https://github.com/vslavik/diff-pdf/blob/master/diff-pdf.cpp#L218
An example form is the standard W-9. I made 2 subtle changes
PART II .. moved the 2. left about 2 pixels
PART II .. on 4., moved word is to the right 2 pixels
and the compare pumps out this (changes in red that you can barely see)
Thank you for any help
You can use -morphology dilate in Imagemagick to increase the size of the red areas. For example, using your two forms:
convert JW0wZ.png 1nHWT.png -compose difference -composite -morphology dilate disk:10 +level-colors black,red result.gif
UPDATE:
If you want the background transparent, then try
convert JW0wZ.png 1nHWT.png -compose difference -composite -morphology dilate disk:10 +level-colors "black,red" -fuzz 20% -transparent black result.png
Or better
convert JW0wZ.png 1nHWT.png -compose difference -composite -morphology dilate disk:10 -alpha copy -background red -alpha shape result2.png
Update 2: Here is how to overlay a 50% yellow marking onto you two originals. Change the value as desired. I create a difference image and dilate the white. Then I create a 50% yellow image. Then I composite each of the originals with the yellow using the difference image as a mask. See https://www.imagemagick.org/Usage/compose/#compose
convert JW0wZ.png 1nHWT.png \
\( -clone 0,1 -compose difference -composite -morphology dilate disk:10 \) \
\( -clone 0 -fill yellow -colorize 100 -channel a -evaluate set 50% +channel \) \
\( -clone 0,3,2 -compose over -composite +write 1.png \) \
\( -clone 1,3,2 -compose over -composite +write 2.png \) \
null:
To view this, if on Unix and have X11 installed, you can do
animate -delay 20 -resize 50% 1.png 2.png

Combining multiple images in ImageMagick with relative (not absolute) offsets

I'm looking for the most efficient way to stitch multiple images together in ImageMagick, on top of a background image, such that the spacing / padding between the overlaid images is consistent?
I've investigated use of +append, convert -composite, and convert with -page and -layers merge.
The following command (convert -composite) works, but requires precalculation of image dimensions in order to specify absolute offsets. Really, I want a 10 pixel gap between the end of the FIRST layered image and the start of the second layered image, but the only way I can see to achieve that is by specifying the absolute offset from the top-left corner of the canvas.
convert \
background.jpg \
first.jpg -gravity Northwest -geometry +10+10 -composite \
second.jpg -geometry +300+10 -composite \
third.jpg -geometry +590+10 -composite \
output.jpg
I am looking for some sort of operator so that the horizontal offset can be interpreted relative to the "last" image in the layering, so instead of specifying +300+10 for the second image and +590+10 for the third, I can somehow specify a +10+10 offset.
I thought gravity would allow me to achieve that (-gravity Northwest), in the same way that float: left; works in CSS positioning, but that is not the case.
I have also had some success with the following:
convert \
-page +10+10 first.jpg \
-page +300+10 second.jpg \
-page +590+10 third.jpg \
-background transparent \
-layers merge \
layered.png
convert background.jpg layered.png -gravity Center -composite output.jpg
Both the techniques described require pre-calculation of absolute offsets, which is a bit of a pain. Is there a better way to do this?
You've overlooked the montage command.
The most simple command to add the wanted spacing with it would be to set a -frame 5 option with -mattecolor none. This works with images of different width values and spaces them all apart with a distance of 10 pixels:
montage \
-alpha on \
-background none \
-mode concatenate \
-tile x1 \
-frame 5 \
-mattecolor none \
*.jpg \
output1.png
You'll easily notice however, that the resulting image's border is only 5 pixels wide on top, right, bottom and left. To remove these 5 pixels all around use:
convert output1.png -shave 5 output2.png
To overlay this result on your background.jpg, use:
convert \
background.jpg \
output2.png \
-gravity Northwest \
-geometry +10+10 \
-composite \
final.jpg
You can also use Kurt's transparent frame trick with append. Using append instead of montage has the advantage that you can use gravity settings to align your images top (north) bottom (south) or center.
Here's an example of how to append images horizontally with a 10 pixel gap between them, and with the images top aligned:
convert \
-frame 5 \
-mattecolor none \
-background none \
-gravity north \
first.jpg second.jpg third.jpg \
+append \
png:- | convert - -shave 5
output.png
To append images vertically use -append instead of +append. I've used a pipe | to shave off the outside frame in the same command.

Resources