How can I use imagemagick attributes in the draw rectangle command? - imagemagick

I am trying to draw a rectangle across the full width of an image with convert [1]. I am trying to use ImageMagick Attributes to accomplish this.
The command I use is this:
convert ImageFile -fill black -stroke black -draw "rectangle 0,0 %[w],42" ImageFileNew
The Output I get is:
convert: NonconformingDrawingPrimitiveDefinition `rectangle' # error/draw.c/DrawImage/3271.
With debug:
convert ImageFile -fill black -stroke black -draw "rectangle 0,0 %[w],42" ImageFileNew
I get:
2016-07-27T14:55:37+02:00 0:00.047 0.047u 7.0.2 Draw CONVERT[6660]: draw.c/DrawImage/1755/Draw
begin draw-image
2016-07-27T14:55:37+02:00 0:00.047 0.047u 7.0.2 Draw CONVERT[6660]: draw.c/DrawImage/3207/Draw
rectangle 0,0
2016-07-27T14:55:37+02:00 0:00.047 0.047u 7.0.2 Draw CONVERT[6660]: draw.c/DrawImage/3258/Draw
end draw-image
CONVERT: NonconformingDrawingPrimitiveDefinition `rectangle' # error/draw.c/DrawImage/3271.
I do not want to use a temporary file and I want to use this eventually after a trim repage. I would also like to replace the '42' with a formula if it is possible to use attributes in this command.
I guess the question is if this is supported( and if not then why the H not)?
[1] Version: ImageMagick 7.0.2-5 Q16 x64 2016-07-22 http://www.imagemagick.org

I guess the question is if this is supported( and if not then why the H not)?
It should be supported, but I would suggest using the fx: protocol to calculate the values.
convert rose: -fill red \
-draw 'rectangle 0 0 %[fx:w] %[fx:h]' \
out_fill.png
I would guess you're seeing NonconformingDrawingPrimitiveDefinition as your starting to draw outside of the range of the authentic pixels. Remember that column / row index start at 0, and got to nth - 1.
convert rose: -fill red -stroke black -strokewidth 2 \
-draw 'rectangle 0 0 %[fx:w-1] %[fx:h-1]' \
out_stroke.png

Related

Find colored box in image and create a mask file

I would like to create a hotfolder for my motion camera, into which I can drop images marking areas, which should be excluded in motion recognition via a *pgm mask. On these images, there is a small area marked with a transparent box with a magenta colored outline. My aim is to replace this box and outline with a black solid box and the rest of the image with white. (Tried to post samples here, but not enough reputation to do so.)
I know how to do this "by foot" using gimp, but I cannot figure out a clever and simple way achieving this with imagemagick.
I tried googling for solutions with -trim and -virtual-pixel, but no luck. Any help would be appreciated.
I'll do this step-by-step so you can see the intermediate parts in case you are on Windows and bash doesn't work.
First, let's make make everything that is not within 10% of your magenta colour, namely rgb(225,75,130), into lime green:
magick source.jpg -fill lime -fuzz 10% +opaque "rgb(225,75,130)" result.png
Ok, now let's get the trim box - i.e. all the constant junk that ImageMagick could trim off to focus on the magenta bit.
magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info:
14x66+426+118
So your magenta box is 14x66pixels and located at offset 426,118 from the top-left. Now we want to get those in bash variables w,h,x,y. We need to change x and + into spaces using tr:
read w h x y < <(magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info: | tr 'x+' ' ')
If we print this we get:
echo $w, $h, $x, $y
14, 66, 426, 118
Now we want to draw a rectangle, but that needs top-left and bottom-right, so we need to do some maths:
((x1=x+w))
((y1=y+h))
Ok, now we can load the original image, make it fully white, then draw our black rectangle:
magick source.jpg -threshold -1 -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm
So, the whole thing boils down to:
#!/bin/bash
read w h x y < <(magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info: | tr 'x+' ' ')
echo $w, $h, $x, $y
((x1=x+w))
((y1=y+h))
magick source.jpg -threshold -1 -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm
There are other (maybe more elegant) ways of doing it, using flood-fills and/or connected components but I didn't want it to rely on your magenta box being "watertight", i.e. not rely on the sides being continuous and complete.
Also, if the size of your images is known and constant, you can avoid reloading the original and making it white by thresholding like I do in the last line and just create a canvas of the known dimensions, i.e.:
magick -size ${W}x${H} xc:white -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm

ImageMagick - change the background color of the selected bounding box

