def initialize_image(self): # Creates a local composite of the original image data for display if self.parent.data.im_type == 'wv02_ms': self.display_image = utils.create_composite([ self.parent.data.original_image[4, :, :], self.parent.data.original_image[2, :, :], self.parent.data.original_image[1, :, :] ], dtype=np.uint8) elif self.parent.data.im_type == 'pan': self.display_image = utils.create_composite([ self.parent.data.original_image, self.parent.data.original_image, self.parent.data.original_image ], dtype=np.uint8) elif self.parent.data.im_type == 'srgb': self.display_image = utils.create_composite([ self.parent.data.original_image[0, :, :], self.parent.data.original_image[1, :, :], self.parent.data.original_image[2, :, :] ], dtype=np.uint8) self.disp_xdim, self.disp_ydim = np.shape(self.display_image)[0:2]
def save_color_image(image_data, output_name, image_type, block_cols, block_rows): """ Write a rgb color image (as png) of the raw image data to disk. """ holder = [] # Find the appropriate bands to use for an rgb representation if image_type == 'wv02_ms': rgb = [5, 3, 2] elif image_type == 'srgb': rgb = [1, 2, 3] else: rgb = [1, 1, 1] red_band = image_data[rgb[0]] green_band = image_data[rgb[1]] blue_band = image_data[rgb[2]] for i in range(len(red_band)): holder.append( utils.create_composite([red_band[i], green_band[i], blue_band[i]])) colorfullimg = utils.compile_subimages(holder, block_cols, block_rows, 3) mimg.imsave(output_name, colorfullimg) colorfullimg = None
def display_watershed(original_data, watershed_data, block=5): # block = 5 watershed = watershed_data[block] original_1 = original_data[6][block] original_2 = original_data[4][block] original_3 = original_data[1][block] # randcolor = colors.ListedColormap(np.random.rand(256,3)) ws_bound = segmentation.find_boundaries(watershed) ws_display = utils.create_composite([original_1, original_2, original_3]) ws_display[:, :, 0][ws_bound] = 240 ws_display[:, :, 1][ws_bound] = 80 ws_display[:, :, 2][ws_bound] = 80 display_im = utils.create_composite([original_1, original_2, original_3]) fig, axes = plt.subplots(1, 2, subplot_kw={'xticks': [], 'yticks': []}) fig.subplots_adjust(hspace=0.3, wspace=0.05) # axes[1].imshow(self.sobel_image,interpolation='none',cmap='gray') axes[0].imshow(display_im, interpolation='none') axes[1].imshow(ws_display, interpolation='none') plt.show()
def watershed_transformation(image_data, sobel_threshold, amplification_factor): ''' Runs a watershed transform on the main dataset 1. Create a gradient image using the sobel algorithm 2. Adjust the gradient image based on given threshold and amplification. 3. Find the local minimum gradient values and place a marker 4. Construct watersheds on top of the gradient image starting at the markers. 5. Recombine neighboring image segments using a region adjacency graph. ''' # If this block has no data, return a placeholder watershed. if np.amax(image_data[0]) <= 1: return np.zeros(np.shape(image_data)) # Create a gradient image using a sobel filter sobel_image = filters.sobel(image_data[2]) # Adjust the sobel image based on the given threshold and amp factor. upper_threshold = np.amax(sobel_image) / amplification_factor if upper_threshold < 0.20: upper_threshold = 0.20 sobel_image = exposure.rescale_intensity(sobel_image, in_range=(0, upper_threshold), out_range=(0, 1)) # Prevent the watersheds from 'leaking' along the sides of the image sobel_image[:, 0] = 1 sobel_image[:, -1] = 1 sobel_image[0, :] = 1 sobel_image[-1, :] = 1 # Set all values in the sobel image that are lower than the # given threshold to zero. sobel_image[sobel_image <= sobel_threshold] = 0 #sobel_copy = np.copy(sobel_image) # Find local minimum values in the sobel image by inverting # sobel_image and finding the local maximum values inv_sobel = 1 - sobel_image local_min = feature.peak_local_max(inv_sobel, min_distance=2, indices=False, num_peaks_per_label=1) markers = ndimage.label(local_min)[0] # Build a watershed from the markers on top of the edge image im_watersheds = morphology.watershed(sobel_image, markers) im_watersheds = np.array(im_watersheds, dtype='uint16') # Clear gradient image data sobel_image = None # Find the locations that contain no spectral data empty_pixels = np.zeros(np.shape(image_data[0]), dtype='bool') empty_pixels[image_data[2] == 0] = True # Set all values outside of the image area (empty pixels, usually caused by # orthorectification) to one value, at the end of the watershed list. im_watersheds[empty_pixels] = np.amax(im_watersheds) + 1 # Recombine segments that are adjacent and similar to each other. # Created a region adjacency graph. Create_composite() takes a single list # of bands color_im = utils.create_composite( [image_data[0], image_data[1], image_data[2]]) # Clear image data image_data = None # Create the region adjacency graph based on the color image try: im_graph = graph.rag_mean_color(color_im, im_watersheds) except KeyError: pass else: # Clear color image data color_im = None # Combine segments that are adjacent and whose pixel intensity # difference is less than 10. im_watersheds = graph.cut_threshold(im_watersheds, im_graph, 5.0) return im_watersheds
def mode_one(segment_list, label_vector, feature_matrix, input_directory, im_type, tds_filename): ## Build a list of the images in the input directory # for each image in the directory, image list contains the "..._segmented.h5" file if it exists, # and the raw image file if it does not. # If there is an existing segment list, note the required image to continue the # training set if segment_list: required_images = get_required_images(segment_list[len(label_vector):]) else: required_images = [] # Add the images in the provided folder to the image list seg_list = list(utils.get_image_paths(input_directory, keyword='segmented.h5', strict=False)) for ext in utils.valid_extensions: raw_list = utils.get_image_paths(input_directory, keyword=ext) for raw_im in raw_list: if os.path.splitext(raw_im)[0] + "_segmented.h5" not in seg_list: seg_list.append(raw_im) # Save only the unique entries image_list = list(set(seg_list)) utils.remove_hidden(image_list) # Make sure we have all of the required images for image in required_images: if image in image_list: print "Missing required image: {}".format(image) quit() # **************************************** # # For expanding icebridge training set 9.18.18 target_images = ["2016_07_19_01052.JPG", "2017_07_25_01613.JPG", "2017_07_25_01772.JPG", "2017_07_25_01783.JPG", "2017_07_25_06767.JPG"] # **************************************** # # As long as there were files in the input directory, loop indefinitely # This loop breaks when the training GUI is closed. while image_list: # Cycle through each image in the list for next_image in image_list: # If we are out of predetermined segments to classify, start picking # images from those in the directory provided if len(segment_list) == len(label_vector): image_name = os.path.split(next_image)[1] # if image_name not in target_images: # print("Skipping {}".format(image_name)) # continue # Otherwise find the image name from the next unlabeled segment else: image_name = segment_list[len(label_vector)][0] # Convert the image name into the segmented name. This file must # exist to work from an existing tds. That is, we cant resegment the # image, because it will give different results and segment ids will not match. image_name = os.path.splitext(image_name)[0] + "_segmented.h5" # image_root, image_ext = os.path.splitext(image_name) print("Working on image: {}".format(image_name)) # If the image is already segmented if image_name.split('_')[-1] == 'segmented.h5': segmented_name = image_name # Otherwise image_name should point to a raw image else: print("Creating segments on provided image...") # Preprocess next_image data image_data, meta_data = prepare_image(input_directory, image_name, im_type, output_path=input_directory, verbose=True) # Create image segments segmented_name = os.path.splitext(image_name)[0] + '_segmented.h5' seg_path = os.path.join(input_directory, segmented_name) segment_image(image_data, image_type=im_type, write_results=True, dst_file=seg_path, verbose=True) # Convert this entry in image_list to the "..._segmented.h5" name image_list.append(segmented_name) image_list.remove(next_image) h5_file = os.path.join(input_directory, segmented_name) # from segment import load_from_disk original_image = [] original_image_dict, im_type = load_from_disk(h5_file, False) for sub_image in range(len(original_image_dict[1])): # Create a list of image blocks based on the number of bands # in the input image. # Need to verify this for pan images (ncw 9.17.18): if im_type == 'pan': original_image.append(original_image_dict[1]) if im_type == 'wv02_ms': original_image.append( utils.create_composite([original_image_dict[i][sub_image] for i in range(1,9)], dtype=c_int) ) if im_type == 'srgb': original_image.append( utils.create_composite([original_image_dict[i][sub_image] for i in range(1,4)], dtype=c_int) ) with h5py.File(h5_file, 'r') as f: im_date = f.attrs.get("Image Date") watershed_image = f['watershed'][:] # Convert the segmented image to c_int datatype. This is needed for the # Cython methods that calculate attribute of segments. watershed_image = np.ndarray.astype(watershed_image, c_int) #### Initializing the GUI tW = TrainingWindow(original_image, watershed_image, segment_list, image_name, label_vector, feature_matrix, im_type, im_date, 1, tds_filename)
image_list.remove(next_image) h5_file = os.path.join(input_path, segmented_name) # from segment import load_from_disk original_image = [] original_image_dict, im_type = load_from_disk(h5_file, False) for sub_image in range(len(original_image_dict[1])): # Create a list of image blocks based on the number of bands # in the input image. if im_type == 'wv02_ms': original_image.append( utils.create_composite([ original_image_dict[1][sub_image], original_image_dict[2][sub_image], original_image_dict[3][sub_image], original_image_dict[4][sub_image], original_image_dict[5][sub_image], original_image_dict[6][sub_image], original_image_dict[7][sub_image], original_image_dict[8][sub_image] ])) if im_type == 'srgb': original_image.append( utils.create_composite([ original_image_dict[1][sub_image], original_image_dict[2][sub_image], original_image_dict[3][sub_image] ])) # original_image = utils.create_composite([original_image[1], # original_image[2], # original_image[3]])
def display_image(raw, watershed, classified, type): # Save a color empty_color = [.1, .1, .1] #Almost black snow_color = [.9, .9, .9] #Almost white pond_color = [.31, .431, .647] #Blue gray_color = [.65, .65, .65] #Gray water_color = [0., 0., 0.] #Black shadow_color = [.100, .545, .0] #Orange custom_colormap = [ empty_color, snow_color, gray_color, pond_color, water_color, shadow_color ] custom_colormap = colors.ListedColormap(custom_colormap) #Making sure there is atleast one of every pixel so the colors map properly (only changes # display image, not saved data) classified[0][0] = 0 classified[1][0] = 1 classified[2][0] = 2 classified[3][0] = 3 classified[4][0] = 4 classified[5][0] = 5 # Figure that show 3 images: raw, segmented, and classified if type == 1: # Creating the watershed display image with borders highlighted ws_bound = segmentation.find_boundaries(watershed) ws_display = utils.create_composite([raw, raw, raw]) ws_display[:, :, 0][ws_bound] = 255 ws_display[:, :, 1][ws_bound] = 255 ws_display[:, :, 2][ws_bound] = 22 fig, axes = plt.subplots(1, 3, subplot_kw={'xticks': [], 'yticks': []}) fig.subplots_adjust(left=0.05, right=0.99, bottom=0.05, top=0.90, wspace=0.02, hspace=0.2) tnrfont = {'fontname': 'Times New Roman'} axes[0].imshow(raw, cmap='gray', interpolation='None') axes[0].set_title("Raw Image", **tnrfont) axes[1].imshow(ws_display, interpolation='None') axes[1].set_title("Image Segments", **tnrfont) axes[2].imshow(classified, cmap=custom_colormap, interpolation='None') axes[2].set_title("Classification Output", **tnrfont) # Figure that shows 2 images: raw and classified. if type == 2: fig, axes = plt.subplots(1, 2, subplot_kw={'xticks': [], 'yticks': []}) fig.subplots_adjust(hspace=0.3, wspace=0.05) axes[0].imshow(raw, interpolation='None') axes[0].set_title("Raw Image") axes[1].imshow(classified, cmap=custom_colormap, interpolation='None') axes[1].set_title("Classification Output") plt.show()
def classify_image(input_image, watershed_data, training_dataset, meta_data, threads=1, quality_control=False, verbose=False): ''' Run a random forest classification. Input: input_image: preprocessed image data (preprocess.py) watershed_image: Image objects created with the segmentation algorithm. (segment.py) training_dataset: Tuple of training data in the form: (label_vector, attribute_matrix) meta_data: [im_type, im_date] Returns: Raster of classified data. ''' #### Prepare Data and Variables num_blocks = len(input_image[1]) num_bands = len(input_image.keys()) image_type = meta_data[0] image_date = meta_data[1] ## Restructure the input data. # We are creating a single list where each element of the list is one # block (old: subimage) of the image and is a stack of all bands. image_data = [] # [block:row:column:band] for blk in range(num_blocks): image_data.append(utils.create_composite( [input_image[b][blk] for b in range(1,num_bands+1)])) input_image = None ## Parse training_dataset input label_vector = training_dataset[0] training_feature_matrix = training_dataset[1] # Method for assessing the quality of the training dataset. # quality_control = True # if quality_control == True: # debug_tools.test_training(label_vector, training_feature_matrix) # aa = raw_input("Continue? ") # if aa == 'n': # quit() #### Construct the random forest decision tree using the training data set rfc = RandomForestClassifier(n_estimators=100) rfc.fit(training_feature_matrix, label_vector) #### Classify each image block # Define multiprocessing-safe queues containing data to process clsf_block_queue = Queue() num_blocks = len(watershed_data) im_block_queue = construct_block_queue(image_data, watershed_data, num_blocks) # Define the number of threads to create NUMBER_OF_PROCESSES = threads block_procs = [Process(target=process_block_helper, args=(im_block_queue, clsf_block_queue, image_type, image_date, rfc)) for _ in range(NUMBER_OF_PROCESSES)] # Start the worker processes. for proc in block_procs: # Add a stop command to the end of the queue for each of the # processes started. This will signal for the process to stop. im_block_queue.put('STOP') # Start the process proc.start() # Display a progress bar if verbose: try: from tqdm import tqdm except ImportError: print "Install tqdm to display progress bar." verbose = False else: pbar = tqdm(total=num_blocks, unit='block') # Each process adds the classification results to clsf_block_queue, when it # finishes a row. Adds 'None' when there are not more rows left # in the queue. # This loop continues as long as all of the processes have not finished # (i.e. fewer than NUMBER_OF_PROCESSES have returned None). When a row is # added to the queue, the tqdm progress bar updates. # Initialize the output dataset as an empty list of length = input dataset # This needs to be initialized since blocks will be added non-sequentially clsf_block_list = [None for _ in range(num_blocks)] finished_threads = 0 while finished_threads < NUMBER_OF_PROCESSES: if not clsf_block_queue.empty(): val = clsf_block_queue.get() if val == None: finished_threads += 1 else: block_num = val[0] segmnt_data = val[1] clsf_block_list[block_num] = segmnt_data if verbose: pbar.update() # Close the progress bar if verbose: pbar.close() print "Finished Processing. Closing threads..." # Join all of the processes back together for proc in block_procs: proc.join() return clsf_block_list
def classify_image(input_image, watershed_data, training_dataset, meta_data, threads=2, quality_control=False, debug_flag=False, verbose=False): ''' Run a random forest classification. Input: input_image: preprocessed image data (preprocess.py) watershed_image: Image objects created with the segmentation algorithm. (segment.py) training_dataset: Tuple of training data in the form: (label_vector, attribute_matrix) meta_data: [im_type, im_date] Returns: Raster of classified data. ''' #### Prepare Data and Variables num_blocks = len(input_image[1]) num_bands = len(input_image.keys()) image_type = meta_data[0] image_date = meta_data[1] ## Restructure the input data. # We are creating a single list where each element of the list is one # block (old: subimage) of the image and is a stack of all bands. image_data = [] # [block:row:column:band] for blk in range(num_blocks): image_data.append( utils.create_composite( [input_image[b][blk] for b in range(1, num_bands + 1)])) input_image = None # watershed_image = input_file['watershed'][:] # watershed_dimensions = input_file['dimensions'][:] # num_x_subimages = dimensions[0] # num_y_subimages = dimensions[1] ## Parse training_dataset input label_vector = training_dataset[0] training_feature_matrix = training_dataset[1] # im_type = input_file.attrs.get('Image Type') # im_date = input_file.attrs.get('Image Date') #Method for assessing the quality of the training dataset. if quality_control == True: test_training(label_vector, training_feature_matrix) aa = raw_input("Continue? ") if aa == 'n': quit() # # If there is no information in this image file, save a dummy classified image and exit # # This can often happen depending on the original image dimensions and the amount it was split # if np.sum(band_1) == 0: # classified_image_path = os.path.join(output_filepath, output_filename + '_classified_image.png') # outfile = h5py.File(os.path.join(output_filepath, output_filename + '_classified.h5'),'w') # if im_type == 'wv02_ms': # empty_bands = np.zeros(np.shape(band_1)[0],np.shape(band_1)[1],8) # empty_image = utils.compile_subimages(empty_bands, num_x_subimages, num_y_subimages, 8) # elif im_type == 'srgb': # empty_bands = np.zeros(np.shape(band_1)[0],np.shape(band_1)[1],3) # empty_image = utils.compile_subimages(empty_bands, num_x_subimages, num_y_subimages, 3) # elif im_type == 'pan': # empty_image = np.zeros(np.shape(band_1)) # outfile.create_dataset('classified', data=empty_image,compression='gzip',compression_opts=9) # outfile.create_dataset('original', data=empty_image,compression='gzip',compression_opts=9) # outfile.close() # # return a 1x5 array with values of one for the pixel counts # return output_filename, np.ones(5) #### Construct the random forest decision tree using the training data set rfc = RandomForestClassifier() rfc.fit(training_feature_matrix, label_vector) #### Classify each image block # Define multiprocessing-safe queues containing data to process clsf_block_queue = Queue() num_blocks = len(watershed_data) im_block_queue = construct_block_queue(image_data, watershed_data, num_blocks) # Define the number of threads to create NUMBER_OF_PROCESSES = threads block_procs = [ Process(target=process_block_helper, args=(im_block_queue, clsf_block_queue, image_type, image_date, rfc)) for _ in range(NUMBER_OF_PROCESSES) ] # Start the worker processes. for proc in block_procs: # Add a stop command to the end of the queue for each of the # processes started. This will signal for the process to stop. im_block_queue.put('STOP') # Start the process proc.start() # Display a progress bar if verbose: try: from tqdm import tqdm except ImportError: print "Install tqdm to display progress bar." verbose = False else: pbar = tqdm(total=num_blocks, unit='block') # Each process adds the classification results to clsf_block_queue, when it # finishes a row. Adds 'None' when there are not more rows left # in the queue. # This loop continues as long as all of the processes have not finished # (i.e. fewer than NUMBER_OF_PROCESSES have returned None). When a row is # added to the queue, the tqdm progress bar updates. # Initialize the output dataset as an empty list of length = input dataset # This needs to be initialized since blocks will be added non-sequentially clsf_block_list = [None for _ in range(num_blocks)] finished_threads = 0 while finished_threads < NUMBER_OF_PROCESSES: if not clsf_block_queue.empty(): val = clsf_block_queue.get() if val == None: finished_threads += 1 else: block_num = val[0] segmnt_data = val[1] clsf_block_list[block_num] = segmnt_data if verbose: pbar.update() # Close the progress bar if verbose: pbar.close() print "Finished Processing. Closing threads..." # Join all of the processes back together for proc in block_procs: proc.join() return clsf_block_list # Lite version: Save only the classified output, and do not save the original image data compiled_classified = utils.compile_subimages(classified_image, num_x_subimages, num_y_subimages, 1) if verbose: print "Saving..." with h5py.File( os.path.join(output_filepath, output_filename + '_classified.h5'), 'w') as outfile: outfile.create_dataset('classified', data=compiled_classified, compression='gzip', compression_opts=9) #### Count the number of pixels that were in each classification category. sum_snow, sum_gray_ice, sum_melt_ponds, sum_open_water, sum_shadow = utils.count_features( compiled_classified) pixel_counts = [ sum_snow, sum_gray_ice, sum_melt_ponds, sum_open_water, sum_shadow ] # Clear the image datasets from memory compiled_classified = None input_image = None watershed_image = None cur_image = None cur_ws = None entropy_image = None if verbose: print "Done." return output_filename, pixel_counts