Skip to content
This repository has been archived by the owner on Oct 23, 2019. It is now read-only.

Commit

Permalink
Binary logarithm possible
Browse files Browse the repository at this point in the history
  • Loading branch information
l0b0 committed Oct 19, 2010
1 parent ca8e042 commit 7b898ab
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 38 deletions.
2 changes: 1 addition & 1 deletion README
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
img2scad.py - Convert black and white images to OpenSCAD contours.
img2scad.py - Convert black and white images to OpenSCAD structures.

Installation / upgrade: sudo easy_install -U img2scad

Expand Down
74 changes: 47 additions & 27 deletions img2scad/img2scad.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""img2scad - Convert images to OpenSCAD contours
"""img2scad - Convert images to OpenSCAD structures
<http://github.com/l0b0/img2scad>
Default syntax:
img2scad [-m|--minimum=N] < input_file
img2scad [-b|--base=N] [-l|--log] < input_file
Description:
For each pixel in the input, it will create a cube in the output. The height of
the cube corresponds to the whiteness of the corresponding pixel.
If -m or --minimum is specified, all values will be shifted by that value. This
can be positive or negative.
If -b or --base is specified, all values will be shifted by that value. This
can be positive or negative. This can be used to output zero values.
If -l or --log is specified, the logarithm of the grey values will be used for the output.
Examples:
img2scad < example.png > example.scad
Convert example.png to example.scad.
img2scad -m 1 < example.png > example.scad
img2scad -b 1 < example.png > example.scad
Convert example.png to example.scad, with a "bedrock" of height 1.
img2scad -b 2 -l < example.png > example.scad
Convert example.png to example.scad, taking the logarithm for the height
and preserving the black pixels.
<http://www.thingiverse.com/thing:4448>
Bugs:
Expand All @@ -36,6 +42,7 @@
__license__ = 'GPLv3'

from getopt import getopt, GetoptError
import math
from PIL import Image
from signal import signal, SIGPIPE, SIG_DFL
import sys
Expand All @@ -58,8 +65,8 @@
"""Avoid 'Broken pipe' message when canceling piped command."""


def img2scad(stream, minimum):
"""Convert black pixels to OpenSCAD cubes."""
def img2scad(stream, base = 0, log = False):
"""Convert pixels to OpenSCAD cubes."""

img = Image.open(stream)

Expand All @@ -71,23 +78,33 @@ def img2scad(stream, minimum):

img_matrix = img.load()

result = ''

result += 'module topography() {\n'
result = 'module topography() {\n'
result += ' union() {\n'

for row in range(height):
for column in range(width):
pixel = img_matrix[column, row] + minimum
if pixel != 0:
result += ' translate([%(x)s, %(y)s, 0])' % {
'x': BLOCK_SIZE * column - width / 2,
'y': -BLOCK_SIZE * row + height / 2
}
result += 'cube('
result += '[%(block_side)s, %(block_side)s, %(height)s]);\n' % {
'block_side': BLOCK_SIDE,
'height': pixel
}
pixel = img_matrix[column, row] + base

if log:
# Skip unloggable values
if pixel <= 0:
continue
pixel = round(math.log(pixel, 2), 2)

if 0 == pixel:
continue

# Center cubes in (x, y) plane
result += ' translate([%(x)s, %(y)s, 0])' % {
'x': BLOCK_SIZE * column - width / 2,
'y': -BLOCK_SIZE * row + height / 2
}
result += 'cube('
result += '[%(block_side)s, %(block_side)s, %(height)s]);\n' % {
'block_side': BLOCK_SIDE,
'height': pixel
}

result += ' }\n'
result += '}\n'
result += 'topography();'
Expand All @@ -101,24 +118,27 @@ def main(argv = None):
argv = sys.argv

# Defaults
minimum = 0
log = False
base = 0

try:
opts, args = getopt(
argv[1:],
'm:',
['minimum='])
'b:l',
['base=', 'log'])
except GetoptError, err:
sys.stderr.write(str(err) + '\n')
return 2

for option, value in opts:
if option in ('-m', '--minimum'):
minimum = int(value)
if option in ('-b', '--base'):
base = int(value)
if option in ('-l', '--log'):
log = True

assert [] == args, 'There should be no arguments to this command'

result = img2scad(sys.stdin, minimum)
result = img2scad(sys.stdin, base, log)
print result

if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

setup(
name = 'img2scad',
version = '0.2',
version = '0.3',
description = 'Image to OpenSCAD converter',
long_description = module_doc,
url = 'http://github.com/l0b0/img2scad',
Expand Down
Binary file added tests/example_black.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 53 additions & 9 deletions tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from img2scad import img2scad

EXAMPLE_BIG = join(dirname(__file__), './example_big.png')
EXAMPLE_BLACK = join(dirname(__file__), './example_black.png')
EXAMPLE_SMALL = join(dirname(__file__), './example_1px.png')


Expand All @@ -30,24 +31,67 @@ class TestConvert(unittest.TestCase):
# pylint: disable-msg=R0904

def test_small(self):
"""Check that a single pixel image gives output."""
result = img2scad.img2scad(open(EXAMPLE_SMALL), 0)
"""A single pixel image gives one cube."""
result = img2scad.img2scad(open(EXAMPLE_SMALL))
self.assertTrue(search(r'translate.*cube', result))


def test_big(self):
"""Check that a big image gives output."""
result = img2scad.img2scad(open(EXAMPLE_BIG), 1)
def test_black(self):
"""A black image gives no cubes."""
result = img2scad.img2scad(open(EXAMPLE_BLACK))
self.assertFalse(search(r'translate.*cube', result))


def test_black_base(self):
"""A black image gives output if a base is applied."""
result = img2scad.img2scad(
open(EXAMPLE_BLACK),
base = 1)
self.assertTrue(search(r'translate.*cube', result))


def test_zero(self):
"""Check that if the minimum is shifted sufficiently, the result is
empty."""
result = img2scad.img2scad(open(EXAMPLE_SMALL), -152)
def test_shift_to_zero(self):
"""A non-black pixel can be removed by the base offset."""
result = img2scad.img2scad(
open(EXAMPLE_SMALL),
base = -152)
self.assertFalse(search(r'translate.*cube', result))


def test_log(self):
"""Non-black images should be loggable."""
result = img2scad.img2scad(
open(EXAMPLE_SMALL),
log = True)
self.assertTrue(search(r'translate.*cube', result))


def test_log_one(self):
"""Log of 1 is zero."""
result = img2scad.img2scad(
open(EXAMPLE_BLACK),
base = 1,
log = True)
self.assertFalse(search(r'translate.*cube', result))


def test_log_zero(self):
"""Log of 0 is undefined, so we skip those."""
result = img2scad.img2scad(
open(EXAMPLE_BLACK),
log = True)
self.assertFalse(search(r'translate.*cube', result))


def test_big(self):
"""Check that a big image gives output."""
result = img2scad.img2scad(
open(EXAMPLE_BIG),
base = 5,
log = True)
self.assertTrue(search(r'translate.*cube', result))


class TestDoc(unittest.TestCase):
"""Test Python documentation strings."""
def test_doc(self):
Expand Down

0 comments on commit 7b898ab

Please sign in to comment.