def DiscretizeImage(self, maxVal=1, minVal=0): ''' This function takes an image and finds the median pixel value, then compares the value of each pixel to the median. If a pixel is greater than or equal to the median, the pixel value is set to a user supplied value. If the pixel value is less than the median, the pixel value is set to another user supplied value. :param maxVal: the high value use for pixels :param minVal: the low value to use for pixels :return: ''' gui = shareGui.getGui() if len(self.channels) > 1: answer = gui.showMessage('Error', 'Discretization can only be preformed on grayscale images.\nThe current image has the following bands: ' '\n{}\nPress \'Ok\' to continue segmentation without discretizing, or \'Cancel\' to abort.'.format(self.channels), 'warning') if answer == 0: pass if answer == 1: return False shareGui.getGui().updateLog('Discretizing image') temp = numpy.zeros(self.size) for i in range(self.size): temp[i] = self.data[i] sorted_values = sorted(self.data) pixel_values = self.pixels.GetPixelArray() if self.size % 2 == 1: median = sorted_values[((self.size + 1) / 2) - 1] else: median = float((sorted_values[(self.size + 1) / 2] + sorted_values[(self.size - 1) / 2]) / 2.0) for i in range (self.height): for j in range(self.width): stride = (self.width * i) + j if temp[stride] >= median: temp[stride] = maxVal pixel_values[stride] = maxVal else: temp[stride] = minVal pixel_values[stride] = minVal newFile= '{}{}_discretized.{}'.format(self.segmentDir, self.imageFile.split('.')[0], self.fileFormat) self.WriteNewImage(temp, newFile) gui.returnDiscretized(newFile) self.__init__(newFile) self.ReadImage() return True
def SmoothImage(self, iterations): for i in range(iterations): shareGui.getGui().updateLog('Smoothing iteration #%d' % self.iteration) newImage = self.dataImage.filter(ImageFilter.SMOOTH_MORE) self.iteration += 1 self.dataImage = newImage self.data = self.dataImage.getdata() self.pixels = PixelArray(self.width, self.height, self.data) return
def displayPlots(self): figures = plt.get_fignums() histogramFignums = [] for figure in figures: if figure % 2 == 0: histogramFignums.append(figure) histogramFigures = list(map(plt.figure, histogramFignums)) shareGui.getGui().addPlots(histogramFigures, 1) return
def CreateMatrix( self, sigmaI, sigmaX ): # Creates a process pool and distributes their work to all pixels, to calculate weight matrix gui = shareGui.getGui() cpus = mp.cpu_count() # At most, use all but one core for processes poolCount = int(cpus - (math.ceil(cpus * 0.1))) args = [(self, sigmaI, sigmaX, i) for i in range(self.numPixels)] gui.updateLog("Cpu's on machine: %d" % cpus) gui.updateLog("Cpu's to process weight matrix: %d" % poolCount) pool = mp.Pool(processes=poolCount) gui.updateLog("Mapping pool processes") tempData = pool.map(unwrap_CreateMatrix, args) pool.close() pool.join() for ( pixelList ) in tempData: # This puts the data of each pixel, returned from each seperate process, into self.data for pixel in pixelList: self.data[pixel[1]] = pixel[0] self.matrix = numpy.matrix(self.data.reshape(self.columns, self.rows), numpy.float64) gui.updateLog("Weight Matrix shape: {}".format(self.matrix.shape)) return
def getFinalSegments(branches, segmentDir): #Goes through all pixel data from the segmentation and specifies which files correspond to final-size segments gui = shareGui.getGui() dirs = next(os.walk(segmentDir))[1] allPixelPaths = [] finalSegments = [] #each entry is a numpy array (segment) containing pixel indices finalData = [] #each entry is a numpy array (segment) containing pixel values finalPaths = [] #each entry is a path string to the pixel data of a final segment #Finding all pixel files for dir in dirs: if dir.find('pixels_') != -1: dir = '/{}'.format(dir) pixelPaths = next(os.walk('{}{}'.format(segmentDir, dir)))[2] pixelPaths.sort() for n in range(len(pixelPaths)): pixelPaths[n] = segmentDir + dir + '/' + pixelPaths[n] allPixelPaths.extend(pixelPaths) #checking to see which pixel files correspond to final-size segments for n in range(len(branches)): if(branches[n] == 0): finalPaths.append(allPixelPaths[n]) finalSegments.append((numpy.load(allPixelPaths[n]))['locations']) finalData.append((numpy.load(allPixelPaths[n]))['pixels']) finalSegments = numpy.array(finalSegments) gui.updateLog('Algorithm finished with {} final-size segments'.format(len(finalSegments))) return finalSegments, finalData, finalPaths
def displayPlots(self): figures = plt.get_fignums() scatterFignums = [] for figure in figures: if figure % 2 == 1: #on every iteration, the scatter plot is created first, and fignums starts at 1, so if #the current fig_num % 2 = 1, then that figure is a scatter plot scatterFignums.append(figure) #scatterFigures is a list of matplotlib figures/their emory locations scatterFigures = list(map(plt.figure, scatterFignums)) #The 0 passed to gui.addPlots() indicates that the list being passed is a list of scatters shareGui.getGui().addPlots(scatterFigures, 0) return
def CreateMatrix(self, weights): for i in range(self.size): self.data[i] = weights[i].sum() gui = shareGui.getGui() temp = numpy.diag(self.data) self.matrix = numpy.matrix(temp, numpy.float) gui.updateLog("Diagonal Matrix shape: {}".format(self.matrix.shape)) return
def finish(segmentData, maxVar, maxInt): #finish up the segmentation, return are necessary final data branches, segmentDir, image, dimensions = segmentData[0], segmentData[1], segmentData[2], segmentData[3] gui = shareGui.getGui() finalSegments, finalData, finalPaths = getFinalSegments(branches, segmentDir) finalBackground = findBackground(finalData, maxVar, maxInt) finalMap = mapBorders(segmentDir, dimensions, finalSegments, finalBackground) cleanup(segmentDir, finalPaths) results = [finalSegments, finalBackground, finalMap] return results
def toMAPS(segments, backgrounds, dimensions, segmentDir, imageName): #takes all the final data gathered from finalize.py and turns it into hdf5 files, in the format that MAPS expected for ROIs gui = shareGui.getGui() gui.updateLog('Exporting segment data to MAPS ROIs') imageSize = dimensions[0]*dimensions[1] pixelIds = numpy.arange(imageSize) scanNumber = imageName.split('_')[1].split('.')[0] fgSegments = segments[backgrounds==0] #take all indices from 'segments' where the corresponding index in 'backgrounds is 0 bgSegments = segments[backgrounds==1] numFgFiles = int(math.ceil(float(len(fgSegments))/16.0)) #MAPS supports only 16 distinct ROI's in one dataset numBgFiles = int(math.ceil(float(len(bgSegments))/16.0)) #so this will find how mant datasets are needed to account for all segs fgRois = [numpy.zeros(imageSize, int) for _ in range(numFgFiles)] #list of arrays, each the size of the image to be filled with ints 1-16 bgRois = [numpy.zeros(imageSize, int) for _ in range(numBgFiles)] segs = [fgSegments, bgSegments] rois = [fgRois, bgRois] for i in range(len(rois)): regionNum = 1 fileNum = 0 for j in range(len(segs[i])): if(regionNum > 16): regionNum = 1 fileNum += 1 region = segs[i][j] for index in region: rois[i][fileNum][index] = regionNum regionNum += 1 f,b=0,0 #current foreground segment, current background segment for n in range(len(rois[i])): nextData = rois[i][n].reshape(dimensions) #next segment if i==0: file = h5py.File('{}/{}_roi_FG{}.h5'.format(segmentDir, scanNumber, f), 'w') #create a new hdf5 file (should be changed in the future, only one total is really needed file.create_dataset('MAPS_ROIS/fg_roi_{}'.format(n), data=nextData) #create a MAPS group, and add a dataset containing the next segment file.create_dataset('MAPS_ROIS/pixel_id', data=pixelIds) f+=1 elif i==1: file = h5py.File('{}/{}_roi_BG{}.h5'.format(segmentDir, scanNumber, b), 'w') file.create_dataset('MAPS_ROIS/bg_roi_{}'.format(n), data=nextData) file.create_dataset('MAPS_ROIS/pixel_id', data=pixelIds) b+=1 gui.updateLog('Saved ROIs to MAPS compatible HDF5 formats'.format(segmentDir)) return
def findBackground(finalData, maxVar, maxInt): #flags segments as background or foreground (this will need some work, is farily simple at the moment) gui = shareGui.getGui() gui.updateLog('Finding background segments background segments:') gui.updateLog('Using variance threshold of {}'.format(maxVar)) gui.updateLog('Using intensity threshold of {}'.format(maxInt)) background = numpy.zeros(len(finalData), dtype = int) #if the current segment has a variance and mean intensity below both of the threshold values, it is set to '1' in the background list, #indicating that the segment at that index is a background segment for n in range(len(finalData)): if(numpy.var(finalData[n]) < maxVar and numpy.mean(finalData[n]) < maxInt): background[n] = 1 return background
def workSegment(haltThreshold, weightMatrix, data, divideType, displayPlots, cutNumber, paths, imageNumber, image): #Performs the algorithm gui = shareGui.getGui() pixel_path, matrix_path = paths[0], paths[1] prev_pixel_path, prev_matrix_path = paths[2], paths[3] imageSize = data.GetImageSize() imageData = data.GetImageData() pixelLocations = numpy.arange(imageSize) #initial segmentation----------------------- if (cutNumber == 1): pixelLength = imageSize divideData = imageData #each subsequent segmentation--------------- else: # Get the name of the file without the file extension. filename = os.path.splitext(image)[0] gui.updateLog("\nWorking on %s" % image) # Load the image pixels and pixel locations into arrays newPixelsArrays = numpy.load(prev_pixel_path + "/" + filename + ".npz") newPixels = newPixelsArrays['pixels'] divideData = newPixels pixelLocations = newPixelsArrays['locations'] pixelLength = len(newPixels) gui.updateLog("Reading array of size %d from file %s.npz" % (newPixels.shape[0], filename)) #If the size of the loaded image is below the halting threshold, do not segment, # and return (ending this branch of the segmentation) if(len(newPixels) < haltThreshold): gui.updateLog('Skipping file %s: segment size is below specified halting threshold.' % image) #return value of '0' means the segmentation on this branch has ended return(0) # Load the matrix data and create a new WeightMatrix temp = numpy.matrix(numpy.load(prev_matrix_path + "/" + filename + ".npy")) weightMatrix = WM(temp.shape[0], temp.shape[1]) weightMatrix.SetMatrix(temp) gui.updateLog("Reading matrix of size %dx%d from file %s.npy" % (temp.shape[0], temp.shape[1], filename)) #Create a new diagonal matrix. gui.updateLog("Creating diagonal matrix") if (cutNumber > 1): diagonalMatrix = DM(len(newPixels), 1) #had to change .size to len() to count multi-dimensional array items properly else: diagonalMatrix = DM(data.width, data.height) diagonalMatrix.CreateMatrix(weightMatrix.GetMatrix()) gui.updateLog('Solving for eigenvalues') secondVec = solveEigenVectors(diagonalMatrix.GetMatrix(), weightMatrix.GetMatrix()) # Divide the image into two using the second smallest eigenvector. There are three options for dividing the image. # 1) All pixels corresponding to values in the eignevector greater than zero will be in one image, # while all pixels corresponding to values in the eigenvector less than zero will be in the # other image. # # 2) As option 1, except that eigenvector values will be compared to the median value of the eignevector. # # 3) Several different dividing values are tried. The resulting ratio (edges in segment one / edges removed) + # (edges in segment two / edges removed) is minimized. if divideType == 0: dividingValue = 0 numsteps = 1 if divideType == 1: dividingValue = numpy.median(secondVec, axis=0) numsteps = 1 if divideType == 2: numSteps = int(secondVec.size / pow(math.log10(secondVec.size), 2)) stepSize = (numpy.amax(secondVec) - numpy.amin(secondVec)) / numSteps if numSteps < 2: numSteps = 1 dividingValue = numpy.median(secondVec, axis=0) else: maxVal = numpy.amax(secondVec) minVal = numpy.amin(secondVec) stepSize = (maxVal - minVal) / numSteps dividingValue = maxVal gui.updateLog('Dividing image pixel values') segmentInfo = DivideImage(secondVec, divideData, imageSize, pixelLength, pixelLocations, dividingValue) segmentOne = segmentInfo['segOne'] segmentTwo = segmentInfo['segTwo'] posIndices = segmentInfo['posIndices'] negIndices = segmentInfo['negIndices'] # Calculate the weights of the edges that were removed from the image. # Reduce the weight matrix to two new matrices, one for each image segment. cutSize, matrixOne, matrixTwo = weightMatrix.ReduceMatrix(posIndices, negIndices) gui.updateLog("Size of cut = %f" % cutSize) if divideType == 2: prevCutSize = cutSize prevSegInfo = segmentInfo prevMatrixOne = matrixOne prevMatrixTwo = matrixTwo #continue until smallest cut is found for i in range(numSteps - 1): dividingValue = dividingValue - stepSize segmentInfo = DivideImage(secondVec, divideData, imageSize, pixelLength, pixelLocations, dividingValue) segmentOne = segmentInfo['segOne'] segmentTwo = segmentInfo['segTwo'] posIndices = segmentInfo['posIndices'] negIndices = segmentInfo['negIndices'] # Calculate the weights of the edges that were removed from the image. # Reduce the weight matrix to two new matrices, one for each image segment. #edgeSum, matrixOne, matrixTwo = weightMatrix.ReduceMatrix(posIndices, negIndices) cutSize, matrixOne, matrixTwo = weightMatrix.ReduceMatrix(posIndices, negIndices) if cutSize < prevCutSize: prevCutSize = cutSize prevMatrixOne = matrixOne prevMatrixTwo = matrixTwo prevSegInfo = segmentInfo cutSize = prevCutSize matrixOne = prevMatrixOne matrixTwo = prevMatrixTwo gui.updateLog("Size of choosen cut = %f" % cutSize) segmentInfo = prevSegInfo segmentOne = segmentInfo['segOne'] segmentTwo = segmentInfo['segTwo'] posIndices = segmentInfo['posIndices'] negIndices = segmentInfo['negIndices'] #Progress output to gui. gui.updateLog("Pixels in segment one = %d" % len(posIndices)) gui.updateLog("Pixels in segment two = %d" % len(negIndices)) gui.updateLog("Matrix one size = %dx%d" % (matrixOne.GetMatrix().shape[0], matrixOne.GetMatrix().shape[1])) gui.updateLog("Matrix two size = %dx%d" % (matrixTwo.GetMatrix().shape[0], matrixTwo.GetMatrix().shape[1])) # Create two arrays of pixels from the original image using # the indices returned from DivideImage. posPixels = numpy.take(divideData, posIndices, axis=0) negPixels = numpy.take(divideData, negIndices, axis=0) #Final output to image files #Segment One----------------------------------------------- filename = "/segment_%d_%d" % (cutNumber, imageNumber) # Save the pixel locations for each new pixel array. posLocations = numpy.take(pixelLocations, posIndices) gui.updateLog("Writing pixels file {}.npz".format(filename)) posArrays = {'pixels':posPixels, 'locations':posLocations} numpy.savez(pixel_path + "/%s.npz" % filename, **posArrays) gui.updateLog("Writing matrix file {}.npy".format(filename)) numpy.save(matrix_path + "/%s.npy" % filename, matrixOne.GetMatrix()) imageNumber += 1 #Segment Two----------------------------------------------- filename = "/segment_%d_%d" % (cutNumber, imageNumber) negLocations = numpy.take(pixelLocations, negIndices) gui.updateLog("Writing pixels file {}.npz".format(filename)) negArrays = {'pixels':negPixels, 'locations':negLocations} numpy.savez(pixel_path + "/%s.npz" % filename, **negArrays) gui.updateLog("Writing matrix file {}.npy".format(filename)) numpy.save(matrix_path + "/%s.npy" % filename, matrixTwo.GetMatrix()) #Displays scatter plots and histograms in the results panel in the gui if selected (probably to be removed) if(displayPlots): scatPlt = ScatterPlot("Original Image", secondVec) histPlt = HistogramPlot("Original image", secondVec) if(cutNumber > 1): scatPlt.AddPlot(image, secondVec) histPlt.AddPlot(image, secondVec) #return value of '1' means the segmentation is still going on this branch (the current working segment will be segmented again) return(1)
def start(imagePath, divideType, maxPixelDistance, smoothValue, displayPlots, haltThreshold, numImages): #initiates the segmentation; makes directories, loads image data, builds weight matrix, etc. ''' :param divideType = Set the type of dividing to be done. 0 - divide the image using the value of zero 1 - divide the image using the median value of the eignevector 2 - try vector.size / (log(vector.size))^2 evenly spaced dividing points :param maxPixelDistance = how close 2 pixels must be to have a nonzero weight between them :param smoothValue = iterations of smoothing (smoothing not called if smoothValue = 0) :param displayPlots = self explanatory boolean :param haltThreshold = this is the pixel size that will end the cutting of a segment :return segmentDir or None ''' gui = shareGui.getGui() imageDir, imageFile = os.path.split(imagePath) imageName, extension = imageFile.split('.') #creates two directories titled as the current date > the original image name, to save all output segmentDir = '{}/{}_segmentation/{}'.format(imageDir, time.strftime("%m-%d-%Y"), imageName) dateDir = os.path.split(segmentDir)[0] suffix = 1 #This while loop ensures no segmentations of a common image are overwritten while(os.path.isdir(segmentDir) == True): segmentDir = '{}/{}_segmentation/{}_{}'.format(imageDir, time.strftime("%m-%d-%Y"), imageName, suffix) suffix += 1 #Creates the segmentation directory if not os.path.isdir(segmentDir): try: os.makedirs(segmentDir) except OSError: if not os.path.isdir(segmentDir): raise gui.updateLog('------------------------ Starting on image {} ------------------------\n'.format(imageFile)) gui.updateLog('Creating segmentation directory at: %s' % segmentDir) gui.updateLog('Using image located at: %s' % imagePath) gui.updateLog('Using divide type of %d' % divideType) gui.updateLog('Using maximum pixel distance of %d' % maxPixelDistance) gui.updateLog('Using halting threshold of %d' % haltThreshold) #loads selected image into imagedata object, depending on whether the user # selected data is a numpy array file (user opened a hdf5), or a nromal image file if extension == 'npz': data = ImageArrayData(imagePath, segmentDir) else: data = ImageFileData(imagePath, segmentDir) gui.updateLog('\n--- Reading Image ---\n') data.ReadImage() if smoothValue > 0: data.SmoothImage(smoothValue) #Setup gui.advanceProgressBar(10/numImages) imageData = data.GetImageData() imageSize = data.GetImageSize() dimensions = data.GetImageDimensions() channels = data.GetChannels() fileFormat = data.GetFileFormat() # Create an array of pixel locations, location=sqrt(x^2 + y^2) locationValues = data.pixels.CreateLocationArray() sigmaI = numpy.var(imageData) sigmaX = numpy.var(locationValues) #Output image properties gui.updateLog("Image mode is %s" % data.GetImageMode()) gui.updateLog("Data format is %s" % fileFormat) gui.updateLog("Data channels are: {}".format(channels)) gui.updateLog("Number of image pixels = %d" % imageSize) gui.updateLog("Image width = %d, image height = %d" % dimensions) gui.updateLog("Intensity variance = %f" % sigmaI) gui.updateLog("Location variance = %f" % sigmaX) #All of these lines change the graphic of the gui's progress bar gui.advanceProgressBar(10/numImages) #create weight matrix gui.updateLog("\n--- Creating weight matrix ---\n") weightMatrix = WM(data.size, data.size) weightMatrix.SetPixelData(data.GetPixels(), maxPixelDistance) #time weight matrix build t0 = time.time() weightMatrix.CreateMatrix(sigmaI, sigmaX) t2 = '%.2f' % (time.time() - t0) gui.updateLog('Parallel building of weight matrix took {} seconds'.format(t2)) gui.advanceProgressBar(30/numImages) #Starts segmentation gui.updateLog('\n--- Starting segmentation---\n') t0 = time.time() branches = SegmentImage(weightMatrix, data, segmentDir, divideType, displayPlots, haltThreshold, numImages) t2 = '%.2f' % (time.time() - t0) gui.updateLog('\n\n--- Segmentation completed (took {} seconds) ---\n\n'.format(t2)) gui.setSegmentPath(segmentDir) gui.setRawData(list(imageData)) #This is the ultimate return back to the gui, a list of data resultant from the segmentation, for the gui to then pass to finalize.py and finalize segmentData = [branches, segmentDir, data, dimensions] return(segmentData)