I already have the normalized vertices of my selected bounding box (e.g xmin: 0.68, ymin: 0.47, xmax: 0.94, ymax: 0.82) and I want to save this box in an other .jpg file. Furthermore, in the original image I want to make this highlighted box all white. Is this possible using Imagemagick?
Starting with this:
and knowing the top-left corner of the monument is at 400,10 and the bottom-right is at 500,200, you can extract the monument to a file with:
magick photo.jpg -crop 100x190+400+10 extract.jpg
and overpaint in white with:
magick photo.jpg -fill white -draw "rectangle 400,10 500,200" overpainted.jpg
Or, for extra fun, overpaint in semi-transparent white with:
magick photo.jpg -fill "rgba(255,255,255,0.5)" -draw "rectangle 400,10 500,200" overpainted.jpg
You can do both operations in one go with:
magick photo.jpg \( +clone -fill white -draw "rectangle 400,10 500,200" -write overpainted.jpg +delete \) -crop 100x190+400+10 extract.jpg
Using ImageMagick version 6, the command below will create two output images. (An example command for ImageMagick version 7 is further down in the reply.)
The first output image will be cropped from the input image using the bounding box starting at w*0.68xh*0.47 and ending at w*0.94xh*0.82.
The second output will be the input with a white section corresponding to the sub-image cropped out to make the first image.
convert input.png \
-set option:distort:viewport "%[fx:(w*0.94)-(w*0.68)]x%[fx:(h*0.82)-(h*0.47)]" \
\( +clone -distort affine "0,0 -%[fx:w*0.68],-%[fx:h*0.47]" \
-write result1.png -fill white -colorize 100 \) \
-set page "%[fx:u.w]x%[fx:u.h]+%[fx:t*(u.w*0.68)]+%[fx:t*(u.h*0.47)]" \
-flatten result2.png
That starts by reading the input image and calculating the viewport, the dimensions of the sub-image to crop, according to the bounding box dimensions you've provided.
Then inside the parentheses it creates a clone and does a "-distort affine" which, in effect, crops the image and locates it properly in that viewport. It writes that result to the first output image "result1.png". Then, still inside the parentheses, it fills that cropped piece with white.
After that it sets the paging geometry so that white piece can eventually be composited back into its original location over the input image.
It finishes by flattening the white piece onto the input image, and writes the second output image "result2.png".
The same thing can be done using ImageMagick version 7 with a slightly less complicated command...
magick input.png \
\( +clone \
-crop "%[fx:(w*0.94)-(w*0.68)]x%[fx:(h*0.82)-(h*0.47)]+%[fx:w*0.68]+%[fx:h*0.47]" \
-write result1.png -fill white -colorize 100 \) \
-flatten result2.png
That does the calculations directly in the "-crop" operation, and the paging geometry is saved in the cropped piece so it can be flattened back to its original position without resetting the geometry.
Those are in *nix syntax. To make it work in Windows change the continued line backslashes "\" to carets "^", and eliminate those backslashes that escape the parentheses "\(...\)".
Here is one other variation in ImageMagick 6. It crops the image and saves it and then deletes it. Then it use -region to write white into that bounding box.
This is Unix syntax. For Windows, remove \ from parenthesis and change end of line \ to ^.
Input:
convert img.jpg \
\( +clone -crop 100x190+400+10 +repage +write result1.jpg +delete \) \
-region 100x190+400+10 -fill white -colorize 100 +region result2.jpg

Replace only background color of PNG

Here is my code:
#! /usr/bin/env sh
# Generate test image.
convert -size 100x60 xc:blue -fill blue -stroke black -draw "circle 50,30 55,55" in.png
# Make background transparent.
convert in.png -fill none -draw 'matte 0,0 floodfill' -flop -draw 'matte 0,0 floodfill' -flop out.png
# Replace transparent background with green.
mogrify -background green -flatten out.png
# The wrong way.
convert in.png -transparent blue oops.png
mogrify -background green -flatten oops.png
It is based on this snippet: https://snippets.aktagon.com/snippets/558-how-to-remove-a-background-with-imagemagick
Starting with this:
I want to get this:
Not this:
Can I achieve this with a single convert command instead of a convert followed by a mogrify?
I am using ImageMagick 6.8.9-9.
Essentially, you are seeking a "floodfill", like this:
convert in.png -fill green -draw 'color 0,0 floodfill' result.png
That will look at the top-left pixel (0,0) and fill all similarly coloured pixels which are connected to it with green. If your background has slight variations in it, e.g. it's a JPEG, add some fuzz factor
convert in.jpg -fuzz 25% ...
Note that if your circle had touched the top and bottom edges, it would prevent the fill from flooding around to the right side of the diagram. So, let's say you had created your circle like this:
convert -size 100x60 xc:blue -fill blue -stroke black -draw "circle 50,30 50,0" in.png
And then you run the above command, you will get:
If that happens, you can add a single pixel wide border all the way around for the colour to "flow" through first, then flood-fill, and finally remove it later:
convert in.png -bordercolor blue -border 1 -fill green -draw 'color 0,0 floodfill' -shave 1x1 result.png

