B/W Halftone Dither

Questions and postings pertaining to the usage of ImageMagick regardless of the interface. This includes the command-line utilities, as well as the C and C++ APIs. Usage questions are like "How do I use ImageMagick to create drop shadows?".
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: B/W Halftone Dither

Post by anthony »

The middle pattern looks very checkered rather than slightly overlapping circles
It may be that a longer gradient, or animation is needed to properly check the resulting patterns.
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: B/W Halftone Dither

Post by anthony »

I found this very interesting image giving the angles of the offset printing halftone screens
Image
(From http://www.xaraxone.com/webxealot/workbook35/page_5.htm )

Converting images to CYMK patterns of this sort would be highly desirable.
Especially if the scale of the dots (mesh size) can be controlled.

Perhaps a threshold 'screen' image can be created for each of the channels, at different angles,
and merged to form a CMYK 'screen' image.

This 'universal screen' could even be a tilable image that can be size to cover any image.

All that is then needed is a image threshold test against the values of the CMYK image and the CMYK screen to generate a halftone image for that screen 'mesh' density.

That technique should be very quick and possible easier than generating ordered-dither patterns for each channel separately. Basically you would be pre-creating the threshold pattern, on a image wide bases.
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
bonobo2000
Posts: 6
Joined: 2011-01-19T13:44:40-07:00
Authentication code: 8675308

Re: B/W Halftone Dither

Post by bonobo2000 »

Hi there

thanks everybody for sharing all this information. I thought it might be useful to share how I landed here.

my task today was to transform an image to another image using circle as the basis function. Basically I would like to imitate an image with circles of different sizes tiled on the image.

here is the original image:
Image

Below is the target image that I created using single perfect circles. The diameter of the black circle is inversely proportional to the brightness of the image patch where the circle is located.
Image

I did this using the code below:

Code: Select all

rm part_*
echo "creating patches"
convert -crop $PATCH'x'$PATCH _res_$1 part_%06d.png

#transform each patch to a circle with a diameter proportional to the
#brightness of the patch
echo "creating circles"
for i in `find ./part_*`;
    do  
    #extract the mean of the current patch, we will take the 4th column.
    #columns 1-3 are for R,G,B and the 4th pne is for grayscale
    AVE=`convert $i -verbose info: | grep mean | awk '// {print $3}' | sed s/\)// | sed s/\(// | paste - - - -|awk '// {print $4}'`;
    #extract the canvas size and patch position
    GEO=`convert $i -verbose info: | grep "Page geo" | awk '{print $3}'`;
    #compute the diameter
    DIA=`echo "($PATCH*(1-$AVE)*0.5)" | bc | awk '{print int($1)}'`;
echo $i
echo $DIA;
echo $AVE;
echo $GEO
#create the circle containing patch
convert -repage $GEO -size $PATCH"x"$PATCH xc: -fill black  -strokewidth 0 -draw "translate $SHIFT,$SHIFT circle 0,0 $DIA,0" circle_`basename $i .png`.png
done;
#merge all the individual circles
echo "merging"
convert circle_* -background none -layers merge  _Final_$1.png
#delete all the garbage
rm part_* circle_*;
This is naturally extremely inefficient as I have to use harddisk to store each small image patches, create a circle and later merge all these. It took few minutes which is too long. So I started making some research and I soon ended up on the -ordered-dither function of the imagemagick. I noticed that the available circular threshold maps (e.g. c7x7w) are actually not perfect circles. So I created my own mask of width and height 21 pixels.

This is what I got:
Image

It takes only few seconds to generate this image, so it is an enormous improvement compared to my first method. However the drawback is that I dont get perfect circles which is important for my project. I know that it is not possible to obtain perfect circles with this method.

In case any of you have a better idea in imagemagick that would help me to obtain what I want I would appreciate. Maybe one idea would be first to compute an average map of the image using a square window to convolve the image and then using these values to create a vector image such as an svg.
Last edited by bonobo2000 on 2011-01-21T04:01:15-07:00, edited 1 time in total.
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: B/W Halftone Dither

Post by anthony »

Thank you for the script. though It could probably use some reformatting and extra comments.

First the shade of a circle to radius is non-linear, but a squared effect. radius^2 -> area

Also note that for pure black the circles shoudl have a diameter of the 'tile' they occupy. That however means the circle will 'overlap' the neighbouring circles, which means that a linear increase in circle size no longer means a square increase in 'density' or 'represented shade'. Anyone know the actual formula that takes this overlay change into account?

You said one of your 'slow' parts was "use harddisk to store small image patches"

On solution is first pixelate your image into squares of the appropriate grayscale shade.
See example in: Photo handling... Protect Someone's Anonymity
http://www.imagemagick.org/Usage//photos/#anonymity

You can then use a 'dither' solution to replace each square of a specific shade with a circle of the right size.

At no time is the image then broken into small pieces.

Hmmmm...
Given your original 'face' as "face_orig.jpg", and using the largest orthogonal halftone circles "h16x16o" which is based on a 16x16 pixel tiles

The image is 480x409, whcih is not a multiple of 16, so I'll first crop it to 480x400 which is a multiple of 16. dividing that by 16 gives 30x25 so the pixelation size is that size.

Code: Select all

  convert face_orig.jpg -colorspace gray -crop 480x400+0+0 +repage  \
              -resize 30x25 -scale 1600%  face_pixelated.png
Image

Now ordered-dither that...

Code: Select all

  convert face_pixelated.png -ordered-dither h16x16o face_od16.gif
Image

As you can see it works, and also handles black properly (when circles overlap).

However if anti-aliased circles is needed you can DIY the ordered dither by using a lookup sequence of circle image. For example something like shown in...
Dithering with Symbol Patterns
http://www.imagemagick.org/Usage/quantize/#diy_symbols
except each pattern is just a circle image.

Hmmmm, generating the same tile size as previously, and ignoring the non-linear overlap of circles

Code: Select all

  for i in `seq 0 11`; do
      j=`expr $i + 7`
      convert -size 16x16 xc: -draw "circle 7.5,7.5 7.5,$j.5" miff:-
  done | convert - circle_array.gif
Here is the results if you can't get the above to work circle_array.gif
Of course web browsers would mistake this GIF array of images as an animation
just to see this array...

Code: Select all

  convert circle_array.gif +append circle_list.gif
Image
Of course you can make a longer array by using non-integer radius but for our testing purpose this will do.

Now lets do a DIY symbol dither, where each pixel shade is used to index the right image for the circle representing it. The 11.9999 is for the 12 images in the array.

Code: Select all

  convert face_pixelated.png -negate \
          circle_array.gif -virtual-pixel tile -fx 'u[floor(11.9999*u)+1]' \
          face_circles.gif
Image
Of course as FX is being used this can be very slow but it works!
a 3 times speed up, would be to only use FX on one channel only.
See IM Examples FX
http://www.imagemagick.org/Usage/transform/#fx

Also remember the a linear radius was used rather than a radius squared shading, and no attempt to handle overlap shade modifications was done. This is a starting point! Lets see what you and others can do with it :-)

