Appending images using ImageMagick convert - graphics

So I have a bunch of images that I wish to append vertically:
convert *.png -append output.png
However I ran into two problems:
Not all the images are of the same width. Hence images smaller in width than the widest image have white space trailing after them because they are left-aligned.
There's no spacing between the images.
How do you center align all the images and specify spacing between them?

Just use ImageMagick's montage utility. Align the images with the -gravity & -extent options, and adjust the spacing with -geometry.
montage fishscales.png circles.png verticalsaw.png \
-tile 1x -geometry +10+10 \
-gravity Center -extent 120 \
out.png

My approach would be a shell script as follows:
1. Run a loop over all your images and find the width of the widest (using ImageMagick `identify`) - say 8 to 10 lines of shell script
2. Create a transparent "spacer image" the same width as the widest image and the height you want for vertically spacing images - one line of shell script
3. Run a loop over all your images first adding the transparent "spacer" to the bottom of the existing output image then compositing images that are narrower than your widest image onto a transparent background the width of your widest image - then appending that to the output image - maybe 15 lines of shell script.
Here is the output with three images:
Red=80px wide
Green=180 px wide
Blue=190px wide
Could that sort of approach work for you - I could probably code it over a day or two between other things!
Here is what I mean:
#!/bin/bash
# User-editable vertical spacing between images
SPACING=10
function centre() {
echo DEBUG: Centering $1
TEMP=$$tmp.png
w=$(convert "$1" -ping -format "%w" info:)
h=$(convert "$1" -ping -format "%h" info:)
convert -size ${MAXW}x${h} xc:"rgba(0,0,0,0)" PNG32:$TEMP
composite -resize '1x1<' -gravity center "$1" $TEMP $TEMP
if [ $2 -eq 0 ]; then
mv $TEMP output.png
else
convert output.png $TEMP -append output.png
rm $TEMP
fi
}
# Step 1 - determine widest image and save width in MAXW
MAXW=0
for i in *.jpg; do
w=$(convert "$i" -ping -format "%w" info:)
[[ $w -gt $MAXW ]] && MAXW=$w
echo DEBUG: Image $i width is $w
done
echo DEBUG: Widest image is $MAXW
# Step 2 - Create transparent spacer image, with width MAXW
convert -size ${MAXW}x${SPACING} xc:"rgba(0,0,0,0)" PNG32:spacer.png
# Step 3 - Build output image
n=0
for i in *.jpg; do
# Add a vertical spacer if not first image
[[ $n -ne 0 ]] && convert output.png spacer.png -append output.png
# Centre image horizontally and append to output
centre "$i" $n
((n++))
done
Another, completely different option would be to add all heights of the N images and create a transparent canvas the width of the widest image and the height of all the images combined plus (N-1)* vertical spacing. Then overlay the images into the canvas at the corrrect place - this involves more maths and less processing and reprocessing of images which could mean that this approach would lose less quality than the one I have suggested. I would do this approach with PerlMagick as it is better for math than bash.

Related

How to avoid header truncation on the tiff using GhostScript. Convert ps to tiff

