HowTo: Converting a 2D-Image into OpenSCAD

From Wurst-Wasser.net
Revision as of 20:08, 21 February 2016 by Heiko (talk | contribs)
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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 )