imagemagick font metrics -- how to place a text in a grid? - shell

let's have a look at the following image:
I have a horizontal grid and i want to place a text in this grid. The above example is wrong, because what i would like to have is that each character is placed exactly in one of the cells of the grid.
I wonder, if i can adjust the text-output in imagemagick to achieve this, without having to place each of the characters with it's own command.
Some additional facts:
i am using imagemagick from some shell script
i am doing rather complex drawings with imagemagick's MVG -- so it would be nice if the text could be still placed with the MVG commands
i am able to adjust the width of the grid by a few pixel, if this would be required with your solution, but all cells of course need to have the same width
i am always using the same fixed-width font (Courier) for this
i am able parse the font-metrics in my shell script and use this information to apply values to my text-commands
i only care about horizontal placement, vertical placement is not important because i render each row individual
With all this in mind -- is there any solution for my problem?
Thanks a lot!

You can use the kerning option - setting inter-character spacing.
e.g.
for i in 0 3 6 9 12 15
do
convert -kerning $i -font Courier -pointsize 24 label:":Kerning $i:" label_$i.jpg
done
will generate the following images. You must simply find the right kerning value for match the grid. (for monospaced font - like your Courier)

If you have a non mono-typed font that you want to force into a grid, you can use this script:
#!/usr/bin/env bash
rm test*png
font=~/Library/Fonts/WittenbergerFrakturMTStd.otf
gridsize=32x32
chr() {
case "$1" in
64 ) echo '\#' ;;
92 ) echo '\\' ;;
* ) printf \\$(printf '%03o' $1)
esac
}
for i in {32..127}; do
c=$( chr $i )
echo -n "$c: "
convert -background transparent -density 90 -pointsize 12 -gravity center -font "$font" label:"$c" -extent $gridsize test-$i.png
done
convert test-{32..127}.png +append test.png

Related

how to insert text in images sequentially in a directory