In this section I add the header file to the top of the tiff file.
echo "/CourierLatin1 findfont 8 scalefont setfont" >>${PS}
echo "40 2 moveto (${DATE}) show" >>${PS}
echo "200 2 moveto (${NAME}) show" >>${PS}
echo "400 2 moveto (${FROM}) show" >>${PS}
echo "510 2 moveto (${PAGEINFO}) show showpage" >>${PS}
${BIN}/gs -dQUIET -dNOPAUSE -dBATCH -dSAFER -sDEVICE=tiffg4 -sOutputFile=${HDR_FILE} \
-dDEVICEWITHPOINTS=612 -dDEVICEHEIGHTPOINTS=11 ${PS} >>${LOG_FILE}
if [ $? -ne 0 ] ; then
cleanup
echo "${BIN}/gs failed" >>${LOG_FILE}
exit 1
fi
# Overlay the header onto the tiff page
SIZE=`${BIN}/tiffinfo ${f} | grep "Image Width:" | sed -e "s/Image Width//" | sed -e "s/Image Length//" | sed -e "s/ //g"`
WIDTH=`echo ${SIZE} | cut -d: -f2`
LENGTH=`echo ${SIZE} | cut -d: -f3`
OFFSET=`expr ${LENGTH} - 2156`
# no shifting of header line up or down is needed
OFFSET=0
if [ ${OFFSET} -lt 0 ] ; then
OFFSET=0
fi
echo "Adding ${HDR_FILE} to ${f} ${WIDTH}x${LENGTH} at 0,${OFFSET}" >>${LOG_FILE}
${CONVERT} tiff:${f} -draw "image over 0,${OFFSET} 0,0 'tiff:${HDR_FILE}'" -compress Group4 -flatten +matte tiff:${f} >>${LOG_FILE}
if [ $? -ne 0 ] ; then
cleanup
echo "${CONVERT} failed" >>${LOG_FILE}
exit 1
fi
Can come to me in any size page. So I first translate tiff post script to size.
I convert tiff again. Can see the code as below:
# Nasty compromise - Have to convert the tiff file to ps file so that I can turn around
# and create a 'faxable' tiff file (one that Radisys will accept)
${BIN}/tiff2ps -a -h11.0 -w8.5 ${LOCAL_FILE} >${PS_FILE}
if [ $? -ne 0 ] ; then
cleanup
echo "${BIN}/tiff2ps failed" >>${LOG_FILE}
exit 1
fi
# Part 2 of compromise
${BIN}/gs -dQUIET -dNOPAUSE -dBATCH -dSAFER -sDEVICE=tiffg4 -sPAPERSIZE=letter -r204x196 -sOutputFile=${LOCAL_FILE} ${PS_FILE} >>${LOG_FILE} 2>&1
if [ $? -ne 0 ] ; then
cp ${BKUP_FILE} ${ORIG_FILE}
cleanup
echo "${BIN}/gs ps2tiff failed" >>${LOG_FILE}
exit 1
fi
Output tiff is given the Radisys. I dont understand Why header cut for this output.pdf.
You can see output fax image as below. why is Header cutted ?
Looks to me like you are printing the 'header' first, then putting the image from the TIFF file (in this case, the text 'FAX') on top of it.
TIFF files aren't masks, so the white space is not transparent. When you turn a TIFF file into PostScript it encapsulates the TIFF bitmap image as a PostScript image. Where the white space in the image lies on top of the 'header', it prints over it, obscuring part of the 'header'.
Its a little hard to be sure what's going on, I'm not a shell script expert, but it appears you first use Ghostscript to render a piece of PostScript to TIFF. That is your 'header'.
You then invoke $(CONVERT) which I'm going to guess is ImageMagick's convert utility to combine the header and some original TIFF file.
I suspect at this point is where your problem occurs, I'm guessing that you need to put the two tiff files in the reverse order so that the header is placed 'on top' of the original, instead of 'under' it (in Z-order terms. In your invocation of $(CONVERT) try swapping $(f) and $(HDR_FILE).
You can of course check the TIFF file at that point, presumably, to see if all is well.
You then take that TIFF file, use tiff2ps (which I think is part of libtiff) to wrap the image up as PostScript, and then use Ghostscript to re-render the image at a different (fax) resolution.
I'd say that you are using the wrong tool for that last step anyway. You already have a bitmap, you'd be better off using an image manipulation application to downsample it to the desired resolution. While your solution undoubtedly works, I would suspect that something like ImageMagick would do a better job.
At the very least, checking the TIFF file before you send it through tiff2ps should tell you whether the result is correct at that point.

How can I extract the filename without extension (and add a counter) from a variable on the Linux command line?

