How can I do FloydSteinbergAlpha dithering with ImageMagick? - imagemagick

I just changed all of my images to RGBA4444 using TexturePacker for spritesheets and ImageMagick for individual images.
Is it possible to do FloydSteinbergAlpha dithing with ImageMagick? I could only find FloydSteinberg dithering (i.e., without the alpha).
My options are -depth 4 -dither FloydSteinberg

You would apply -dither when dropping color count, along with pixel depth (see Quantization Colors).
convert source.png -colors 64 -dither None out.png
convert source.png -colors 64 -dither FloydSteinberg out.png
convert source.png -colors 64 -dither Riemersma out.png
For pixel depth of 4, we can assume
convert source.png -depth 4 -colors 16 -dither None out.png
convert source.png -depth 4 -colors 16 -dither FloydSteinberg out.png
convert source.png -depth 4 -colors 16 -dither Riemersma out.png
Edit
To include alpha in the dithering, set the -quantize to transparent
convert source.png \
-quantize transparent \
-dither FloydSteinberg \
-depth 4 -colors 16 \
out.png

Related

image-magic: automatically adding the filenames of the stacking images

My bash script uses convert and montage of the Image magic to stack 2xN multi-image chart.
Convert:
convert \( "${output}/type1*.png" -append \) \( "${output}/type2*.png" -append \) +append -background white -alpha deactivate ${output}/summary.png
Montage:
montage \( "${output}/type1*.png" \) \( "${output}/type2*.png" \) -geometry 800x600+1+1 -tile x2 -frame 4 -background white -mattecolor lightgoldenrod2 -mode Frame -bordercolor white ${output}/summary.png
Would it be possible via some option to automatically add the name of the stacked images directly on each initial png file?
Of course. I made 4 randomly coloured input images, each 100x100 like this:
magick -size 2x2 xc: +noise random -crop 1x1 -scale 100x100 +repage type-%02d.png
They are named like this:
-rw-r--r--# 1 mark staff 595 11 Oct 10:35 type-00.png
-rw-r--r--# 1 mark staff 594 11 Oct 10:35 type-01.png
-rw-r--r--# 1 mark staff 595 11 Oct 10:35 type-02.png
-rw-r--r--# 1 mark staff 595 11 Oct 10:35 type-03.png
The basic technique you need is to:
iterate over the input images
add a label to each one
aggregate and pipe all the resulting individual images into montage
#!/bin/bash
for f in type-*png; do
magick "$f" -gravity South -fill black -background Plum -font "/System/Library/Fonts/Supplemental/Comic Sans MS Bold.ttf" -splice 0x18 -annotate +0+2 "$f" miff:-
done | magick montage -geometry +1+1 -tile x2 miff:- montage.png
Here's another example, but with an under-colour underneath the text and the text directly on the image and a transparent background to the montaged output:
#!/bin/bash
for f in type-*png; do
magick "$f" -fill white -undercolor '#00000080' -gravity South -annotate +0+5 "$f" miff:-
done | magick montage -background none -geometry +1+1 -tile x2 miff:- montage.png
Obviously you can diddle around with the colours, fonts, positioning as you wish, but the core technique remains the same.
Note: MIFF: is just "Magick Image File Format", a format specific to ImageMagick that is guaranteed to maintain and pass on all aspects of its input - from bit-depth, through comments, through quality, through transparency down to EXIF data and beyond.
Note: The annotation methods I have used are derived from the excellent work by Anthony Thyssen linked here and there are many, many other examples - well worth a read.
Here is a simple way to do that in Imagemagick.
Lets create images as in Mark Setchell's answer:
convert -size 2x2 xc: +noise random -crop 1x1 -scale 100x100 +repage type-%02d.png
Now we use the -label option in montage:
(use %t if you do not want the suffix and %f if you do want the suffix on the file name)
montage -label "%t" *.png -tile 2x2 -geometry +5+5 result.png
Or
montage -label "%t" *.png -tile 2x2 -geometry +5+5 result2.png