I have tried the following:
n=1
for index in image*.png;
do convert $index -fill white -undercolor '#00000080' -gravity South -pointsize 44 -annotate +0+550 “image`echo $n`”;
((n++));
done
This results in an error:
convert: `“image1”' # error/convert.c/ConvertImageCommand/3272.
and so on till the end of all image files in the directory.
Basically I'm trying to insert text as the filename in the image.
Your plain double quotes around “imageecho $n” have been replaced by opening/closing double quotes(*). Also, the echo is unnecessary, try "image$n.png"
(*) possibly the deed of an editor not meant to edit code, editing with Wordpad perhaps?

sed to manipulate output of imagemagick in bash

I'm trying to write a bash script to trim the scanner white space around some old photos that were scanned in ages ago. I've got hundreds of photos so I'm not doing it manually.
Fred's imagemagick scripts don't manage to select the appropriate area.
I am no programmer so please dont be too offended by my terrible attempts at scripting!
I've found a combination of commands using imagemagick that does it.
first I use a blurring filter to confuse imagemagick into correctly selecting the photo size:
convert input -virtual-pixel edge -blur 0x15 -fuzz 15% -trim info:
This spits out data as follows:
0001.jpeg JPEG 3439x2437 4960x6874+1521+115 8-bit DirectClass 0.070u 0:00.009
I then use the numbers to do a crop which has been very accurate on my scans. The following is an example using the numbers from above.
convert inputfile -crop 3439x2437+1521+115 +repage outputfile
My problem is in writing the bash file to go through a directory of pictures and automate the process.
Here's what I have so far:
#!/bin/bash
ls *.jpeg > list
cat list | while read line; do
convert $line -virtual-pixel edge -blur 0x15 -fuzz 15% -trim info: > blurtrim.txt
#need a line to manipulate the output of the above to spit out the crop coordinates for the next command
crop=$(<crop.txt)
convert $line -crop $crop +repage trim$line.jpeg
rm blurtext.txt
rm crop.txt
done
rm list
The key bit I can't do is changing the string output of the first imagemagick command.
the file goes along the lines of:
input fileformat 1111x2222 3333x4444+5555+666 and then a load of crap i dont care about
the numbers I need in my script are:
1111x2222+5555+666
the cherry on the top is that while most of the numbers are four digits long not all of them are so I cant rely on that.
any ideas on how to use sed or preferably something else less demonic to get the above numbers in my script?
an explanation of the syntax would be nice (but i understand if the explantion is the size of a book then its best left out).
thanks in advance!
You don't need to parse anything! ImageMagick can tell you the trim box directly itself, using the %# format:
convert image.jpg -virtual-pixel edge -blur 0x15 -fuzz 15% -format "%#" info:
1111x2222+5555+666
So, you can say:
trimbox=$(convert image.jpg -virtual-pixel edge -blur 0x15 -fuzz 15% -format "%#" info:)
convert image.jpg -crop $trimbox ...
Benefits include the fact that this approach works on Windows too, where there is no sed.
So, the full solution would be something like:
#!/bin/bash
shopt -s nullglob
for f in *.jpeg; do
trimbox=$(convert "$f" -virtual-pixel edge -blur 0x15 -fuzz 15% -format "%#" info:)
convert "$f" -crop "$trimbox" +repage "trimmed-$f"
done
Solution
This will parse your file line by line, extract the desired parameters, concatenate them together, and use it as the argument value to 'crop' for the convert program:
regex='([0-9]+x[0-9]+) [0-9]+x[0-9]+\+([0-9]+\+[0-9]+)'
while read line
do
if [[ $line =~ $regex ]]
then
cropParam="${BASH_REMATCH[1]}+${BASH_REMATCH[2]}"
convert inputfile -crop $cropParam +repage outputfile
else
echo "ERROR: Line was not in the expected format ($line)"
exit 1;
fi
done < blurtrim.txt
Explanation
The regex variable holds a regular expression (brief introduction to regular expressions in bash here: http://www.tldp.org/LDP/abs/html/x17129.html) which describes the format of the numbers you describe in your question. The () around parts of the pattern denotes something called a capture group. If the pattern matches, the part that is in the first () is captured in a bash variable BASH_REMATCH[1], and the second () is captured in BASH_REMATCH[2]. BASH_REMATCH[0] contains the whole match, in case you're wondering why we start at index 1.
The line [[ $line =~ $regex ]] is what actually executes the pattern matching algorithm for us. In Bash [[ is called the extended test command, and the operator =~ is called the regular expression matching operator. This article explains the operator in more detail: http://www.linuxjournal.com/content/bash-regular-expressions.
I would propose a similar solution to Jonathan:
re='([0-9x]+) [0-9x]+(\+[0-9+]+)'
for file in *.jpeg; do
output=$(convert "$file" -virtual-pixel edge -blur 0x15 -fuzz 15% -trim info:)
if [[ $output =~ $re ]]; then
crop="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
convert "$file" -crop "$crop" +repage "trim$file.jpeg"
fi
done
The regular expression captures any group containing characters within the range 0-9 or x and then a + followed by numbers and + characters. It is a less strict pattern as it includes the x and + inside the bracket expressions, so technically would allow things like 0x9x9x0 but I can't imagine that this would present a problem based on the output you've shown us.
The other differences between this and your original attempt are that no temporary files are created and the loop is run over the list of files, rather than using ls, the parsing of which should generally be avoided in scripts.

gimp script to square multiple images, maintaining aspect ratio and minimum dimensions

I have hundreds of jpgs of varying sizes (e.g. 2304px x 2323px).
In gimp I can use a batch filter to change these to certain sizes, relative or absolute. But for some configurations I have to do the following manually, which for all the images takes forever:
Change the size of the shortest side to 500px, maintaining the aspect ratio so the longer side is at least 500px. So if the image was 1000 x 1200, it will now be 500 x 600. The images come in both portrait and landscape.
Change the canvas size so the image is a 500px x 500px square, centered. This will cut off part of the image (which is fine, most images are almost square anyway).
Export the file with a -s appended to the file name.
Is there a script I can use to automate these steps?
Something like this in ImageMagick. It's not as hard as it looks as most of it is comment. Try to on a COPY of your files - it does all the JPEGs in the current directory.
#!/bin/bash
shopt -s nullglob
shopt -s nocaseglob
for f in *.jpg; do
echo Processing $f...
# Get width and height
read w h < <(convert "$f" -format "%w %h" info: )
echo Width: $w, Height: $h
# Determine new name, by stripping extension and adding "s"
new=${f%.*}
new="${new}-s.jpg"
echo New name: $new
# Determine lesser of width and height
if [ $w -lt $h ]; then
geom="500x"
else
geom="x500"
fi
convert "$f" -resize $geom -gravity center -crop 500x500+0+0! "$new"
done
Unless you find your way with gimp, you may want to look into ImageMagick: the mogrify tool allows to modify and resize images.
Beware: mogrify will overwrite your file, unless you use stdin/stdout. You probably want a script like this:
#!/bin/sh
for image in `ls /your/path/*jpg`; do
mogrify -resize ... - < "$image" > "${image%%.png}-s.jpg"
done

ImageMagick change image width and height

I am using ImageMagick to resize image resolution by using below command-line option
convert abc.png -set units PixelsPerInch -density 75 abc_a.png
I am in need of this: if any images has more than 300 width OR more than 100 height, I want to convert it to width 300 width and 100 height, with changing above dpi (i.e. 75dpi).
Can any one help me on this?
If you are on Linux/OSX, you can get the image dimensions like this:
identify -format "%w %h" input.jpg
So, if you want the width and height in variables w and h, do this:
read w h < <(identify -format "%w %h" input.jpg)
Now you can test the width and height and do further processing if necessary:
[ $w -gt 300 -o $h -gt 100 ] && convert input.jpg -set units ...
Or, if you want to be more verbose:
if [ $w -gt 300 -o $h -gt 100 ]; then
convert ...
fi
So, the total solution I am proposing looks like this:
#!/usr/bin/bash
read w h < <(identify -format "%w %h" input.jpg)
[ $w -gt 300 -o $h -gt 100 ] && convert input.jpg -set units ...
JPEG or PNG makes no difference, so just replace my JPG with PNG if that is the format of your choice.
Updated for Windows
Ok, no-one else is helping so I will get out my (very) rusty Windows skills. Get the image width something like this under Windows:
identify -format "%w" input.png > w.txt
set /p w=<w.txt
Now get the height:
identify -format "%h" input.png > h.txt
set /p h=<h.txt
You should now have the width and height of image input.png in 2 variables, w and h, check by typing
echo %w%
echo %h%
Now you need to do some IF statements:
if %w% LEQ 300 GOTO SKIP
if %h% LEQ 100 GOTO SKIP
convert ....
:SKIP
Note:: You may need ^ in front of the percent sign in Windows.
Note: You may need double # signs in scripts because Windows is illogical.
You cannot control the -density and the width plus height at the same time!
Density (or resolution), when creating an image, will automatically resize the image to a certain number of pixels in width and height (or it will have been ignored).
Density (or resolution), when displaying an image (like in a browser window, within a HTML page, or on a PDF page), will not change the original images dimensions: instead it will zoom in or zoom out the respective view on the image.
Density (or resolution), when used in the metadata of an image (which is not supported by every file format), does not change the image dimensions -- it just gives a hint to the displaying software, at what zoom level the image wants to be displayed (which is not supported by every image viewer).
Now to your question...
Try this command:
convert abc.png -scale 300x100\> abc_a.png
This will scale the image only if
either the original image's width is larger than 300 pixels,
or the original image's height is larger than 100 pixels.
The scaling will preserve the aspect ratio of the original image. -- Is this what you are looking for?
If the image is smaller, then no scaling will happen and abc_a.png will have the original dimensions.
If you want to *emphatically scale the image to 300x100, no matter what, and loose the aspect ratio, bearing with some distortion of the original image, the use:
convert abc.png -scale 300x100\! abc_b.png
(However, this will also scale smaller images...)

command does not execute as variable?

Whenever I try to execute the following shell command , it works properly .
convert maanavulu_GIST-TLOTKrishna.tif -alpha set -matte -virtual-pixel transparent -set option:distort:viewport 1000x1000 -distort perspective-projection '1.06,0.5,0,0,1.2,0,0,0' -trim 1.jpg
But , whenever I try assign the command to a variable and then execute it , it reports the following error .
convert.im6: invalid argument for option PerspectiveProjection : 'Needs 8 coefficient values' # error/distort.c/GenerateCoefficients/873.
The short of it: it's best to:
store your arguments in an array
not including the command itself, for safety (preferable to an eval solution)
then invoke the command with the array
# Store options in array - note that the filenames are excluded here, too,
# for modularity
opts=(-alpha set -matte -virtual-pixel transparent -set option:distort:viewport \
1000x1000 -distort perspective-projection '1.06,0.5,0,0,1.2,0,0,0' -trim)
# Invoke command with filenames and saved options
convert maanavulu_GIST-TLOTKrishna.tif "${opts[#]}" 1.jpg
Afterthought: As #konsolebox and #chepner point out: using a function is probably the best choice (clear separation between fixed and variable parts, encapsulation, full range of shell commands available).
The proper way to assign-and-execute a command is to use an array:
COMMAND=(convert maanavulu_GIST-TLOTKrishna.tif -alpha set -matte -virtual-pixel transparent -set option:distort:viewport 1000x1000 -distort perspective-projection '1.06,0.5,0,0,1.2,0,0,0' -trim 1.jpg)
Then execute it:
"${COMMAND[#]}"
I like eval but it's definitely not the solution this time.
And just a tip: If you can use a function, use a function. And quote your arguments properly.
Quotes are not processed after expanding a variable. The only processing that occurs is word splitting and wildcard expansion. If you need to perform all the normal steps of command execution, you have to use eval:
eval "$variable"

Resources