Permalink
Browse files

Binary logarithm possible

  • Loading branch information...
1 parent ca8e042 commit 7b898ab6ad15c20bfda6079c11b11cda1f11335c @l0b0 l0b0 committed Oct 19, 2010
Showing with 102 additions and 38 deletions.
  1. +1 −1 README
  2. +47 −27 img2scad/img2scad.py
  3. +1 −1 setup.py
  4. BIN tests/example_black.png
  5. +53 −9 tests/tests.py
View
2 README
@@ -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
View
@@ -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:
@@ -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
@@ -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)
@@ -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();'
@@ -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__':
View
@@ -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',
View
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
@@ -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')
@@ -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):

0 comments on commit 7b898ab

Please sign in to comment.