Use ImageMagick 7 to create and modify a batch of tiles into repeating images

Version: ImageMagick 7.0.10-55 Q16 x64
I have a Windows folder full of hundreds of TIFF tiles (can repeat seamlessly) of various fabric scans. I need to generate a 10" x 10" image of each fabric based on each tile's dpi. I would like to then scale each image down to a 1000x1000 JPEG with 85% quality. The end result would be a folder full of 1000x1000 images that look like 10"x10" swatches of the scanned fabrics.
I imagine a successful script would do something like this for each tile in the folder:
get a tile
get the tile dpi
dpi * inches = canvas size
create a canvas for the new tiled image
repeat the tile to fill the canvas
format, scale, optimize the new image
save the new image in different folder
I've been able to create tiles one at a time and modify images as a batch. But I can't figure out how to modify and tile or tile in a batch. I also don't know how to retrieve the dpi and use it to modify the canvas size.
This command creates a single 5000x5000 tiled image (but seems to change the dpi).
magick mogrify -path tiled -size 5000x5000 tile:tile01.tif
This command formats, scales, and optimizes a batch of images:
magick mogrify -path resized -format jpg -resize 1000x1000 -quality 85 *.tif
Here are two sample tiles. They are 28 dpi and 237 dpi.
And here's what a 10"x10" swatch of each would look like if you cut it from the roll and took a photo.
Here's my final solution. It uses the dpi of each tile to calculate and fill the resolution needed for a 10"x10" swatch. It then scales the image down to a few different resolutions setting the dpi consistent with its 10"x10" swatch size.
FOR %I in (I:\Covers\tiles\*.tif) DO magick %I -write mpr:tile -set
option:dnsty "%x" -size "%[fx:dnsty*10]x%[fx:dnsty*10]" +delete tile:mpr:tile ^
( -clone 0 -resize "1000x1000\>" -format jpg -quality 85 -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Large\%~nI.jpg ) ^
( -clone 0 -resize "800x800\>" -format jpg -quality 85 -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Medium\%~nI.jpg ) ^
( -clone 0 -resize "400x400\>" -format jpg -quality 85 -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Small\%~nI.jpg ) ^
( -clone 0 -resize "220x220\>" -format jpg -quality 85 -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Largeviews\%~nI.jpg ) ^
( -clone 0 -resize "120x120\>" -format jpg -quality 85 -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Thumbnails\%~nI.jpg ) null:
Simplified with Mark's input:
FOR %I in (I:\Covers\tiles\*.tif) DO magick %I -write mpr:tile -set option:dnsty "%x" -size "%[fx:dnsty*10]x%[fx:dnsty*10]" +delete tile:mpr:tile ^
( -clone 0 -resize "1000x1000\>" -quality 85 -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Large\%~nI.jpg ) ^
( -clone 0 -resize "800x800\>" -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Medium\%~nI.jpg ) ^
( -clone 0 -resize "400x400\>" -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Small\%~nI.jpg ) ^
( -clone 0 -resize "220x220\>" -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Largeviews\%~nI.jpg ) ^
( -clone 0 -resize "120x120\>" -set option:wdth "%w" -density "%[fx:wdth/10]" -units pixelsperinch -write I:\Covers\tiles\tiled\Thumbnails\%~nI.jpg ) null:
My suggestion, thought I still am not sure exactly what you want, would be to simply write a script loop over all the files in your directory and use ImageMagick magick (not magick mogrify) to do the following:
magick tweed.png -write mpr:img +delete -size 1000x1000 tile:mpr:img -density 100 -units pixelsperinch -quality 85 tweed_tiled.jpg
The resulting tiled image has a resolution of 100 dpi. So for 1000 pixels it will print 10 inches.
Image:
Filename: tweed_tiled.jpg
Format: JPEG (Joint Photographic Experts Group JFIF format)
Mime type: image/jpeg
Class: DirectClass
Geometry: 1000x1000+0+0
Resolution: 100x100
Print size: 10x10
Units: PixelsPerInch
Alternate
You can use magick mogrify to process a whole folder of image using the following command to produce results like I have shown above:
cd desktop/in
magick mogrify -path ../out -format jpg -set option:distort:viewport 1000x1000+0+0 -virtual-pixel tile -filter point -distort SRT 0 -density 100 -units pixelsperinch -quality 85 *.png
Perhaps this is what you want, given you last question, using a fairly current version of ImageMagick 7 (only), you can do the following:
Input:
magick I6xQd.png -write mpr:tile -set option:dens "%x" -size "%[fx:dens*10]x%[fx:dens*10]" +delete tile:mpr:tile result.png

