Can ImageMagick return the image size? - image

I'm using ImageMagick from the command line to resize images:
convert -size 320x240 image.jpg
However, I don't know how to determine the size of the final image. Since this is a proportional image scale, it's very possible that new image is 100x240 or 320x90 in size (not 320x240).
Can I call the 'convert' command to resize the image and return the new image dimensions? For example, pseudo code:
convert -size 320x240 -return_new_image_dimension image.jpg // returns the new resized image dimensions

-ping option
This option is also recommended as it prevents the entire image from being loaded to memory, as mentioned at: https://stackoverflow.com/a/22393926/895245:
identify -ping -format '%w %h' image.jpg
man identify says:
-ping efficiently determine image attributes
We can for example test it out with some of the humongous images present on Wikimedia's "Large image" category e.g. this ultra high resolution image of Van Gogh's Starry Night which Wikimedia claims is 29,696 × 29,696 pixels, file size: 175.67 MB:
wget -O image.jpg https://upload.wikimedia.org/wikipedia/commons/e/e8/Van_Gogh_-_Starry_Night_-_Google_Art_Project-x0-y0.jpg
time identify -ping -format '%w %h' image.jpg
time identify -format '%w %h' image.jpg
I however observed that -ping at least in this case did not make any difference on the time, maybe it only matters for other image formats?
Tested on ImageMagick 6.9.10, Ubuntu 20.04.
See also: Fast way to get image dimensions (not filesize)

You could use an extra call to identify:
convert -size 320x240 image.jpg; identify -format "%[fx:w]x%[fx:h]" image.jpg

I'm not sure with the %w and %h format. While Photoshop says my picture is 2678x3318 (and I really trust Photoshop), identify gives me:
identify -ping -format '=> %w %h' image.jpg
=> 643x796
(so does [fx:w] and [fx:h])
I had to use
identify -ping -format '=> %[width] %[height]' image.jpg
=> 2678x3318
I don't know what's going on here, but you can see both values on standard output (where the width and height before the => are the correct ones)
identify -ping image.jpg
image.jpg PAM 2678x3318=>643x796 643x796+0+0 16-bit ColorSeparation CMYK 2.047MB 0.000u 0:00.000
The documentation says %w is the current width and %[width] is original width. Confusing.
%w and %h may be correct for most uses, but not for every picture.

If you specify option -verbose, convert prints:
original.jpg=>scaled.jpg JPEG 800x600=>100x75 100x75+0+0 8-bit sRGB 4.12KB 0.020u 0:00.009
^^^^^^

Related

ImageMagick: guess raw image height

I'm using convert utility from ImageMagick to convert raw image bytes to usable image format such as PNG. My raw files are generated by code, so there is no any headers, just pure pixels.
In order to convert my image I'm using command:
$ convert -depth 1 -size 576x391 -identify gray:image.raw image.png
gray:image.raw=>image.raw GRAY 576x391 576x391+0+0 1-bit Gray 28152B 0.010u 0:00.009
The width is fixed and pretty known for me. However I have to evaluate the height of the image from the file size each time which is annoying.
Without height specified or if wrong height is specified the utility compains:
$ convert -depth 1 -size 576 -identify gray:image.raw image.png
convert-im6.q16: must specify image size `image.raw' # error/gray.c/ReadGRAYImage/143.
convert-im6.q16: no images defined `image.png' # error/convert.c/ConvertImageCommand/3258.
$ convert -depth 1 -size 576x390 -identify gray:iphone.raw iphone.png
convert-im6.q16: unexpected end-of-file `image.raw': No such file or directory # error/gray.c/ReadGRAYImage/237.
convert-im6.q16: no images defined `image.png' # error/convert.c/ConvertImageCommand/3258.
So I wonder is there a way to automatically detect the image height based on the file/blob size?
A couple of ideas...
You may not be aware of the NetPBM format, but it is very simple and you may be able to change your software that creates the raw images so that it directly generates PBM format images which are readable and useable by OpenCV, Photoshop, GIMP, feh, eog and ImageMagick of course. It would not require any libraries or extra dependencies in your software, all you need to do is put a textual PBM header on the front, so your file looks like this:
P4
576 391
... YOUR EXISTING BINARY DATA ...
Do not forget to put newlines (i.e. linefeed character) after P4 and after 391.
You can try it for yourself and add a header onto one of your files like this and then view it with GIMP or other tool:
printf "P4\n576 391\n" > image.pbm
cat image.raw >> image.pbm
If you prefer a one-liner, just use a bash command grouping like this - which is equivalent to the 2 lines above:
{ printf "P4\n576 391\n"; cat image.raw; } > image.pbm
Be careful to have all the spaces and semi-colons exactly as I have them!
Another idea, just putting some meat on Fred's answer, might be the following one-liner which uses a bash arithmetic context and a bash command substitution, you can do this:
convert -depth 1 -size "576x$(($(stat -c "%s" image.raw)*8/576))" gray:image.raw image.png
Note that if you are on macOS, stat is a little different, so you may prefer the slightly less efficient, but more portable:
convert -depth 1 -size "576x$(($(wc -c < image.raw)*8/576))" gray:image.raw image.png
You have to know the -depth and width to compute the height for ImageMagick raw format. If depth is 1, then your image is binary (b/w). So height = 8 * file size (in B)/(width). 28152*8/391 = 576