How to underlay circle with mogrify in one command

I wrote this script and it's working fine, but I would like to do all of it in one step on the fly, without the extra temp image.
explanation: i have a lot of broken image files and i want to draw a circle underneath each image. for this i have to create a temporary image circle.png and then use "image DstOver" to place it below each of the images:
convert -size 200x200 xc:transparent -fill red -draw 'translate 100,100 circle 0,0 100,0' circle.png
mogrify -draw "image DstOver 0,0 0,0 'circle.png'" images/*.png
Something along the lines of:
mogrify -fill red -draw "DstOver translate 100,100 circle 0,0 100,0" images/*.png
But this is always giving me an error, no matter where i place the DstOver:
mogrify: non-conforming drawing primitive definition `DstOver' # error/draw.c/DrawImage/3169.
Composition operators like "DstOver" are only used with the "image" primitive of "-draw". Just omit it. See the "-draw" entry in the ImageMagick commandline documentation.
You can have multiple "-draw " options, some drawing figures such as "circle ..." and others such as "image DstOver ...".
I am not sure what you are trying to do, but in general, mogrify will have trouble doing anything with multi-image operators or stack operators. The only exception I know of is the -draw image operator, so you need to create your image up-front and then use that:
# Blue rectangle with transparent centre
convert -size 200x200 xc:none -bordercolor blue -border 50 start.png
# Your circle
convert -size 200x200 xc:white -fill red -draw 'translate 100,100 circle 0,0 100,0' circle.png
# Now underlay
mogrify -draw "image DstOver 0,0 0,0 'circle.png'" start.png

Start path relative to corner of image

I have written the following script which uses the ImageMagick* convert utility to append axis labels to an existing image.
LEFT_="l -30,0 +2,+2 -6,-2 +6,-2 -2,+2 z"
RIGHT_="l 30,0 -2,+2 +6,-2 -6,-2 +2,+2 z"
convert -size 240x160 pattern:SMALLFISHSCALES \
-pointsize 16 -fill black -background white \
-gravity SouthEast -splice 0x20 \
-draw "translate 40,0 text 0,0 'Time' stroke red path 'm 5,2 $RIGHT_'" \
-gravity NorthWest -splice 20x0 \
-draw "rotate +90 translate 40,-10 text 0,0 'Value' path 'm -5,2 $LEFT_'" \
example.png
Which produces the following image:
This is almost exactly what I am after, except that the red arrow is out of place. I expected the red arrow to appear next to the Time label, since its start point is specified as a relative position in the same draw command. Unfortunately, it looks like the -gravity option is affecting the text primitive, but not the path primitive.
Is there a way to reference the SouthEast corner, or the Time text label when specifying the start position of the red arrow? I can't use absolute coordinates, because the size of the image varies.
*ImageMagick 6.7.8-9 on CentOS 7
Updated Answer
Maybe you can make Unicode text arrows like this then they will be affected by gravity...
perl -e 'binmode(STDOUT,":utf8"); print "Time ... \x{2192}\x{2191}";'|
convert -font TimesNewRoman -pointsize 36 label:#- arrows.png
Depending on your OS, the following may do as a replacement for the Perl above...
printf "%b" "\u2192" | convert ...
Original Answer
I am not at all familiar with paths, but I can suggest a way to achieve what you want that doesn't use gravity at all, and maybe that will help.
Rather than use -splice, you can clone your original image and crop it to the size you planned to splice on, and then -append the strips that label the axes. It is easier to show you the command than explain it!
convert -size 240x160 pattern:SMALLFISHSCALES \
\( +clone -crop x20+0+0 -fill blue -colorize 100% \) \
-append \
\( +clone -crop 20x+0+0 -fill red -colorize 100% \) \
+swap +append result.png
I have filled the x-axis blue, but remove that and add whatever labelling and arrows you need, and I filled the y-axis red, but likewise remove that and add labelling and arrows - rotating as necessary.
Two tricky things to note...
-append will append the second image below the first
+append will append the second image to the right of the first, so I +swap beforehand to put it on the left side.

Resources