class InteractionWindow(Canvas): def __init__(self, master, _ndpi_file): self.master = master # For WBC-hunt self.hunter = False # Call the super constructor Canvas.__init__(self, master) #self.canvas = Canvas(self, master) # Zoom or roi mode (defaults to roi) self.mode = "roi" # Setup the input file self.ndpi_file = _ndpi_file self.im = None # Actual PhotoImage object of the image self.rgba_im = None # This is needed as a kept reference so that we can resize image on user action self.current_level = None # Scale space level currently used for viewing (for handling zoom etc) self.image_handle = None # Used to delete and recreate images self.setup_image() # Stuff for box selection self.init_box_pos = None # In WINDOW coordinates self.curr_box_bbox = None # Also in current WINDOW coordinates self.last_selection_region = None # This is in level 0 coordinates self.zoom_level = 0 # How many times have we zoomed? self.zoom_region = [] # Stack of regions in level 0 coords that tells us where the zoom is, needed for transforming # Since we want multiple ROIs, save them in this list self.roi_list = [] # Contains Triple: (ROInumber, ROI_imagedata, (bbox, [sub_rois_list])) self.roi_counter = 0 self.progress = Progressbar(self, orient="horizontal", length=100, mode="determinate", value=0) # ProgressBar to show user what is going on. Only one active at a time # Bind some event happenings to appropriate methods self.bind('<Configure>', self.resize_image) self.bind('<B1-Motion>', self.select_box) self.bind('<Button-1>', self.set_init_box_pos) self.bind('<ButtonRelease-1>', self.set_box) self.bind('<Button-3>', self.zoom_out) self.bind('<Left>', self.move_up) # DEBUG self.counter = 0 # Handles initial setup of the input image def setup_image(self): # Select best scale space level and get its size # First get current canvas size, to determine the downsampling factor' self.master.update() # Redraw screen so that the sizes are correct factor = float(self.ndpi_file.level_dimensions[0][0])/float(self.winfo_width()) self.current_level = self.ndpi_file.get_best_level_for_downsample(factor) self.rgba_im = self.ndpi_file.read_region((0, 0), self.current_level, self.ndpi_file.level_dimensions[self.current_level]) self.rgba_im = self.rgba_im.resize((self.winfo_width(), self.winfo_height())) # Use TKs PhotoImage to show the image (needed for selecting ROIs) self.im = itk.PhotoImage(self.rgba_im) self.image_handle = self.create_image(0, 0, image=self.im, anchor=NW) # Whenever all ROIs and subROIs need to be redrawn, call this def redraw_ROI(self): # Start with drawing all main (red) rois for num, roi_container, bbox_container in self.roi_list: sub_rois = bbox_container[1] bbox = bbox_container[0] if len(sub_rois) is not 0: for sub_roi in sub_rois: self.draw_rectangle(sub_roi, "green", 5, "subroi" + str(num)) self.draw_rectangle(bbox, "red", 4, "roi"+str(num)) # Method that ensures automatic resize in case the windows is resized by the user def resize_image(self, event): try: new_width = event.width new_height = event.height except AttributeError: #print "Attribute error in resize. Trying tuple variant. You are most probably in zoom mode so don't worry." new_width = event[0] new_height = event[1] self.master.update() # Redraw screen so that the sizes are correct if self.last_selection_region is not None: factor = float(self.last_selection_region[1][1])/float(self.winfo_width()) else: factor = float(self.ndpi_file.level_dimensions[0][0])/float(self.winfo_width()) print "Factor: " + str(factor) self.current_level = self.ndpi_file.get_best_level_for_downsample(factor) print "Current level: " + str(self.current_level) # We have to make sure that we only show the zoomed-in area if self.last_selection_region is not None:# and self.mode is "zoom": self.render_status_text((100, 100), "Zooming...", 0, 50) # Need to transform l0 coordinates to coordinates of current_level, but only width and height! width, height = self.transform_to_arb_level(self.last_selection_region[1][0], self.last_selection_region[1][1], self.current_level) self.rgba_im = self.ndpi_file.read_region(self.last_selection_region[0], # The x and y needs to be in l0... self.current_level, (int(width), int(height))) else: self.rgba_im = self.ndpi_file.read_region((0, 0), self.current_level, self.ndpi_file.level_dimensions[self.current_level]) self.rgba_im = self.rgba_im.resize((new_width, new_height)) self.im = itk.PhotoImage(self.rgba_im) self.delete(self.image_handle) self.create_image(0, 0, image=self.im, anchor=NW) self.clear_status_text() def set_init_box_pos(self, event): # If we are in clear mode, make sure that we handle it correctly. if self.mode is "clear": # First transform the coordinates to level 0 (mouse_x, mouse_y) = self.transform_to_level_zero(event.x, event.y) # Loop through roi_list and see if we clicked on one of them for num, roi, bbox_container in self.roi_list: bbox = bbox_container[0] if bbox[0][0] < mouse_x < bbox[0][0]+bbox[1][0]: if bbox[0][1] < mouse_y < bbox[0][1]+bbox[1][1]: # Found a ROI! self.delete("roi"+str(num)) self.delete("boxselector") self.delete("subroi"+str(num)) self.roi_list.remove((num, roi, bbox_container)) break else: self.init_box_pos = (event.x, event.y) # From level 0 to arbitrary level coordinates def transform_to_arb_level(self, x_coord, y_coord, to_level): # We know that x and y are in level 0. factor = self.ndpi_file.level_downsamples[to_level] return float(x_coord)/factor, float(y_coord)/factor # From window coordinates to level 0 coordinates def transform_to_level_zero(self, x_coord, y_coord): x_percent = float(x_coord)/self.winfo_width() y_percent = float(y_coord)/self.winfo_height() if self.zoom_level is 0: ld = self.ndpi_file.level_dimensions[0] return x_percent*ld[0], y_percent*ld[1] else: ld = self.zoom_region[-1] return x_percent*ld[1][0]+ld[0][0], y_percent*ld[1][1]+ld[0][1] # Member function for clearing all ROI def clear_all_roi(self): for num, roi_container, bbox_container in self.roi_list: sub_rois = bbox_container[1] if len(sub_rois) is not 0: self.delete("subroi" + str(num)) self.delete("roi"+str(num)) self.roi_list = [] self.roi_counter = 0 self.delete("boxselector") def select_box(self, event): bbox = (event.x, event.y) self.curr_box_bbox = (self.init_box_pos, bbox) self.delete("boxselector") # Depending on the mode, we get different colors on the box if self.mode is "roi": width = self.curr_box_bbox[1][0] height = self.curr_box_bbox[1][1] topx = self.curr_box_bbox[0][0] topy = self.curr_box_bbox[0][1] l0_width, l0_height = self.transform_to_level_zero(width, height) l0_topx, l0_topy = self.transform_to_level_zero(topx, topy) # Get absolute pixel differences l0_width = abs(l0_width - l0_topx) l0_height = abs(l0_height - l0_topy) if (l0_width > 1000) or (l0_height > 1000): self.create_rectangle(self.curr_box_bbox, outline="red", tags="boxselector") else: self.create_rectangle(self.curr_box_bbox, outline="yellow", tags="boxselector") elif self.mode is "zoom": self.create_rectangle(self.curr_box_bbox, outline="green", tags="boxselector") # Lets us render a status bar, letting users know how far something has gotten def render_status_text(self, position, text, percentage, length): percentage = int(percentage*100.0) x = position[0] y = position[1] self.progress["length"] = length self.progress["value"] = percentage self.progress.place(x=x, y=y) self.create_text(x, y, text=text, anchor=SW, font=("Purisa", 16), tags="progresstext", fill="white") self.master.update() def clear_status_text(self): self.progress.place_forget() self.delete("progresstext") self.master.update() def set_box(self, event): # This is executed when the user has dragged a selection box (either zoom or ROI) and he/she has now let go of # the mouse. Our goal here is to get the level 0 coordinates of this box and then pass this box on depending # on what we want to do. width = self.curr_box_bbox[1][0] height = self.curr_box_bbox[1][1] topx = self.curr_box_bbox[0][0] topy = self.curr_box_bbox[0][1] l0_width, l0_height = self.transform_to_level_zero(width, height) l0_topx, l0_topy = self.transform_to_level_zero(topx, topy) # Get absolute pixel differences l0_width = abs(l0_width - l0_topx) l0_height = abs(l0_height - l0_topy) # We need to be able to access this region in the resize function self.last_selection_region = [(int(l0_topx), int(l0_topy)), (int(l0_width), int(l0_height))] #entire_roi = numpy.array(self.ndpi_file.read_region((int(l0_topx), int(l0_topy)), 0, (int(l0_width), int(l0_height))), dtype=numpy.uint8) # Multiply percentages of totals and transform to high res level # ------------------NEW---------------- # Code below is ugly, might be able to reduce it to single double loop # Check if the ROI is too big, in that case split it and put into list box = [] sub_rois = [] # Needed for drawing the subrois later on if not self.hunter: """ if self.mode is "roi" and (l0_width > 1000 or l0_height > 1000): fixed_size = 500.0 no_of_x_subrois = math.floor(float(l0_width)/float(fixed_size)) no_of_y_subrois = math.floor(float(l0_height)/float(fixed_size)) total = (no_of_x_subrois+1.0)*(no_of_y_subrois+1.0) # The total number of subrois needed to be done counter = 0 # Used for knowing how far we've gotten in the loops for curr_x in range(int(no_of_x_subrois)): for curr_y in range(int(no_of_y_subrois)): curr_topx = l0_topx + fixed_size*float(curr_x) curr_topy = l0_topy + fixed_size*float(curr_y) box.append(numpy.array(self.ndpi_file.read_region((int(curr_topx), int(curr_topy)), 0, (int(fixed_size), int(fixed_size))), dtype=numpy.uint8)) # For now, just print boxes to show where we cut it roi = [(int(curr_topx), int(curr_topy)), (int(fixed_size), int(fixed_size))] sub_rois.append(roi) # Render a status text aswell counter += 1 self.render_status_text((topx, topy-20), "Reading data...", float(counter)/total, width-topx) # Now we need to handle the rest of the ROI that didn't fit perfectly into the fixed_size boxes # Remember, this also sort of needs to loop # Idea: Fix x or y, loop through the other one # Start with looping over x, y is fixed topy_rest = float(l0_topy) + no_of_y_subrois*fixed_size height_rest = (float(l0_topy) + float(l0_height)) - float(topy_rest) for curr_x in range(int(no_of_x_subrois)): curr_topx = l0_topx + fixed_size*float(curr_x) box.append(numpy.array(self.ndpi_file.read_region((int(curr_topx), int(topy_rest)), 0, (int(fixed_size), int(height_rest))), dtype=numpy.uint8)) roi = [(int(curr_topx), int(topy_rest)), (int(fixed_size), int(height_rest))] sub_rois.append(roi) # Render a status text aswell counter += 1 self.render_status_text((topx, topy-20), "Reading data...", float(counter)/total, width-topx) # Now loop over y and x is fixed topx_rest = float(l0_topx) + no_of_x_subrois*fixed_size width_rest = (float(l0_topx) + float(l0_width)) - float(topx_rest) for curr_y in range(int(no_of_y_subrois)): curr_topy = l0_topy + fixed_size*float(curr_y) box.append(numpy.array(self.ndpi_file.read_region((int(topx_rest), int(curr_topy)), 0, (int(width_rest), int(fixed_size))), dtype=numpy.uint8)) roi = [(int(topx_rest), int(curr_topy)), (int(width_rest), int(fixed_size))] sub_rois.append(roi) # Render a status text aswell counter += 1 self.render_status_text((topx, topy-20), "Reading data...", float(counter)/total, width-topx) # This is the last one, in the lower right corner topx_rest = float(l0_topx) + no_of_x_subrois*fixed_size topy_rest = float(l0_topy) + no_of_y_subrois*fixed_size width_rest = (float(l0_topx) + float(l0_width)) - float(topx_rest) height_rest = (float(l0_topy) + float(l0_height)) - float(topy_rest) roi = [(int(topx_rest), int(topy_rest)), (int(width_rest), int(height_rest))] sub_rois.append(roi) # Render a status text aswell counter += 1 self.render_status_text((topx, topy-20), "Reading data...", float(counter)/total, width-topx) tmp = self.ndpi_file.read_region((int(topx_rest), int(topy_rest)), 0, (int(width_rest), int(height_rest))) box.append(numpy.array(tmp, dtype=numpy.uint8)) """ # Now depending on the mode, do different things if self.mode is "roi": # This is the case that the ROI selected is small enough to be alone box.append(numpy.array(self.ndpi_file.read_region((int(l0_topx), int(l0_topy)), 0, (int(l0_width), int(l0_height))), dtype=numpy.uint8)) if self.mode is "roi": print "No of subboxes in roi you just selected: " + str(len(box)) if self.hunter: box.append(numpy.array(self.ndpi_file.read_region((int(l0_topx), int(l0_topy)), 0, (int(l0_width), int(l0_height))), dtype=numpy.uint8)) self.set_roi(box, sub_rois) elif self.mode is "zoom": self.zoom() self.delete("boxselector") self.clear_status_text() def set_roi(self, box, sub_rois): # Add the ROI to our list if not self.hunter: self.roi_list.append((self.roi_counter, box, (self.last_selection_region, sub_rois))) else: new_region = [self.last_selection_region[0], (500, 500)] self.roi_list.append((self.roi_counter, box, (new_region, sub_rois))) # Keep drawing the ROIs self.redraw_ROI() self.roi_counter += 1 #self.create_text(self.curr_box_bbox[0][0], self.curr_box_bbox[0][1], text=str(len(self.roi_list)), # anchor=SW, font=("Purisa", 16), tags="roi"+str(len(self.roi_list))) def draw_rectangle(self, level_0_coords, outline, line_width, tag): # We need to transform the level 0 coords to the current window #factor = 1.0/self.ndpi_file.level_downsamples[self.current_level] top_x = level_0_coords[0][0] top_y = level_0_coords[0][1] width = level_0_coords[1][0] height = level_0_coords[1][1] # Get the percentages if self.zoom_level is 0: ld = self.ndpi_file.level_dimensions[0] else: ld = self.zoom_region[-1][1] top_x = top_x - self.zoom_region[-1][0][0] top_y = top_y - self.zoom_region[-1][0][1] top_x_percent = float(top_x)/ld[0] top_y_percent = float(top_y)/ld[1] width_percent = float(width)/ld[0] height_percent = float(height)/ld[1] top_x_view = top_x_percent*self.winfo_width() top_y_view = top_y_percent*self.winfo_height() width_view = width_percent*self.winfo_width() height_view = height_percent*self.winfo_height() box = [(top_x_view, top_y_view), (width_view+top_x_view, height_view+top_y_view)] self.create_rectangle(box, outline=outline, tags=tag, width=line_width) def zoom(self): # Set the zoom_region in level 0 coords x, y = self.transform_to_level_zero(self.curr_box_bbox[0][0], self.curr_box_bbox[0][1]) width, height = self.transform_to_level_zero(self.curr_box_bbox[1][0], self.curr_box_bbox[1][1]) self.zoom_region.append([(int(x), int(y)), (int(width-x), int(height-y))]) self.zoom_level += 1 self.resize_image((self.winfo_width(), self.winfo_height())) # We also need to make sure that the ROIs are (visually) transformed to the new zoom level self.redraw_ROI() def move_up(self, event): print "HEEEEJ" self.move(self.image_handle, 0, 10) # Right click zooms to previous zoom level def zoom_out(self, event): if self.zoom_level is not 0: if self.zoom_level is 1: self.reset_zoom() else: # Pop one off of the stack and reduce the zoom level by 1 self.zoom_region.pop() self.zoom_level -= 1 # The selection_region is used when resizing, so simulate that we just selected a region that is further # down the zoom stack self.last_selection_region = self.zoom_region[-1] self.resize_image((self.winfo_width(), self.winfo_height())) self.redraw_ROI() def reset_zoom(self): self.last_selection_region = None self.zoom_region = [] self.zoom_level = 0 self.resize_image((self.winfo_width(), self.winfo_height())) self.redraw_ROI() def run_roi(self): if not self.hunter: # Loop through the roi list and do segmentation and classification for each roi cell_list = [] # Initial loop to get the total amount of rois to do, for the progress bar total_no_of_rois = 0 for num, rois, bbox_container in self.roi_list: for roi in rois: test = ImageShower(self, roi, "Before segmentation, roi number " + str(num)) #scipy.misc.imsave('testttttttt.png', roi) im = Image.fromarray(roi) im.save('../results/before_segmentation_roi_' + str(num) + '.png') total_no_of_rois += 1 print "Total no of rois to do: " + str(total_no_of_rois) # Counter for the progress bar counter = 0 for num, rois, bbox_container in self.roi_list: #rois = roi_container[1] for roi in rois: tmp_list, new_roi = rbc_seg.segment_and_remove_from_roi(roi) cell_list = cell_list + tmp_list # Show a segmented version of the ROI, "intermediate result" test = ImageShower(self, new_roi, "After segmentation, roi number " + str(num)) im = Image.fromarray(new_roi) im.save('../results/after_segmentation_roi_' + str(num) + '.png') # Also make a progress bar counter += 1 self.render_status_text((100, 100), "Running segmentation...", float(counter/float(total_no_of_rois)), 100) self.clear_status_text() # Call the classification if len(cell_list) is 0: show_msg("No WBC", self, "Only RBCs were detected in this/these roi/rois.") else: prediction = classer.predict_cells(cell_list) print "No of classified unknowns: " + str(len(prediction)) #numpy.save("red_shit2.npy", self.roi_list[len(self.roi_list)-1][1][0]) test = ResultDisplayer(cell_list, prediction) """ testimg = cv2.imread("../npyimages/only_whites_3.png") testimg = cv2.cvtColor(testimg, cv2.COLOR_BGR2RGB) cell_list = rbc_seg.segmentation(testimg) prediction = classer.predict_cells(cell_list) test = ResultDisplayer(cell_list, prediction) #fff = open('../only_smeared.pik', 'w+') #pickle.dump(cell_list, fff)""" if self.hunter: for num, rois, bbox_container in self.roi_list: #rois = roi_container[1] for roi in rois: numpy.save("../npyimages/c_test" + str(self.counter) + ".npy", roi) self.counter += 1