def loaddata(dicomdata): indata = pydicom_series.read_files(dicomdata,showProgress=True)[0] pixelsize = float(indata.info["0028","0030"].value[0]) rows = indata.info["0028","0010"].value cols = indata.info["0028","0011"].value newshape = (rows,cols) print "shape of dataslices: ", newshape bitdepth = indata.info["0028","0100"].value #bits allocated if bitdepth == 16: bv = np.int16 elif bitdepth == 8: bv = np.int8 elif bitdepth == 32: bv = np.int32 elif bitdepth == 64: bv = np.int64 datlist = [] sequence = indata.__dict__['_datasets'] for i in range(len(sequence)): tmpkeys = sequence[i].keys() datlist.append(np.reshape(np.fromstring(sequence[i][dicom.tag.Tag("7fe0","0010")].value,dtype=bv),newshape)) array = np.array(datlist) print "loaddata complete" return pixelsize, array, indata
def dataSlicing(dataPath, masks=None): #Gui init startTime = time.time() global app, win, imv1, imv2, imv3, hLine1, hLine2, hLine3, vLine1, vLine2, vLine3 app = QtGui.QApplication([]) win = QtGui.QMainWindow() win.setWindowTitle('DataSlicing') win.resize(600,800) cw = QtGui.QWidget() win.setCentralWidget(cw) l = QtGui.QGridLayout() cw.setLayout(l) imv1 = pg.ImageView() imv2 = pg.ImageView() imv3 = pg.ImageView() l.addWidget(imv1, 0, 0) l.addWidget(imv2, 1, 0) l.addWidget(imv3, 2, 0) win.show() #Get 3D data vol = diSeries.read_files(dataPath) data = vol[0].get_pixel_array() img1RGBA = np.zeros((data.shape[0],data.shape[1],4),dtype=np.ubyte) img2RGBA = np.zeros((data.shape[0],data.shape[2],4),dtype=np.ubyte) img3RGBA = np.zeros((data.shape[1],data.shape[2],4),dtype=np.ubyte) #input mask is a np array with numbered regions, 0 specifies no colour if masks is not None: alpha = int(255/float(len(masks))) #every mask in the list gets its own lookup table to avoid problems #with reset numbering of regions in different masks and # different number of regions in each of the masks masksCol = [] for j in range(len(masks)): if data.shape != masks[j].shape: raise Exception('The dimensions of the mask%d and data do not match.',j) #command np.unique(someArray) returns list of unique values in array colCount = len(np.unique(masks[j])) lookupTable = np.zeros((colCount,4),dtype=np.ubyte) lookupTable[0] = [0,0,0,0] for i in range(1,colCount): lookupTable[i]=np.random.randint(1,256,size=3).tolist()+[alpha] masksCol.append(pg.applyLookupTable(masks[j],lookupTable)) maskBack = masksCol.pop(0) mask = np.zeros(maskBack.shape,dtype=np.ubyte) for m in masksCol: mask[:,:,:,3]=((m[:,:,:,3].astype(float)/255)+(maskBack[:,:,:,3].astype(float)/255)*(1-(m[:,:,:,3].astype(float)/255)))*255 #when the alpha is zero, RGBs should be zero too #dividing by large number in float will produce int zero values mask[mask==0]=1000 mask[:,:,:,0]=(m[:,:,:,0]*(m[:,:,:,3].astype(float)/255)+maskBack[:,:,:,0]*(maskBack[:,:,:,3].astype(float)/255)*(1-(m[:,:,:,3].astype(float)/255)))/(mask[:,:,:,3].astype(float)/255) mask[:,:,:,1]=(m[:,:,:,1]*(m[:,:,:,3].astype(float)/255)+maskBack[:,:,:,1]*(maskBack[:,:,:,3].astype(float)/255)*(1-(m[:,:,:,3].astype(float)/255)))/(mask[:,:,:,3].astype(float)/255) mask[:,:,:,2]=(m[:,:,:,2]*(m[:,:,:,3].astype(float)/255)+maskBack[:,:,:,2]*(maskBack[:,:,:,3].astype(float)/255)*(1-(m[:,:,:,3].astype(float)/255)))/(mask[:,:,:,3].astype(float)/255) maskBack = mask maskBack[maskBack==0]=255 maskCol = maskBack else: maskCol = None #creates crosshairs, positioned in the middle of the picture, limited by #dimensions of the picture hLine1 = pg.InfiniteLine(angle=0, movable=True, pos=img1RGBA.shape[1]/2, bounds=[0,img1RGBA.shape[1]-1]) vLine1 = pg.InfiniteLine(angle=90, movable=True, pos=img1RGBA.shape[0]/2, bounds=[0,img1RGBA.shape[0]-1]) hLine2 = pg.InfiniteLine(angle=0, movable=True, pos=img2RGBA.shape[1]/2, bounds=[0,img2RGBA.shape[1]-1]) vLine2 = pg.InfiniteLine(angle=90, movable=True, pos=img2RGBA.shape[0]/2, bounds=[0,img2RGBA.shape[0]-1]) hLine3 = pg.InfiniteLine(angle=0, movable=True, pos=img3RGBA.shape[1]/2, bounds=[0,img3RGBA.shape[1]-1]) vLine3 = pg.InfiniteLine(angle=90, movable=True, pos=img3RGBA.shape[0]/2, bounds=[0,img3RGBA.shape[0]-1]) imv1.addItem(vLine1) imv1.addItem(hLine1) imv2.addItem(vLine2) imv2.addItem(hLine2) imv3.addItem(vLine3) imv3.addItem(hLine3) #{v,h}Line{1,2,3}.value() gives only one value, since the angle of lines is #zero or right #left upper corner of the picture is [0,0] coordinate def updateV1(): #updates image views that get affected by dragging in imv1 in vertical dimension global imv3, img3, img3RGBA, vLine1, vLine2 vLine2.setValue(vLine1.value()) img3 = data[vLine1.value(),:,:] img3RGBA = pg.makeRGBA(img3,levels=[np.amin(img3),np.amax(img3)])[0] if maskCol is not None: maskSlice = maskCol[vLine1.value(),:,:,:] img3RGBA[:,:,0:3] = maskSlice[:,:,0:3].astype(float)/255*img3RGBA[:,:,0:3] img3RGBA[:,:,3] = 255 imv3.setImage(img3RGBA.astype(int)) def updateH1(): #updates image views that get affected by dragging in imv1 in horizontal #dimension global img2, img2RGBA, imv2, hLine1, vLine3 vLine3.setValue(hLine1.value()) img2 = data[:,hLine1.value(),:] img2RGBA = pg.makeRGBA(img2,levels=[np.amin(img2),np.amax(img2)])[0] if maskCol is not None: maskSlice = maskCol[:,hLine1.value(),:,:] img2RGBA[:,:,0:3] = maskSlice[:,:,0:3].astype(float)/255*img2RGBA[:,:,0:3] img2RGBA[:,:,3] = 255 imv2.setImage(img2RGBA.astype(int)) vLine1.sigDragged.connect(updateV1) hLine1.sigDragged.connect(updateH1) def updateV2(): global imv3, img3, img3RGBA, vLine2, vLine1 vLine1.setValue(vLine2.value()) img3 = data[vLine2.value(),:,:] img3RGBA = pg.makeRGBA(img3,levels=[np.amin(img3),np.amax(img3)])[0] if maskCol is not None: maskSlice = maskCol[vLine2.value(),:,:,:] img3RGBA[:,:,0:3] = maskSlice[:,:,0:3].astype(float)/255*img3RGBA[:,:,0:3] img3RGBA[:,:,3] = 255; imv3.setImage(img3RGBA.astype(int)) def updateH2(): global imv1, img1, img1RGBA, hLine2, hLine3 hLine3.setValue(hLine2.value()) img1 = data[:,:,hLine2.value()] img1RGBA = pg.makeRGBA(img1,levels=[np.amin(img1),np.amax(img1)])[0] if maskCol is not None: maskSlice = maskCol[:,:,hLine2.value(),:] img1RGBA[:,:,0:3] = maskSlice[:,:,0:3].astype(float)/255*img1RGBA[:,:,0:3] img1RGBA[:,:,3] = 255 imv1.setImage(img1RGBA.astype(int)) vLine2.sigDragged.connect(updateV2) hLine2.sigDragged.connect(updateH2) def updateV3(): global imv2, img2, img2RGBA, vLine3, hLine1 hLine1.setValue(vLine3.value()) img2 = data[:,vLine3.value(),:] img2RGBA = pg.makeRGBA(img2,levels=[np.amin(img2),np.amax(img2)])[0] if maskCol is not None: maskSlice = maskCol[:,vLine3.value(),:,:] img2RGBA[:,:,0:3] = maskSlice[:,:,0:3].astype(float)/255*img2RGBA[:,:,0:3] img2RGBA[:,:,3] = 255 imv2.setImage(img2RGBA.astype(int)) def updateH3(): global imv1, img1, img1RGBA, hLine3, hLine2 hLine2.setValue(hLine3.value()) img1 = data[:,:,hLine3.value()] img1RGBA = pg.makeRGBA(img1,levels=[np.amin(img1),np.amax(img1)])[0] if maskCol is not None: maskSlice = maskCol[:,:,hLine3.value(),:] img1RGBA[:,:,0:3] = maskSlice[:,:,0:3].astype(float)/255*img1RGBA[:,:,0:3] img1RGBA[:,:,3] imv1.setImage(img1RGBA.astype(int)) vLine3.sigDragged.connect(updateV3) hLine3.sigDragged.connect(updateH3) #sets initial images, when script starts updateV1() updateH1() updateH2() endTime = time.time() elapsed = endTime - startTime print "time elapsed: ", elapsed
def prepareInput(instancedict,headers_only,logTag="[prepareInput] "): """ Reads inputfile as an EnhancedDICOM object; if an EnhancedDICOM object is detected, prepareEnhancedInput is called. Checks if the input is as expected: number of slices etc. If not headers_only, scaling the pixel values according to RescaleIntercept and RescaleIntercept of each frame and transposing for pyqtgraph format. Raises ValueError if file cannot be opened as a DICOM object Returns raw dcmfile, scaled and transposed pixeldata (or None), type of DICOM object. """ # compile a list of valid files to read instancedict = removeBogusDICOMfiles(instancedict) dcmInfile = None pixeldataIn = None # Repair Mapping for MR; not correct for all scanner keymapping = [ ("0040,9096,0040,9224","0028,1052"), # Real World Value Intercept -> Rescale Intercept ("0040,9096,0040,9225","0028,1053"), # Real World Value Slope -> Rescale Slope ] # Check if input data is as expected: MR needs 3D data if len(instancedict) == 1: ModeEnhanced = False ModeEnhanced = testIfEnhancedDICOM(instancedict[0]) if ModeEnhanced: return prepareEnhancedInput(instancedict[0],headers_only=headers_only) # scaled and transposed if dcmInfile is None: filename = instancedict[0] dcmInfile = _readSingleImage(filename,headers_only=headers_only) if dcmInfile is None: # Not a dicom file raise ValueError("{} ERROR! {} is not a valid non-Enhanced DICOM object".format(logTag, filename)) modality = dcmInfile.Modality if not headers_only: # need scaling for single slice, already done for series in pydicom_series, but not for some MR files if modality == 'CT' or modality == 'MR': if not "RescaleIntercept" in dcmInfile: # in wrong place define for some MR files dcmInfile.RescaleIntercept = readDICOMtag(keymapping[0][0],dcmInfile,0) dcmInfile.RescaleSlope = readDICOMtag(keymapping[1][0],dcmInfile,0) pixeldataIn = np.int16(np.transpose(dcmInfile.pixel_array.astype(int),(1,0))) slope = dcmInfile.RescaleSlope intercept = dcmInfile.RescaleIntercept pixeldataIn = intercept + slope*pixeldataIn elif modality == 'MG' and getDICOMMode(dcmInfile) == stModeBTO: print '!WARNING! MG BTO dataset! DICOM info is NOT properly adjusted, no scaling applied yet!' pixeldataIn = np.transpose(dcmInfile.pixel_array,(0,2,1)) elif modality == 'MG' or modality == 'CR': pixeldataIn = dcmInfile.pixel_array.transpose() elif modality == 'RF': # fixme! 2D and 3D pixeldataIn = dcmInfile.pixel_array.transpose() elif modality == 'US': rgbmode = (dcmInfile.SamplesPerPixel == 3) if not rgbmode: if len(np.shape(dcmInfile.pixel_array)) == 2: pixeldataIn = dcmInfile.pixel_array.transpose() elif len(np.shape(dcmInfile.pixel_array)): pixeldataIn = np.transpose(dcmInfile.pixel_array,(0,2,1)) pixeldataIn = pixeldataIn[0] else: # AS: this fix is needed in pydicom < 1.0; maybe solved in later versions? try: nofframes = dcmInfile.NumberOfFrames except AttributeError: nofframes = 1 if dcmInfile.PlanarConfiguration==0: pixel_array = dcmInfile.pixel_array.reshape(nofframes, dcmInfile.Rows, dcmInfile.Columns, dcmInfile.SamplesPerPixel) else: pixel_array = dcmInfile.pixel_array.reshape(dcmInfile.SamplesPerPixel, nofframes, dcmInfile.Rows, dcmInfile.Columns) if len(np.shape(pixel_array)) == 3: #2d rgb pixeldataIn = pixel_array[:,:,0].transpose() pixeldataInR = (pixel_array[:,:,0]).transpose() pixeldataInG = (pixel_array[:,:,1]).transpose() pixeldataInB = (pixel_array[:,:,2]).transpose() elif len(np.shape(pixel_array)) == 4: #3d rgb pixeldataIn = (pixel_array[-1,:,:,0]).transpose() pixeldataInR = (pixel_array[-1,:,:,0]).transpose() pixeldataInG = (pixel_array[-1,:,:,1]).transpose() pixeldataInB = (pixel_array[-1,:,:,2]).transpose() # remove rgb info for y in range(dcmInfile.Rows): for x in range(dcmInfile.Columns): r = pixeldataInR[x,y] g = pixeldataInG[x,y] b = pixeldataInB[x,y] ma = max(r,g,b) mi = min(r,g,b) if ma != mi: pixeldataIn[x,y] = 0 else: path = os.path.dirname(instancedict[0]) for ip in instancedict: if os.path.dirname(ip) != path: raise ValueError("{} ERROR! multiple would-be dicom files scattered over multiple dirs!".format(logTag)) try: dcmInfile = dcmseries.read_files(path, True, readPixelData=False)[0] if not headers_only: # NOTE: Rescaling is already done pydicom_series, but maybe not for stupid MR pixeldataIn = np.transpose(dcmInfile.get_pixel_array(),(0,2,1)) except: raise ValueError("{} ERROR! {} is not a valid non-Enhanced DICOM series".format(logTag, path)) return dcmInfile,pixeldataIn,getDICOMMode(dcmInfile)
def prepareInput(instancedict,headers_only,logTag="[prepareInput] "): """ Reads inputfile as an EnhancedDICOM object; if an EnhancedDICOM object is detected, prepareEnhancedInput is called. Checks if the input is as expected: number of slices etc. If not headers_only, scaling the pixel values according to RescaleIntercept and RescaleIntercept of each frame and transposing for pyqtgraph format. Raises ValueError if file cannot be opened as a DICOM object Returns raw dcmfile, scaled and transposed pixeldata (or None), type of DICOM object. """ # compile a list of valid files to read instancedict = removeBogusDICOMfiles(instancedict) dcmInfile = None pixeldataIn = None # Repair Mapping for MR; not correct for all scanner keymapping = [ ("0040,9096,0040,9224","0028,1052"), # Real World Value Intercept -> Rescale Intercept ("0040,9096,0040,9225","0028,1053"), # Real World Value Slope -> Rescale Slope ] # Check if input data is as expected: MR needs 3D data if len(instancedict) == 1: ModeEnhanced = False ModeEnhanced = testIfEnhancedDICOM(instancedict[0]) if ModeEnhanced: return prepareEnhancedInput(instancedict[0],headers_only=headers_only) # scaled and transposed if dcmInfile is None: filename = instancedict[0] dcmInfile = _readSingleImage(filename,headers_only=headers_only) if dcmInfile is None: # Not a dicom file raise ValueError("{} ERROR! {} is not a valid non-Enhanced DICOM object".format(logTag, filename)) modality = dcmInfile.Modality if not headers_only: # need scaling for single slice, already done for series in pydicom_series, but not for some MR files if modality == 'CT' or modality == 'MR': if not "RescaleIntercept" in dcmInfile: # in wrong place define for some MR files dcmInfile.RescaleIntercept = readDICOMtag(keymapping[0][0],dcmInfile,0) dcmInfile.RescaleSlope = readDICOMtag(keymapping[1][0],dcmInfile,0) pixeldataIn = np.int16(np.transpose(dcmInfile.pixel_array.astype(int),(1,0))) slope = dcmInfile.RescaleSlope intercept = dcmInfile.RescaleIntercept pixeldataIn = intercept + slope*pixeldataIn elif modality == 'MG' and getDICOMMode(dcmInfile) == stModeBTO: print('!WARNING! MG BTO dataset! DICOM info is NOT properly adjusted, no scaling applied yet!') pixeldataIn = np.transpose(dcmInfile.pixel_array,(0,2,1)) elif modality == 'MG' or modality == 'CR' or modality == 'DX': pixeldataIn = dcmInfile.pixel_array.transpose() elif modality == 'RF': # fixme! 2D and 3D pixeldataIn = dcmInfile.pixel_array.transpose() elif modality == 'US': rgbmode = (dcmInfile.SamplesPerPixel == 3) if not rgbmode: if len(np.shape(dcmInfile.pixel_array)) == 2: pixeldataIn = dcmInfile.pixel_array.transpose() elif len(np.shape(dcmInfile.pixel_array)): pixeldataIn = np.transpose(dcmInfile.pixel_array,(0,2,1)) pixeldataIn = pixeldataIn[0] else: # AS: this fix is needed in pydicom < 1.0; maybe solved in later versions? try: nofframes = dcmInfile.NumberOfFrames except AttributeError: nofframes = 1 if dcmInfile.PlanarConfiguration==0: pixel_array = dcmInfile.pixel_array.reshape(nofframes, dcmInfile.Rows, dcmInfile.Columns, dcmInfile.SamplesPerPixel) else: pixel_array = dcmInfile.pixel_array.reshape(dcmInfile.SamplesPerPixel, nofframes, dcmInfile.Rows, dcmInfile.Columns) # force using only the RED channel in RGB. if US_RGB_USE_RED == True: if len(np.shape(pixel_array)) == 3: #2d rgb pixeldataIn = pixel_array[:,:,0].transpose() elif len(np.shape(pixel_array)) == 4: #3d rgb pixeldataIn = (pixel_array[-1,:,:,0]).transpose() else: # remove all data where there is a difference between R,G,B if len(np.shape(pixel_array)) == 3: #2d rgb pixeldataIn = pixel_array[:,:,0].transpose() pixeldataInR = (pixel_array[:,:,0]).transpose() pixeldataInG = (pixel_array[:,:,1]).transpose() pixeldataInB = (pixel_array[:,:,2]).transpose() elif len(np.shape(pixel_array)) == 4: #3d rgb pixeldataIn = (pixel_array[-1,:,:,0]).transpose() pixeldataInR = (pixel_array[-1,:,:,0]).transpose() pixeldataInG = (pixel_array[-1,:,:,1]).transpose() pixeldataInB = (pixel_array[-1,:,:,2]).transpose() # remove rgb info for y in range(dcmInfile.Rows): for x in range(dcmInfile.Columns): r = pixeldataInR[x,y] g = pixeldataInG[x,y] b = pixeldataInB[x,y] ma = max(r,g,b) mi = min(r,g,b) if ma != mi: pixeldataIn[x,y] = 0 else: path = os.path.dirname(instancedict[0]) for ip in instancedict: if os.path.dirname(ip) != path: raise ValueError("{} ERROR! multiple would-be dicom files scattered over multiple dirs!".format(logTag)) try: dcmInfile = dcmseries.read_files(path, True, readPixelData=False)[0] if not headers_only: # NOTE: Rescaling is already done pydicom_series, but maybe not for stupid MR pixeldataIn = np.transpose(dcmInfile.get_pixel_array(),(0,2,1)) except Exception as e: raise ValueError("{} ERROR! {} is not a valid non-Enhanced DICOM series".format(logTag, path)) return dcmInfile,pixeldataIn,getDICOMMode(dcmInfile)