I want to do a batch convert and resize of some images in a folder on the Linux (Mint 17) command line, so I am using:
for $file in *.jpg; do convert -resize 25% $file $file.png; done
but that leaves me with a bunch of files like:
image1.jpg.png
photo4.jpg.png
picture7.jpg.png
...
is there a way that I can easily clip the file extension from the file name so that it only has the .png instead of the preceding .jpg as well?
or, better yet, can I include a counter so that I can run the command with something like this:
for $file in *.jpg using $count; do convert -resize 25% $file image$count.png; done
so that I end up with something like:
image1.png
image2.png
image3.png
image4.png
...
I'd rather not have to worry about creating a batch script, so if that is too hard and there is a simple way of just removing the .jpg inline, then I'm happy with that..
thanks!
EDIT:
I think this question is okay to be a question in it's own right because it also has a counter element.
for file in *.jpg; do convert -resize 25% $file ${file%.*}.png; done
$ ls *.jpg
DSCa_.jpg* DSCb_.jpg* DSCc_.jpg*
$ count=0; for file in *.jpg; do (( count++ )); convert -resize 25% "$file" "${file%\.*}$count.png"; done
$ ls *.png
DSCa_1.png DSCb_2.png DSCc_3.png
${file%\.*} uses parameter substitution to remove the shortest matching string from the right of $file. You can read more about parameter substitution here
Something like this?
i=image1.jpg
echo ${i/.jpg/.png}
image1.png
Also, you can use the command basename (strip suffixe from filename) :
for file in *.jpg; do base=$( basename ${file} .jpg); convert -resize 25% $file ${base}.png; done

Cropping images with loop in Imagemagick

I'm trying to batch crop images in a folder. Whenever I try doing this loop
for image in '/home/donald/Desktop/Im'*.jpg; do
convert "$image" -gravity center -crop 95X95% "${image%.jpg}"-modified.jpg
done
I end up with the error:
convert.im6: unable to open image `/home/donald/Desktop/Im*.jpg': No such file or directory # error/blob.c/OpenBlob/2638.
convert.im6: no images defined `/home/donald/Desktop/Im*-modified.jpg' # error/convert.c/ConvertImageCommand/3044.
Which is bizarre because there are definitely images in the folder and according the meta-data they are jpg
When I run shopt | grep glob I get this output:
dotglob off
extglob on
failglob off
globstar off
globasciiranges off
nocaseglob off
nullglob off
Here's my version information:
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)
Echo $- outputs
himBH
The correct syntax is:
#!/bin/bash
shopt -s nullglob
for image in $HOME/Desktop/Im*.jpg; do
convert "$image" ...
done
Another option may be to use the mogrify command which is part of the ImageMagick suite:
# Create an output directory for cropped images
mkdir cropped
# Crop image and direct output to sub-directory
mogrify -gravity center -crop 95x95% -path cropped *.jpg
You should definitely not parse the output of ls.
In the beginning of your for statement, are these quotes or backquotes ?
You must use backquotes.
Try this :
for image in `ls -1 /home/donald/Desktop/Im*.jpg`; do
convert "$image" -gravity center -crop 95X95% "${image%.*}"-modified.jpg ;
done

ImageMagick not rendering all Unicode characters

I want to render small single character images with ImageMagick.
I'm calling it like this:
echo -n "\u1407" | convert -background black -fill white \
-font /usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf
-pointsize 12 label:#- gif:1407.gif
When I just echo on my terminal, that has this font (DejaVu-Sans-Mono) set, I see this triangle: ᐇ but in the GIF is just a question mark. No special markings, just a question mark. It works for some other characters, like these special parens: ⟨⟩ at 27e8 and 27e9, but the next pair isn't working again.
What do I need to do to enable all characters that the font supplies? Did I set the wrong font?
My distribution is LMDE.
Working with bash & unicode, I always need to step back, and do an extra step.
echo -n ᐇ | hexdump
This gives me the correct hex-triplet
0000000 e1 90 87
So then my unicode escape sequence in bash would be..
echo -ne "\xE1\x90\x87"
(omitting the 0x0A which is the line-feed)
echo -ne "\xE1\x90\x87" | convert -background black -fill white \
-font /usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf \
-pointsize 12 label:#- gif:1407.gif
However this produces the following image , as the character is not apart of the DejaVu Sans Mono set, but it is apart of the DejaVu Sans.
echo -ne "\xE1\x90\x87" | convert -background black -fill white \
-font /usr/share/fonts/truetype/dejavu/DejaVuSans.ttf \
-pointsize 12 label:#- gif:1407.gif
Additional info about ᐇ
There could be a perfectly valid reason why this glyph is not included. A polite thing would be to reach out to the DejaVu team, and inquire if this is a bug, or included elsewhere. IRC & Mailing lists are a good start.

