Automated Cookie Cutters: Height Map Image File Preparation

Having established the OpenSCAD can produce a height map from an input array, a bit more doodling showed how to produce such an array from a grayscale image. I certainly didn’t originate all of this, but an hour or two of searching with the usual keywords produced snippets that, with a bit of programming-as-an-experimental-science tinkering, combine into a useful whole.

Not being much of an artist, I picked a suitable SVG image from the Open ClipArt Library:

Jellyfish - color
Jellyfish – color

That’s pretty, but we need a grayscale image. Some Inkscape fiddling eliminated all the nice gradients, changed the outline to a dark gray, made all the interior fills a lighter gray, and tweaked the features:

Jellyfish - gray
Jellyfish – gray

Admittedly, it looks rather dour without the big smile, but so it goes. This is still an SVG file, so you have vector-mode lines & areas.

A bit more work changed the grays to produce different heights, duplicated one of the spots for obvious asymmetry, and exported it as a gritty 160×169 pixel PNG image:

Jellyfish - height map image
Jellyfish – height map image

The low resolution corresponds to a 2 pixel/mm scale factor: 169 pixel = 84.5 mm tall. The cutter wrapped around this image will have a lip that adds about 12 mm, a 1 or 2 mm gap separates the press from the cutter, and there’s a skirt around the whole affair. My Thing-O-Matic build platform measures a scant 120 mm in the Y direction, which puts a real crimp on the proceedings.

That’s assuming the usual 1 unit = 1 mm conversion factor. If your toolchain regards units as inches, then you need a different scale factor.

Low resolution also speeds up the OpenSCAD processing; you can use as many pixel/mm as you wish, but remember that the extruded filament is maybe 0.5 mm wide, so anything beyond 4 pixel/mm might not matter, even if the motion control could benefit from the smoother sides. Features down near the resolution limit of the model may produce unusual effects for thin walls near the thread width, due to interpolation & suchlike (which is why I got rid of the smile). The processing time varies roughly with the number of pixels, so twice the resolution means four times more thumb-twiddling.


  • You’re looking at a cookie lying on a table: this is the top view
  • Background surrounding the image should be full white = 255
  • Highest points should be very light gray, not full white, to avoid creating islands
  • Lowest points may be black; I use a very dark gray
  • No need for an outline
  • Smooth gradients are OK, although they’ll become harshly quantized by the layer thickness
  • You can probably use JPG instead of PNG, but these aren’t big files
  • Remember this is a cookie press, not a work of art

With a suitable PNG image file in hand, use ImageMagick to prepare the image:

  • Crop to just the interesting part: -trim (depends on the four corners having background color)
  • Convert the image to grayscale: -type Grayscale (in case it’s a color image)
  • Make it 8 bit/pixel: -depth 8 (more won’t be helpful)
  • Stretch the contrast: -auto-level (to normalize the grayscale to the full range = full height)
  • Reverse left-to-right to make a cookie press: -flop (think about it)
  • Invert the grayscale to make the cookie press surface: -negate (again, think about it)
  • Reverse top-to-bottom to correct for upcoming OpenSCAD surface() reversal: -flip


convert filename.png -trim -type Grayscale -depth 8 -auto-level -flop -negate -flip filename_prep.png

Which produces this image:

Jellyfish - prepared image
Jellyfish – prepared image

Combining -flop and -flip just rotates the image 180° around its center, but I can’t help but believe transposing the bits works out better & faster than actually rotating the array & interpolating the result back to a grid. On the other paw, if there isn’t a special case for (multiples of) right-angle rotation(s), there should be. [grin]

The prepared image is 149×159, because the -trim operation removed the surrounding whitespace. You can do that manually, of course, keeping in mind that the corners must be full white to identify the background.

Next: convert that image to a data array suitable for OpenSCAD’s surface() function…