How to find out the average color (rgb) of masked area of an Image

I would like to get the average color in rgb of the mask area of a png image.
the following just outputs the average color of the whole image
convert demo.png -mask demo_mask.png -resize 1x1 txt:-
In ImageMagick, -scale 1x1! will ignore the transparent pixels and give you the average of the opaque pixels. So if you want the average of the masked region, you can put the mask into the alpha channel and then use -scale 1x1! to average it down to one pixel. The mask should be white where you want to get the average and black where it should be transparent and ignore those pixels in the average. So this should do it.
convert image.png mask.png -alpha off -compose copy_opacity -composite -scale 1x1! -alpha off -format "%[pixel:u.p{0,0}]" info:
For example if I make the logo: image transparent where it is white and then get the average, I get
convert logo: -transparent white -scale 1x1! -alpha off -format "%[pixel:u.p{0,0}]" info:
srgb(100,81,99)
You can show that this works, by doing it the long way. Multiply the mask by the image, then get the average of each channel of the product. Then get the average of the mask. Then compute the ratios scaled to the range 0 to 255.
convert logo: -transparent white logot.png
convert logot.png -alpha extract mask.png
declare `convert \( logot.png -alpha off \) mask.png -compose multiply -composite -format "IR=%[fx:mean.r]\nIG=%[fx:mean.g]\nIB=%[fx:mean.b]\n" info:`
echo "IR=$IR; IG=$IG; IB=$IB"
IR=0.0651798; IG=0.0529989; IB=0.0641607
MM=`convert mask.png -format "%[fx:mean]\n" info:`
echo "MM=$MM"
MM=0.165872
convert xc: -format "srgb(%[fx:round(255*$IR/$MM)],%[fx:round(255*$IG/$MM)],%[fx:round(255*$IB/$MM)])\n" info:
srgb(100,81,99)
Result is the same as above.
ASIDE: Note that
convert \( logot.png -alpha off \) mask.png -compose multiply -composite ...
in this case is the same as just
convert logot.png -alpha remove ...
But I show it the long way, if the user has a separate mask and image with no transparency.
You should be able to get very nearly what you want with a command something like this...
convert image.png mask.png -compose copyopacity -composite -resize 1x1! txt:-
To output only the color information you can try something like this...
convert image.png mask.png \
-compose copyopacity -composite -resize 1x1! -format "%[pixel:p]" info:
I haven't tried it, but you may have to "-negate" your mask image depending on which version of IM you're using because there have been changes in the way the alpha channel is handled.
You'll get a very slightly different result if you "-trim" before the "-resize".
If you don't want to output the alpha channel information, you can add "-alpha off" after the "-resize".
Perhaps...
convert demo.png -mask demo_mask.png -trim -fx mean -extent 1x1 txt:- |\
tail -1 | cut -d ' ' -f 4
This would work because -trim will reduce the masked image down to the MBR of the ROI. Fx operator -fx mean will convert all pixels into the overall average. And finally the -extent 1x1 will isolate the first pixel in the image. The rest is basic unix utilities.
Another option with better performance...
MEAN=$(convert demo.png -mask demo_mask.png -trim -format '%[fx:mean]' info:-)
convert null: -depth 8 -format "%[pixel:$MEAN]" info:-
Or from the Quantization documentation...
convert demo.png -mask demo_mask.png -trim -scale 1x1\! '%[pixel:s]' info:-