Image format calculator and converter with GNU/Linux

Wouldn't it be great if there with a command for GNU/Linux that would do the following:
Open -Recursive *.png -Not-Case-Sensitive if exported-to-jpg#100%quality=less bytes than the original png then write jpg and delete the png
It would also be able to do the inverse of that command:
if png=less bytes than jpg then delete jpg
Looking for the One True Command is not going to help: if it existed, it would only be useful for you and the (presumably) small set of people who had exactly your needs in the future.
The UNIX Way is to link together several commands to do what you want. For example:
"open-recursive": feed files into the hopper using "find", eg find /path -type f -name '*.png' -print and then send the list out through a pipe.
"not case-sensitive": either increase the scope of the find (-o) or get find to dump out all the files and then use grep to look for what you want, eg find . -print | grep -i '.png'
"if-exported-to-jpg": this is slightly tricky because I believe that the only way to check if the conversion saves bytes is to actually convert it and see. You can use the convert tool from the ImageMagick package to do this. ImageMagick has been standard in the big name distros for years so should be easy to find.
"if less bytes than": straightforward to do in the shell or your favorite scripting language - Perl, python etc.
The net is that you build up what you want from these smaller pieces and you should be able to do what you want now and have something that you can modify in the future or share with others for their unique needs. That is the UNIX Way. Ommmm :)
Some time ago, I wrote a script to convert my photos. The script reduces the dimensions of all JPG file in current folder if any width or height is greater than MAX (default = 1024), keeping aspect ratio, and put them in a different folder (created). I hope this help you.
#!/bin/bash
if [ ! -d reduced ]
then
mkdir reduced
fi
if [ $# -lt 1 ]
then
MAX=1024
else
MAX=$1
fi
for obj in *.jpg
do
echo "------> File: $obj"
tam=$(expr `identify -format "%b" "$obj" | tr -d "B"` / 1024)
width=$(identify -format "%w" "$obj")
height=$(identify -format "%h" "$obj")
echo -e "\tDimensions: $width x $height px"
echo -e "\tFile size: $tam kB"
if [ $width -gt $height ] && [ $width -gt $MAX ]
then
convert "$obj" -resize $MAX "reduced/$obj.jpg"
cd reduced
mv "$obj".jpg "${obj%.jpg}".jpg;
tam=$(expr `identify -format "%b" "$obj" | tr -d "B"` / 1024)
width=$(identify -format "%w" "$obj")
height=$(identify -format "%h" "$obj")
echo -e "\tNew dimensions: $width x $height px"
echo -e "\tNew file size: $tam kB"
cd ..
echo -e "\tOk!"
elif [ $height -gt $MAX ]
then
convert "$obj" -resize x$MAX "reduced/$obj.jpg"
cd reduced
mv "$obj".jpg "${obj%.jpg}".jpg;
tam=$(expr `identify -format "%b" "$obj" | tr -d "B"` / 1024)
width=$(identify -format "%w" "$obj")
height=$(identify -format "%h" "$obj")
echo -e "\tNew dimensions: $width x $height px"
echo -e "\tNew file size: $tam kB"
cd ..
echo -e "\tOk!"
else
cp "$obj" reduced/
echo -e "\tDo not modify!"
fi
done
Err, in answer to your question - "No, it probably wouldn't".
Firstly, PNG files can support transparency and JPEGs cannot, so if this was scripted to your specification, you could lose countless hours of work that went into creating transparent masks for thousands of images.
Secondly, PNG files do not support EXIF/IPTC data, so you would also lose all your Copyright, camera and lens settings, GPS data, dates, and oodles of other metadata.
Thirdly, your PNG file may contain 16 bits per channel, whereas JPEG can only store 8 bits per channel so you could potentially lose an awful lot of fine colour gradations by moving from PNG to JPEG.
Fourthly, you could potentially lose compatibility with older Web browsers which had spotty support for PNGs.

Resources