def run(args): try: if op.exists(args.out): if not args.replace: print('Skipping creation of image "{}", it already exists.'. format(args.out)) result = {'Status': 'Skipped'} report.success(result) return stack = [] lefttop = [1000000, 1000000] rightbottom = [0, 0] bgColor = None for r in args.input_range: fname = args.inp % (r) img = numpy.array(Image.open(fname)) if bgColor is None: bgColor = img[0, 0] mask = nit.imageMask(img, [bgColor]) nonzero = numpy.argwhere(mask) if nonzero.size > 0: lt = nonzero.min(0) rb = nonzero.max(0) if lt[0] < lefttop[0]: lefttop[0] = lt[0] if lt[1] < lefttop[1]: lefttop[1] = lt[1] if rb[0] > rightbottom[0]: rightbottom[0] = rb[0] if rb[1] > rightbottom[1]: rightbottom[1] = rb[1] stack.append(img) for i in range(0, len(stack)): stack[i] = stack[i][lefttop[0]:rightbottom[0], lefttop[1]:rightbottom[1]] images2gif.writeGif(args.out, stack, duration=args.time_step / 1000.0, dither=0) result = {'Status': 'Done'} report.success(result) except: report.fail(__file__)
def main(self,input_nii,slicedir,outFolder,pctile,origin,colormap,reorient,replace,boundingbox_bgcolor,count_pixels): nii = nibabel.load(input_nii) dir2dim = {'x':0,'y':1,'z':2,'0':0,'1':1,'2':2,'coronal':0,'saggital':1,'horizontal':2,'axial':2} sliceDim = dir2dim[slicedir.lower()] # Nifti data is supposed to be in RAS orientation. # For Nifti files that violate the standard, the reorient string can be used to correct the orientation. if reorient: nii = nit.reorient(nii,reorient) hdr = nii.get_header() q = hdr.get_best_affine(); ornt = nibabel.io_orientation(q) print('The orientation is: {}'.format(ornt)) dims0 = [int(d) for d in nii.shape if d>1] dims = list(dims0) for i,d in enumerate(ornt): dims[i] = dims0[int(d[0])] print('The dimensions are: {}'.format(dims)) numSlices = dims[sliceDim]; baseName = op.basename(input_nii) baseName = re.sub('.gz$', '',baseName) baseName = re.sub('.nii$', '',baseName) if not op.exists(outFolder): os.makedirs(outFolder) print('Created output folder "{}".'.format(outFolder)) rgbMode = False img_dtype = nii.get_data_dtype(); if len(dims)==4 and dims[3]==3: rgbMode = True elif img_dtype.names: if len(img_dtype.names)==3: rgbMode = 'record' rescale = pctile is not None fmt = 'png' if rescale or rgbMode: fmt = 'jpg' filePattern = baseName+'_%04d.{}'.format(fmt) filePattern_py = filePattern.replace('_%04d','_{:04d}') if origin: try: if origin.startswith('['): ijk0 = json.loads(origin) elif origin == 'center': dims = hdr.get_data_shape() ijk0 = [dims[0]/2,dims[1]/2,dims[2]/2] q = hdr.get_best_affine() q[0:3,3] = -q[0:3,0:3].dot(ijk0) hdr.set_sform(q) except: raise Exception('Invalid origin "{}".'.format(origin)) # save coordinate system (Right Anterior Superior) information rasLimits = nit.rasLimits(hdr) # Some nii files have no origin set (other than i=0, j=0, k=0) # Use the origin parameter to overrule this. ##if origin == 'center': ## def ctr(x1,x2): ## w=x2-x1 ## return -w/2,w/2 ## rasLimits = [ctr(xx[0],xx[1]) for xx in rasLimits] with open(op.join(outFolder,'raslimits.json'), 'w') as fp: json.dump(rasLimits,fp) slicePos = (rasLimits[sliceDim][0] + (numpy.arange(0.0,dims[sliceDim])+0.5)*(rasLimits[sliceDim][1]-rasLimits[sliceDim][0])/dims[sliceDim]).tolist() with open(op.join(outFolder,'slicepos.json'), 'w') as fp: json.dump(slicePos,fp) # quit if ALL slices already exist if not replace: done = True for i in range(0,numSlices): outFile = filePattern_py.format(i) fullFile = op.join(outFolder,outFile) if not op.exists(fullFile): done = False break if done: return FancyDict( filePattern = op.join(outFolder,filePattern), rasLimits = rasLimits ) # load image, it is needed img = nii.get_data() img = nibabel.apply_orientation(img,ornt) img = numpy.squeeze(img) if rgbMode == 'record': img = nit.record2rgb(img) print('Nifti image loaded, shape "{}",data type "{}"'.format(dims,img.dtype)) maxSlices = 2048; if numSlices>maxSlices: raise Exception('Too many slices (more than '+str(maxSlices)+')'); if not rgbMode: minmax = nit.get_limits(img) if rescale: minmax = nit.get_limits(img,pctile) print('minmax {}, rescale {}'.format(minmax,rescale)) index2rgb = nit.parse_colormap(colormap,minmax) if isinstance(index2rgb,dict): rgbLen = len(index2rgb[index2rgb.keys()[0]]) else: rgbLen = len(index2rgb[0]) # save index2rgb if not rescale: if isinstance(index2rgb,dict): index2rgb_hex = {index:'{:02X}{:02X}{:02X}'.format(rgb[0],rgb[1],rgb[2]) for (index,rgb) in index2rgb.iteritems()} else: index2rgb_hex = ['{:02X}{:02X}{:02X}'.format(rgb[0],rgb[1],rgb[2]) for rgb in index2rgb] with open(op.join(outFolder,'index2rgb.json'), 'w') as fp: json.dump(index2rgb_hex,fp) elif rgbMode: grayscale = img.dot(numpy.array([0.2989,0.5870,0.1140])) minmax = nit.get_limits(grayscale) if rescale: minmax = nit.get_limits(grayscale,pctile) rescale = True rgbLen = 3 index2rgb = False else: rescale = False rgbLen = 3 index2rgb = False bbg = boundingbox_bgcolor if bbg is '': bbg = False if bbg: boundingBox = {} boundingBoxFile = op.join(outFolder,'boundingbox.json') if op.exists(boundingBoxFile): with open(boundingBoxFile, 'r') as fp: boundingBox = json.load(fp) pxc = count_pixels if pxc: pixCount = {}; pixCountFile = op.join(outFolder,'pixcount.json') if op.exists(pixCountFile): with open(pixCountFile, 'r') as fp: pixCount = json.load(fp) for i in range(0,numSlices): outFile = filePattern_py.format(i) fullFile = op.join(outFolder,outFile) if op.exists(fullFile): if i==0: print('image {}{} already exists as {}-file "{}".'.format(sliceDim,i,fmt,fullFile)) if not replace: continue slc = nit.get_slice(img,sliceDim,i) if pxc: labels = numpy.unique(slc) cnt = {} for b in labels: cnt[str(b)] = numpy.count_nonzero(slc == b) pixCount[i] = cnt if index2rgb: slc = nit.slice2rgb(slc,index2rgb,rescale,minmax[0],minmax[1]) elif rescale: slc = nit.rgbslice_rescale(slc,minmax[0],minmax[1]) # Save image ans = scipy.misc.toimage(slc).save(op.join(outFolder,outFile)) if bbg: if(bbg == "auto"): # do this only once bgColor = nit.autoBackgroundColor(slc) print('boundingbox auto bgColor is {}'.format(bgColor)) else: bgColor = nit.hex2rgb(bgg) mask = nit.imageMask(slc,[bgColor]) print 'mask shape {} {}'.format(bgColor,mask.shape) ## bounding box nonzero = numpy.argwhere(mask) #print 'nonzero {}'.format(nonzero) if nonzero.size>0: lefttop = nonzero.min(0)[::-1] # because y=rows and x=cols rightbottom = nonzero.max(0)[::-1] bb = lefttop.tolist() bb.extend(rightbottom-lefttop+(1,1)) boundingBox[i] = bb if i==0: print('image {}{} saved to {}-file "{}".'.format(sliceDim,i,fmt,fullFile)) if bbg: if len(boundingBox)>0: bb0 = boundingBox.itervalues().next() xyMin = [bb0[0],bb0[1]] xyMax = [bb0[0]+bb0[2],bb0[1]+bb0[3]] for bb in boundingBox.itervalues(): if bb[0]<xyMin[0]: xyMin[0] = bb[0] if bb[1]<xyMin[1]: xyMin[1] = bb[1] if bb[0]+bb[2]>xyMax[0]: xyMax[0] = bb[0]+bb[2] if bb[1]+bb[3]>xyMax[1]: xyMax[1] = bb[1]+bb[3] boundingBox['combined'] = [xyMin[0],xyMin[1],xyMax[0]-xyMin[0],xyMax[1]-xyMin[1]] with open(boundingBoxFile, 'w') as fp: json.dump(boundingBox,fp) if pxc: with open(pixCountFile, 'w') as fp: json.dump(pixCount,fp) return FancyDict( filePattern=op.join(outFolder,filePattern), rasLimits=rasLimits )
def run(args): try: # inspired by /my/github/3dbar/lib/pymodules/python2.6/bar/rec/default_pipeline.xml if op.exists(args.out): if not args.replace: print('Skipping creation of image "{}", it already exists.'. format(args.out)) result = {'Status': 'Skipped'} report.success(result) return nii = nibabel.load(args.inp) # Nifti data is supposed to be in RAS orientation. # For Nifti files that violate the standard, the reorient string can be used to correct the orientation. if isinstance(args.reorient, str): nii = nit.reorient(nii, args.reorient) nii = nibabel.as_closest_canonical(nii) # numpy.array() will copy image to 'clean' data buffer img = numpy.squeeze(nii.get_data()) if (args.bg_color == "auto"): bgColor = img[0, 0, 0] else: bgColor = int(args.bg_color) mask = nit.imageMask(img, [bgColor]) img[mask] = 8 if img.dtype != numpy.uint8: img = img.astype(numpy.uint8) #if numpy.prod(img.shape) > 512*512*512: hdr = nii.get_header() q = hdr.get_best_affine() scalef = [1 + v / 512 for v in img.shape] if any(numpy.array(scalef) > 1): print('Downsampling by a factor {} to reduce memory load'.format( scalef)) print 'Best affine before downsampling: {}'.format(q) (img, q) = nit.downsample3d(img, q.tolist(), scalef) print 'Best affine after downsampling: {}'.format(q) # image zero-padding bb = nit.get_boundingbox(img, 2) w = 2 padded = numpy.zeros((bb[3] + 2 * w, bb[4] + 2 * w, bb[5] + 2 * w), numpy.uint8, order='F') padded[w:-w, w:-w, w:-w] = img[bb[0]:bb[0] + bb[3], bb[1]:bb[1] + bb[4], bb[2]:bb[2] + bb[5]] img = padded dims = img.shape spacing = q.diagonal()[0:3] print('Origin before {}'.format(q[0:3, 3])) # origin adjusted for zero-padding q[0:3, 3] = apply_affine(q, [bb[0] - w, bb[1] - w, bb[2] - w]) origin = q[0:3, 3] print('Origin after {}'.format(origin)) doRender = args.render # create a rendering window and renderer ren = vtk.vtkRenderer() if doRender: renWin = vtk.vtkRenderWindow() iren = vtk.vtkRenderWindowInteractor() iren.SetRenderWindow(renWin) else: renWin = vtk.vtkRenderWindow() renWin.SetOffScreenRendering(1) renWin.AddRenderer(ren) WIDTH = 800 HEIGHT = 600 renWin.SetSize(WIDTH, HEIGHT) # import data dataImporter = vtk.vtkImageImport() dataImporter.SetImportVoidPointer(img) dataImporter.SetDataScalarTypeToUnsignedChar() dataImporter.SetNumberOfScalarComponents(1) dataImporter.SetDataExtent(0, dims[0] - 1, 0, dims[1] - 1, 0, dims[2] - 1) dataImporter.SetWholeExtent(0, dims[0] - 1, 0, dims[1] - 1, 0, dims[2] - 1) # new since October 2015 dataImporter.SetDataSpacing(spacing) dataImporter.SetDataOrigin(origin) print 'ORIGIN: {}'.format(dataImporter.GetDataOrigin()) print 'Dimensions {}'.format(dims) # create iso surface iso = vtk.vtkMarchingCubes() iso.SetInputConnection(dataImporter.GetOutputPort()) iso.ComputeNormalsOff() iso.SetValue(0, 3) iso.Update() #DEBUG #from vtk.util.numpy_support import vtk_to_numpy #iso.Update() #output = iso.GetOutput() #A = vtk_to_numpy(output.GetPoints().GetData()) #print 'iso Output {}'.format(A) tf = vtk.vtkTriangleFilter() tf.SetInput(iso.GetOutput()) # apply smoothing smth = vtk.vtkSmoothPolyDataFilter() smth.SetRelaxationFactor(0.5) smth.SetInput(tf.GetOutput()) # reduce triangles qc = vtk.vtkQuadricClustering() qc.SetNumberOfXDivisions(90) qc.SetNumberOfYDivisions(90) qc.SetNumberOfZDivisions(90) qc.SetInput(smth.GetOutput()) # map data volumeMapper = vtk.vtkPolyDataMapper() volumeMapper.SetInput(qc.GetOutput()) volumeMapper.ScalarVisibilityOff() # actor act = vtk.vtkActor() act.SetMapper(volumeMapper) # assign actor to the renderer ren.AddActor(act) bgColor = [0, 0, 0] ren.SetBackground(bgColor) camera = ren.GetActiveCamera() camera.SetViewUp(0, 0, 1) camera.SetFocalPoint(dims[0] / 2, dims[1] / 2, dims[2] / 2) camera.SetPosition(dims[0], dims[1] / 2, dims[2] / 2) camera.ParallelProjectionOn() camera.Yaw(180) ren.SetActiveCamera(camera) ren.ResetCamera() ren.ResetCameraClippingRange() ren.GetActiveCamera().Zoom(1.5) if args.out: take_screenshot(args.out, ren) im = Image.open(args.out) A = numpy.array(im) mask = nit.imageMask(A, [bgColor]) bgColor = [238, 221, 170] A[numpy.invert(mask)] = bgColor nonzero = numpy.argwhere(mask) if nonzero.size > 0: lefttop = [v if v > 0 else 0 for v in (nonzero.min(0) - 4)] rightbottom = [ v if v < A.shape[i] else A.shape[i] for i, v in enumerate(nonzero.max(0) + 4) ] A = A[lefttop[0]:rightbottom[0] + 1, lefttop[1]:rightbottom[1] + 1] im = Image.fromarray(A) sz = numpy.array(im.size, 'double') sz = numpy.ceil((128.0 / sz[1]) * sz) print 'sz {}'.format(sz) im.thumbnail(sz.astype(int), Image.ANTIALIAS) im = im.convert('P', palette=Image.ADAPTIVE, colors=256) palette = numpy.array(im.getpalette()).reshape(256, 3) match = numpy.where(numpy.all(palette == bgColor, axis=1)) im.save(args.out, transparency=match[0]) print('Created image "{}"'.format(args.out)) if args.x3d: vtkX3DExporter = vtk.vtkX3DExporter() vtkX3DExporter.SetInput(renWin) vtkX3DExporter.SetFileName(args.x3d) vtkX3DExporter.Write() if args.stl: stlWriter = vtk.vtkSTLWriter() stlWriter.SetInputConnection(qc.GetOutputPort()) stlWriter.SetFileTypeToBinary() stlWriter.SetFileName(args.stl) stlWriter.Write() # enable user interface interactor if doRender: iren.Initialize() renWin.Render() pos = camera.GetPosition() ornt = camera.GetOrientation() print 'Camera position; orientation {};{}'.format(pos, ornt) iren.Start() result = {'Status': 'Done'} report.success(result) except: report.fail(__file__)
def run(args): try: skipped = 0 print('Input PNG Folder: ' + args.png_src) pngFolder = args.png_src svgFolder = args.svg_dest if not (op.exists(svgFolder)): os.makedirs(svgFolder) print 'Created output folder "{}".'.format(svgFolder) # generate hulls in temp dir tmpdir = tempfile.mkdtemp(prefix='hull') includedExtensions = ['png'] fileNames = [ fn for fn in os.listdir(pngFolder) if any([fn.endswith(ext) for ext in includedExtensions]) ] for pngFile in fileNames: svgFile = op.splitext(pngFile)[0] + '.svg' outputFile = op.join(svgFolder, svgFile) if op.exists(outputFile): if not args.replace: print('Skipping svg-hull "{}", it already exists.'.format( svgFile)) skipped += 1 continue img = scipy.misc.imread(op.join(pngFolder, pngFile)) # ignore alpha if img.shape[-1] == 4: if len(img.shape) == 3: img = img[:, :, 0:3] elif len(img.shape) == 4: img = img[:, :, :, 0:3] if (args.bg_color == "auto"): bgColor = nit.autoBackgroundColor(img) print('bgColor is {}'.format(bgColor)) else: bgColor = nit.hex2rgb(args.bg_color) if img.shape[-1] == 4: bgColor.append(255) mask = nit.imageMask(img, [bgColor]) print 'mask shape {}'.format(mask.shape) hullFile = op.join(tmpdir, pngFile) if numpy.any(mask): print 'Computing convex hull for image "{}"'.format(hullFile) hull = convex_hull_image(mask) else: hull = mask scipy.misc.toimage(hull).save(hullFile) print('vector image saved to svg file "{}".'.format(svgFile)) # do the svg conversion result = png2svg.vectorize(report, tmpdir, svgFolder, args.replace, args.curve_tolerance, args.line_tolerance, bgColor='#000000') # cleanup for fn in os.listdir(tmpdir): os.remove(op.join(tmpdir, fn)) os.rmdir(tmpdir) result['skipped'] = skipped report.success(result) except: report.fail(__file__)