How to use imagemagick "convert" to create Google Earth pyramid files

I have a large image that I am using imagemagick to convert into tiles for use in a Google Earth KML as explained here
instructions on image pyramid construction
The idea is to chop up the images into 4 pieces, then 16, then 64, etc.
To keep things simple, I made the image canvas 4096x4096 so that dividing it in will produce equal size files. The basic command is very simple. For example:
convert large.png -crop 512x512 tiles.png
The issue is the convert command creates file names sequentially, while google needs a format of row column. For instance if there were four files output, the file names should be:
tiles00.png
tiles01.png
tiles10.png
tiles11.png
I brute forced renaming scripts for up to 64 files, but before doing the 256 file case, I'd like to know if there is a simpler way to generate the file names. I'm using linux.
Here is one way in Imagemagick 6 using for loops.
lena.png
The lena.png image is 256x256. I choose 128x128 size tiles. So there will be a total of 2 rows and 2 columns for four output images.
infile="lena.png"
tx=128
ty=128
ncols=`convert -ping "$infile" -format "%[fx:floor(w/$tx)]" info:`
nrows=`convert -ping "$infile" -format "%[fx:floor(h/$ty)]" info:`
for ((j=0; j<nrows; j++)); do
offy=$((j*ty))
for ((i=0; i<ncols; i++)); do
offx=$((i*tx))
convert lena.png -crop ${tx}x${ty}+${offx}+${offy} +repage lena_tile${j}${i}.png
done
done
lena_tile00
lena_tile01
lena_tile10
lena_tile11
An alternate, more compact way is to use -set filename command with fx calculations to name the files in the image chain.
infile="lena.png"
tx=128
ty=128
ncols=`convert -ping "$infile" -format "%[fx:floor(w/$tx)]" info:`
nrows=`convert -ping "$infile" -format "%[fx:floor(h/$ty)]" info:`
convert "$infile" -crop ${tx}x${ty} -set filename:row_col "%[fx:floor(t/$nrows)]%[fx:mod(t,$ncols)]" "lena_tile%[filename:row_col].png"
See:
https://imagemagick.org/Usage/basics/#set
https://imagemagick.org/script/fx.php
Are you trying to make your own in order to learn about the process? If not, existing tools like dzsave can build complete pyramids for you very quickly in a single command. For example:
$ vipsheader wtc.jpg
wtc.jpg: 10000x10000 uchar, 3 bands, srgb, jpegload
$ /usr/bin/time -f %M:%e vips dzsave wtc.jpg x --layout google
211224:1.52
$ ls -R x | wc
2404 2316 15186
So that's making a google-style pyramid of 2400 tiles in directory x from a 10,000 x 10,000 pixel JPG image. It takes about 1.5s and 210mb of ram.
There's a chapter in the manual introducing dzsave:
http://libvips.github.io/libvips/API/current/Making-image-pyramids.md.html

mix single channel images into color image

