class ImageViewer(WidgetPanel, WidgetWithMetadata): _widget_list = ("image_panel", "pyplot_panel") image_panel = widget_descriptors.ImagePanelDescriptor( "image_panel") # type: ImagePanel pyplot_panel = widget_descriptors.PanelDescriptor( "pyplot_panel", PyplotImagePanel) # type: PyplotImagePanel def __init__(self, primary): """ Parameters ---------- primary : tkinter.Toplevel|tkinter.Tk """ self.root = primary self.primary_frame = basic_widgets.Frame(primary) WidgetPanel.__init__(self, self.primary_frame) WidgetWithMetadata.__init__(self, primary) self.variables = AppVariables() self.init_w_horizontal_layout() self.set_title() # define menus menubar = tkinter.Menu() # file menu filemenu = tkinter.Menu(menubar, tearoff=0) filemenu.add_command(label="Open Image", command=self.callback_select_files) filemenu.add_command(label="Open Directory", command=self.callback_select_directory) filemenu.add_separator() filemenu.add_command(label="Exit", command=self.exit) # menus for informational popups popups_menu = tkinter.Menu(menubar, tearoff=0) popups_menu.add_command(label="Metaicon", command=self.metaicon_popup) popups_menu.add_command(label="Metaviewer", command=self.metaviewer_popup) # ensure menus cascade menubar.add_cascade(label="File", menu=filemenu) menubar.add_cascade(label="Metadata", menu=popups_menu) # handle packing self.primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) primary.config(menu=menubar) # hide extraneous tool elements self.image_panel.hide_tools('shape_drawing') self.image_panel.hide_shapes() # bind canvas events for proper functionality # this makes for bad performance on a larger image - do not activate # self.image_panel.canvas.bind('<<SelectionChanged>>', self.handle_selection_change) self.image_panel.canvas.bind('<<SelectionFinalized>>', self.handle_selection_change) self.image_panel.canvas.bind('<<RemapChanged>>', self.handle_remap_change) self.image_panel.canvas.bind('<<ImageIndexChanged>>', self.handle_image_index_changed) def set_title(self): """ Sets the window title. """ file_name = None if self.variables.image_reader is None else self.variables.image_reader.file_name if file_name is None: the_title = "Image Viewer" elif isinstance(file_name, (list, tuple)): the_title = "Image Viewer, Multiple Files" else: the_title = "Image Viewer for {}".format( os.path.split(file_name)[1]) self.winfo_toplevel().title(the_title) def exit(self): self.root.destroy() # noinspection PyUnusedLocal def handle_selection_change(self, event): """ Handle a change in the selection area. Parameters ---------- event """ if self.variables.image_reader is None: return full_image_width = self.image_panel.canvas.variables.state.canvas_width fill_image_height = self.image_panel.canvas.variables.state.canvas_height self.image_panel.canvas.zoom_to_canvas_selection( (0, 0, full_image_width, fill_image_height)) self.display_canvas_rect_selection_in_pyplot_frame() # noinspection PyUnusedLocal def handle_remap_change(self, event): """ Handle that the remap for the image canvas has changed. Parameters ---------- event """ if self.variables.image_reader is not None: self.display_canvas_rect_selection_in_pyplot_frame() #noinspection PyUnusedLocal def handle_image_index_changed(self, event): """ Handle that the image index has changed. Parameters ---------- event """ self.my_populate_metaicon() def update_reader(self, the_reader): """ Update the reader. Parameters ---------- the_reader : ImageReader """ # change the tool to view self.image_panel.canvas.set_current_tool_to_view() self.image_panel.canvas.set_current_tool_to_view() # update the reader self.variables.image_reader = the_reader self.image_panel.set_image_reader(the_reader) self.set_title() # refresh appropriate GUI elements self.pyplot_panel.make_blank() self.my_populate_metaicon() self.my_populate_metaviewer() def callback_select_files(self): fnames = askopenfilenames(initialdir=self.variables.browse_directory, filetypes=common_use_collection) if fnames is None or fnames in ['', ()]: return # update the default directory for browsing self.variables.browse_directory = os.path.split(fnames[0])[0] the_reader = None if len(fnames) > 1: the_reader = ComplexImageReader(fnames) if the_reader is None: try: the_reader = ComplexImageReader(fnames[0]) except IOError: the_reader = None if the_reader is None: the_reader = DerivedImageReader(fnames[0]) if the_reader is None: showinfo( 'Opener not found', message='File {} was not successfully opened as a SICD type ' 'or SIDD type file.'.format(fnames)) return self.update_reader(the_reader) def callback_select_directory(self): dirname = askdirectory(initialdir=self.variables.browse_directory, mustexist=True) if dirname is None or dirname in [(), '']: return # update the default directory for browsing self.variables.browse_directory = os.path.split(dirname)[0] # TODO: handle non-complex data possibilities here? the_reader = ComplexImageReader(dirname) self.update_reader(the_reader) def display_canvas_rect_selection_in_pyplot_frame(self): def get_extent(coords): left = min(coords[1::2]) right = max(coords[1::2]) top = max(coords[0::2]) bottom = min(coords[0::2]) return left, right, top, bottom threshold = self.image_panel.canvas.variables.config.select_size_threshold select_id = self.image_panel.canvas.variables.select_rect.uid rect_coords = self.image_panel.canvas.get_shape_image_coords(select_id) extent = get_extent(rect_coords) if abs(extent[1] - extent[0]) < threshold or abs(extent[2] - extent[3]) < threshold: self.pyplot_panel.make_blank() else: image_data = self.image_panel.canvas.get_image_data_in_canvas_rect_by_id( select_id) if image_data is not None: self.pyplot_panel.update_image(image_data, extent=extent) else: self.pyplot_panel.make_blank() def my_populate_metaicon(self): """ Populate the metaicon. """ if self.image_panel.canvas.variables.canvas_image_object is None or \ self.image_panel.canvas.variables.canvas_image_object.image_reader is None: image_reader = None the_index = 0 else: image_reader = self.image_panel.canvas.variables.canvas_image_object.image_reader the_index = self.image_panel.canvas.get_image_index() self.populate_metaicon(image_reader, the_index) def my_populate_metaviewer(self): """ Populate the metaviewer. """ if self.image_panel.canvas.variables.canvas_image_object is None: image_reader = None else: image_reader = self.image_panel.canvas.variables.canvas_image_object.image_reader self.populate_metaviewer(image_reader)
class CanvasResize(WidgetPanel): _widget_list = ("image_panel", "button_panel") image_panel = widget_descriptors.ImagePanelDescriptor( "image_panel") # type: ImagePanel button_panel = widget_descriptors.PanelDescriptor("button_panel", Buttons) # type: Buttons def __init__(self, primary): self._shape_ids = {} self.primary = primary primary_frame = tkinter.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.init_w_horizontal_layout() image_npix_x = 2000 image_npix_y = 1500 self.image_panel.set_max_canvas_size(image_npix_x, image_npix_y) image_data = numpy.linspace(0, 255, image_npix_x * image_npix_y) image_data = numpy.reshape(image_data, (image_npix_y, image_npix_x)) image_reader = NumpyImageReader(image_data) self.image_panel.set_image_reader(image_reader) self.drag_xlim_1 = image_npix_x * 0.1 self.drag_xlim_2 = image_npix_x * 0.9 self.drag_ylim_1 = image_npix_y * 0.1 self.drag_ylim_2 = image_npix_y * 0.9 self.image_panel.current_tool = ToolConstants.PAN self.image_panel.set_min_canvas_size(100, 100) primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) self.button_panel.draw_rect.config(command=self.callback_draw_rect) self.button_panel.draw_line.config(command=self.callback_draw_line) self.button_panel.draw_arrow.config(command=self.callback_draw_arrow) self.button_panel.draw_point.config(command=self.callback_draw_point) self.button_panel.draw_ellipse.config( command=self.callback_draw_ellipse) self.button_panel.draw_polygon.config( command=self.callback_draw_polygon) # handle the image creation event self.image_panel.canvas.bind('<<ShapeCreate>>', self.handle_new_shape) @property def point_id(self): """ None|int: The point id. """ return self._shape_ids.get(ShapeTypeConstants.POINT, None) @property def line_id(self): """ None|int: The line id. """ return self._shape_ids.get(ShapeTypeConstants.LINE, None) @property def arrow_id(self): """ None|int: The arrow id. """ return self._shape_ids.get(ShapeTypeConstants.ARROW, None) @property def rect_id(self): """ None|int: The rectangle id. """ return self._shape_ids.get(ShapeTypeConstants.RECT, None) @property def ellipse_id(self): """ None|int: The ellipse id. """ return self._shape_ids.get(ShapeTypeConstants.ELLIPSE, None) @property def polygon_id(self): """ None|int: polygon id. """ return self._shape_ids.get(ShapeTypeConstants.POLYGON, None) def callback_draw_point(self): self.image_panel.canvas.set_current_tool_to_draw_point(self.point_id) def callback_draw_line(self): self.image_panel.canvas.set_current_tool_to_draw_line(self.line_id) def callback_draw_arrow(self): self.image_panel.canvas.set_current_tool_to_draw_arrow(self.arrow_id) def callback_draw_rect(self): self.image_panel.canvas.set_current_tool_to_draw_rect(self.rect_id) def callback_draw_ellipse(self): self.image_panel.canvas.set_current_tool_to_draw_ellipse( self.ellipse_id) def callback_draw_polygon(self): self.image_panel.canvas.set_current_tool_to_draw_polygon( self.polygon_id) def handle_new_shape(self, event): """ Handle the creation of a new shape. Parameters ---------- event """ # we'll be tracking the most recently created self._shape_ids[event.y] = event.x def exit(self): self.quit()
class LocalFrequencySupportTool(WidgetPanel, WidgetWithMetadata): _widget_list = ("image_panel", "frequency_panel") image_panel = widget_descriptors.ImagePanelDescriptor("image_panel") # type: ImagePanel frequency_panel = widget_descriptors.ImagePanelDescriptor("frequency_panel") # type: ImagePanel def __init__(self, primary): self.root = primary self.primary_frame = basic_widgets.Frame(primary) WidgetPanel.__init__(self, self.primary_frame) WidgetWithMetadata.__init__(self, primary) self.variables = AppVariables() self.init_w_horizontal_layout() self.set_title() # define menus menubar = tkinter.Menu() # file menu filemenu = tkinter.Menu(menubar, tearoff=0) filemenu.add_command(label="Open Image", command=self.callback_select_files) filemenu.add_command(label="Open Directory", command=self.callback_select_directory) filemenu.add_separator() filemenu.add_command(label="Exit", command=self.exit) # menus for informational popups popups_menu = tkinter.Menu(menubar, tearoff=0) popups_menu.add_command(label="Metaicon", command=self.metaicon_popup) popups_menu.add_command(label="Metaviewer", command=self.metaviewer_popup) # ensure menus cascade menubar.add_cascade(label="File", menu=filemenu) menubar.add_cascade(label="Metadata", menu=popups_menu) # handle packing self.primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) primary.config(menu=menubar) # hide extraneous tool elements self.image_panel.hide_tools('shape_drawing') self.image_panel.hide_shapes() self.frequency_panel.hide_tools(['shape_drawing', 'select']) self.frequency_panel.hide_shapes() self.frequency_panel.hide_select_index() # bind canvas events for proper functionality self.image_panel.canvas.bind('<<SelectionChanged>>', self.handle_selection_change) self.image_panel.canvas.bind('<<SelectionFinalized>>', self.handle_selection_change) self.image_panel.canvas.bind('<<ImageIndexChanged>>', self.handle_image_index_changed) def set_title(self): """ Sets the window title. """ file_name = None if self.variables.image_reader is None else self.variables.image_reader.file_name if file_name is None: the_title = "Frequency Support Tool" elif isinstance(file_name, (list, tuple)): the_title = "Frequency Support Tool, Multiple Files" else: the_title = "Frequency Support Tool for {}".format(os.path.split(file_name)[1]) self.winfo_toplevel().title(the_title) def exit(self): self.root.destroy() def set_default_selection(self): """ Sets the default selection on the currently selected image. """ if self.variables.image_reader is None: return # get full image size full_rows = self.variables.image_reader.full_image_ny full_cols = self.variables.image_reader.full_image_nx default_size = 512 middle = ( max(0, int(0.5*(full_rows - default_size))), max(0, int(0.5*(full_cols - default_size))), min(full_rows, int(0.5*(full_rows + default_size))), min(full_cols, int(0.5*(full_cols + default_size)))) self.image_panel.canvas.zoom_to_full_image_selection((0, 0, full_rows, full_cols)) # set selection rectangle self.image_panel.canvas.set_current_tool_to_select() self.image_panel.canvas.modify_existing_shape_using_image_coords( self.image_panel.canvas.variables.select_rect.uid, middle) self.image_panel.canvas.show_shape(self.image_panel.canvas.variables.select_rect.uid) # we need to set some drawing state here...this is ugly, but not worth anchor = self.image_panel.canvas.get_shape_canvas_coords(self.image_panel.canvas.variables.select_rect.uid)[:2] self.image_panel.canvas.set_select_initial_state(anchor) # noinspection PyUnusedLocal def handle_selection_change(self, event): """ Handle a change in the selection area. Parameters ---------- event """ if self.variables.image_reader is None: return full_image_width = self.image_panel.canvas.variables.state.canvas_width fill_image_height = self.image_panel.canvas.variables.state.canvas_height self.image_panel.canvas.zoom_to_canvas_selection((0, 0, full_image_width, fill_image_height)) self.update_displayed_selection() #noinspection PyUnusedLocal def handle_image_index_changed(self, event): """ Handle that the image index has changed. Parameters ---------- event """ self.my_populate_metaicon() self.set_default_selection() def update_reader(self, the_reader): """ Update the reader. Parameters ---------- the_reader : ImageReader """ # change the tool to view self.image_panel.canvas.set_current_tool_to_view() self.image_panel.canvas.set_current_tool_to_view() # update the reader self.variables.image_reader = the_reader self.image_panel.set_image_reader(the_reader) self.set_title() # refresh appropriate GUI elements self.set_default_selection() self.my_populate_metaicon() self.my_populate_metaviewer() def callback_select_files(self): fnames = askopenfilenames(initialdir=self.variables.browse_directory, filetypes=common_use_collection) if fnames is None or fnames in ['', ()]: return # update the default directory for browsing self.variables.browse_directory = os.path.split(fnames[0])[0] the_reader = None if len(fnames) == 1: the_reader = ComplexImageReader(fnames[0]) else: the_reader = ComplexImageReader(fnames) if the_reader is None: showinfo('Opener not found', message='File {} was not successfully opened as a SICD type ' 'file.'.format(fnames)) return self.update_reader(the_reader) def callback_select_directory(self): dirname = askdirectory(initialdir=self.variables.browse_directory, mustexist=True) if dirname is None or dirname in [(), '']: return # update the default directory for browsing self.variables.browse_directory = os.path.split(dirname)[0] the_reader = ComplexImageReader(dirname) self.update_reader(the_reader) def _initialize_bandwidth_lines(self): if self.variables.row_line_low is None or \ self.frequency_panel.canvas.get_vector_object(self.variables.row_line_low) is None: self.variables.row_deltak1 = self.frequency_panel.canvas.create_new_line( (0, 0, 0, 0), make_current=False, increment_color=False, fill='red') self.variables.row_deltak2 = self.frequency_panel.canvas.create_new_line( (0, 0, 0, 0), make_current=False, increment_color=False, fill='red') self.variables.col_deltak1 = self.frequency_panel.canvas.create_new_line( (0, 0, 0, 0), make_current=False, increment_color=False, fill='red') self.variables.col_deltak2 = self.frequency_panel.canvas.create_new_line( (0, 0, 0, 0), make_current=False, increment_color=False, fill='red') self.variables.row_line_low = self.frequency_panel.canvas.create_new_line( (0, 0, 0, 0), make_current=False, increment_color=False, fill='blue', dash=(3, )) self.variables.row_line_high = self.frequency_panel.canvas.create_new_line( (0, 0, 0, 0), make_current=False, increment_color=False, fill='blue', dash=(3, )) self.variables.col_line_low = self.frequency_panel.canvas.create_new_line( (0, 0, 0, 0), make_current=False, increment_color=False, fill='blue', dash=(3, )) self.variables.col_line_high = self.frequency_panel.canvas.create_new_line( (0, 0, 0, 0), make_current=False, increment_color=False, fill='blue', dash=(3, )) else: self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.row_deltak1, (0, 0, 0, 0)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.row_deltak2, (0, 0, 0, 0)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.col_deltak1, (0, 0, 0, 0)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.col_deltak2, (0, 0, 0, 0)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.row_line_low, (0, 0, 0, 0)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.row_line_high, (0, 0, 0, 0)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.col_line_low, (0, 0, 0, 0)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.col_line_high, (0, 0, 0, 0)) def update_displayed_selection(self): def get_extent(coords): return min(coords[0::2]), max(coords[0::2]), min(coords[1::2]), max(coords[1::2]) def draw_row_delta_lines(): deltak1 = (row_count - 1)*(0.5 + the_sicd.Grid.Row.SS*the_sicd.Grid.Row.DeltaK1) + 1 deltak2 = (row_count - 1)*(0.5 + the_sicd.Grid.Row.SS*the_sicd.Grid.Row.DeltaK2) + 1 self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.row_deltak1, (deltak1, 0, deltak1, col_count)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.row_deltak2, (deltak2, 0, deltak2, col_count)) def draw_col_delta_lines(): deltak1 = (col_count - 1)*(0.5 + the_sicd.Grid.Col.SS*the_sicd.Grid.Col.DeltaK1) + 1 deltak2 = (col_count - 1)*(0.5 + the_sicd.Grid.Col.SS*the_sicd.Grid.Col.DeltaK2) + 1 self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.col_deltak1, (0, deltak1, row_count, deltak1)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.col_deltak2, (0, deltak2, row_count, deltak2)) def draw_row_bandwidth_lines(): try: delta_kcoa_center = the_sicd.Grid.Row.DeltaKCOAPoly(row_phys, col_phys) except Exception: delta_kcoa_center = 0.0 row_bw_low = (row_count - 1)*( 0.5 + the_sicd.Grid.Row.SS*(delta_kcoa_center - 0.5*the_sicd.Grid.Row.ImpRespBW)) + 1 row_bw_high = (row_count - 1)*( 0.5 + the_sicd.Grid.Row.SS*(delta_kcoa_center + 0.5*the_sicd.Grid.Row.ImpRespBW)) + 1 row_bw_low = (row_bw_low % row_count) row_bw_high = (row_bw_high % row_count) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.row_line_low, (row_bw_low, 0, row_bw_low, col_count)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.row_line_high, (row_bw_high, 0, row_bw_high, col_count)) def draw_col_bandwidth_lines(): try: delta_kcoa_center = the_sicd.Grid.Col.DeltaKCOAPoly(row_phys, col_phys) except Exception: delta_kcoa_center = 0.0 col_bw_low = (col_count - 1) * ( 0.5 + the_sicd.Grid.Col.SS*(delta_kcoa_center - 0.5*the_sicd.Grid.Col.ImpRespBW)) + 1 col_bw_high = (col_count - 1) * ( 0.5 + the_sicd.Grid.Col.SS*(delta_kcoa_center + 0.5*the_sicd.Grid.Col.ImpRespBW)) + 1 col_bw_low = (col_bw_low % col_count) col_bw_high = (col_bw_high % col_count) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.col_line_low, (0, col_bw_low, row_count, col_bw_low)) self.frequency_panel.canvas.modify_existing_shape_using_image_coords( self.variables.col_line_high, (0, col_bw_high, row_count, col_bw_high)) threshold = self.image_panel.canvas.variables.config.select_size_threshold select_id = self.image_panel.canvas.variables.select_rect.uid rect_coords = self.image_panel.canvas.get_shape_image_coords(select_id) extent = get_extent(rect_coords) # left, right, bottom, top row_count = extent[1] - extent[0] col_count = extent[3] - extent[2] the_sicd = self.variables.image_reader.get_sicd() row_phys, col_phys = get_physical_coordinates( the_sicd, 0.5*(extent[0]+extent[1]), 0.5*(extent[2]+extent[3])) if row_count < threshold or col_count < threshold: junk_data = numpy.zeros((100, 100), dtype='uint8') self.frequency_panel.set_image_reader(NumpyImageReader(junk_data)) self._initialize_bandwidth_lines() else: image_data = self.variables.image_reader.base_reader[extent[0]:extent[1], extent[2]:extent[3]] if image_data is not None: self.frequency_panel.set_image_reader( NumpyImageReader(remap.density(fftshift(fft2_sicd(image_data, the_sicd))))) self._initialize_bandwidth_lines() draw_row_delta_lines() draw_col_delta_lines() draw_row_bandwidth_lines() draw_col_bandwidth_lines() else: junk_data = numpy.zeros((100, 100), dtype='uint8') self.frequency_panel.set_image_reader(NumpyImageReader(junk_data)) self._initialize_bandwidth_lines() def my_populate_metaicon(self): """ Populate the metaicon. """ if self.variables.image_reader is None: image_reader = None the_index = 0 else: image_reader = self.variables.image_reader the_index = self.variables.image_reader.index self.populate_metaicon(image_reader, the_index) def my_populate_metaviewer(self): """ Populate the metaviewer. """ self.populate_metaviewer(self.variables.image_reader)
class CanvasResize(WidgetPanel): _widget_list = ("image_panel", "button_panel") image_panel = widget_descriptors.ImagePanelDescriptor("image_panel") # type: ImagePanel button_panel = widget_descriptors.PanelDescriptor("button_panel", Buttons) # type: Buttons def __init__(self, primary): self.rect_id = None self.line_id = None self.arrow_id = None self.point_id = None self.polygon_id = None self.n_shapes = 0 self.primary = primary primary_frame = tkinter.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.init_w_horizontal_layout() self.image_panel.image_frame.set_canvas_size(800, 600) self.image_panel.resizeable = True image_npix_x = 1200 image_npix_y = 500 image_data = numpy.random.random((image_npix_y, image_npix_x)) image_data = image_data * 255 image_reader = NumpyImageReader(image_data) self.image_panel.set_image_reader(image_reader) self.drag_xlim_1 = image_npix_x * 0.25 self.drag_xlim_2 = image_npix_x * 0.75 self.drag_ylim_1 = image_npix_y * 0.1 self.drag_ylim_2 = image_npix_y * 0.9 self.image_panel.current_tool = ToolConstants.PAN_TOOL self.image_panel.axes_canvas.image_x_min_val = 500 self.image_panel.axes_canvas.image_x_max_val = 1200 self.image_panel.axes_canvas.image_y_min_val = 5000 self.image_panel.axes_canvas.image_y_max_val = 2000 primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) self.image_panel.canvas.set_canvas_size(800, 800) self.button_panel.draw_rect.on_left_mouse_click(self.callback_draw_rect) self.button_panel.draw_line.on_left_mouse_click(self.callback_draw_line) self.button_panel.draw_arrow.on_left_mouse_click(self.callback_draw_arrow) self.button_panel.draw_point.on_left_mouse_click(self.callback_draw_point) self.button_panel.draw_polygon.on_left_mouse_click(self.callback_draw_polygon) self.button_panel.edit_shape.on_left_mouse_click(self.callback_edit_shape) self.image_panel.canvas.on_left_mouse_release(self.callback_on_left_mouse_release) def callback_draw_rect(self, event): self.image_panel.canvas.set_current_tool_to_draw_rect(self.rect_id) def callback_draw_line(self, event): self.image_panel.canvas.set_current_tool_to_draw_line_by_dragging(self.line_id) def callback_draw_arrow(self, event): self.image_panel.canvas.set_current_tool_to_draw_arrow_by_dragging(self.arrow_id) def callback_draw_point(self, event): self.image_panel.canvas.set_current_tool_to_draw_point(self.point_id) def callback_draw_polygon(self, event): self.image_panel.canvas.set_current_tool_to_draw_polygon_by_clicking(self.polygon_id) def callback_edit_shape(self, event): self.image_panel.canvas.set_current_tool_to_edit_shape() def callback_on_left_mouse_release(self, event): self.image_panel.canvas.callback_handle_left_mouse_release(event) n_shapes = len(self.image_panel.canvas.get_non_tool_shape_ids()) if n_shapes > self.n_shapes: if self.image_panel.current_tool == ToolConstants.DRAW_RECT_BY_DRAGGING: self.rect_id = self.image_panel.canvas.variables.current_shape_id elif self.image_panel.current_tool == ToolConstants.DRAW_LINE_BY_DRAGGING: self.line_id = self.image_panel.canvas.variables.current_shape_id elif self.image_panel.current_tool == ToolConstants.DRAW_ARROW_BY_DRAGGING: self.arrow_id = self.image_panel.canvas.variables.current_shape_id elif self.image_panel.current_tool == ToolConstants.DRAW_POINT_BY_CLICKING: self.point_id = self.image_panel.canvas.variables.current_shape_id elif self.image_panel.current_tool == ToolConstants.DRAW_POLYGON_BY_CLICKING: self.polygon_id = self.image_panel.canvas.variables.current_shape_id self.image_panel.canvas.get_vector_object( self.image_panel.canvas.variables.current_shape_id).image_drag_limits = (self.drag_ylim_1, self.drag_xlim_1, self.drag_ylim_2, self.drag_xlim_2) def exit(self): self.quit()
class FullFrequencySupportTool(WidgetPanel, WidgetWithMetadata): _widget_list = ("row_centered_image_panel", "column_centered_image_panel") row_centered_image_panel = widget_descriptors.ImagePanelDescriptor( "row_centered_image_panel") # type: ImagePanel column_centered_image_panel = widget_descriptors.ImagePanelDescriptor( "column_centered_image_panel") # type: ImagePanel def __init__(self, primary): self.root = primary self.primary_frame = basic_widgets.Frame(primary) self.variables = AppVariables() WidgetPanel.__init__(self, self.primary_frame) WidgetWithMetadata.__init__(self, primary) self.init_w_horizontal_layout() self.set_title() # define menus menubar = tkinter.Menu() # file menu filemenu = tkinter.Menu(menubar, tearoff=0) filemenu.add_command(label="Open Image", command=self.callback_select_files) filemenu.add_command(label="Open Directory", command=self.callback_select_directory) filemenu.add_separator() filemenu.add_command(label="Exit", command=self.exit) # menus for informational popups popups_menu = tkinter.Menu(menubar, tearoff=0) popups_menu.add_command(label="Weight Plots", command=self.create_weights_plot) popups_menu.add_command(label="Metaicon", command=self.metaicon_popup) popups_menu.add_command(label="Metaviewer", command=self.metaviewer_popup) # ensure menus cascade menubar.add_cascade(label="File", menu=filemenu) menubar.add_cascade(label="Metadata", menu=popups_menu) # handle packing self.primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) primary.config(menu=menubar) # hide extraneous tool elements self.row_centered_image_panel.hide_tools(['shape_drawing', 'select']) self.row_centered_image_panel.hide_shapes() self.row_centered_image_panel.hide_select_index() self.column_centered_image_panel.hide_tools( ['shape_drawing', 'select']) self.column_centered_image_panel.hide_shapes() self.column_centered_image_panel.hide_select_index() # TODO: allow changing the image index somewhere? # bind the handle_image_index_changed method def set_title(self): """ Sets the window title. """ file_name = None if self.variables.image_reader is None else self.variables.image_reader.file_name if file_name is None: the_title = "Full Frequency Support Tool" elif isinstance(file_name, (list, tuple)): the_title = "Full Frequency Support Tool, Multiple Files" else: the_title = "Full Frequency Support Tool for {}".format( os.path.split(file_name)[1]) self.winfo_toplevel().title(the_title) def exit(self): self._delete_files() self.root.destroy() def _delete_files(self): """ This is a helper function for cleaning up our state. """ if not hasattr(self, 'variables') or self.variables is None: return self.variables.row_fourier_reader = None self.variables.column_fourier_reader = None if self.variables.row_fourier_file is not None \ and os.path.exists(self.variables.row_fourier_file): os.remove(self.variables.row_fourier_file) logger.debug('Removing temp file % s' % self.variables.row_fourier_file) if self.variables.column_fourier_file is not None and \ os.path.exists(self.variables.column_fourier_file): os.remove(self.variables.column_fourier_file) logger.debug('Removing temp file % s' % self.variables.column_fourier_file) self.variables.row_fourier_file = None self.variables.column_fourier_file = None def _clear_display(self): """ Clear the row and column Fourier transform displays """ self._delete_files() junk_data = numpy.zeros((100, 100), dtype='uint8') self.row_centered_image_panel.set_image_reader( NumpyImageReader(junk_data)) self.column_centered_image_panel.set_image_reader( NumpyImageReader(junk_data)) def _calculate_fourier_data(self): def set_row_data(): # calculate the fourier transform with deskew in the row direction row_file, row_memmap, row_mean_value = create_deskewed_transform( self.variables.image_reader, dimension=0) self.variables.row_fourier_file = row_file self.variables.row_fourier_reader = ComplexImageReader( FlatSICDReader(self.variables.image_reader.get_sicd(), row_memmap)) self.row_centered_image_panel.set_image_reader( self.variables.row_fourier_reader) draw_deltak_lines(self.row_centered_image_panel.canvas) # rescale the row_mean_value so that the smoothed max value is essentially 1 if row_mean_value.size < 200: the_max = numpy.amax(row_mean_value) else: the_size = int(numpy.ceil(row_mean_value.size / 200.)) smoothed = numpy.convolve(row_mean_value, numpy.full((the_size, ), 1. / the_size, dtype='float64'), mode='valid') the_max = numpy.amax(smoothed) row_mean_value /= the_max self.variables.scaled_row_mean = row_mean_value # construct the proper weights and prepare information for weight plotting self.variables.derived_row_weights = the_sicd.Grid.Row.define_weight_function( populate=False) def set_col_data(): # calculate the fourier transform with deskew in the column direction col_file, col_memmap, col_mean_value = create_deskewed_transform( self.variables.image_reader, dimension=1) self.variables.column_fourier_file = col_file self.variables.column_fourier_reader = ComplexImageReader( FlatSICDReader(self.variables.image_reader.get_sicd(), col_memmap)) self.column_centered_image_panel.set_image_reader( self.variables.column_fourier_reader) draw_deltak_lines(self.column_centered_image_panel.canvas) # rescale the row_mean_value so that the smoothed max value is essentially 1 if col_mean_value.size < 200: the_max = numpy.amax(col_mean_value) else: the_size = int(numpy.ceil(col_mean_value.size / 200.)) smoothed = numpy.convolve(col_mean_value, numpy.full((the_size, ), 1. / the_size, dtype='float64'), mode='valid') the_max = numpy.amax(smoothed) col_mean_value /= the_max self.variables.scaled_column_mean = col_mean_value # construct the proper weights and prepare information for weight plotting self.variables.derived_column_weights = the_sicd.Grid.Col.define_weight_function( populate=False) def draw_deltak_lines(canvas): # calculate the row deltak1/deltak2 values deltak1 = (row_count - 1) * ( 0.5 + the_sicd.Grid.Row.SS * the_sicd.Grid.Row.DeltaK1) + 1 deltak2 = (row_count - 1) * ( 0.5 + the_sicd.Grid.Row.SS * the_sicd.Grid.Row.DeltaK2) + 1 # draw the deltak1/deltak2 lines deltak1_id = canvas.create_new_line((0, 0, 0, 0), make_current=False, increment_color=False, fill='red') canvas.modify_existing_shape_using_image_coords( deltak1_id, (deltak1, 0, deltak1, col_count)) deltak2_id = canvas.create_new_line((0, 0, 0, 0), make_current=False, increment_color=False, fill='red') canvas.modify_existing_shape_using_image_coords( deltak2_id, (deltak2, 0, deltak2, col_count)) # calculate the column deltak1/deltak2 values deltak1 = (col_count - 1) * ( 0.5 + the_sicd.Grid.Col.SS * the_sicd.Grid.Col.DeltaK1) + 1 deltak2 = (col_count - 1) * ( 0.5 + the_sicd.Grid.Col.SS * the_sicd.Grid.Col.DeltaK2) + 1 # draw the deltak1/deltak2 lines deltak1_id = canvas.create_new_line((0, 0, 0, 0), make_current=False, increment_color=False, fill='red') canvas.modify_existing_shape_using_image_coords( deltak1_id, (0, deltak1, row_count, deltak1)) deltak2_id = canvas.create_new_line((0, 0, 0, 0), make_current=False, increment_color=False, fill='red') canvas.modify_existing_shape_using_image_coords( deltak2_id, (0, deltak2, row_count, deltak2)) # delete any previous state variables and clear displays self._clear_display() self.variables.scaled_row_mean = None self.variables.derived_row_weights = None self.variables.scaled_column_mean = None self.variables.derived_column_weights = None if self.variables.image_reader is None: return self.update_idletasks() the_sicd = self.variables.image_reader.get_sicd() row_count = the_sicd.ImageData.NumRows col_count = the_sicd.ImageData.NumCols set_row_data() self.update_idletasks() set_col_data() def create_weights_plot(self): """ Create a matplotlib (using the user default backend) of the weight information. """ if self.variables.image_reader is None or \ self.variables.scaled_row_mean is None or \ self.variables.scaled_column_mean is None: return # nothing to be done currently the_sicd = self.variables.image_reader.get_sicd() fig, axs = pyplot.subplots(nrows=2, ncols=1) the_title = 'Weight information for file {}'.format( os.path.split(self.variables.image_reader.file_name)[1]) fig.suptitle(the_title) # plot the row information axs[0].set_ylabel('Row Information') axs[0].set_xlabel('Krow (cycles/meter)') axs[0].plot(numpy.linspace(-0.5 / the_sicd.Grid.Row.SS, 0.5 / the_sicd.Grid.Row.SS, self.variables.scaled_row_mean.size), self.variables.scaled_row_mean, 'b', lw=1, label='Observed Data') if self.variables.derived_row_weights is not None: axs[0].plot( numpy.linspace(-0.5 * the_sicd.Grid.Row.ImpRespBW, 0.5 * the_sicd.Grid.Row.ImpRespBW, self.variables.derived_row_weights.size), self.variables.derived_row_weights, 'g--', lw=3, label='Row Derived Weights') if the_sicd.Grid.Row.WgtFunct is not None: axs[0].plot(numpy.linspace(-0.5 * the_sicd.Grid.Row.ImpRespBW, 0.5 * the_sicd.Grid.Row.ImpRespBW, the_sicd.Grid.Row.WgtFunct.size), the_sicd.Grid.Row.WgtFunct, 'r:', lw=3, label='Row.WgtFunct') axs[0].set_xlim( min(-0.5 / the_sicd.Grid.Row.SS, -0.5 * the_sicd.Grid.Row.ImpRespBW), max(0.5 / the_sicd.Grid.Row.SS, 0.5 * the_sicd.Grid.Row.ImpRespBW)) axs[0].legend(loc='upper right') axs[1].set_ylabel('Column Information') axs[1].set_xlabel('Kcol (cycles/meter)') axs[1].plot(numpy.linspace(-0.5 / the_sicd.Grid.Col.SS, 0.5 / the_sicd.Grid.Col.SS, self.variables.scaled_column_mean.size), self.variables.scaled_column_mean, 'b', lw=1, label='Observed Data') if self.variables.derived_column_weights is not None: axs[1].plot(numpy.linspace( -0.5 * the_sicd.Grid.Col.ImpRespBW, 0.5 * the_sicd.Grid.Col.ImpRespBW, self.variables.derived_column_weights.size), self.variables.derived_column_weights, 'g--', lw=3, label='Col Derived Weights') if the_sicd.Grid.Col.WgtFunct is not None: axs[1].plot(numpy.linspace(-0.5 * the_sicd.Grid.Col.ImpRespBW, 0.5 * the_sicd.Grid.Col.ImpRespBW, the_sicd.Grid.Col.WgtFunct.size), the_sicd.Grid.Col.WgtFunct, 'r:', lw=3, label='Col.WgtFunct') axs[1].set_xlim( min(-0.5 / the_sicd.Grid.Col.SS, -0.5 * the_sicd.Grid.Col.ImpRespBW), max(0.5 / the_sicd.Grid.Col.SS, 0.5 * the_sicd.Grid.Col.ImpRespBW)) axs[1].legend(loc='upper right') # create a toplevel, and put our figure inside it root = tkinter.Toplevel(self.root) root.wm_title("Embedding in Tk") canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea. canvas.draw() canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1) toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update() canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1) # grab the focus, so this is blocking root.grab_set() root.wait_window() def handle_image_index_changed(self): """ Handle that the image index has changed. """ self.my_populate_metaicon() self._calculate_fourier_data() def update_reader(self, the_reader): """ Update the reader. Parameters ---------- the_reader : ImageReader """ # change the tool to view self.row_centered_image_panel.canvas.set_current_tool_to_view() self.row_centered_image_panel.canvas.set_current_tool_to_view() # update the reader self.variables.image_reader = the_reader self.variables.image_index = 0 self.set_title() # refresh appropriate GUI elements self._calculate_fourier_data() self.my_populate_metaicon() self.my_populate_metaviewer() def callback_select_files(self): fnames = askopenfilenames(initialdir=self.variables.browse_directory, filetypes=common_use_collection) if fnames is None or fnames in ['', ()]: return # update the default directory for browsing self.variables.browse_directory = os.path.split(fnames[0])[0] the_reader = None if len(fnames) == 1: the_reader = ComplexImageReader(fnames[0]) else: the_reader = ComplexImageReader(fnames) if the_reader is None: showinfo( 'Opener not found', message='File {} was not successfully opened as a SICD type ' 'file.'.format(fnames)) return self.update_reader(the_reader) def callback_select_directory(self): dirname = askdirectory(initialdir=self.variables.browse_directory, mustexist=True) if dirname is None or dirname in [(), '']: return # update the default directory for browsing self.variables.browse_directory = os.path.split(dirname)[0] the_reader = ComplexImageReader(dirname) self.update_reader(the_reader) def my_populate_metaicon(self): """ Populate the metaicon. """ self.populate_metaicon(self.variables.image_reader, self.variables.image_reader.index) def my_populate_metaviewer(self): """ Populate the metaviewer. """ self.populate_metaviewer(self.variables.image_reader) def __del__(self): self._delete_files()
class CanvasDemo(WidgetPanel): _widget_list = ("button_panel", "canvas_demo_image_panel", "pyplot_panel") button_panel = widget_descriptors.PanelDescriptor( "button_panel", CanvasDemoButtonPanel) # type: CanvasDemoButtonPanel pyplot_panel = widget_descriptors.PyplotImagePanelDescriptor( "pyplot_panel") # type: PyplotImagePanel canvas_demo_image_panel = widget_descriptors.ImagePanelDescriptor( "canvas_demo_image_panel") # type: ImagePanel def __init__(self, primary): primary_frame = basic_widgets.Frame(primary) WidgetPanel.__init__(self, primary_frame) self.variables = AppVariables() self.init_w_horizontal_layout() primary_frame.pack(fill=tkinter.BOTH, expand=tkinter.YES) self.button_panel.pack(fill=tkinter.X, expand=tkinter.NO) self.pyplot_panel.pack(expand=tkinter.YES) self.canvas_demo_image_panel.pack(expand=tkinter.YES) # bind events to callbacks here self.button_panel.fname_select.config( command=self.callback_initialize_canvas_image) self.button_panel.rect_select.config( command=self.callback_set_to_select) self.button_panel.draw_line.config(command=self.callback_draw_line) self.button_panel.draw_arrow.config(command=self.callback_draw_arrow) self.button_panel.draw_rect.config(command=self.callback_draw_rect) self.button_panel.draw_polygon.config( command=self.callback_draw_polygon) self.button_panel.draw_point.config(command=self.callback_draw_point) self.button_panel.edit.config(command=self.callback_edit) self.button_panel.color_selector.config( command=self.callback_activate_color_selector) self.button_panel.remap_dropdown.on_selection(self.callback_remap) self.canvas_demo_image_panel.canvas.on_left_mouse_click( self.callback_handle_canvas_left_mouse_click) self.canvas_demo_image_panel.canvas.on_left_mouse_release( self.callback_handle_canvas_left_mouse_release) def callback_save_kml(self): kml_save_fname = asksaveasfilename( initialdir=os.path.expanduser("~/Downloads")) kml_util = KmlUtil() for shape_id in self.canvas_demo_image_panel.canvas.variables.shape_ids: image_coords = self.canvas_demo_image_panel.canvas.get_shape_image_coords( shape_id) shape_type = self.canvas_demo_image_panel.canvas.get_shape_type( shape_id) if image_coords: sicd_meta = self.canvas_demo_image_panel.canvas.variables.canvas_image_object.reader_object.sicdmeta image_points = numpy.zeros((int(len(image_coords) / 2), 2)) image_points[:, 0] = image_coords[0::2] image_points[:, 1] = image_coords[1::2] ground_points_ecf = point_projection.image_to_ground( image_points, sicd_meta) ground_points_latlon = geocoords.ecf_to_geodetic( ground_points_ecf) world_y_coordinates = ground_points_latlon[:, 0] world_x_coordinates = ground_points_latlon[:, 1] xy_point_list = [ (x, y) for x, y in zip(world_x_coordinates, world_y_coordinates) ] if shape_id == self.canvas_demo_image_panel.canvas.variables.zoom_rect.uid: pass elif shape_type == self.canvas_demo_image_panel.canvas.variables.select_rect.uid: pass elif shape_type == ShapeTypeConstants.POINT: kml_util.add_point(str(shape_id), xy_point_list[0]) elif shape_type == ShapeTypeConstants.LINE: kml_util.add_linestring(str(shape_id), xy_point_list) elif shape_type == ShapeTypeConstants.POLYGON: kml_util.add_polygon(str(shape_id), xy_point_list) elif shape_type == ShapeTypeConstants.RECT: kml_util.add_polygon(str(shape_id), xy_point_list) kml_util.write_to_file(kml_save_fname) def callback_handle_canvas_left_mouse_click(self, event): self.canvas_demo_image_panel.canvas.callback_handle_left_mouse_click( event) current_shape = self.canvas_demo_image_panel.canvas.current_shape_id if current_shape: self.variables.shapes_in_selector.append(current_shape) self.variables.shapes_in_selector = sorted( list(set(self.variables.shapes_in_selector))) def callback_handle_canvas_left_mouse_release(self, event): self.canvas_demo_image_panel.canvas.callback_handle_left_mouse_release( event) if self.canvas_demo_image_panel.canvas.variables.select_rect.uid == self.canvas_demo_image_panel.canvas.current_shape_id: self.update_selection() def callback_edit(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_edit_shape( select_closest_first=True) def callback_activate_color_selector(self): self.canvas_demo_image_panel.canvas.activate_color_selector() def callback_draw_line(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_line() def callback_draw_arrow(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_arrow() def callback_draw_rect(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_rect() def callback_draw_polygon(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_polygon() def callback_draw_point(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_draw_point() def callback_set_to_select(self): self.canvas_demo_image_panel.canvas.set_current_tool_to_select() # define custom callbacks here def callback_remap(self): self.update_selection() def update_selection(self): remap_dict = {entry[0]: entry[1] for entry in remap.get_remap_list()} selection = self.button_panel.remap_dropdown.get() self.variables.image_reader.set_remap_type(remap_dict[selection]) image_data = self.canvas_demo_image_panel.canvas.get_image_data_in_canvas_rect_by_id( self.canvas_demo_image_panel.canvas.variables.select_rect.uid) self.pyplot_panel.update_image(image_data) self.canvas_demo_image_panel.canvas.update_current_image() def callback_initialize_canvas_image(self): image_file_extensions = ['*.nitf', '*.NITF', '*.ntf', '*.NTF'] ftypes = [ ('NITF files', image_file_extensions), ('All files', '*'), ] new_fname = askopenfilename(initialdir=self.variables.browse_directory, filetypes=ftypes) if new_fname: self.variables.browse_directory = os.path.split(new_fname)[0] self.variables.image_reader = ComplexImageReader(new_fname) self.canvas_demo_image_panel.set_image_reader( self.variables.image_reader)