If everything can be worked it it is posible that this could be built it for speed!!!!

PS: how to apply this to generate a angled pixelated tiling, to get a proper offset halftone of color is going to be a real challenge! Lets see how smart users really are 8)
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
User avatar
fmw42
Posts: 25562
Joined: 2007-07-02T17:14:51-07:00
Authentication code: 1152
Location: Sunnyvale, California, USA

Re: B/W Halftone Dither

Post by fmw42 »

I don't know how efficient this might be. But you might sample the brightness of the image (graylevel) by scaling the image down by some factor (equal in each dimension). Then convert the image to txt format so you could get a list of coordinates and graylevels. Modify the coordinates by the scale factor and shift to the center of each square at full resolution. Then use -draw to draw your perfect circles centered at each coordinate and of the appropriate radius determined by the graylevel.

Also you might get some useful ideas from my bash shell script, stainedglass, at the link below, though it is not directly relevant.

Sorry this overlapped with Anthony's much better solution. One note, is that you can write your own function to replace the slow -fx using the MagickFilterKit-1.0.0. In fact, I wrote something similar to Anthony's -fx expression for color reduction.
Last edited by fmw42 on 2011-01-19T18:49:42-07:00, edited 1 time in total.
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: B/W Halftone Dither

Post by anthony »

bonobo2000 wrote:Maybe one idea would be first to compute an average map of the image using a square window to convolve the image and then using these values to create a vector image such as an svg.
That may be a excellent idea. and the pixelated image (without up-scaling) would do for a orthogonal array of circles.

However for a angled array, it may be that blurring the image with a special circle mask can be used to generate a averaged shade/color for the center of each circle. Once you have that 'blurred' image just look up the appropriate color value at each circle center, and draw the appropriate CMYK color circle to form an angled array. Teh SVG images for each 'CYMK channel can then be 'combined' appropriately.

This may actually be faster and will produce perfect circles, not just 'square clipped' circles.
Still you need a proper color value to circle radius function that takes into account some 'overlap' effects.
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
bonobo2000
Posts: 6
Joined: 2011-01-19T13:44:40-07:00
Authentication code: 8675308

Re: B/W Halftone Dither

Post by bonobo2000 »

