imagemagick: how to use the %[fx:...] operator in draw rectangle command? - shell

I can do this to draw a red rectangle on an image:
convert original.png -fill red -draw "rectangle 10,20 150,40" result.png
The 150,40 is the right bottom coordinate. However if I use the %[fx:...] operator in there, like so:
convert original.png -fill red -draw "rectangle 10,20 %[fx:w-30],40" result.png
The %[fx:w-30] is supposed to evalute to the image's width minus 30.
However I'm getting an error:
convert: non-conforming drawing primitive definition `rectangle' # error/draw.c/DrawImage/4227.
I have also tried single quotes (') instead of double (") but that made no difference.
My imagemagick version is 7.0.7-36.
What am I doing wrong? What's the correct way of using the %[fx:...] operator in the above example?

Thanks to #GeeMack the solution is: use magick instead of convert, so it becomes:
magick original.png -fill red -draw "rectangle 10,20 %[fx:w-30],40" result.png

If you're still on ImageMagick 6, you can use the identify command to output a formatted string and then use that in the convert command. Using shell syntax, this can be expressed as follows:
convert original.png -fill red -draw "$(identify -format 'rectangle 10,20 %[fx:w-30],40' original.png)" result.png

Related

For loop to anotate images with imagemagick from text file

I want to simplify my Christmas card making process. I have already built a frame with some text, and I wanted to personalize each card with the recipients name by using imagemagick and some bash power. But so far I'm failing bad :(
This is what I tried at first:
for i in names.txt;
do convert -font Akaya-Telivigala-Regular -fill black -stroke black -strokewidth 1\
-pointsize 190 -draw 'text 640,730 $i' chrisFrame.svg -resize 70%\
greetings_$i.jpg;
done
But it fails with convert-im6.q16: non-conforming drawing primitive definition i' # error/draw.c/RenderMVGContent/4300.`.
Seeing that imagemagick was trying to use $i as a primitive, I tried to enclose it in double quotes, as I would do if I was passing the names by hand:
for i in names.txt;
do convert -font Akaya-Telivigala-Regular -fill black -stroke black -strokewidth 1\
-pointsize 190 -draw 'text 640,730 "$i"' chrisFrame.svg -resize 70%\
greetings_$i.jpg;
done
Of course it generated a beautiful file named greetings_names.txt.jpg with $i instead of the name.
The content of the names.txt file is nothing else than
John,
Jane,
What am I doing wrong and how can I make it work?
With the gracious help of #GeeMack and after correcting an error in the call of the for loop, the solution is this:
for i in `cat names.txt`;
do convert -font Akaya-Telivigala-Regular -fill black -stroke black\
-strokewidth 1 -pointsize 190 -draw "text 640,730 '$i'"\
chrisFrame.svg -resize 70% greeting_$i.jpg;
done
Effectively, inverting the single and double quotes made the trick.
Please notice that it was necessary to change also the line
for i in names.txt
for
for i in `cat names.txt`
EDIT TO ADD:
After #Jetchisel comment regarding why one should not use for to read lines out of a file, the correct code will be:
while read i
do convert -font Akaya-Telivigala-Regular -fill black\
-stroke black -strokewidth 1 -pointsize 190
-draw "text 640,730 '$i'" chrisFrame.svg\
-resize 70% greetings_$i.jpg;
done < names.txt

Use greek letters in adding annotation in convert imagemagick

I need to annotate some label/text containing greek letters on a figure using convert from imagemagick on linux.
What is the best option?
Trivial choices such as:
ii=1
label="α β $ii"
convert in.png -fill black -annotate "$label" out.png
won't work.
I am not familiar with font coding options.
One way that works for me in ImageMagick 6.9.9.23 Mac OSX Sierra is to use a Greek font. (The symbol font also works)
ii=1
label="a b $ii"
convert logo: -font GreekMathSymbols -pointsize 64 -gravity center -fill red -annotate +0+0 "$label" out.png
Alternately, use a UTF-8 compatible text editor and type your text using a font that supports those characters. For example I put your alpha beta one characters into a text file using the same GreekMathSymbols font.
Then
convert logo: -font GreekMathSymbols -pointsize 64 -gravity center -fill red -annotate +0+0 "#greek.txt" out2.png
See also http://www.imagemagick.org/Usage/text/#unicode

