HowTo: Converting a 2D-Image into OpenSCAD

From Wurst-Wasser.net
Jump to: navigation, search

The Challenge

Maybe it's just me - but converting some 2D-image for use in OpenSCAD is insanely hard. But why? Converting an image into an DXF for extruding in OpenSCAD is fairly easy, there a plenty of solutions[1] available. But usually I ended up with an DXF that OpenSCAD wasn't able to parse. Sounds familiar? Yes, you ended up with this:

WARNING: Unsupported DXF Entity 'SEQEND' (40) in "Herforder_Brauerei_logo_v6_sw_cropped.dxf". 
WARNING: Unsupported DXF Entity 'VERTEX' (1235) in "Herforder_Brauerei_logo_v6_sw_cropped.dxf". 
WARNING: Unsupported DXF Entity 'POLYLINE' (40) in "Herforder_Brauerei_logo_v6_sw_cropped.dxf". 

The Solution (v1)

Then it came to me: The printer has a very limited resolution when printing - so why bother using vector images? Why not just convert a pixel-based image into a bunch of cube()s? KISS!

This is what I came up with after about 20 minutes:

<?php
$image="/Users/heiko/Downloads/2DSCADConversionDemo.png";

$outputASCII=false;
$outputSCAD=true;

	$im = imagecreatefrompng($image); // http://php.net/manual/de/function.imagecreatefrompng.php

	if ($im)
	{

		$w = imagesx($im); // image width
		$h = imagesy($im); // image height
	
		for($y = 0; $y < $h; $y++) 
		{
		  for($x = 0; $x < $w; $x++) 
		  {
	      	      $rgb = imagecolorat($im, $x, $y);
		      $r = ($rgb >> 16) & 0xFF;
		      $g = ($rgb >> 8) & 0xFF;
		      $b = $rgb & 0xFF;
			  
			  if ($r+$g+$b>(2*256/3))
			  {
				  /* "Weisses Pixel" */
				  if ($outputASCII===true)
				  {
						echo(" ");
				  }
				  else if ($outputSCAD)
				  {
					  // nix
				  }
			  }
			  else
			  {
				  /* "Schwarzes Pixel" */
				  if ($outputASCII===true)
				  {
						echo("#");
				  }
				  else if ($outputSCAD)
				  {
						echo("translate([" . $x . ", " . $y . ", 0]) cube([1, 1, 1]);\n");
				  }
			  }
			} // x
			
		  	if ($outputASCII===true)
		  	{
			  	echo("\n");
		  	}
			
		} // y

		echo("Dimensions:\n");
		echo("x: " . $x . "\n");
		echo("y: " . $y . "\n");
	
		imagedestroy($im); // free resource
	}
	else
	{
		echo("Fail!");
	}


?>

And this is, what it looks like:

Before:
2DSCADConversionDemo.png

After:
2DSCADConversionDemoResultSCAD.png

Not bad, eh?

The Solution (v6)

Then I ran into some "memory exhausted", performance and of course, my favourite "object is not 2-manifold" issues....which lead me to this: (v6)

<?php

/* History:
2015-08-29,	20:58, h:	Created, published at http://www.wurst-wasser.net/wiki/index.php/HowTo:_Converting_a_2D-Image_into_OpenSCAD
2015-08-31,	20:43, h:	Let's see if we can improve this.
						- not 2-manifold -> make cubes overlap :) --DONE (v3)
						- Create PCX (RLE)-like compression
							(adding cubes up if pixels in row have the same colour) (v5)
						- Refactor (v6)


ToDo:

*/


$image="/Users/heiko/Documents/3D-Modelle/_Meine Designs/Herforder Sandfoermchen/DSCF5594 - Version 3.2 400px c.png";

$outputSCAD=true; // !true (or false if you want aalib-style output (or quick review))
$outputASCII = ! $outputSCAD; // XOR :) - one output method only
$sw=0.666; //0.75; //0.5; // 0.25;

	$im = imagecreatefrompng($image); // http://php.net/manual/de/function.imagecreatefrompng.php

	if ($im)
	{

		$w = imagesx($im); // image width
		$h = imagesy($im); // image height
	
		if ($outputSCAD)
		{
			echo("//Created with png2scad. ( http://www.wurst-wasser.net/wiki/index.php/HowTo:_Converting_a_2D-Image_into_OpenSCAD )\n\n");
			echo("\$fn=23;\n");
			echo("someObject();\n\n");
			echo("module someObject()\n");
			echo("union()\n{\n");
		}

		for($y = 0; $y < $h; $y++) 
		{
			for($x1 = 0; $x1 < $w-1; $x1++) 
			{
				$p1=getPixel($im, $x1, $y);
				
				for($x2=$x1+1; $x2 < $w-1; $x2++) 
				{
					$p2=getPixel($im, $x2, $y);
					if ($p2!==$p1) break;
				}

				if ($outputASCII===true)
				{
					for ($i=$x1; $i<=$x2; $i++)
						if ($p1===true) echo("#");
						else echo(" ");
				}
				else if ($outputSCAD)
				{
					if ($p1===true)
					{
						$xLen=$x2-$x1+1+0.01;
						echo("\ttranslate([" . $x1 . ", " . $y . ", 0]) cube([$xLen, 1.01, 1.01]);\n");
					}
				}
				
				$x1=$x2;
			}
			
			/* EOL (not the club) */
			if ($outputASCII===true)
			{
				echo("\n");
			}
		} // y loop (rows)

		/*echo("Dimensions:\n");
		echo("x: " . $x . "\n");
		echo("y: " . $y . "\n");*/
	
		if ($outputSCAD)
		{
			echo("}\n");
		}

		imagedestroy($im); // free resource
	}
	else
	{
		echo("Fail!");
	}

function	getPixel($lIm, $lX, $lY)
{
	global $sw;
	
	$rgb = imagecolorat($lIm, $lX, $lY);
	$r = ($rgb >> 16) & 0xFF;
	$g = ($rgb >> 8) & 0xFF;
	$b = $rgb & 0xFF;

	$grey=($r+$g+$b)/3;
	if ($grey>(256*$sw)) // < oder > nach Geschmack
	{
		return(false); // rather white
	}
	else
	{
		return(true); // rather black
	}
}

?>


Please note: 3D-Designs Fineprint.


  • References and Footnotes:
  1. Cloud Services as well as InkScape-Extensions…See: http://www.thingiverse.com/thing:25036/#instructions or https://cloudconvert.com/svg-to-dxf )