each representing a channel and I want to merge them into a single one, like RGB image. The images are RAW, so no file header. I have managed to mix them with
cat imgPl0.raw imgPl1.raw imgPl2.raw >> img.rgb
but this is mixing the planes one after the other, but is there a way to do an interlaced mix ?
Maybe using imagemagick there is another way ?
Well, what I need at the output is a rgb image (not a png, sadly imagemagick is creating a png as output) containing the data interlaced. To be more explicit, cat-ing the images is going to make a rgb image plane-interlaced (that is rrrrrr...ggggggg...bbbbbbb). What I want is an operation that is creating a rgb image line-interlaced (that is rrr...ggg...bbb...rrr...ggg...bbb...rrr...ggg...bbb...). Sorry for not being explicit from the beginning.
About the data, it seems it is 12 bpp or 14 bpp little endian
ImageMagick offers a few techniques, but you'll need to be responsible for defining all the information missing from the headers. Stuff like image size, quantum depth, and colorspace.
One approach.
convert -size 70x46 -depth 8 \
r:imgPl0.raw g:imgPl1.raw b:imgPl2.raw \
-set colorspace RGB -combine -colorspace sRGB \
output.rgb
Another option is to create a blank canvas, and copy the data from the raw files over to the correct canvas channels.
convert -size 70x46 xc: -depth 8 \
r:imgPl0.raw -compose CopyRed -composite \
g:imgPl1.raw -compose CopyGreen -composite \
b:imgPl2.raw -compose CopyBlue -composite \
-colorspace sRGB output.rgb
Other examples can be found here.
Also note: I'm assuming that these .raw data files only contain single channel samples, are unsigned character color sizes, and have a 70x46 image size. YMMV
Update
Well, what I need at the output is a rgb image (not a png, sadly imagemagick is creating a png as output)
Sorry about that. Just switch output.png to output.rgb. ImageMagick will do the rest.
About the data, it seems it is 12 bpp or 14 bpp little endian
Adjust -depth from 8 to 12, or 14 bits-per-part.
There's also a -endian LSB option, but I don't think that's needed.
What I want is an operation that is creating a rgb image line-interlaced
Easy. Set the -interlace Line options.
So... My previous answer is still helpful, but just needs some additional options.
convert -size 70x46 -depth 12 \
r:imgPl0.raw g:imgPl1.raw b:imgPl2.raw \
-set colorspace RGB -combine -colorspace sRGB \
-interlace Line output.rgb
or
convert -size 70x46 xc: -depth 12 \
r:imgPl0.raw -compose CopyRed -composite \
g:imgPl1.raw -compose CopyGreen -composite \
b:imgPl2.raw -compose CopyBlue -composite \
-colorspace sRGB -interlace Line output.rgb
Hope that get's you close.
If your data is 8 bits per sample, you can do it like this which whilst not very efficient, doesn't require any code writing or compiling or anything:
#!/bin/bash
# Bytes per row
bpr=100
row=0
# Loop through all rows of file
while :; do
# Read a row from each channel and output on stdout redirected to result.rgb
for ((chan=0;chan<3;chan++)); do
dd if=imgPl${chan}.raw bs=$bpr count=1 skip=$row > row.tmp 2> /dev/null
[ ! -s row.tmp ] && exit
cat row.tmp
done
((row+=1))
done > result.rgb

How to shrink and optimize images?

I'm currently using jpegoptim on CentOS 6. It lets you set a quality and file size benchmark. However, it doesn't let you resize the images.
I have 5000 images of all file sizes and dimensions that I want to shrink to a max width and max file size.
For example, I'd like to get all images down to a maximum width of 500 pixels and 50 KB.
How can I shrink and optimize all of these images?
You can do this with ImageMagick, but it is hard to say explicitly which way to do it as it depends on whether all the files are in the same directory and also whether you have or can use, GNU Parallel.
Generally, you can reduce the size of a single image to a specific width of 500 like this:
# Resize image to 500 pixels wide
convert input.jpg -resize 500x result.jpg
where input.jpg and result.jpg are permitted to be the same file. If you wanted to do the height, you would use:
# Resize image to 500 pixels high
convert input.jpg -resize x500 result.jpg
since dimensions are specified as width x height.
If you only want to reduce files that are larger than 500 pixels, and not do any up-resing (increasing resolution), you add > to the dimension:
# Resize images wider than 500 pixels down to 500 pixels wide
convert image.jpg -resize '500x>' image.jpg
If you want to reduce the file size of the result, you must use a -define to guide the JPEG encoder as follows:
# Resize image to no more than 500px wide and keep output file size below 50kB
convert image.jpg -resize '500x>' -define jpeg:extent=50KB result.jpg
So, now you need to put a loop around all your files:
#!/bin/bash
shopt -s nullglob
shopt -s nocaseglob
for f in *.jpg; do
convert "$f" -resize '500x>' -define jpeg:extent=50KB "$f"
done
If you like thrashing all your CPU cores, do that using GNU Parallel to get the job done faster.
Note that if you have a file that is smaller than 500px wide, ImageMagick will not process it so if it is smaller than 500 pixels wide and also larger than 50kB, it will not get reduced in terms of filesize. To catch that unlikely edge case, you may need to run another find afterwards to find any files over 50kB and then run them through convert but without the -resize, something like this:
find . -type f -iname "*.jpg" -size -51200c -exec convert {} -define jpeg:extent=50KB {} \;

ImageMagick error with montage command

I'm stitching 8 images of 8k by 8k pixels in a row using the montage command.
This is what I enter in:
montage -mode concatenate -limit area 0 -tile x1 image1.png image2.png image3.png image4.png image5.png image6.png image7.png image8.png out1.png
This is the error I get out:
montage: magick/quantum.c:215: DestroyQuantumInfo: Assertion `quantum_info->signature == 0xabacadabUL' failed.
Abort
Can anyone help? Thanks
You may get on better with this command which does what I think you are trying to do:
convert +append image{1..8}.png out.png
As you can see from the following identify command, the images have been laid out side-by-side to make an image 64k pixels wide as a result of the +append command. Just FYI, use -append to lay them out one above the other in a 64k pixel tall stack.
identify out.png
out.png PNG 64000x8000 64000x8000+0+0 8-bit sRGB 2c 62.4KB 0.000u 0:00.000
Your originally posted command also works fine on my ImageMagick Version:
ImageMagick 6.8.9-5 Q16 x86_64 2014-07-29

Resources