Commit bea41aef authored by nextime's avatar nextime

Add color reduction and show preview

parent 804a9bf4
...@@ -80,7 +80,10 @@ For the latest revision please visit https://github.com/TurnkeyTyranny/laser-gco ...@@ -80,7 +80,10 @@ For the latest revision please visit https://github.com/TurnkeyTyranny/laser-gco
<param name="unit" type="enum" _gui-text="Units: "> <param name="unit" type="enum" _gui-text="Units: ">
<item value="mm">mm</item> <item value="mm">mm</item>
<item value="in">in</item> <item value="in">in</item>
</param> </param>
<param name="dither" type="boolean" _gui-text="Apply dithering">false</param>
<param name="colorspace" type="int" _gui-text="color space. 0 to reduce derived from power">256</param>
<param name="showimg" type="boolean" _gui-text="Show generated raster images">false</param>
</page> </page>
</param> </param>
</inkscape-extension> </inkscape-extension>
\ No newline at end of file
...@@ -490,7 +490,9 @@ class Gcode_tools(inkex.Effect): ...@@ -490,7 +490,9 @@ class Gcode_tools(inkex.Effect):
self.OptionParser.add_option("", "--pronterface", action="store", type="inkbool", dest="pronterface", default=True, help="Are you using Pronterface? If so we need to change some characters in the GCode raster data to keep pronterface happy. Slight loss of intensity on pure blacks but nothing major.") self.OptionParser.add_option("", "--pronterface", action="store", type="inkbool", dest="pronterface", default=True, help="Are you using Pronterface? If so we need to change some characters in the GCode raster data to keep pronterface happy. Slight loss of intensity on pure blacks but nothing major.")
self.OptionParser.add_option("", "--origin", action="store", type="string", dest="origin", default="topleft", help="Origin of the Y Axis") self.OptionParser.add_option("", "--origin", action="store", type="string", dest="origin", default="topleft", help="Origin of the Y Axis")
self.OptionParser.add_option("", "--optimiseraster", action="store", type="inkbool", dest="optimiseraster", default=True, help="Optimise raster horizontal scanning speed") self.OptionParser.add_option("", "--optimiseraster", action="store", type="inkbool", dest="optimiseraster", default=True, help="Optimise raster horizontal scanning speed")
self.OptionParser.add_option("", "--dither", action="store", type="inkbool", dest="dither", default=False, help="Enable Dithering")
self.OptionParser.add_option("", "--showimg", action="store", type="inkbool", dest="showimg", default=False, help="Show rastered images")
self.OptionParser.add_option("", "--colorspace", action="store", type="int", dest="colorspace", default="256", help="reduce colorspace")
def parse_curve(self, path): def parse_curve(self, path):
# if self.options.Xscale!=self.options.Yscale: # if self.options.Xscale!=self.options.Yscale:
...@@ -635,7 +637,7 @@ class Gcode_tools(inkex.Effect): ...@@ -635,7 +637,7 @@ class Gcode_tools(inkex.Effect):
return " ".join(args) return " ".join(args)
def generate_raster_gcode(self, curve, laserPower, altfeed=None, altdither=False): def generate_raster_gcode(self, curve, laserPower, altfeed=None, altdither=False, altcolorspace=256, altshowimg=False):
gcode = '' gcode = ''
#Setup our feed rate, either from the layer name or from the default value. #Setup our feed rate, either from the layer name or from the default value.
...@@ -956,7 +958,7 @@ class Gcode_tools(inkex.Effect): ...@@ -956,7 +958,7 @@ class Gcode_tools(inkex.Effect):
self.skipped = 0 self.skipped = 0
def compile_paths(parent, node, trans, laserPower=100, altdither=False): def compile_paths(parent, node, trans, laserPower=100, altdither=False, altcolorspace=256, altshowimg=False):
# Apply the object transform, along with the parent transformation # Apply the object transform, along with the parent transformation
mat = node.get('transform', None) mat = node.get('transform', None)
path = {} path = {}
...@@ -1016,40 +1018,52 @@ class Gcode_tools(inkex.Effect): ...@@ -1016,40 +1018,52 @@ class Gcode_tools(inkex.Effect):
#Fetch the image Data #Fetch the image Data
filename = "%stmpinkscapeexport.png" % (tmp) filename = "%stmpinkscapeexport.png" % (tmp)
if (self.options.origin == 'topleft'): if (self.options.origin == 'topleft'):
im = Image.open(filename).transpose(Image.FLIP_TOP_BOTTOM).convert('L') img = Image.open(filename).transpose(Image.FLIP_TOP_BOTTOM).convert('L')
else: else:
im = Image.open(filename).convert('L') img = Image.open(filename).convert('L')
img = ImageOps.invert(im)
img = ImageOps.invert(img)
#Get the image size #Get the image size
imageDataWidth, imageDataheight = img.size imageDataWidth, imageDataheight = img.size
if altdither: pixel = img.load()
if altdither and altcolorspace < 256:
# dithering image and compile the pixels # dithering image and compile the pixels
inkex.errormsg("start dithering.") if altcolorspace == 0:
palette=list(set(img.convert("P", palette=Image.ADAPTIVE, colors=laserPower-1).getpalette())) palette=list(set(img.convert("P", palette=Image.ADAPTIVE, colors=laserPower-1).getpalette()))
inkex.errormsg("dither for "+str(len(palette))+" colors palette...") else:
pixel = img.load() palette=list(set(img.convert("P", palette=Image.ADAPTIVE, colors=altcolorspace-1).getpalette()))
pixels = [] inkex.errormsg("reduce colorspace to "+str(len(palette))+" colors palette...")
if altdither:
inkex.errormsg("dithering enabled")
for line in range(0, imageDataheight): for line in range(0, imageDataheight):
r=[]
for row in range(0, imageDataWidth): for row in range(0, imageDataWidth):
oldpix = pixel[row, line] oldpix = pixel[row, line]
newpix = min(palette, key=lambda p:abs(p-oldpix)) newpix = min(palette, key=lambda p:abs(p-oldpix))
quant_error = oldpix - newpix pixel[row, line] = newpix
try: if altdither:
pixel[row+1, line] = pixel[row+1, line] + quant_error * 7/16 quant_error = oldpix - newpix
pixel[row-1, line+1] = pixel[row-1, line+1] + quant_error * 3/16 try:
pixel[row, line+1] = pixel[row, line+1] + quant_error * 5/16 pixel[row+1, line] = pixel[row+1, line] + quant_error * 7/16
pixel[row+1, line+1] = pixel[row+1, line+1] + quant_error * 1/16 pixel[row-1, line+1] = pixel[row-1, line+1] + quant_error * 3/16
except: pixel[row, line+1] = pixel[row, line+1] + quant_error * 5/16
pass pixel[row+1, line+1] = pixel[row+1, line+1] + quant_error * 1/16
except:
pass
pixels = [[pixel[r, l] for r in xrange(imageDataWidth)] for l in xrange(imageDataheight)] if altshowimg:
else: showim = img.copy()
#Compile the pixels. if (self.options.origin == 'topleft'):
pixels = list(img.getdata()) showim = showim.transpose(Image.FLIP_TOP_BOTTOM)
pixels = [pixels[i * (imageDataWidth):(i + 1) * (imageDataWidth)] for i in xrange(imageDataheight)] ImageOps.invert(showim).show()
showim.show()
img = ImageOps.invert(img)
pixels = [[pixel[r, l] for r in xrange(imageDataWidth)] for l in xrange(imageDataheight)]
path['type'] = "raster" path['type'] = "raster"
path['width'] = imageDataWidth path['width'] = imageDataWidth
...@@ -1196,7 +1210,9 @@ class Gcode_tools(inkex.Effect): ...@@ -1196,7 +1210,9 @@ class Gcode_tools(inkex.Effect):
# Check if the layer specifies an alternative (from the default) feed rate # Check if the layer specifies an alternative (from the default) feed rate
altfeed = layerParams.get("feed", self.options.feed) altfeed = layerParams.get("feed", self.options.feed)
altppm = layerParams.get("ppm", None) altppm = layerParams.get("ppm", None)
altdither = layerParams.get("dither", False) altdither = layerParams.get("dither", self.options.dither)
altcolorspace = layerParams.get("colorspace", self.options.colorspace)
altshowimg = layerParams.get("showimg", self.options.showimg)
logger.write("layer %s" % layerName) logger.write("layer %s" % layerName)
if (layerParams): if (layerParams):
...@@ -1230,12 +1246,12 @@ class Gcode_tools(inkex.Effect): ...@@ -1230,12 +1246,12 @@ class Gcode_tools(inkex.Effect):
selected.remove(node) selected.remove(node)
try: try:
newPath = compile_paths(self, node, trans, laserPower, altdither).copy(); newPath = compile_paths(self, node, trans, laserPower, altdither, altcolorspace, altshowimg).copy();
pathList.append(newPath) pathList.append(newPath)
inkex.errormsg("Built gcode for "+str(node.get("id"))+" - will be cut as %s." % (newPath['type']) ) inkex.errormsg("Built gcode for "+str(node.get("id"))+" - will be cut as %s." % (newPath['type']) )
except: except:
messageOnce = True messageOnce = True
for objectData in compile_paths(self, node, trans, laserPower, altdither): for objectData in compile_paths(self, node, trans, laserPower, altdither, altcolorspace, altshowimg):
#if (messageOnce): #if (messageOnce):
inkex.errormsg("Built gcode for group "+str(node.get("id"))+", item %s - will be cut as %s." % (objectData['id'], objectData['type']) ) inkex.errormsg("Built gcode for group "+str(node.get("id"))+", item %s - will be cut as %s." % (objectData['id'], objectData['type']) )
#messageOnce = False #messageOnce = False
...@@ -1283,7 +1299,7 @@ class Gcode_tools(inkex.Effect): ...@@ -1283,7 +1299,7 @@ class Gcode_tools(inkex.Effect):
gcode += header_data+self.generate_gcode(curve, 0, laserPower, altfeed=altfeed, altppm=altppm) gcode += header_data+self.generate_gcode(curve, 0, laserPower, altfeed=altfeed, altppm=altppm)
elif (curve['type'] == "raster"): elif (curve['type'] == "raster"):
gcode_raster += header_data+self.generate_raster_gcode(curve, laserPower, altfeed=altfeed, altdither=altdither) gcode_raster += header_data+self.generate_raster_gcode(curve, laserPower, altfeed=altfeed, altdither=altdither, altcolorspace=altcolorspace, altshowimg=altshowimg)
#Turnkey - Need to figure out why inkscape sometimes gets to this point and hasn't found the objects above. #Turnkey - Need to figure out why inkscape sometimes gets to this point and hasn't found the objects above.
...@@ -1300,12 +1316,12 @@ class Gcode_tools(inkex.Effect): ...@@ -1300,12 +1316,12 @@ class Gcode_tools(inkex.Effect):
trans = simpletransform.parseTransform("") trans = simpletransform.parseTransform("")
for node in selected: for node in selected:
try: try:
newPath = compile_paths(self, node, trans, laserPower, altdither).copy(); newPath = compile_paths(self, node, trans, laserPower, altdither, altcolorspace, altshowimg).copy();
pathList.append(newPath) pathList.append(newPath)
inkex.errormsg("Built gcode for "+str(node.get("id"))+" - will be cut as %s." % (newPath['type']) ) inkex.errormsg("Built gcode for "+str(node.get("id"))+" - will be cut as %s." % (newPath['type']) )
except: except:
messageOnce = True messageOnce = True
for objectData in compile_paths(self, node, trans, laserPower, altdither): for objectData in compile_paths(self, node, trans, laserPower, altdither, altcolorspace, altshowimg):
#if (messageOnce): #if (messageOnce):
inkex.errormsg("Built gcode for group "+str(node.get("id"))+", item %s - will be cut as %s." % (objectData['id'], objectData['type']) ) inkex.errormsg("Built gcode for group "+str(node.get("id"))+", item %s - will be cut as %s." % (objectData['id'], objectData['type']) )
#messageOnce = False #messageOnce = False
...@@ -1358,7 +1374,7 @@ class Gcode_tools(inkex.Effect): ...@@ -1358,7 +1374,7 @@ class Gcode_tools(inkex.Effect):
gcode += header_data+self.generate_gcode(curve, 0, laserPower, altfeed=altfeed, altppm=altppm) gcode += header_data+self.generate_gcode(curve, 0, laserPower, altfeed=altfeed, altppm=altppm)
elif (curve['type'] == "raster"): elif (curve['type'] == "raster"):
gcode_raster += header_data+self.generate_raster_gcode(curve, laserPower, altfeed=altfeed, altdither=altdither) gcode_raster += header_data+self.generate_raster_gcode(curve, laserPower, altfeed=altfeed, altdither=altdither, altcolorspace=altcolorspace, altshowimg=altshowimg)
if self.options.homeafter: if self.options.homeafter:
gcode += "\n\nG00 X0 Y0 F4000 ; home" gcode += "\n\nG00 X0 Y0 F4000 ; home"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment