def test_2D_indices_col(self): """ The returned 2D index should be the same as control. """ control = numpy.where(self.array_2D == 66) wh = numpy.where(self.array_1D_1 == 66) ind2D = array_indices(array=self.array_2D, index=wh[0]) n = len(control[1]) self.assertEqual((control[1] == ind2D[1]).sum(), n)
def test_dimensions(self): """ Test that the dimensions keyword works and yields the same result as the control. """ control = numpy.where(self.array_3D == 66) control_sum = numpy.sum(self.array_3D[control]) wh = numpy.where(self.array_1D_2 == 66) ind3D = array_indices(array=self.array_3D.shape, index=wh[0], dimensions=True) test_sum = numpy.sum(self.array_3D[ind3D]) self.assertEqual(control_sum, test_sum)
def getSegmentLocations(self, segmentID=1): """ Retrieve the pixel locations corresponding to a segmentID. """ ri = self.ri i = segmentID if ri[i+1] > ri[i]: idx = ri[ri[i]:ri[i+1]] idx = array_indices(self.dims, idx, dimensions=True) else: idx = (numpy.array([]), numpy.array([])) return idx
def obj_centroid(array): """ Calculates centroids per object. """ dims = array.shape h = histogram(array.flatten(), Min=1, reverse_indices='ri') hist = h['histogram'] ri = h['ri'] cent = [] for i in numpy.arange(hist.shape[0]): if (hist[i] == 0): continue idx = numpy.array(array_indices(dims, ri[ri[i]:ri[i+1]], dimensions=True)) cent_i = numpy.mean(idx, axis=1) cent.append(cent_i) return cent
def obj_rectangularity(array): """ Calculates rectangularity per object. """ dims = array.shape h = histogram(array.flatten(), Min=1, reverse_indices='ri') hist = h['histogram'] ri = h['ri'] rect = [] for i in numpy.arange(hist.shape[0]): if (hist[i] == 0): continue idx = numpy.array(array_indices(dims, ri[ri[i]:ri[i+1]], dimensions=True)) min_yx = numpy.min(idx, axis=1) max_yx = numpy.max(idx, axis=1) diff = max_yx - min_yx + 1 # Add one to account for zero based index bbox_area = numpy.prod(diff) rect.append(hist[i] / bbox_area) return rect
# Create the mask via the thresholds mask = (array >= lower) & (array <= upper) # The label function segments the image into contiguous blobs label_array, num_labels = ndimage.label(mask, structure=s) # Find the labels associated with the ROI labels = label_array[ROIPixels] mx_lab = numpy.max(labels) # Find unique labels, excluding zero (background) ulabels = (numpy.unique(labels[labels > 0]) ).tolist() # Convert to list; Makes for neater indexing # Generate a histogram to find the label locations h = histogram(label_array.flatten(), min=0, max=mx_lab, reverse_indices='ri') hist = h['histogram'] ri = h['ri'] for lab in ulabels: if hist[lab] == 0: continue idx.extend(ri[ri[lab]:ri[lab + 1]]) idx = numpy.array(idx) idx = array_indices(dims, idx, dimensions=True) return idx
def obj_get_boundary_method1(labelled_array, fill_holes=True): """ Get the pixels that mark the object boundary/perimeter. Method 1. 8 neighbourhood chain code 5 6 7 4 . 0 3 2 1 4 neighbourhood chain code . 3 . 2 . 0 . 1 . """ dims = labelled_array.shape rows = dims[0] cols = dims[1] if fill_holes: orig_binary = (labelled_array > 0).astype('uint8') fill = obj_fill_holes(labelled_array) s = [[1,1,1],[1,1,1],[1,1,1]] labelled_array, nlabels = ndimage.label(fill, structure=s) # We'll opt for the perimeter co-ordinates to be ordered in a clockwise fashion. GIS convention??? pix_directions = numpy.array([[ 0, 1], [ 1, 1], [ 1, 0], [ 1,-1], [ 0,-1], [-1,-1], [-1, 0], [-1, 1]]) # Set up the distances as we traverse across a pixel diag = numpy.sqrt(2.0) # NumPy will return a float64, but just in case future versions change.... pix_distances = {0 : 1.0, 1 : diag, 2 : 1.0, 3 : diag, 4 : 1.0, 5 : diag, 6 : 1.0, 7, diag } # Determine the co-ordinates (indices) of each segement # The first index of each segment will be used to define the start and end of a boundary/perimeter h = histogram(labelled_array.flatten(), Min=1, reverse_indices='ri') hist = h['histogram'] ri = h['ri'] nlabels = hist.shape[0] seg_start_idxs = numpy.zeros(nlabels, dtype='int') # Boundary or perimeter ?? Will go with perimeter, akin to a method implement earlier which uses a # convolution operator to determine perimeter length. # Obtain the start indices of each segment/object for i in numpy.arange(nlabels): #if (hist[i] == 0): # The labeled array should be consecutive # continue seg_start_idxs[i] = ri[ri[i]:ri[i+1]][0] # Return the first index # Convert the 1D indices to 2D indices used by NumPy seg_start_idxs = array_indices(dims, seg_start_idxs, dimensions=True) # Lots to figure out here. Dealing with 'from' and 'too' directions can make things confusing # Keep track of the direction we last travelled, that way we can start at the next clockwise direction # For single pixel objects or 'islands' use the histogram value to skip the follow boundary/search routine """ The memory of the order of the array should be 'C-Style': column, column, column, then next row. eg a 2x5 array 0, 1, 2, 3, 4 5, 6, 7, 8, 9 Therefore the first index will only have labels to the right and below (below left, below right). eg an object with a label ID of 6 0, 0, 6, 6, 6 6, 6, 6, 6, 6 As such, the first direction travelled will be to the right '0' in the freeman chain, and the first index will be the first boundary co-ordinate and will be the final co-ordinate to close the boundary thereby creating a polygon. As for linear features... """ # Probably deal with these inside the boundary tracking routine # Let the tracking routine handle to/from and just return the final result to_ = 0 from_ = 4 perimeter_info = {} for i in range(hist.shape[0]): if hist[i] == 0: continue if hist[i] == 1: # What is the perimeter of a single pixel, 0.0, 4.0??? #perimeter_co_ords[i] = seg_start_idxs[i[0],i[1]] # Still need to design the function and how to return the result continue idx = (seg_start_idxs[0][i], seg_start_idxs[1][i]) label = i + 1 perimeter_info[i] = track_object_boundary(labelled_array, start_index=idx, label_id=label) # Might need to format the perimeter_info dictionary before returning, ie turn the co-ords into numpy arrays. # Or even into a polygon object using the shapely library??? # Using shapely might be easier to report geometrical attributes # Still need to deal with holes within an object as ENVI does. They will increase an objects perimeter length. # SciPy have a binary_fill_holes function. label the filled array, then get the indices, and then retrive only those indices # for each object that are 0 in the original array. # That might be one way to do it, which means re-writing the above function....ughhh :) #!!!!This isn't the correct place for the handling of object holes, but just get the rough structure out!!! if fill_holes: for i in range(hist.shape[0]): if hist[i] == 0: continue if hist[i] == 1: # What is the perimeter of a single pixel, 0.0, 4.0??? continue idx = (seg_start_idxs[0][i], seg_start_idxs[1][i]) label = i + 1 perimeter_info[i] = track_object_boundary(labelled_array, start_index=idx, label_id=label) # Can we trust that the labelling of the filled and unfilled arrays will give the same object index?? # If we can we could use the area differences to determine if there are holes and only go through the # hole perimeter tracking if needed. single_object = numpy.zeros((rows*cols), dtype='uint8') single_object[ri[ri[i]:ri[i+1]]] = 1 holes = numexpr.evaluate("(single_object - orig_binary) == 1") labs, nlabs = ndimage.label(holes, s) h_holes = histogram(labs, Min=1, reverse_indices='ri') hist_holes = h_holes['histogram'] ri_h = h_holes['ri'] seg_holes_start_idxs = numpy.zeros(nlabs, dtype='int') for j in numpy.arange(nlabels): #if (hist[i] == 0): # The labeled array should be consecutive # continue seg_holes_start_idxs[j] = ri_h[ri_h[j]:ri_h[j+1]][0] # Return the first index # Convert the 1D indices to 2D indices used by NumPy seg_holes_start_idxs = array_indices(dims, seg_holes_start_idxs, dimensions=True) for k in range(hist_holes.shape): if hist_holes[k] == 0: continue if hist[i] == 1: # What is the perimeter of a single pixel, 0.0, 4.0??? continue idx = (seg_holes_start_idxs[0][k], seg_holes_start_idxs[1][k]) holes_label = k + 1 holes_result = track_object_boundary(labs, start_index=idx, label_id=holes_label) perimeter_info[i]['Holes'] = holes_result['Vertices'] perimeter_info[i]['Perimeter_Length'] += perimeter_info['Perimeter_Length'] return perimeter_info