def openPhotonFile(filename): global photonfile global dispimg global layerimg global previmg global layerNr # read file photonfile = PhotonFile(filename) photonfile.readFile() layerNr = 0 # reset this to 0 so we prevent crash if previous photonfile was navigated to layer above the last layer of new photonfile layerimg = photonfile.getBitmap(layerNr, layerForecolor, layerBackcolor) previmg[0] = photonfile.getPreviewBitmap(0) previmg[1] = photonfile.getPreviewBitmap(1) dispimg = layerimg refreshHeaderControls() refreshPreviewControls() refreshLayerControls()
def testcases(): # Test cases # read / save photon file # read / save cbddlp file (should be same as photon file) # read / save properties for header/layers # print("Save All...") photon_filepath_org = '/home/nard/PhotonFile/test/bunny.photon' photonfile = PhotonFile() photonfile.load(photon_filepath_org) photon_dirpath_new = '/home/nard/PhotonFile/test/bunny.img' photonfile.layers.saveAll(photon_dirpath_new) print("...success.") return print("Test signatures...") photonfile = PhotonFile() photon_filepath_org = '/home/nard/PhotonFile/test/bunny.photon' photonfile.load(photon_filepath_org) assert (photonfile.signature() == 'ChituBox 1.4.0') print("...success.") print("Test read/write .cbddlp file...") filepath = '/home/nard/PhotonFile/test/bunny.cbddlp' filepath_new = '/home/nard/PhotonFile/test/bunny2.cbddlp' org_filesize = os.path.getsize(filepath) photonfile = PhotonFile(filepath) photonfile.load() photonfile.save(filepath_new) new_filesize = os.path.getsize(filepath_new) assert new_filesize == org_filesize print("...success.") print("Test read/write .photon file...") filepath = '/home/nard/PhotonFile/test/bunny.photon' filepath_new = '/home/nard/PhotonFile/test/bunny2.photon' org_filesize = os.path.getsize(filepath) photonfile = PhotonFile() #photonfile.load() photonfile.load(filepath) photonfile.load(filepath) # do this 2 time to check clear old vars photonfile.save(filepath_new) new_filesize = os.path.getsize(filepath_new) assert new_filesize == org_filesize print("...success.") print("Test layers.count / layers.last / layers.height / volume...") filepath = '/home/nard/PhotonFile/test/bunny.photon' photonfile.load(filepath) assert photonfile.layers.count() == 1716 assert photonfile.layers.last() == 1716 - 1 assert photonfile.layers.height(0) == 0.05 assert photonfile.volume(retUnit='ml') == 9.0 print("...success.") print("Test Property get/set...") dlpWidth_write = 2560 prevWidth_write = 320 layerHeight_write = 0.04 photonfile.setProperty("Resolution X", dlpWidth_write) photonfile.previews.setProperty(0, "Resolution X", prevWidth_write) photonfile.layers.setProperty(0, "Layer height (mm)", layerHeight_write) dlpWidth_read = photonfile.getProperty("Resolution X") prevWidth_read = photonfile.previews.getProperty(0, "Resolution X") layerHeight_read = photonfile.layers.getProperty(0, "Layer height (mm)") assert dlpWidth_read == dlpWidth_write assert prevWidth_read == prevWidth_write assert layerHeight_read == layerHeight_write print("...success.") print("Test layerimage save to file...") filepath0 = '/home/nard/PhotonFile/test/images/test0.png' filepath4 = '/home/nard/PhotonFile/test/images/test4.png' photonfile.layers.save(0, filepath0) photonfile.layers.save(4, filepath4) print("...success.") print("Test image insert to layer...") filepath0 = '/home/nard/PhotonFile/test/images/test0.png' filepath4 = '/home/nard/PhotonFile/test/images/test4.png' photonfile.layers.insert(filepath4, 4) photonfile.layers.insert(filepath0, 0) assert photonfile.layers.count() == 1716 + 2 print("...success.") print("Test layer delete...") photonfile.layers.delete(0) photonfile.layers.delete(4) assert photonfile.layers.count() == 1716 photonfile.save(filepath_new) new_filesize = os.path.getsize(filepath_new) assert new_filesize == org_filesize print("...success.") print("Test image encoding...") filepath = '/home/nard/PhotonFile/test/images/test.png' filepath_new = '/home/nard/PhotonFile/test/images/test2.png' photonfile.layers.insert(filepath, 4) photonfile.layers.save(4, filepath_new) org_imgfilesize = os.path.getsize(filepath) org_imgobjsize = PIL.Image.open(filepath).size new_imgfilesize = os.path.getsize(filepath_new) new_imgobjsize = PIL.Image.open(filepath_new).size assert (new_imgobjsize == org_imgobjsize) assert (new_imgfilesize == org_imgfilesize) print("...success.") print("Test layer replace layer with new image...") photon_filepath_org = '/home/nard/PhotonFile/test/bunny.photon' photonfile.load(photon_filepath_org) img_filepath4 = '/home/nard/PhotonFile/test/images/test4.png' photonfile.layers.replace(4, img_filepath4) assert photonfile.layers.count() == 1716 photon_filepath_new = '/home/nard/PhotonFile/test/bunny2.photon' photonfile.save(photon_filepath_new) org_filesize = os.path.getsize(photon_filepath_org) new_filesize = os.path.getsize(photon_filepath_new) assert new_filesize == org_filesize print("...success.") print("Test append photonfile with new image...") photon_filepath_org = '/home/nard/PhotonFile/test/bunny.photon' photon_filepath_new = '/home/nard/PhotonFile/test/bunny2.photon' photonfile.load(photon_filepath_org) img_filepath4 = '/home/nard/PhotonFile/test/images/test4.png' photonfile.layers.append(img_filepath4) photonfile.layers.delete(photonfile.layers.last()) photonfile.save(photon_filepath_new) assert new_filesize == org_filesize print("...success.") print("Save All...") photon_filepath_org = '/home/nard/PhotonFile/test/bunny.photon' photonfile.load(photon_filepath_org) photon_dirpath_new = '/home/nard/PhotonFile/test/bunny.img' photonfile.layers.saveAll(photon_dirpath_new) print("...success.") print("get All...") print("...success.") print("Save Replace...") print("...success.") print("Test copy image (via clipboard)...") print("...success.") print("Test cut/paste image (via clipboard)...") print("...success.") print("Test undo (via clipboard)...") print("...success.") print("Save Preview...") print("...success.") print("Replace Preview...") print("...success.") return
def __init__(self, svgfilename, scale=1, outputpath=None, # should end with '/' layerheight=0.05, photonfilename=None, # keep outputpath=None if output to photonfilename normalexposure=8.0, bottomexposure=90, bottomlayers=8, offtime=6.5, gui=False ): # Set Gui self.gui=gui # Get path of script/exe for local resources like iconpath and newfile.photon if getattr(sys, 'frozen', False):# frozen self.installpath = os.path.dirname(sys.executable) else: # unfrozen self.installpath = os.path.dirname(os.path.realpath(__file__)) if gui: import tkinter as tk from tkinter import ttk # Construct window self.popup = tk.Tk()#tk.Toplevel() self.popup.geometry('240x32') # Set window icon img=tk.PhotoImage(file=os.path.join(self.installpath,'PhotonSlicer.gif')) self.popup.tk.call('wm','iconphoto',self.popup._w,img) #tk.Label(self.popup, text="Slicing...").grid(row=0, column=0) self.popup.title("Slicing...") self.progress_var = tk.DoubleVar() progress_bar = ttk.Progressbar(self.popup, variable=self.progress_var, maximum=100, length=240) progress_bar.pack(fill=tk.Y, expand=1, side=tk.BOTTOM) # Measure how long it takes t1 = time.time() # Setup output path if outputpath==None and photonfilename==None:return #create path if not exists if not outputpath==None: if not os.path.exists(outputpath): os.makedirs(outputpath) # if we output to PhotonFile we need a place to store RunLengthEncoded images if not photonfilename==None: rlestack=[] # read and parse svg file xmldoc = minidom.parse(svgfilename) layers = xmldoc.getElementsByTagName('g') #contourColor = (255, 255, 255) # alpha 255 is NOT transparent #contourColor = (255) # alpha 255 is NOT transparent # innerColor = (0, 0, 255) innerColor = (128) scale=scale/0.047 # draw layer for layer sliceNr=0 nrSlices=len(layers) pLayers=[] for layer in layers: layer_id = layer.attributes['id'].value layer_z = layer.attributes['slic3r:z'].value layer_polygons =layer.getElementsByTagName('polygon') img = numpy.zeros((2560, 1440, 1), numpy.uint8) pPolys=[] for layer_polygon in layer_polygons: layer_polygon_points = layer_polygon.attributes['points'].value pointString=layer_polygon_points.replace(',',' ') np_points = numpy.fromstring(pointString,dtype=float,sep=' ') np_points = np_points * scale nr_points = np_points.size nr_coords = nr_points//2 np_coords = np_points.reshape(nr_coords,2) pPolys.append(np_coords) x = np_coords[:, 0] z = np_coords[:, 1] self.cmin = (min(self.cmin[0],x.min()), min(self.cmin[1],z.min())) self.cmax = (max(self.cmax[0],x.max()), max(self.cmax[1],z.max())) pLayers.append(pPolys) sliceNr=sliceNr+1 # Show progress in terminal if not self.gui: msg="Reading ... "+str(sliceNr)+" / " + str(nrSlices) sys.stdout.write (msg) sys.stdout.write('\r') sys.stdout.flush() print("") # Center model and put on base trans = [0, 0] trans[0] = -(self.cmax[0] - self.cmin[0]) / 2 - self.cmin[0] trans[1] = -(self.cmax[1] - self.cmin[1]) / 2 - self.cmin[1] # We want the model centered in 2560x1440 # 2560x1440 pixels equals 120x67 trans[0] = trans[0] + 1440 / 2 trans[1] = trans[1] + 2560 / 2 sliceNr=0 nrSlices=len(layers) for pLayer in pLayers: img = numpy.zeros((2560, 1440, 1), numpy.uint8) for pPoly in pLayer: # Center numpy array of points which is returned for fast OGL model loading pPoly = pPoly + trans # Fill poly cv2.fillPoly(img, numpy.array([pPoly],dtype='int32'), color=innerColor) if photonfilename==None: cv2.imwrite(filename, img) else: imgarr8 = img img1D=imgarr8.flatten(0) rlestack.append(rleEncode.encodedBitmap_Bytes_numpy1DBlock(img1D)) # Show progress in terminal if not self.gui: msg="Saving ... "+str(sliceNr)+" / " + str(nrSlices) sys.stdout.write (msg) sys.stdout.write('\r') sys.stdout.flush() # Update GUI progress bar if gui active if self.gui: try: # Check if user aborted/closed window self.popup.update() progress=100*sliceNr/nrSlices self.progress_var.set(progress) except Exception: sys.exit() # quit() does not work if we make this an exe with cx_Freeze sliceNr += 1 if not self.gui: print () # close progress stdout and go to new line if not photonfilename==None: tempfilename=os.path.join(self.installpath,"newfile.photon") photonfile=PhotonFile(tempfilename) photonfile.readFile() photonfile.Header["Layer height (mm)"]= PhotonFile.float_to_bytes(layerheight) photonfile.Header["Exp. time (s)"] = PhotonFile.float_to_bytes(normalexposure) photonfile.Header["Exp. bottom (s)"] = PhotonFile.float_to_bytes(bottomexposure) photonfile.Header["# Bottom Layers"] = PhotonFile.int_to_bytes(bottomlayers) photonfile.Header["Off time (s)"] = PhotonFile.float_to_bytes(offtime) photonfile.replaceBitmaps(rlestack) photonfile.writeFile(photonfilename) if not self.gui: print("Elapsed: ", "%.2f" % (time.time() - t1), "secs") #test = Svg2Slices( # svgfilename='STLs/pikachu_repaired.svg', # photonfilename="STLs/pikachu_svg.photon", # gui=False # )
def __init__( self, stlfilename, scale=1, outputpath=None, # should end with '/' layerheight=0.05, photonfilename=None, # keep outputpath=None if output to photonfilename normalexposure=8.0, bottomexposure=90, bottomlayers=8, offtime=6.5, gui=False): # Set Gui self.gui = gui # Get path of script/exe for local resources like iconpath and newfile.photon if getattr(sys, 'frozen', False): # frozen self.installpath = os.path.dirname(sys.executable) else: # unfrozen self.installpath = os.path.dirname(os.path.realpath(__file__)) if gui: import tkinter as tk from tkinter import ttk # Construct window self.popup = tk.Tk() #tk.Toplevel() self.popup.geometry('240x32') # Set window icon img = tk.PhotoImage( file=os.path.join(self.installpath, 'PhotonSlicer.gif')) self.popup.tk.call('wm', 'iconphoto', self.popup._w, img) #tk.Label(self.popup, text="Slicing...").grid(row=0, column=0) self.popup.title("Slicing...") self.progress_var = tk.DoubleVar() progress_bar = ttk.Progressbar(self.popup, variable=self.progress_var, maximum=100, length=240) progress_bar.pack(fill=tk.Y, expand=1, side=tk.BOTTOM) # Measure how long it takes t1 = time.time() # Setup output path if outputpath == None and photonfilename == None: return #create path if not exists if not outputpath == None: if not os.path.exists(outputpath): os.makedirs(outputpath) # if we output to PhotonFile we need a place to store RunLengthEncoded images if not photonfilename == None: rlestack = [] # Load 3d Model in memory points, normals = self.load_binary_stl(stlfilename, scale=scale) # Check if inside build area if (self.cmin[0] < 0 or self.cmin[2] < 0 or self.cmin[1] < 0 or self.cmax[0] > 1440 or self.cmax[2] > 2560): size = (self.cmax[0] - self.cmin[0], self.cmax[1] - self.cmin[1], self.cmax[2] - self.cmin[2]) sizestr = "(" + str(int(size[0] * 0.047)) + "x" + str( int(size[2] * 0.047)) + ")" areastr = "(65x115)" errmsg = "Model is too big " + sizestr + "for build area " + areastr + ". Maybe try another orientation, use the scale argument (-s or --scale) or cut up the model." if not self.gui: print(errmsg) else: sys.tracebacklimit = None raise Exception(errmsg) sys.tracebacklimit = 0 sys.exit( ) # quit() does not work if we make this an exe with cx_Freeze # extract x and z to make 2d normals and normalize these normals2D = numpy.delete(normals, 1, 1) l = numpy.sqrt(numpy.power(normals, 2).sum(-1))[..., numpy.newaxis] normals2D = 2 * normals2D / l # Slice settings # model dimensions are in pixels/voxels of size 0.047mm, # so we need to recalc layerheight in mm to voxelheight of 0.047mm layerHeight = layerheight / 0.047 # recalc to 0.047mm units maxHeight = self.modelheight # modelheight is in 0.047mm units nrSlices = 1 + int(maxHeight / layerHeight) # Determine which triangles are in which slices slicepointindices = [] # construct layers for sliceNr in range(nrSlices + 1): l = [] slicepointindices.append(l) # map tris to layer # all coordinates are now in 0.047 size voxels # aso layerHeight is in 0.047 steps y = 1 #print ("modelHeight,layerheight,nrSlices",maxHeight,layerheight,nrSlices) pointsAppended = 0 for i in range(0, len(points), 3): p0 = points[i + 0] p1 = points[i + 1] p2 = points[i + 2] minY = min(p0[y], p1[y], p2[y]) maxY = max(p0[y], p1[y], p2[y]) minSlice = int(minY / layerHeight) maxSlice = int(maxY / layerHeight) + 1 #print ("Tri:",i,minY,maxY,minSlice,maxSlice) for sliceNr in range(minSlice, maxSlice + 1): slicepointindices[sliceNr].append(i) #print ("append to",sliceNr) pointsAppended = pointsAppended + 1 if (len(points) // 3 != pointsAppended): raise Exception( "Bug found. Not all triangles are stored in layers!") #print (len(points)//3,pointsAppended) #show layers #for sliceNr in range(nrSlices+1): # print (sliceNr,len(slicepointindices[sliceNr])) # Slice model #contourColor = (255, 255, 255) # alpha 255 is NOT transparent contourColor = (255) # alpha 255 is NOT transparent # innerColor = (0, 0, 255) innerColor = (128) for sliceNr in range(0, nrSlices): # Filename for new slice sliceBottom = sliceNr * layerHeight sliceTop = sliceBottom + layerHeight if not outputpath == None: Sstr = "%04d" % sliceNr filename = outputpath + Sstr + ".png" # Start with empty image for slice (3 bytes depth is 2.5x slower than 1 byte depth) #img = numpy.zeros((2560, 1440, 3), numpy.uint8) img = numpy.zeros((2560, 1440, 1), numpy.uint8) # Clear fillpoints fillpoints = [] """ lines=[] nors=[] ps=[] """ # Traverse all triangles for pidx in slicepointindices[sliceNr]: p0 = points[pidx + 0] p1 = points[pidx + 1] p2 = points[pidx + 2] n2d = normals2D[pidx // 3] # Check which part of triangle is inside slice polypoints = triInSlice.triInSlice(p0, p1, p2, sliceBottom, sliceTop) # Draw filled poly, fillConvexPoly is much faster than fillPoly, but poly should be convex... if polypoints: # if we do fill on all lines, do we need fillConvexPoly? cv2.fillConvexPoly(img, numpy.array([polypoints], dtype='int32'), color=contourColor) # Add points for which to floodfillpoints using normal but only if normal not along y if not (n2d[0] == 0 and n2d[1] == 0): nrpoints = len(polypoints) for idx in range(nrpoints): pfrom = polypoints[idx] pto = polypoints[(idx + 1) % nrpoints] pmid = ((pfrom[0] + pto[0]) / 2, (pfrom[1] + pto[1]) / 2) pfill = (int( (pmid[0] - n2d[0])), int((pmid[1] - n2d[1]))) # Check if point inside triangle(s) - https://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle # Pikachu_repaired.STL with check 9-11sec, without 10-12sec #""" #inTri1=False #inTri2=False """ inTri1=Stl2Slices.PointInTriangle(pfill, #inTri1=triInSlice.PointInTriangle(pfill, polypoints[(idx+0) % nrpoints], polypoints[(idx+1) % nrpoints], polypoints[(idx+2) % nrpoints]) if nrpoints>3: inTri2=Stl2Slices.PointInTriangle(pfill, #inTri2=triInSlice.PointInTriangle(pfill, polypoints[(idx+0) % nrpoints], polypoints[(idx+1) % nrpoints], polypoints[(idx-1) % nrpoints]) else: inTri2=inTri1 if not inTri1 and not inTri2: fillpoints.append(pfill) """ fillpoints.append(pfill) """ pfrom = polypoints[idx % nrpoints]#(int(pfrom[0]),int(pfrom[1])) pto = polypoints[(idx+1) % nrpoints] #(int(pto[0]), int(pto[1])) lines.append((pfrom,pto)) nors.append(n2d) ps.append([p0,p1,p2]) """ # Floodfill all points tester = img.copy() nrTests = 0 nrFills = 0 nrRedos = 0 nr = 0 #fillPoint=(100,30) #cv2.circle(img,fillPoint,10,192,1) #cv2.floodFill(img, mask=None, seedPoint=fillPoint, newVal=100) # 80% of time #cv2.floodFill(img, mask=None, seedPoint=fillPoint, newVal=100) # 80% of time #cv2.imwrite("STLs/legocog/_test.png", img) #quit() #for idx,fillPoint in enumerate(fillpoints): """ for y in range(0,2560): oldcol=0 inShape=False for x in range (0,1440): col=1#img[y,x] if oldcol!=col: inShape!=inShape col=oldcol if inShape: img[y,x]=innerColor """ for fillPoint in fillpoints: # Check if fill is necessary at fillpoint (if fillpoint still has background color = 0,0,0)) and not fill color (=innerColor) pxColor = ( img[fillPoint[1], fillPoint[0], 0] #, #img[fillPoint[1], fillPoint[0], 1], #img[fillPoint[1], fillPoint[0], 2] ) #if pxColor == (0, 0, 0): if pxColor == (0): # Do a testfill on tester cv2.floodFill(tester, mask=None, seedPoint=fillPoint, newVal=innerColor) # 80% of time nrTests += 1 # And check if fill (on tester) reaches (0,0) and thus we are filling outside of model contour #outerColor = (tester[0, 0, 0], tester[0, 0, 1], tester[0, 0, 2]) outerColor = (tester[0, 0, 0]) # If fill was necessary and fill in tester stayed inside model, then we apply fill on img #if outerColor == (0, 0, 0): if outerColor == (0): cv2.floodFill(img, mask=None, seedPoint=fillPoint, newVal=innerColor) nrFills += 1 else: # we destroyed tester and have to repair it by making a copy of img """ fname=("STLs/3DBenchy/%04d" % sliceNr) +"-"+("%04d" % nrTests)+".png" print (fname) print ("fillPoint,oldColor: ",fillPoint,pxColor) print ("line,normal : ",lines[idx],nors[idx]) print ("(0,0):",outerColor) print ("3D Tri:") q0=(0,0,0) q1 = (ps[idx][1][0]-ps[idx][0][0],ps[idx][1][1]-ps[idx][0][1],ps[idx][1][2]-ps[idx][0][2]) q2 = (ps[idx][2][0] - ps[idx][0][0], ps[idx][2][1] - ps[idx][0][1], ps[idx][2][2] - ps[idx][0][2]) print("p0: {:.3f} {:.3f} {:.3f}".format(ps[idx][0][0], ps[idx][0][1], ps[idx][0][2])) print("p1: {:.3f} {:.3f} {:.3f}".format(ps[idx][1][0], ps[idx][1][1], ps[idx][1][2])) print("p2: {:.3f} {:.3f} {:.3f}".format(ps[idx][2][0], ps[idx][2][1], ps[idx][2][2])) print ("q0->1: {:.3f} {:.3f} {:.3f}".format(q1[0],q1[1],q1[2])) print ("q0->2: {:.3f} {:.3f} {:.3f}".format(q2[0], q2[1], q2[2])) #cv2.floodFill(tester, mask=None, seedPoint=fillPoint, newVal=48) # 80% of time fillPointColor = (128) lines[idx]=((int(lines[idx][0][0]),int(lines[idx][0][1])),(int(lines[idx][1][0]), int(lines[idx][1][1]))) cv2.line(img, pt1=lines[idx][0], pt2=lines[idx][1], color=48, thickness=1) cv2.line(img, pt1=fillPoint, pt2=fillPoint, color=fillPointColor, thickness=1) cv2.circle(img,fillPoint,20,fillPointColor,1) cv2.imwrite(fname, img) quit() """ tester = img.copy() nrRedos += 1 # Debug: print nr of retries #print("sliceNr, nrTests, nrFills, nrRedos", sliceNr,nrTests, nrFills, nrRedos) # Debug: mark fill points # fillPointColor = (0,255,255) """ fillPointColor = (128) for fillpoint in fillpoints: # Debug cv2.line(img, pt1=fillpoint, pt2=fillpoint, color=fillPointColor, thickness=1) """ # Save image #if (img[0, 0, 0], img[0, 0, 1], img[0, 0, 2]) == (0, 0, 0): if (img[0, 0]) == (0): if photonfilename == None: #print("Saved ",sliceNr, "/", nrSlices, "->", filename) cv2.imwrite(filename, img) else: #print("Encoded", sliceNr, "/", nrSlices, "->", filename) # Convert slice to 1 color component (keep white and red) #imgarr8 = img[:, :, 2] imgarr8 = img # we need to rotate img 90 degrees #imgarr8 = numpy.rot90(imgarr8, axes=(1, 0)) # we need 1440x2560 # encode bitmap numpy array to rle img1D = imgarr8.flatten(0) rlestack.append( rleEncode.encodedBitmap_Bytes_numpy1DBlock(img1D)) #rlestack.append(bytes([0x00])) else: if not self.gui: print("Slice Error: ", filename) # Show progress in terminal if not self.gui: msg = "Slicing ... " + str(sliceNr) + " / " + str(nrSlices) sys.stdout.write(msg) sys.stdout.write('\r') sys.stdout.flush() # Update GUI progress bar if gui active if self.gui: try: # Check if user aborted/closed window self.popup.update() progress = 100 * sliceNr / nrSlices self.progress_var.set(progress) except Exception: sys.exit( ) # quit() does not work if we make this an exe with cx_Freeze if not self.gui: print() # close progress stdout and go to new line if not photonfilename == None: tempfilename = os.path.join(self.installpath, "newfile.photon") photonfile = PhotonFile(tempfilename) photonfile.readFile() photonfile.Header["Layer height (mm)"] = PhotonFile.float_to_bytes( layerheight) photonfile.Header["Exp. time (s)"] = PhotonFile.float_to_bytes( normalexposure) photonfile.Header["Exp. bottom (s)"] = PhotonFile.float_to_bytes( bottomexposure) photonfile.Header["# Bottom Layers"] = PhotonFile.int_to_bytes( bottomlayers) photonfile.Header["Off time (s)"] = PhotonFile.float_to_bytes( offtime) photonfile.replaceBitmaps(rlestack) photonfile.writeFile(photonfilename) if not self.gui: print("Elapsed: ", "%.2f" % (time.time() - t1), "secs")
def __init__( self, stlfilename, scale=1, outputpath=None, # should end with '/' layerheight=0.05, photonfilename=None, # keep outputpath=None if output to photonfilename normalexposure=8.0, bottomexposure=90, bottomlayers=8, offtime=6.5, ): self.viewport = GL_Viewport.Viewport() # Get path of script/exe for local resources like iconpath and newfile.photon if getattr(sys, 'frozen', False): # frozen self.installpath = os.path.dirname(sys.executable) else: # unfrozen self.installpath = os.path.dirname(os.path.realpath(__file__)) # Measure how long it takes t1 = time.time() # Setup output path if outputpath == None and photonfilename == None: return #create path if not exists if not outputpath == None: if not os.path.exists(outputpath): os.makedirs(outputpath) # if we output to PhotonFile we need a place to store RunLengthEncoded images if not photonfilename == None: rlestack = [] # Load 3d Model in memory points, normals = self.load_binary_stl(stlfilename, scale=scale) # Check if inside build area size = (self.cmax[0] - self.cmin[0], self.cmax[1] - self.cmin[1], self.cmax[2] - self.cmin[2]) if size[0] > 65 or size[1] > 115: sizestr = "(" + str(int(size[0])) + "x" + str(int(size[2])) + ")" areastr = "(65x115)" errmsg = "Model is too big " + sizestr + " for build area " + areastr + ". Maybe try another orientation, use the scale argument (-s or --scale) or cut up the model." if not self.gui: print(errmsg) else: sys.tracebacklimit = None raise Exception(errmsg) sys.tracebacklimit = 0 sys.exit( ) # quit() does not work if we make this an exe with cx_Freeze # Load mesh #print ("loading mesh") self.viewport.loadMesh(points, normals, self.cmin, self.cmax) #self.viewport.display() # this will loop until window is closed self.viewport.draw() microns = layerheight * 1000 #document.getElementById("height").value; bounds = self.viewport.getBounds() #print ((bounds['zmax']-bounds['zmin']) , self.viewport.printer.getGLscale()) #quit() zrange_mm = (bounds['zmax'] - bounds['zmin']) / self.viewport.printer.getGLscale() count = math.ceil(zrange_mm * 1000 / microns) #print ("b",bounds) #print ("z",zrange_mm) #print ("m",microns) #print ("c",count) if not photonfilename == None: rlestack = [] for i in range(0, count): data = self.viewport.getSliceAt(i / count) img = data.reshape(2560, 1440, 4) imgarr8 = img[:, :, 1] if photonfilename == None: Sstr = "%04d" % i filename = outputpath + Sstr + ".png" print(i, "/", count, filename) cv2.imwrite(filename, imgarr8) else: img1D = imgarr8.flatten(0) rlestack.append( rleEncode.encodedBitmap_Bytes_numpy1DBlock(img1D)) if not photonfilename == None: tempfilename = os.path.join(self.installpath, "newfile.photon") photonfile = PhotonFile(tempfilename) photonfile.readFile() photonfile.Header["Layer height (mm)"] = PhotonFile.float_to_bytes( layerheight) photonfile.Header["Exp. time (s)"] = PhotonFile.float_to_bytes( normalexposure) photonfile.Header["Exp. bottom (s)"] = PhotonFile.float_to_bytes( bottomexposure) photonfile.Header["# Bottom Layers"] = PhotonFile.int_to_bytes( bottomlayers) photonfile.Header["Off time (s)"] = PhotonFile.float_to_bytes( offtime) photonfile.replaceBitmaps(rlestack) photonfile.writeFile(photonfilename) print("Elapsed: ", "%.2f" % (time.time() - t1), "secs")
def benchmarks(): print("BENCHMARK...") photonfilepath = '/home/nard/PhotonFile/test/bunny.cbddlp' photonfile = PhotonFile(photonfilepath) photonfile.load() n = 50 print() print(" UNITS...") #PIL.Image.open imgfilepath = '/home/nard/PhotonFile/test/images/test.png' t = time.time() for i in range(0, n): im = PIL.Image.open(imgfilepath) nparr = numpy.asarray(im) d = (time.time() - t) / n print(f" PIL.Image.open x 1000 : {round(1000*d,2)} sec") #cv2.imread imgfilepath = '/home/nard/PhotonFile/test/images/test.png' t = time.time() for i in range(0, n): im = cv2.imread(imgfilepath) nparr = im d = (time.time() - t) / n print(f" cv2.imread x 1000 : {round(1000*d,2)} sec") #PIL.Image.save (comp level = 3 is fastest) imgfilepath = '/home/nard/PhotonFile/test/images/test.png' imgfilepath_new = '/home/nard/PhotonFile/test/images/test_save2pil.png' im = PIL.Image.open(imgfilepath) t = time.time() for i in range(0, n): im.save(imgfilepath_new, compress_level=3) d = (time.time() - t) / n print(f" PIL.Image.save(comp=6) x 1000: {round(1000*d,2)} sec") #CV.imwrite imgfilepath = '/home/nard/PhotonFile/test/images/test.png' imgfilepath_new = '/home/nard/PhotonFile/test/images/test_save2cv2.png' img = cv2.imread(imgfilepath, -1) t = time.time() for i in range(0, n): cv2.imwrite(imgfilepath_new, img) #cv2.imwrite(imgfilepath_new,img,[int(cv2.IMWRITE_PNG_COMPRESSION), 1]) d = (time.time() - t) / n print(f" CV2.Image.write x 1000 : {round(1000*d,2)} sec") #PIL.Image -> Numpy 1D t = time.time() for i in range(0, n): np = numpy.asarray(im) if np.ndim == 3: np = np[:, :, 0] npf = np.flatten() d = (time.time() - t) / n print(f" PIL.Image -> Numpy 1D x 1000 : {round(1000*d,2)} sec") #cv2.Image -> Numpy 1D t = time.time() for i in range(0, n): np = numpy.array(img) if np.ndim == 3: np = np[:, :, 0] npf = np.flatten() d = (time.time() - t) / n print(f" CV2.Image -> Numpy 1D x 1000 : {round(1000*d,2)} sec") #decodeRLE rleData = photonfile.layers.get(3, retType='rle') t = time.time() for i in range(0, n): nparr = RLE.decodeRLE28bImage(rleData) d = (time.time() - t) / n print(f" RLE.decodeRLE28bImage x 1000 : {round(1000*d,2)} sec") #encodeRLE t = time.time() for i in range(0, n): rleData = RLE.encode8bImage2RLE(npf) d = (time.time() - t) / n print(f" RLE.encode8bImage2RLE x 1000 : {round(1000*d,2)} sec") #get nrpixels rleData = photonfile.layers.get(4, retType='rle') t = time.time() for i in range(0, n): pixelsInLayer = RLE.RLEPixels(rleData) d = (time.time() - t) / n print(f" RLE.RLEPixels x 1000 : {round(1000*d,2)} sec") #get rle bytes t = time.time() for i in range(0, n): rleData = photonfile.layers.get(4, 'rle') d = (time.time() - t) / n print(f" layers.get(4,'rle') x 1000 : {round(1000*d,2)} sec") #get numpy bytes t = time.time() for i in range(0, n): numpy2D = photonfile.layers.get(4, retType='numpy') d = (time.time() - t) / n print(f" layers.get(4,'numpy') x 1000 : {round(1000*d,2)} sec") #get volume t = time.time() for i in range(0, n): numpy2D = photonfile.volume() d = (time.time() - t) / n print(f" photonfile.volume() x 1 : {round(d,2)} sec") print() print(" EXPORT/IMPORT...") #export image from rle data in layer using PIL.Image.save imgfilepath_new = '/home/nard/PhotonFile/test/images/test_export2pil.png' t = time.time() for i in range(0, n): rleData = photonfile.layers.get(3, retType='rle') numpyArray1Duint8 = RLE.decodeRLE28bImage(rleData) numpyArray2Duint8 = numpyArray1Duint8.reshape(2560, 1440) im = PIL.Image.fromarray(numpyArray2Duint8) im.save(imgfilepath_new, compress_level=3) d = (time.time() - t) / n print(f" RLE->PIL.Image.save x 1000 : {round(1000*d,2)} sec") #export image from rle data in layer using CV2.imwrite imgfilepath_new = '/home/nard/PhotonFile/test/images/test_export2cv2.png' t = time.time() for i in range(0, n): rleData = photonfile.layers.get(3, retType='rle') numpyArray1Duint8 = RLE.decodeRLE28bImage(rleData) numpyArray2Duint8 = numpyArray1Duint8.reshape(2560, 1440, 1) cv2.imwrite(imgfilepath_new, numpyArray2Duint8) d = (time.time() - t) / n print(f" RLE->CV2.Image.write x 1000 : {round(1000*d,2)} sec") #import image from rle data in layer using PIL.Image.open imgfilepath_new = '/home/nard/PhotonFile/test/images/test.png' t = time.time() for i in range(0, n): im = PIL.Image.open(imgfilepath_new) npArray = numpy.asarray(im) if npArray.ndim == 3: npArray = npArray[:, :, 0] npArray = npArray.flatten() rleData = RLE.encode8bImage2RLE(npArray) d = (time.time() - t) / n imgfilepath_chk = '/home/nard/PhotonFile/test/images/test_checkload_pil.png' numpyArray1Duint8 = RLE.decodeRLE28bImage(rleData) numpyArray2Duint8 = numpyArray1Duint8.reshape(2560, 1440) im = PIL.Image.fromarray(numpyArray2Duint8) im.save(imgfilepath_chk, compress_level=3) print(f" PIL.Image.open->RLE x 1000 : {round(1000*d,2)} sec") #import image from rle data in layer using CV2.imread imgfilepath_new = '/home/nard/PhotonFile/test/images/test.png' t = time.time() for i in range(0, n): npArray = cv2.imread(imgfilepath_new, cv2.IMREAD_UNCHANGED) # is native numpy array if npArray.ndim == 3: npArray = npArray[:, :, 0] npArray = npArray.flatten() rleData = RLE.encode8bImage2RLE(npArray) d = (time.time() - t) / n imgfilepath_chk = '/home/nard/PhotonFile/test/images/test_checkload_cv2.png' numpyArray1Duint8 = RLE.decodeRLE28bImage(rleData) numpyArray2Duint8 = numpyArray1Duint8.reshape(2560, 1440, 1) cv2.imwrite(imgfilepath_chk, numpyArray2Duint8) print(f" CV2.imread->RLE x 1000 : {round(1000*d,2)} sec")
def readPhotonFile(filename): global files global layerHeight photonfile = PhotonFile() photonfile.load(filename) # Check if file not already present for file in files: if file[1] == filename: root.option_add('*Dialog.msg.font', 'Helvetica 10') messagebox.showerror('File already loaded', 'You already loaded this file.') return # If first file we copy properties if len(files) == 0: layerHeight = photonfile.getProperty("Layer height (mm)") entry_BottomLayers.delete(0, tkinter.END) entry_NormalExp.delete(0, tkinter.END) entry_BottomExp.delete(0, tkinter.END) entry_Offtime.delete(0, tkinter.END) entry_BottomLayers.insert(0, photonfile.getProperty("# Bottom Layers")) entry_NormalExp.insert(0, photonfile.getProperty("Exp. time (s)")) entry_BottomExp.insert(0, photonfile.getProperty("Exp. bottom (s)")) entry_Offtime.insert(0, photonfile.getProperty("Off time (s)")) # Check if layerheight is same as other layerheights if photonfile.getProperty("Layer height (mm)") != layerHeight: root.option_add('*Dialog.msg.font', 'Helvetica 10') messagebox.showerror('Different Layer Height', 'All photonfiles should have samen layerheight.') return # set layerheight label_layerHeight['text'] = str(layerHeight) # Show messagebox to let use know we are analyzing app = showProgress(root, "Analyzing...", 0, 1, True, False, False) xmin, ymin, xmax, ymax = 9999, 9999, 0, 0 for layerNr in range(photonfile.layers.count()): im = photonfile.layers.get( layerNr, 'n') # 2560 rows, 1440 cols; element is called im[y,x] res = cv2.findContours(im.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # In the current OpenCV's master branch the return statements have changed, see http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=findcontours. if len(res) == 3: _, contours, hierarchy = res if len(res) == 2: contours, hierarchy = res #contours, hierarchy = cv2.findContours(im.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) idx = 0 for cnt in contours: idx += 1 x, y, w, h = cv2.boundingRect(cnt) xmin = min(x, xmin) ymin = min(y, ymin) xmax = max(x + w, xmax) ymax = max(y + h, ymax) app.setProgressPerc(int(100 * layerNr / photonfile.layers.count())) app.hide() #put some margin around regions margin = 10 def clamp(n, smallest, largest): return max(smallest, min(n, largest)) xmin = clamp(xmin - margin, 0, 1439) ymin = clamp(ymin - margin, 0, 2559) xmax = clamp(xmax + margin, 0, 1439) ymax = clamp(ymax + margin, 0, 2559) #files entries are tuples of:filename,photonfile classobject,position,size files.append([ len(files), filename, photonfile, (xmin, ymin), (xmax - xmin, ymax - ymin) ]) print("Read:", filename, photonfile.layers.count(), (xmin, ymin), (xmax - xmin, ymax - ymin)) print("Read:", files[len(files) - 1]) basename = os.path.basename(filename) barename = os.path.splitext(basename)[0] item = str(len(files)) + " " + barename listbox.insert(tkinter.END, item) return photonfile
def mnSaveAs(): global objects # Check if we got a filename hideHiddenFilesInDialog() filename = tkinter.filedialog.asksaveasfilename( initialdir=".", title="Select file", filetypes=(("photon files", "*.photon"), ("all files", "*.*"))) if not filename: return print("Saving...", filename) # Create new photonfile global entry_BottomLayers, entry_BottomExp, entry_NormalExp, entry_Offtime, layerHeight, scale outPhotonFile = PhotonFile() outPhotonFile.new() outPhotonFile.layers.clear() outPhotonFile.filename = filename outPhotonFile.setProperty("# Bottom Layers", int(entry_BottomLayers.get())) outPhotonFile.setProperty("Exp. time (s)", float(entry_NormalExp.get())) outPhotonFile.setProperty("Exp. bottom (s)", float(entry_BottomExp.get())) outPhotonFile.setProperty("Off time (s)", float(entry_Offtime.get())) outPhotonFile.setProperty("Layer height (mm)", float(layerHeight)) #return # Check max layer nrLayers = 0 for obj in objects: file = obj[0] photonfile = file[2] nrLayers = max(nrLayers, photonfile.layers.count()) # Show messagebox to let use know we are analyzing app = showProgress(root, "Merging...", 0, 1, True, False, False) # Construct each layer for layerNr in range(nrLayers): totIm = numpy.zeros((2560, 1440, 1), dtype=numpy.uint8) for obj in objects: file, xdest, ydest, wdest, hdest, rot = obj #print ("obj: ",xdest,ydest,wdest,hdest,rot,file) (xdest, ydest) = (ydest, xdest) xdest = int(xdest / scale) ydest = int(ydest / scale) wdest, hdest = file[4] if rot == 90: wdest, hdest = hdest, wdest #if layerNr<10: # print ("dest2",xdest,ydest,wdest,hdest) #file entries are tuples of:filename,photonfile classobject,position,size idx, filename, photonfile, (xsrc, ysrc), (wsrc, hsrc) = file # check if this photonfile has enough layers #print (photonfile.layers.count(),">",layerNr) if layerNr < photonfile.layers.count(): # Get full layer locIm = photonfile.layers.get(layerNr, 'i') # Get active region in layer locIm = locIm[ysrc:ysrc + hsrc, xsrc:xsrc + wsrc] # Rotate if requested #print ("shape1:",locIm.shape) if rot == 90: locIm = numpy.rot90(locIm) #print ("shape2:",locIm.shape) # Put in destination #print ("dest: [",ydest,":",ydest+hdest,",",xdest,":",xdest+wdest,"]") totIm[ydest:ydest + hdest, xdest:xdest + wdest] = locIm if layerNr == 10: print("write") cv2.imwrite("test/out1.png", locIm) cv2.imwrite("test/out2.png", totIm) # For some res outPhotonFile.layers.append(totIm) #if layerNr>3: return app.setProgressPerc(int(100 * layerNr / nrLayers)) app.hide() outPhotonFile.save()