crop image with imagemagick offset given in percentage - imagemagick

imagemagick's crop command supports cropping to a percentage of an image but the offset values must be specified in pixel values, e.g.:
convert image.png -crop 50%x+10+20
I want to crop with offset values x and y given in percentage of the image width, and height respectively. The pixel values can be calculated, for instance if the image size is 100x200 an offset of 10% would result in 10 and 20 respectively. Is it possible to do this calculation as part of the call to convert? Width and height are available as %w and %h at some places, but this does not work:
convert image.png -crop 50%x+(0.1*%w)+(0.1*%h)

If you're running IM v6 you can use FX expressions with "-set" to set image attributes. By setting the page geometry you can specify the offsets to a calculated percentage and do the crop like this...
convert image.png -set page -%[fx:w*0.1]-%[fx:h*0.1] -crop 50%x+0+0 result.png
That reads the image, sets the geometry for the upper left corner to a location outside the original canvas, and crops to the new top left corner specified by the geometry.
Note the offsets are negative numbers.
Also, if you're doing additional processing in the same command you'll probably want to "+repage" after the crop in order to reset the page geometry to the new WxH+0+0.
Edited to add: You can even include the width and height dimensions for the crop when using "-set page". This command would crop an output of 50% the input width and height, and starting at 10% in from the left and top...
convert image.png \
-set page %[fx:w*0.5]x%[fx:h*0.5]-%[fx:w*0.1]-%[fx:h*0.1] -crop +0+0 result.png
Notice how the crop operation is simply "-crop +0+0" since the dimensions and offsets are in the page geometry.
This method lets you use more complex calculations than just using a percent or number of pixels for the cropped output dimensions.

You cannot do that in ImageMagick 6. But you can do that in ImageMagick 7.
magick image.png -crop "50%x+%[fx:0.1*w]+%[fx:0.1*h]" +repage result.png
In ImageMagick 6, you need to do the computations ahead of the command, store them in a variable and use the variable in the crop command.
However, in ImageMagick 6, you can do the equivalent using -distort with viewport processing as follows:
convert image.png -set option:distort:viewport "%[fx:0.5*w]x%[fx:0.5*h]+%[fx:0.1*w]+%[fx:0.1*h]" -filter point -distort SRT 0 result.png

With v7 ImageMagick, make start image:
magick -size 200x100 gradient: a.jpg
Now crop using lots of calculated widths, heights, offsets:
magick a.jpg -crop "%[fx:w*0.9]x%[fx:h*0.8]+%[fx:w*0.1]+%[fx:h*0.05]" b.png
Check:
identify b.png
b.png PNG 180x80 200x100+20+5 8-bit Gray 256c 408B 0.000u 0:00.000
If you only have v6, use bash and integer arithmetic:
read w h < <(identify -format "%w %h" a.jpg)
convert a.jpg -crop $((w*80/100))x$((h*90/100))+$((w*10/100))+$((h*5/100)) result.png
Check:
identify result.png
result.png PNG 160x90 200x100+20+5 8-bit Gray 256c 412B 0.000u 0:00.000

Related

convert doesn't conserve size when scaling down and up

In order to make applying blur faster I'm first scaling my image down and then scale it back up:
convert - -scale 10% -blur 0x2.5 -resize 1000% RGB:-
This works most of the time but sometimes the output resolution is slightly different from the original input. Is there a way to force the pipeline to be size-preserving?
You should be able to access the original geometry of the image via %G, so you can do:
convert input.jpg -scale 10% -blur 0x2.5 -resize '%G!' RGB:-
If you are using Windows, you probably want "%G!" in double rather than single quotes.
If you are using v7 ImageMagick, replace convert with magick.
I think you are getting errors because if you take 10% of 72 pixels (say), you will get a whole number of pixels, i.e. 7 pixels and then when you scale back up by a factor of 10 you'll get 70 rather than your initial 72.
If you are using Imagemagick 7, you can do the following:
magick input.jpg -set option:wd "%w" -set option:ht "%h" -scale "%[fx:wd/10]x%[fx:ht/10]" -blur 0x2.5 -resize "%[fx:wd]x%[fx:ht]\!" RGB:-
This stores the input width and height. Then uses the stored values to scale by 1/10 of those dimensions, then does the blur, then resizes exactly back to the origin input size. Note the ! that forces the resize to the exact dimensions.
or simpler without storing the input width and height:
magick lena.jpg -scale "%[fx:w/10]x%[fx:h/10]" -blur 0x2.5 -resize "%[fx:w]x%[fx:h]\!" lena_x.jpg

Imagemagick: How to bulk convert images into square

I want the size of the square should be equal to the largest side of the original photo
E.g.
Original: 500 x 400 => Output:500 x 500
Original: 400 x 600 => Output: 600 x 600
You can do that as follows for any given image in Imagemagick 7. To do multiple image, you would need to write a "for" loop, whose syntax depends upon your OS.
magick image.suffix -gravity center -background black -extent "%wx%h^" result.suffix
or
magick image.suffix -gravity center -background black -extent "%[fx:max(w,h)]x%[fx:max(w,h)]" result.suffix
Here is a better way to convert all images in a folder with either Imagemagick 6 or 7. I use -distort SRT's viewport to do the padding with a no-op warping (i.e. no rotation, scale or translation) in mogrify to process all images in the folder.
First create a new folder to hold your output images, if desired and use the -path option to mogrify. Specify a background color (in this case I use red).
For Imagemagick 7:
magick mogrify -path path_to/new_folder -virtual-pixel background -background red -set option:distort:viewport "%[fx:max(w,h)]x%[fx:max(w,h)]-%[fx:0.5*(max(w,h)-w)]-%[fx:0.5*(max(w,h)-h)]" +distort srt 0 *
For Imagemagick 6, remove "magick" before mogrify.

Applying watermark on an image

I'm trying to apply watermark on an image using the following imagemagick command
convert input.png watermark.png.png -gravity northwest -composite output.png
The input png file size is 16KB and the watermark file size is 900bytes, but when I executed the above command to apply a watermark, the output png size is 61KB which is almost 4X the size of the original input png file. Is there any better way of applying a watermark to an image file with much better result in terms of output filesize
Test Image: https://res.cloudinary.com/deks86ilr/image/upload/v1533015495/1_rnpbye.png
Test watermark: https://res.cloudinary.com/deks86ilr/image/upload/v1533015494/2_usmonh.png
Here are my results of processing of your images with my PNG8 output using ImageMagick 6.9.10.8 Q16 with libpng 1.6.34.
I note that your input image was type palette, which means it is 8-bits of color per pixel and not 24-bit color. So it is already a low quality image.
Input (~16 KB):
Watermark Image (white on transparency -- so it is invisible here):
Convert to 24-bit PNG:
convert input.png watermark.png -gravity northwest -compose over -composite input_with_watermark.png
I see no significant visible quality loss but the output is now increase from 16 KB to 60 KB. But you can use tools such as pngcrush to compress it further.
Convert to 8-bit PNG:
convert input.png watermark.png -gravity northwest -compose over -composite PNG8:input_with_watermark2.png
The file size is now back to about 16 KB. But as you note the quality is a little poorer. This is likely because the input image (at 8-bits and has 217 colors) was first read back to 24-bits, then watermarked, which included new shades of white and then quantized back to 8-bits, but contains only 84 colors colors.
Another way is to add +dither -colors 256 to the command (the +dither turns off dithering):
convert 1_rnpbye.png 2_usmonh.png -gravity northwest -compose over -composite +dither -colors 256 PNG8:watermark3.png
This is a bit better, since it now uses 189 colors and still has a file size of 16 KB.
One final method is to save the colors from your input to a colortable image. Then use -remap to recolor the output using that colortable:
convert 1_rnpbye.png -unique-colors colortable.gif
convert 1_rnpbye.png 2_usmonh.png -gravity northwest -compose over -composite +dither -remap colortable.gif PNG8:watermark4.png
This results in 8-bit output with 227 colors and still a file size of about 16 KB. So it has a few more colors than your input and visually looks about the same quality as your input.
If you cannot reproduce these results, then perhaps you should upgrade either or both ImageMagick and libpng.