Image Magick - Creating animated gif less weight than Photoshop

I could create a less weight animated gif with same variables(colors, dither, etc) using the following function:
convert -delay 4 -loop 0 *.png -coalesce -matte -alpha remove -depth 8 -layers optimizeFrame -colors 128 animated.gif
But there is a Photoshop function called "Includes transparency based on color opacity" that loose a lot of weight, and I can't find an Image Magick equivalent function.
Thanks!
Found it, I have to del -coalesce
convert -delay 4 -loop 0 *.png -matte -alpha remove -depth 8 -layers optimizeFrame -colors 128 animated.gif

Convert RGB to Grayscale in ImageMagick command-line

How do I convert a RGB image (3 channels) to a grayscale one, using the (r+g+b)/3 method?
I look through an examples page: http://www.imagemagick.org/Usage/color_mods/#grayscale
but the desired method:
convert test.png -fx '(r+g+b)/3' gray_fx_average.png
gave me a wrong result - the resulted image has still 3 channels.
You can check this by running a command: identify -format "%[colorspace] <== %f\n" *.png.
convert <img_in> -set colorspace Gray -separate -average <img_out> gives the best result for any image for me.
Using the (r+g+b)/3 method will apply the effects of grayscale, but the image will remain in sRGB (which is the expected behavior for this method). You'll need to specify the desired colorspace along with the -fx command.
convert test.png -fx '(r+g+b)/3' -colorspace Gray gray_fx_average.png
Verify with identify -format "%[colorspace] <== %f\n" gray_fx_average.png
Gray <== gray_fx_average.png
To batch convert images in Fish shell:
for file in *.jpg; convert -colorspace Gray $file $file; end;
A few ways to that in Imagemagick command line are:
convert test.png -grayscale average gray_average.png
or
convert test.png -colorspace OHTA -channel r -separate +channel gray_average.png
or
convert test.png -intensity average -colorspace gray gray_average.png
or
convert test.png -colorspace HSI -channel blue -separate +channel gray_average.png
See
https://imagemagick.org/script/command-line-options.php#grayscale
https://imagemagick.org/script/command-line-options.php#intensity
https://imagemagick.org/script/command-line-options.php#colorspace
Seems like you are taking the red channel to do that, on
convert test.png -colorspace OHTA -channel r -separate +channel gray_average.png
i prefer the green channel (i heard that way works on tv sice ancient days, maybe the best)
I use convert mostly to convert colour pictures of documents into grey-scale pdf documents in order to perform OCR. My best results are using Rec709Luminance. So I recommend
convert colourpicture.png -grayscale Rec709Luminance greyscalepicture.png
Short command, nice outputs.
I use this with good result for gray-scale images (I convert from PNG):
ls ./*.png | xargs -L1 -I {} convert {} -strip -interlace JPEG -sampling-factor 4:2:0 -gaussian-blur 0.05 -colorspace Gray -quality 20 {}.jpg
I use this for scanned B&W pages get them to gray-scale images (the extra arguments cleans shadows from previous pages):
ls ./*.png | xargs -L1 -I {} convert {} -strip -interlace JPEG -sampling-factor 4:2:0 -gaussian-blur 0.05 -colorspace Gray -quality 20 -density 300 -fill white -fuzz 40% +opaque "#000000" -density 300 {}.jpg
I had an issue to convert an sRGB colorspace to a Gray colorspace. I had to delete Alpha channel manually before a conversion. In other case, the image will stay sRGB.
convert image_original.tga -alpha off -set colorspace Gray image_converted.tga

Resources