anthony wrote:Thank you for the script. though It could probably use some reformatting and extra comments.
I edited the version and inserted more comments for documentation. Please note that it is now completely obsolete as a script.
anthony wrote:First the shade of a circle to radius is non-linear, but a squared effect. radius^2 -> area
right, so basically I need to sqrt the diameter parameter parameter to make the area of the circle linearly dependent on brightness.
anthony wrote:Also note that for pure black the circles shoudl have a diameter of the 'tile' they occupy. That however means the circle will 'overlap' the neighbouring circles, which means that a linear increase in circle size no longer means a square increase in 'density' or 'represented shade'. Anyone know the actual formula that takes this overlay change into account?
that is of course more difficult. somebody has to do the mathematics. In my case I do not prefer to have anything than circles so I don't have this problem.
anthony wrote:On solution is first pixelate your image into squares of the appropriate grayscale shade. [..] You can then use a 'dither' solution to replace each square of a specific shade with a circle of the right size.
thank you very very for this elegant solution. I really like it and it works very perfectly fast.
anthony wrote: convert face_pixelated.png -negate circle_array.gif -virtual-pixel tile -fx 'u[floor(11.9999*u)+1]' \
so basically, if I understood correctly the code runs over pixels (all the pixels? or the ones that are at the center of the 16x16 tile blocks?) of the input image (the pixellated one?) and uses the pixel value as an index to select a given block that is used to tile up the image.
If everything can be worked it it is posible that this could be built it for speed!
"built in"? do you mean, how?
Last edited by bonobo2000 on 2011-01-21T06:23:36-07:00, edited 2 times in total.
bonobo2000
Posts: 6
Joined: 2011-01-19T13:44:40-07:00
Authentication code: 8675308

Re: B/W Halftone Dither

Post by bonobo2000 »

fmw42 wrote:Sorry this overlapped with Anthony's much better solution. One note, is that you can write your own function to replace the slow -fx using the MagickFilterKit-1.0.0. In fact, I wrote something similar to Anthony's -fx expression for color reduction.
great information thank you!
User avatar
anthony
Posts: 8883
Joined: 2004-05-31T19:27:03-07:00
Authentication code: 8675308
Location: Brisbane, Australia

Re: B/W Halftone Dither

Post by anthony »

bonobo2000 wrote:
anthony wrote: convert face_pixelated.png -negate circle_array.gif -virtual-pixel tile -fx 'u[floor(11.9999*u)+1]' \
so basically, if I understood correctly the code runs over pixels (all the pixels? or the ones that are at the center of the 16x16 tile blocks?) of the input image (the pixellated one?) and uses the pixel value as an index to select a given block that is used to tile up the image.
If everything can be worked it it is posible that this could be built it for speed!
"built in"? do you mean, how?
Yes that is exactly what it does. It uses an array of images as a lookup table index by shade. As the images line up with the greyscale square tiles, the result is that each greyscale tile will be replaced by the complete image from the image array. Note the image array does not need to be circles but can be any image. In fact the original example used unique 16x16 pixel bitmap symbols. Each image is looked up using virtual pixel tile, but you could use mirror tile instead.

As for built in I mean build the formula into Imagemagick in some way. That way you are not 'parsing' FX for each pixel lookup, (a very slow process).
But you can speed up the FX 3 times by limiting it to just one channel, and extracting that channel, as you are only sing greyscale image.

However a MagickFilter will also work to give it a tremendous speed boost, and would be a good first step to getting it built in. -- Please share if you create such a filter as it is a useful function.

FX is a great prototyping method, and was actually used to work out the exact maths needed for the replacement ordered dither methods with multiple (linear) greylevels. See my notes on this at... Bugs/Development, Ordered Dither Upgrade
http://www.imagemagick.org/Usage/bugs/ordered-dither/
Anthony Thyssen -- Webmaster for ImageMagick Example Pages
https://imagemagick.org/Usage/
3DTOPO
Posts: 28
Joined: 2010-07-05T16:13:53-07:00
Authentication code: 8675308

Re: B/W Halftone Dither

Post by 3DTOPO »

I think I figured out how to create a true CMYK halftone effect, see: viewtopic.php?f=1&t=18409&p=70754#p70754
benoitguigal
Posts: 1
Joined: 2016-01-17T07:26:48-07:00
Authentication code: 1151

Re: B/W Halftone Dither

Post by benoitguigal »

I have managed to create 45° angled circle shaped halftone using paper.js and IM.
The project is documented here:
https://github.com/Postcard/halftone
snibgo
Posts: 12159
Joined: 2010-01-23T23:01:33-07:00
Authentication code: 1151
Location: England, UK

Re: B/W Halftone Dither

Post by snibgo »

Lovely result.
snibgo's IM pages: im.snibgo.com
Bonzo
Posts: 2971
Joined: 2006-05-20T08:08:19-07:00
Location: Cambridge, England

Re: B/W Halftone Dither

Post by Bonzo »

I like it as well; looks a lot better than other examples I have found.
Post Reply