color pixel count with GraphicsMagick

I need to count pixels in an image that are not background color.
I am calling this from PHP (it's from ImageMagick):
gm convert test.png -fill black +opaque "rgb(255,255,255)" -fill white -opaque "rgb(255,255,255)" -print "pixels = %[fx:w*h*mean]\n"
But it does not give any result, nothing.
I tried using histogram instead:
gm convert test.png -define histogram:unique-colors=true -format %c histogram:info.txt
That works, but gives values for every color and more details, I just need a single number please.
You have got a couple of issues here. You seem to be trying to mix GraphicsMagick with ImageMagick when they are not the same thing.
Firstly, GraphicsMagick does not have the +opaque operator that ImageMagick has.
Secondly, it doesn't have the -fx operator that ImageMagick has for doing maths.
I would suggest you move to, the more powerful, ImageMagick. Then it will work as you expect:
# Create a test image
convert -size 200x200 xc:black xc:white xc:red +append image.png
# Count the white pixels
convert image.png -fill black +opaque "rgb(255,255,255)" -print "pixels = %[fx:w*h*mean]\n" nul:
pixels = 40000
If you really, really must do it with GraphicsMagick, I can only suggest the following - which is heavily based on #GlennRanders-Pehrson answer here:
gm convert image.png +matte -matte -transparent white -operator matte negate 1 result.png
gm identify -verbose result.png | grep -EA5 "Opacity:|Geometry:" | grep -E "Mean|Geometry"
Geometry: 600x200
Mean: 43690.00 (0.6667)
Mean: 43690.00 (0.6667)
And your answer will be:
600 * 200 * (1 - 0.667)

Imagemagick - Select area to replace pixel colour

I am trying to replace the colour of some pixels in an image using imagemagick, BUT not in the whole image just within a part of the image.
The command I am using now is:
convert original_image.jpg -fuzz 5% -fill '#f2f2f2' -opaque white image_without_colour.jpg
This works but it replaces #f2f2f2 everywhere rather than just where i want i want.
I would ideally do something like:
convert original_image.jpg -fuzz 5% -fill '#f2f2f2' -area '100,100,50,50' -opaque white image_without_colour.jpg
I am thinking about a more elegant solution, but I think this does what you want:
convert x.jpg -crop 100x100+50+50 y.jpg # Extract region to work on into y.jpg
convert y.jpg -fuzz 5% -fill '#f2f2f2' -opaque white z.jpg # Do your stuff and save as z.jpg
convert x.jpg z.jpg -geometry +50+50 -composite out.jpg # Whack it back whence it came!
Output file isout.jpg
I think this is a more elegant solution:
convert x.png \( +clone -crop 100x100+50+50 -fuzz 5% -fill '#f2f2f2' -opaque white \) -flatten out.jpg
ImageMagick's "-region" option is equivalent to your suggested "-area" option:
convert original_image.jpg -fuzz 5% -fill '#f2f2f2' -region 100x100+50+50 \
-opaque white image_without_colour.jpg
changes white pixels in the specified region to gray.

Split a Bash script with ImageMagick instructions into smaller pieces?

I have this bash script, feeding drawing information into ImageMagick, which starts like this:
#!/bin/bash
convert -size 2200x2200 xc:white \
-fill '#FFFEFF' -draw 'point 1112,1111' \
-fill '#FFFEFE' -draw 'point 1112,1112' \
-fill '#FFFEFE' -draw 'point 1111,1112' \
-fill '#FFFEFE' -draw 'point 1110,1112' \
-fill '#FFFEFE' -draw 'point 1110,1111' \
******ON & ON 4.2 MILLION LINES MORE*******
spectrumspiral.png;
My problem is I keep getting warnings about argument list too long, terminal says 'Killed', warning about 'fork: cannot allocate memory etc.
I've tried adjusting ulimit -s to a much higher value to no avail. Really want to make this image. Any idea how I can feed the terminal chunks of this script at a time? Or something to that end.
I've heard xargs can be used for things like this, but I haven't been able to find a specific implementation that fits the nature of this problem.
If you create an image with 4 pixels in it (1 red, 1 white, 1 blue and 1 black), you can tell ImageMagick to output the resulting image as text file as follows:
convert -size 1x1 xc:red xc:white xc:blue xc:black +append -depth 8 -colorspace RGB image.txt
# ImageMagick pixel enumeration: 4,1,255,rgb
0,0: (255,0,0) #FF0000 rgb(255,0,0)
1,0: (255,255,255) #FFFFFF rgb(255,255,255)
2,0: (0,0,255) #0000FF rgb(0,0,255)
3,0: (0,0,0) #000000 rgb(0,0,0)
By the same token, if you feed that text file into ImageMagick, it can recreate the image:
cat image.txt | convert txt:- output.img
So, all we need to do is convert your -fill commands into a text file of the format that ImageMagick likes. So we can do this to yourFile
sed -E 's/.*(#[0-9A-F]+).* ([0-9]+)\,([0-9]+).*/\2,\3:\1/g' yourFile
and we will get something like this:
0,0:#3C4AD8
0,1:#269531
0,2:#CF2C7C
...
...
which we can then pipe into awk to rearrange how ImageMagick likes it:
awk -F: '
BEGIN{print "# ImageMagick pixel enumeration: 2200,2200,255,rgb"}
{
coords=$1;colour=$2
rh="0x" substr(colour,2,2);
gh="0x" substr(colour,4,2);
bh="0x" substr(colour,6,2);
r=strtonum(rh);
g=strtonum(gh);
b=strtonum(bh);
printf "%s: (%d,%d,%d) %s\n",coords,r,g,b,colour;
}'
So, if we put all that together, the following script should be able to create your beloved image into the file spectrumspiral.png:
#!/bin/bash
sed -E 's/.*(#[0-9A-F]+).* ([0-9]+)\,([0-9]+).*/\2,\3:\1/g' yourFile | awk -F: '
BEGIN{print "# ImageMagick pixel enumeration: 2200,2200,255,rgb"}
{
coords=$1;colour=$2
rh="0x" substr(colour,2,2);
gh="0x" substr(colour,4,2);
bh="0x" substr(colour,6,2);
r=strtonum(rh);
g=strtonum(gh);
b=strtonum(bh);
printf "%s: (%d,%d,%d) %s\n",coords,r,g,b,colour;
}' | convert txt:- spectrumspiral.png
Try using the shell's here-document
#!/bin/bash
convert - <<EOS spectrumspiral.png
-size 2200x2200 xc:white
-fill '#FFFEFF' -draw point '1112,1111'
-fill '#FFFEFE' -draw 'point 1112,1112'
-fill '#FFFEFE' -draw 'point 1111,1112'
-fill '#FFFEFE' -draw 'point 1110,1112'
-fill '#FFFEFE' -draw 'point 1110,1111'
# ******ON & ON 4.2 MILLION LINES MORE*****
EOS
My system doesn't have convert so I can test that this works.
Most linux programs (including convert) can accept input from StdIn, which in the cmd-line above is represented by the - char. The - tells the command to expect input, not from file, but as if it was being typed at the keyboard, ie StdIn. The <<EOS .... EOS is the here-doc, and it represents bash 'typing' in all of that text into the commands stdIn input.
You may or may not need all of the quoting, if you already have there, I don't think it will hurt. If this doesn't work as is, use a small sample file of input (like above) and test various scenarios until it creates a file for you.
IHTH

Resources