Why does convert png->jpg blow a picture up to the original size?

I have a png whose width is 2551 pixels and whose height is 3578 pixels.
On this png, I use the -crop option of convert to cut out an image whose dimensions are 2362 x 3389 pixels:
convert original_2551x3578.png -crop 2362x3389+94+94 crop_2362x3389.png
This works as intended.
Then, I use convert to create a jpg:
convert crop_2362x3389.png -format jpg -flatten -background white out.jpg
I expected this command to produce a jpg with the same dimension (2362 x 3389). Much to my surprise, the produced jpg has the dimension 2551 x 3578 pixels (which is the same as the original image).
So it seems that somehow the original size is stored along with crop_2362x3389.png.
How can I use convert to convert a png into a jpg and have it keep the dimension of crop_2362x3389.png?
The reason you are seeing this is that when you do your initial crop, the image "remembers" it was part of a larger image and where in that image it used to be.
You can see this if you do your original crop and then run identify and look at the 4th field, just left of 8-bit.
convert original_2551x3578.png -crop 2362x3389+94+94 crop_2362x3389.png
You can also tell ImageMagick to "forget" it by using +repage like this:
# Repage after changing geometry to forget earlier geometry
convert original_2551x3578.png -crop 2362x3389+94+94 +repage crop_2362x3389.png
# Check IM has forgotten image used to be a part of a bigger one
identify crop_2362x3389.png
crop_2362x3389.png PNG 2362x3389 2362x3389+0+0 8-bit sRGB 256c 15.1KB 0.000u 0:00.000
Ok, I've found a solution. The geometry after the -crop parameter must be followed by a !:
convert original_2551x3578.png -crop 2362x3389+94+94! crop_2362x3389.png
This works as intended.

Changing exposure of jpeg

Given a jpeg, what is the formula to change the exposure of that jpeg by +/-1 stop or as known as 1 EV? I want to simulate this exposure change. Is there a formula/ method to do so?
I can demonstrate that using ImageMagick, which is included in most Linux distros and available for OSX and Windows from here.
First, at the Terminal command line create an image:
convert -size 512x512 gradient:black-yellow gradient.png
Now, the way to effect +1 stop exposure increase is to composite the image with itself using the Screen blending mode - it is available in Photoshop and ImageMagick and is described here.
So, the formula to composite image A with image B is:
1-stop brighter image = 1-(1-A)(1-B)
but as we are compositing the image with itself, A and B are the same, so we effectively have
1-(1-A)(1-A)
ImageMagick refers to the pixels of an image using p rather than A, so we can do a 1-stop increase like this:
convert gradient.png -colorspace RGB -fx "(1-(1-p)(1-p))" result.png
Note that the Wikipedia article, and ImageMagick's -fx both assume your pixel intensities vary between 0 and 1.0. If you are using 8-bit images, you should calculate with 255 in place of 1, namely
+1 stop brighter image = 255-(255-A)(255-A)
or if using 16-bit values
+1 stop brighter image = 65535-(65535-A)(65535-A)
The above fx-based method is however, very slow because the -fx is interpreted rather than compiled, so a faster way to do it is:
convert gradient.png gradient.png -colorspace RGB -compose screen -composite screen.png
Just for fun, another way of looking at that is that we take the inverse of A, that is 1-A, and square it, and then take the inverse, so it can be done like this:
convert gradient.png -colorspace RGB -negate -evaluate pow 2 -negate result.png
The equivalent of -1 stop exposure decrease is to composite the image with itself using the Multiply blend mode, the formula being
1-stop darker image = A x B
which you would do faster with
convert gradient.png gradient.png -colorspace RGB -compose multiply -composite result.png
or even faster, by using memory-to-memory cloning rather than reading from disk twice, with
convert gradient.png -colorspace RGB +clone -compose multiply -composite result.png
but could do equally with
convert gradient.png -colorspace RGB -evaluate pow 2 result.png

Resources