示例#1
0
def run(args):
    try:
        import nibabel
        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)
        hdr = nii.get_header()
        q = hdr.get_best_affine()
        img = numpy.squeeze(nii.get_data())
        dims = img.shape
        f = int(args.factor)

        outFile = args.out
        if op.isfile(outFile):
            print 'Skipping downsample by factor {}. Output file {} exists.'.format(
                f, outFile)
        else:
            (img, q) = nit.downsample3d(img, q, f)
            if img.shape[-1] == 3:
                img3 = img
                img = numpy.zeros(img3.shape[:-1],
                                  dtype=[('R', 'u1'), ('G', 'u1'),
                                         ('B', 'u1')])
                img['R'] = img3[:, :, :, 0]
                img['G'] = img3[:, :, :, 1]
                img['B'] = img3[:, :, :, 2]
            nii = nibabel.Nifti1Image(img, q)
            # nii.set_sform(None,0) # set the sform code to 0 'unknown', to make it viewable in MRIcron
            nibabel.nifti1.save(nii, outFile)
            print 'Saved downsampled image to "{}" '.format(outFile)

        result = {
            'shape': (numpy.array(dims) / f).tolist(),
            'filesize': op.getsize(outFile)
        }
        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
    )
示例#3
0
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__)