Example #1
0
    def display(self, workspace, figure):
        orig_pixels = workspace.display_data.orig_pixels
        output_pixels = workspace.display_data.output_pixels

        figure.set_subplots((2, 2))
        figure.subplot_imshow_grayscale(0, 0, orig_pixels,
                                        "Original: %s" % self.image_name.value)
        if self.method == M_CANNY:
            # Canny is binary
            figure.subplot_imshow_bw(0,
                                     1,
                                     output_pixels,
                                     self.output_image_name.value,
                                     sharexy=figure.subplot(0, 0))
        else:
            figure.subplot_imshow_grayscale(0,
                                            1,
                                            output_pixels,
                                            self.output_image_name.value,
                                            sharexy=figure.subplot(0, 0))
        color_image = np.zeros(
            (output_pixels.shape[0], output_pixels.shape[1], 3))
        color_image[:, :, 0] = stretch(orig_pixels)
        color_image[:, :, 1] = stretch(output_pixels)
        figure.subplot_imshow(1,
                              0,
                              color_image,
                              "Composite image",
                              sharexy=figure.subplot(0, 0))
Example #2
0
 def test_01_01_rescale(self):
     np.random.seed(0)
     image = np.random.uniform(-2, 2, size=(10, 10))
     image[0, 0] = -2
     image[9, 9] = 2
     expected = (image + 2.0) / 4.0
     result = F.stretch(image)
     self.assertTrue(np.all(result == expected))
Example #3
0
 def test_01_02_rescale_plus_mask(self):
     np.random.seed(0)
     image = np.random.uniform(-2, 2, size=(10, 10))
     mask = np.zeros((10, 10), bool)
     mask[1:9, 1:9] = True
     image[0, 0] = -4
     image[9, 9] = 4
     image[1, 1] = -2
     image[8, 8] = 2
     expected = (image[1:9, 1:9] + 2.0) / 4.0
     result = F.stretch(image, mask)
     self.assertTrue(np.all(result[1:9, 1:9] == expected))
Example #4
0
def entropy2(x, y):
    """Joint entropy of paired samples X and Y"""
    #
    # Bin each image into 256 gray levels
    #
    x = (stretch(x) * 255).astype(int)
    y = (stretch(y) * 255).astype(int)
    #
    # create an image where each pixel with the same X & Y gets
    # the same value
    #
    xy = 256 * x + y
    xy = xy.flatten()
    sparse = scipy.sparse.coo_matrix((np.ones(xy.shape), (xy, np.zeros(xy.shape))))
    histogram = sparse.toarray()
    n = np.sum(histogram)
    if n > 0 and np.max(histogram) > 0:
        histogram = histogram[histogram > 0]
        return np.log2(n) - np.sum(histogram * np.log2(histogram)) / n
    else:
        return 0
Example #5
0
def entropy2(x, y):
    '''Joint entropy of paired samples X and Y'''
    #
    # Bin each image into 256 gray levels
    #
    x = (stretch(x) * 255).astype(int)
    y = (stretch(y) * 255).astype(int)
    #
    # create an image where each pixel with the same X & Y gets
    # the same value
    #
    xy = 256 * x + y
    xy = xy.flatten()
    sparse = scipy.sparse.coo_matrix(
        (np.ones(xy.shape), (xy, np.zeros(xy.shape))))
    histogram = sparse.toarray()
    n = np.sum(histogram)
    if n > 0 and np.max(histogram) > 0:
        histogram = histogram[histogram > 0]
        return np.log2(n) - np.sum(histogram * np.log2(histogram)) / n
    else:
        return 0
    def display(self, workspace, figure):
        orig_pixels = workspace.display_data.orig_pixels
        output_pixels = workspace.display_data.output_pixels

        figure.set_subplots((2, 2))
        figure.subplot_imshow_grayscale(0, 0, orig_pixels,
                                        "Original: %s" %
                                        self.image_name.value)
        if self.method == M_CANNY:
            # Canny is binary
            figure.subplot_imshow_bw(0, 1, output_pixels,
                                     self.output_image_name.value,
                                     sharexy=figure.subplot(0, 0))
        else:
            figure.subplot_imshow_grayscale(0, 1, output_pixels,
                                            self.output_image_name.value,
                                            sharexy=figure.subplot(0, 0))
        color_image = np.zeros((output_pixels.shape[0],
                                output_pixels.shape[1], 3))
        color_image[:, :, 0] = stretch(orig_pixels)
        color_image[:, :, 1] = stretch(output_pixels)
        figure.subplot_imshow(1, 0, color_image, "Composite image",
                              sharexy=figure.subplot(0, 0))
 def run_image_gabor(self, image_name, scale, workspace):
     image = workspace.image_set.get_image(image_name,
                                           must_be_grayscale=True)
     pixel_data = image.pixel_data
     labels = np.ones(pixel_data.shape, int)
     if image.has_mask:
         labels[~image.mask] = 0
     pixel_data = stretch(pixel_data, labels > 0)
     best_score = 0
     for angle in range(self.gabor_angles.value):
         theta = np.pi * angle / self.gabor_angles.value
         g = gabor(pixel_data, labels, scale, theta)
         score_r = np.sum(g.real)
         score_i = np.sum(g.imag)
         score = np.sqrt(score_r**2 + score_i**2)
         best_score = max(best_score, score)
     statistics = self.record_image_measurement(workspace, image_name,
                                                scale, F_GABOR, best_score)
     return statistics
 def test_05_02_otsu_entropy(self):
     '''Test the entropy version of Otsu'''
     np.random.seed(0)
     image = np.hstack((np.random.exponential(1.5,size=600),
                        np.random.poisson(15,size=300)))
     image.shape=(30,30)
     image = stretch(image)
     limage, d = T.log_transform(image)
     threshold = entropy(limage)
     threshold = T.inverse_log_transform(threshold, d)
     expected = image > threshold
     workspace, module = self.make_workspace(image)
     module.binary.value = A.BINARY
     module.threshold_scope.value = I.TS_GLOBAL
     module.threshold_method.value = T.TM_OTSU
     module.use_weighted_variance.value = I.O_ENTROPY
     module.two_class_otsu.value = I.O_TWO_CLASS
     module.run(workspace)
     output = workspace.image_set.get_image(OUTPUT_IMAGE_NAME)
     self.assertTrue(np.all(output.pixel_data == expected))
 def test_05_02_otsu_entropy(self):
     '''Test the entropy version of Otsu'''
     np.random.seed(0)
     image = np.hstack((np.random.exponential(1.5, size=600),
                        np.random.poisson(15, size=300)))
     image.shape = (30, 30)
     image = stretch(image)
     limage, d = T.log_transform(image)
     threshold = entropy(limage)
     threshold = T.inverse_log_transform(threshold, d)
     expected = image > threshold
     workspace, module = self.make_workspace(image)
     module.binary.value = A.BINARY
     module.threshold_scope.value = I.TS_GLOBAL
     module.threshold_method.value = T.TM_OTSU
     module.use_weighted_variance.value = I.O_ENTROPY
     module.two_class_otsu.value = I.O_TWO_CLASS
     module.run(workspace)
     output = workspace.image_set.get_image(OUTPUT_IMAGE_NAME)
     self.assertTrue(np.all(output.pixel_data == expected))
Example #10
0
def log_transform(image):
    '''Renormalize image intensities to log space
    
    Returns a tuple of transformed image and a dictionary to be passed into
    inverse_log_transform. The minimum and maximum from the dictionary
    can be applied to an image by the inverse_log_transform to 
    convert it back to its former intensity values.
    '''
    orig_min, orig_max = scipy.ndimage.extrema(image)[:2]
    #
    # We add 1/2 bit noise to an 8 bit image to give the log a bottom
    #
    limage = image.copy()
    noise_min = orig_min + (orig_max-orig_min)/256.0+np.finfo(image.dtype).eps
    limage[limage < noise_min] = noise_min
    d = { "noise_min":noise_min}
    limage = np.log(limage)
    log_min, log_max = scipy.ndimage.extrema(limage)[:2]
    d["log_min"] = log_min
    d["log_max"] = log_max
    return stretch(limage), d
 def test_05_04_otsu3_wv_high(self):
     '''Test the three-class otsu, weighted variance middle = foreground'''
     np.random.seed(0)
     image = np.hstack((np.random.exponential(1.5, size=300),
                        np.random.poisson(15, size=300),
                        np.random.poisson(30, size=300)))
     image.shape = (30, 30)
     image = stretch(image)
     limage, d = T.log_transform(image)
     t1, t2 = otsu3(limage)
     threshold = T.inverse_log_transform(t1, d)
     workspace, module = self.make_workspace(image)
     module.binary.value = A.BINARY
     module.threshold_scope.value = I.TS_GLOBAL
     module.threshold_method.value = T.TM_OTSU
     module.use_weighted_variance.value = I.O_WEIGHTED_VARIANCE
     module.two_class_otsu.value = I.O_THREE_CLASS
     module.assign_middle_to_foreground.value = I.O_FOREGROUND
     module.run(workspace)
     m = workspace.measurements
     m_threshold = m[cpmeas.IMAGE, I.FF_ORIG_THRESHOLD % module.get_measurement_objects_name()]
     self.assertAlmostEqual(m_threshold, threshold)
Example #12
0
 def test_05_06_otsu3_entropy_high(self):
     '''Test the three-class otsu, entropy, middle = background'''
     np.random.seed(0)
     image = np.hstack(
         (np.random.exponential(1.5, size=300),
          np.random.poisson(15, size=300), np.random.poisson(30, size=300)))
     image.shape = (30, 30)
     image = stretch(image)
     limage, d = T.log_transform(image)
     t1, t2 = entropy3(limage)
     threshold = T.inverse_log_transform(t1, d)
     expected = image > threshold
     workspace, module = self.make_workspace(image)
     module.binary.value = A.BINARY
     module.threshold_scope.value = I.TS_GLOBAL
     module.threshold_method.value = T.TM_OTSU
     module.use_weighted_variance.value = I.O_ENTROPY
     module.two_class_otsu.value = I.O_THREE_CLASS
     module.assign_middle_to_foreground.value = I.O_FOREGROUND
     module.run(workspace)
     output = workspace.image_set.get_image(OUTPUT_IMAGE_NAME)
     self.assertTrue(np.all(output.pixel_data == expected))
 def test_05_04_otsu3_wv_high(self):
     '''Test the three-class otsu, weighted variance middle = foreground'''
     np.random.seed(0)
     image = np.hstack((np.random.exponential(1.5,size=300),
                        np.random.poisson(15,size=300),
                        np.random.poisson(30,size=300)))
     image.shape=(30,30)
     image = stretch(image)
     limage, d = T.log_transform(image)
     t1,t2 = otsu3(limage)
     threshold = T.inverse_log_transform(t1, d)
     workspace, module = self.make_workspace(image)
     module.binary.value = A.BINARY
     module.threshold_scope.value = I.TS_GLOBAL
     module.threshold_method.value = T.TM_OTSU
     module.use_weighted_variance.value = I.O_WEIGHTED_VARIANCE
     module.two_class_otsu.value = I.O_THREE_CLASS
     module.assign_middle_to_foreground.value = I.O_FOREGROUND
     module.run(workspace)
     m = workspace.measurements
     m_threshold = m[cpmeas.IMAGE, I.FF_ORIG_THRESHOLD % module.get_measurement_objects_name()]
     self.assertAlmostEqual(m_threshold, threshold)
 def test_05_06_otsu3_entropy_high(self):
     '''Test the three-class otsu, entropy, middle = background'''
     np.random.seed(0)
     image = np.hstack((np.random.exponential(1.5, size=300),
                        np.random.poisson(15, size=300),
                        np.random.poisson(30, size=300)))
     image.shape = (30, 30)
     image = stretch(image)
     limage, d = T.log_transform(image)
     t1, t2 = entropy3(limage)
     threshold = T.inverse_log_transform(t1, d)
     expected = image > threshold
     workspace, module = self.make_workspace(image)
     module.binary.value = A.BINARY
     module.threshold_scope.value = I.TS_GLOBAL
     module.threshold_method.value = T.TM_OTSU
     module.use_weighted_variance.value = I.O_ENTROPY
     module.two_class_otsu.value = I.O_THREE_CLASS
     module.assign_middle_to_foreground.value = I.O_FOREGROUND
     module.run(workspace)
     output = workspace.image_set.get_image(OUTPUT_IMAGE_NAME)
     self.assertTrue(np.all(output.pixel_data == expected))
Example #15
0
 def run_image_gabor(self, image_name, scale, workspace):
     image = workspace.image_set.get_image(image_name,
                                           must_be_grayscale=True)
     pixel_data = image.pixel_data
     labels = np.ones(pixel_data.shape, int)
     if image.has_mask:
         labels[~image.mask] = 0
     pixel_data = stretch(pixel_data, labels > 0)
     best_score = 0
     for angle in range(self.gabor_angles.value):
         theta = np.pi * angle / self.gabor_angles.value
         g = gabor(pixel_data, labels, scale, theta)
         score_r = np.sum(g.real)
         score_i = np.sum(g.imag)
         score = np.sqrt(score_r ** 2 + score_i ** 2)
         best_score = max(best_score, score)
     statistics = self.record_image_measurement(workspace,
                                                image_name,
                                                scale,
                                                F_GABOR,
                                                best_score)
     return statistics
 def test_05_05_otsu3_entropy_low(self):
     '''Test the three-class otsu, entropy, middle = background'''
     np.random.seed(0)
     image = np.hstack((np.random.exponential(1.5, size=300),
                        np.random.poisson(15, size=300),
                        np.random.poisson(30, size=300)))
     image.shape = (30, 30)
     image = stretch(image)
     limage, d = T.log_transform(image)
     t1, t2 = entropy3(limage)
     threshold = T.inverse_log_transform(t2, d)
     workspace, module = self.make_workspace(image)
     module.binary.value = A.BINARY
     module.threshold_scope.value = I.TS_GLOBAL
     module.threshold_method.value = T.TM_OTSU
     module.use_weighted_variance.value = I.O_ENTROPY
     module.two_class_otsu.value = I.O_THREE_CLASS
     module.assign_middle_to_foreground.value = I.O_BACKGROUND
     module.run(workspace)
     output = workspace.image_set.get_image(OUTPUT_IMAGE_NAME)
     m = workspace.measurements
     m_threshold = m[cpmeas.IMAGE, I.FF_ORIG_THRESHOLD % module.get_measurement_objects_name()]
     self.assertAlmostEqual(m_threshold, threshold)
 def test_05_05_otsu3_entropy_low(self):
     '''Test the three-class otsu, entropy, middle = background'''
     np.random.seed(0)
     image = np.hstack((np.random.exponential(1.5,size=300),
                        np.random.poisson(15,size=300),
                        np.random.poisson(30,size=300)))
     image.shape=(30,30)
     image = stretch(image)
     limage, d = T.log_transform(image)
     t1,t2 = entropy3(limage)
     threshold = T.inverse_log_transform(t2, d)
     workspace, module = self.make_workspace(image)
     module.binary.value = A.BINARY
     module.threshold_scope.value = I.TS_GLOBAL
     module.threshold_method.value = T.TM_OTSU
     module.use_weighted_variance.value = I.O_ENTROPY
     module.two_class_otsu.value = I.O_THREE_CLASS
     module.assign_middle_to_foreground.value = I.O_BACKGROUND
     module.run(workspace)
     output = workspace.image_set.get_image(OUTPUT_IMAGE_NAME)
     m = workspace.measurements
     m_threshold = m[cpmeas.IMAGE, I.FF_ORIG_THRESHOLD % module.get_measurement_objects_name()]
     self.assertAlmostEqual(m_threshold, threshold)
Example #18
0
    def handle_interaction(self, current_shape, orig_image):
        '''Show the cropping user interface'''
        import matplotlib as M
        import matplotlib.cm
        import wx
        from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
        pixel_data = stretch(orig_image)
        #
        # Create the UI - a dialog with a figure inside
        #
        style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
        dialog_box = wx.Dialog(wx.GetApp().TopWindow, -1,
                               "Select the cropping region",
                               size=(640,480),
                               style = style)
        sizer = wx.BoxSizer(wx.VERTICAL)
        figure = matplotlib.figure.Figure()
        panel = FigureCanvasWxAgg(dialog_box, -1, figure)
        sizer.Add(panel, 1, wx.EXPAND)
        btn_sizer = wx.StdDialogButtonSizer()
        btn_sizer.AddButton(wx.Button(dialog_box, wx.ID_OK))
        btn_sizer.AddButton(wx.Button(dialog_box, wx.ID_CANCEL))
        btn_sizer.Realize()
        sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 5)
        dialog_box.SetSizer(sizer)
        dialog_box.Size = dialog_box.BestSize
        dialog_box.Layout()

        axes = figure.add_subplot(1,1,1)
        assert isinstance(axes, matplotlib.axes.Axes)
        if pixel_data.ndim == 2:
            axes.imshow(pixel_data, matplotlib.cm.Greys_r, origin="upper")
        else:
            axes.imshow(pixel_data, origin="upper")
        #t = axes.transData.inverted()
        current_handle = [ None ]
        def data_xy(mouse_event):
            '''Return the mouse event's x & y converted into data-relative coords'''
            x = mouse_event.xdata
            y = mouse_event.ydata
            return (x,y)

        class handle(M.patches.Rectangle):
            dm = max((10,min(pixel_data.shape)/50))
            height, width = (dm,dm)
            def __init__(self, x, y, on_move):
                x = max(0, min(x, pixel_data.shape[1]))
                y = max(0, min(y, pixel_data.shape[0]))
                self.__selected = False
                self.__color = cpprefs.get_primary_outline_color()
                self.__color = np.hstack((self.__color,[255])).astype(float) / 255.0
                self.__on_move = on_move
                super(handle, self).__init__((x-self.width/2, y-self.height/2),
                                             self.width, self.height,
                                             edgecolor = self.__color,
                                             facecolor = "none")
                self.set_picker(True)

            def move(self, x, y):
                self.set_xy((x-self.width/2, y-self.height/2))
                self.__on_move(x, y)

            def select(self, on):
                self.__selected = on
                if on:
                    current_handle[0] = self
                    self.set_facecolor(self.__color)

                else:
                    self.set_facecolor("none")
                    if current_handle[0] == self:
                        current_handle[0] = None
                figure.canvas.draw()
                dialog_box.Update()

            @property
            def is_selected(self):
                return self.__selected

            @property
            def center_x(self):
                '''The handle's notion of its x coordinate'''
                return self.get_x() + self.get_width() / 2

            @property
            def center_y(self):
                '''The handle's notion of its y coordinate'''
                return self.get_y() + self.get_height() / 2

            def handle_pick(self, event):
                mouse_event = event.mouseevent
                x, y = data_xy(mouse_event)
                if mouse_event.button == 1:
                    self.select(True)
                    self.orig_x = self.center_x
                    self.orig_y = self.center_y
                    self.first_x = x
                    self.first_y = y

            def handle_mouse_move_event(self, event):
                x,y = data_xy(event)
                if x is None or y is None:
                    return
                x = x - self.first_x + self.orig_x
                y = y - self.first_y + self.orig_y
                if x < 0:
                    x = 0
                if x >= pixel_data.shape[1]:
                    x = pixel_data.shape[1] -1
                if y < 0:
                    y = 0
                if y >= pixel_data.shape[0]:
                    y = pixel_data.shape[0] -1
                self.move(x, y)

        class crop_rectangle(object):
            def __init__(self, top_left, bottom_right):
                self.__left, self.__top = top_left
                self.__right, self.__bottom = bottom_right
                color = cpprefs.get_primary_outline_color()
                color = np.hstack((color,[255])).astype(float) / 255.0
                self.rectangle = M.patches.Rectangle(
                    (min(self.__left, self.__right),
                     min(self.__bottom, self.__top)),
                    abs(self.__right - self.__left),
                    abs(self.__top - self.__bottom),
                    edgecolor = color,
                    facecolor = "none"
                )
                self.top_left_handle = handle(top_left[0], top_left[1],
                                              self.handle_top_left)
                self.bottom_right_handle = handle(bottom_right[0],
                                                  bottom_right[1],
                                                  self.handle_bottom_right)

            def handle_top_left(self, x, y):
                self.__left = x
                self.__top = y
                self.__reshape()

            def handle_bottom_right(self, x, y):
                self.__right = x
                self.__bottom = y
                self.__reshape()

            def __reshape(self):
                self.rectangle.set_xy((min(self.__left, self.__right),
                                       min(self.__bottom,self.__top)))
                self.rectangle.set_width(abs(self.__right - self.__left))
                self.rectangle.set_height(abs(self.__bottom - self.__top))
                self.rectangle.figure.canvas.draw()
                dialog_box.Update()

            @property
            def patches(self):
                return [self.rectangle, self.top_left_handle,
                        self.bottom_right_handle]

            @property
            def handles(self):
                return [self.top_left_handle, self.bottom_right_handle]

            @property
            def left(self):
                return min(self.__left, self.__right)

            @property
            def right(self):
                return max(self.__left, self.__right)

            @property
            def top(self):
                return min(self.__top, self.__bottom)

            @property
            def bottom(self):
                return max(self.__top, self.__bottom)

        class crop_ellipse(object):
            def __init__(self, center, radius):
                '''Draw an ellipse with control points at the ellipse center and
                a given x and y radius'''
                self.center_x, self.center_y = center
                self.radius_x = self.center_x + radius[0] / 2
                self.radius_y = self.center_y + radius[1] / 2
                color = cpprefs.get_primary_outline_color()
                color = np.hstack((color,[255])).astype(float) / 255.0
                self.ellipse = M.patches.Ellipse(center, self.width, self.height,
                                                 edgecolor = color,
                                                 facecolor = "none")
                self.center_handle = handle(self.center_x, self.center_y,
                                            self.move_center)
                self.radius_handle = handle(self.radius_x, self.radius_y,
                                            self.move_radius)

            def move_center(self, x, y):
                self.center_x = x
                self.center_y = y
                self.redraw()

            def move_radius(self, x, y):
                self.radius_x = x
                self.radius_y = y
                self.redraw()

            @property
            def width(self):
                return abs(self.center_x - self.radius_x) * 4

            @property
            def height(self):
                return abs(self.center_y - self.radius_y) * 4

            def redraw(self):
                self.ellipse.center = (self.center_x, self.center_y)
                self.ellipse.width = self.width
                self.ellipse.height = self.height
                self.ellipse.figure.canvas.draw()
                dialog_box.Update()

            @property
            def patches(self):
                return [self.ellipse, self.center_handle, self.radius_handle]

            @property
            def handles(self):
                return [self.center_handle, self.radius_handle]

        if self.shape == SH_ELLIPSE:
            if current_shape is None:
                current_shape = {
                    EL_XCENTER: pixel_data.shape[1] / 2,
                    EL_YCENTER: pixel_data.shape[0] / 2,
                    EL_XRADIUS: pixel_data.shape[1] / 2,
                    EL_YRADIUS: pixel_data.shape[0] / 2
                    }
            ellipse = current_shape
            shape = crop_ellipse((ellipse[EL_XCENTER], ellipse[EL_YCENTER]),
                                 (ellipse[EL_XRADIUS], ellipse[EL_YRADIUS]))
        else:
            if current_shape is None:
                current_shape = {
                    RE_LEFT: pixel_data.shape[1] / 4,
                    RE_TOP: pixel_data.shape[0] / 4,
                    RE_RIGHT: pixel_data.shape[1] * 3 / 4,
                    RE_BOTTOM: pixel_data.shape[0] * 3 / 4
                    }
            rectangle = current_shape
            shape = crop_rectangle((rectangle[RE_LEFT], rectangle[RE_TOP]),
                                   (rectangle[RE_RIGHT], rectangle[RE_BOTTOM]))
        for patch in shape.patches:
            axes.add_artist(patch)

        def on_mouse_down_event(event):
            axes.pick(event)

        def on_mouse_move_event(event):
            if current_handle[0] is not None:
                current_handle[0].handle_mouse_move_event(event)

        def on_mouse_up_event(event):
            if current_handle[0] is not None:
                current_handle[0].select(False)

        def on_pick_event(event):
            for h in shape.handles:
                if id(h) == id(event.artist):
                    h.handle_pick(event)

        figure.canvas.mpl_connect('button_press_event', on_mouse_down_event)
        figure.canvas.mpl_connect('button_release_event', on_mouse_up_event)
        figure.canvas.mpl_connect('motion_notify_event', on_mouse_move_event)
        figure.canvas.mpl_connect('pick_event', on_pick_event)

        try:
            if dialog_box.ShowModal() != wx.ID_OK:
                raise ValueError("Cancelled by user")
        finally:
            dialog_box.Destroy()
        if self.shape == SH_RECTANGLE:
            return {
                RE_LEFT: shape.left,
                RE_TOP: shape.top,
                RE_RIGHT: shape.right,
                RE_BOTTOM: shape.bottom
                }
        else:
            return {
                EL_XCENTER: shape.center_x,
                EL_YCENTER: shape.center_y,
                EL_XRADIUS: shape.width / 2,
                EL_YRADIUS: shape.height / 2
                }
Example #19
0
 def stretch(self, input_image):
     '''Stretch the input image to the range 0:1'''
     if input_image.has_mask:
         return stretch(input_image.pixel_data, input_image.mask)
     else:
         return stretch(input_image.pixel_data)
 def stretch(self, input_image):
     '''Stretch the input image to the range 0:1'''
     if input_image.has_mask:
         return stretch(input_image.pixel_data, input_image.mask)
     else:
         return stretch(input_image.pixel_data)
Example #21
0
 def test_00_04_half(self):
     result = F.stretch(np.ones((10, 10)) * .5)
     self.assertTrue(np.all(result == .5))
Example #22
0
 def test_00_05_half_plus_mask(self):
     result = F.stretch(np.ones((10, 10)) * .5, np.ones((10, 10), bool))
     self.assertTrue(np.all(result == .5))
Example #23
0
 def test_00_03_zeros_plus_mask(self):
     result = F.stretch(np.zeros((10, 10)), np.ones((10, 10), bool))
     self.assertTrue(np.all(result == 0))
Example #24
0
 def test_00_02_zeros(self):
     result = F.stretch(np.zeros((10, 10)))
     self.assertTrue(np.all(result == 0))
Example #25
0
 def test_00_01_empty_plus_mask(self):
     result = F.stretch(np.zeros((0, )), np.zeros((0, ), bool))
     self.assertEqual(len(result), 0)
Example #26
0
 def test_00_00_empty(self):
     result = F.stretch(np.zeros((0, )))
     self.assertEqual(len(result), 0)
Example #27
0
    def handle_interaction(self, current_shape, orig_image):
        '''Show the cropping user interface'''
        import matplotlib as M
        import matplotlib.cm
        import wx
        from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg
        pixel_data = stretch(orig_image)
        #
        # Create the UI - a dialog with a figure inside
        #
        style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
        dialog_box = wx.Dialog(wx.GetApp().TopWindow, -1,
                               "Select the cropping region",
                               size=(640,480),
                               style = style)
        sizer = wx.BoxSizer(wx.VERTICAL)
        figure = matplotlib.figure.Figure()
        panel = FigureCanvasWxAgg(dialog_box, -1, figure)
        sizer.Add(panel, 1, wx.EXPAND)
        btn_sizer = wx.StdDialogButtonSizer()
        btn_sizer.AddButton(wx.Button(dialog_box, wx.ID_OK))
        btn_sizer.AddButton(wx.Button(dialog_box, wx.ID_CANCEL))
        btn_sizer.Realize()
        sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 5)
        dialog_box.SetSizer(sizer)
        dialog_box.Size = dialog_box.BestSize
        dialog_box.Layout()

        axes = figure.add_subplot(1,1,1)
        assert isinstance(axes, matplotlib.axes.Axes)
        if pixel_data.ndim == 2:
            axes.imshow(pixel_data, matplotlib.cm.Greys_r, origin="upper")
        else:
            axes.imshow(pixel_data, origin="upper")
        #t = axes.transData.inverted()
        current_handle = [ None ]
        def data_xy(mouse_event):
            '''Return the mouse event's x & y converted into data-relative coords'''
            x = mouse_event.xdata
            y = mouse_event.ydata
            return (x,y)
        
        class handle(M.patches.Rectangle):
            dm = max((10,min(pixel_data.shape)/50))
            height, width = (dm,dm)
            def __init__(self, x, y, on_move):
                x = max(0, min(x, pixel_data.shape[1]))
                y = max(0, min(y, pixel_data.shape[0]))
                self.__selected = False
                self.__color = cpprefs.get_primary_outline_color()
                self.__color = np.hstack((self.__color,[255])).astype(float) / 255.0
                self.__on_move = on_move
                super(handle, self).__init__((x-self.width/2, y-self.height/2),
                                             self.width, self.height,
                                             edgecolor = self.__color,
                                             facecolor = "none")
                self.set_picker(True)
                
            def move(self, x, y):
                self.set_xy((x-self.width/2, y-self.height/2))
                self.__on_move(x, y)
                
            def select(self, on):
                self.__selected = on
                if on:
                    current_handle[0] = self
                    self.set_facecolor(self.__color)
                    
                else:
                    self.set_facecolor("none")
                    if current_handle[0] == self:
                        current_handle[0] = None
                figure.canvas.draw()
                dialog_box.Update()
                
            @property
            def is_selected(self):
                return self.__selected
            
            @property
            def center_x(self):
                '''The handle's notion of its x coordinate'''
                return self.get_x() + self.get_width() / 2
            
            @property
            def center_y(self):
                '''The handle's notion of its y coordinate'''
                return self.get_y() + self.get_height() / 2
            
            def handle_pick(self, event):
                mouse_event = event.mouseevent
                x, y = data_xy(mouse_event)
                if mouse_event.button == 1:
                    self.select(True)
                    self.orig_x = self.center_x
                    self.orig_y = self.center_y
                    self.first_x = x
                    self.first_y = y
                    
            def handle_mouse_move_event(self, event):
                x,y = data_xy(event)
                if x is None or y is None:
                    return
                x = x - self.first_x + self.orig_x
                y = y - self.first_y + self.orig_y
                if x < 0:
                    x = 0
                if x >= pixel_data.shape[1]:
                    x = pixel_data.shape[1] -1
                if y < 0:
                    y = 0
                if y >= pixel_data.shape[0]:
                    y = pixel_data.shape[0] -1
                self.move(x, y)
            
        class crop_rectangle(object):
            def __init__(self, top_left, bottom_right):
                self.__left, self.__top = top_left
                self.__right, self.__bottom = bottom_right
                color = cpprefs.get_primary_outline_color()
                color = np.hstack((color,[255])).astype(float) / 255.0
                self.rectangle = M.patches.Rectangle(
                    (min(self.__left, self.__right), 
                     min(self.__bottom, self.__top)),
                    abs(self.__right - self.__left),
                    abs(self.__top - self.__bottom),
                    edgecolor = color,
                    facecolor = "none"
                )
                self.top_left_handle = handle(top_left[0], top_left[1],
                                              self.handle_top_left)
                self.bottom_right_handle = handle(bottom_right[0], 
                                                  bottom_right[1], 
                                                  self.handle_bottom_right)
                
            def handle_top_left(self, x, y):
                self.__left = x
                self.__top = y
                self.__reshape()
                
            def handle_bottom_right(self, x, y):
                self.__right = x
                self.__bottom = y
                self.__reshape()
                
            def __reshape(self):
                self.rectangle.set_xy((min(self.__left, self.__right), 
                                       min(self.__bottom,self.__top)))
                self.rectangle.set_width(abs(self.__right - self.__left))
                self.rectangle.set_height(abs(self.__bottom - self.__top))
                self.rectangle.figure.canvas.draw()
                dialog_box.Update()
                
            @property
            def patches(self):
                return [self.rectangle, self.top_left_handle, 
                        self.bottom_right_handle]
            
            @property
            def handles(self):
                return [self.top_left_handle, self.bottom_right_handle]
            
            @property
            def left(self):
                return min(self.__left, self.__right)
            
            @property
            def right(self):
                return max(self.__left, self.__right)
            
            @property
            def top(self):
                return min(self.__top, self.__bottom)
            
            @property
            def bottom(self):
                return max(self.__top, self.__bottom)

        class crop_ellipse(object):
            def __init__(self, center, radius):
                '''Draw an ellipse with control points at the ellipse center and
                a given x and y radius'''
                self.center_x, self.center_y = center
                self.radius_x = self.center_x + radius[0] / 2
                self.radius_y = self.center_y + radius[1] / 2
                color = cpprefs.get_primary_outline_color()
                color = np.hstack((color,[255])).astype(float) / 255.0
                self.ellipse = M.patches.Ellipse(center, self.width, self.height,
                                                 edgecolor = color,
                                                 facecolor = "none")
                self.center_handle = handle(self.center_x, self.center_y,
                                            self.move_center)
                self.radius_handle = handle(self.radius_x, self.radius_y,
                                            self.move_radius)
                
            def move_center(self, x, y):
                self.center_x = x
                self.center_y = y
                self.redraw()
                
            def move_radius(self, x, y):
                self.radius_x = x
                self.radius_y = y
                self.redraw()
                
            @property
            def width(self):
                return abs(self.center_x - self.radius_x) * 4
            
            @property
            def height(self):
                return abs(self.center_y - self.radius_y) * 4
            
            def redraw(self):
                self.ellipse.center = (self.center_x, self.center_y)
                self.ellipse.width = self.width
                self.ellipse.height = self.height
                self.ellipse.figure.canvas.draw()
                dialog_box.Update()
                
            @property
            def patches(self):
                return [self.ellipse, self.center_handle, self.radius_handle] 
            
            @property
            def handles(self):
                return [self.center_handle, self.radius_handle]
            
        if self.shape == SH_ELLIPSE:
            if current_shape is None:
                current_shape = {
                    EL_XCENTER: pixel_data.shape[1] / 2,
                    EL_YCENTER: pixel_data.shape[0] / 2,
                    EL_XRADIUS: pixel_data.shape[1] / 2,
                    EL_YRADIUS: pixel_data.shape[0] / 2
                    }
            ellipse = current_shape
            shape = crop_ellipse((ellipse[EL_XCENTER], ellipse[EL_YCENTER]),
                                 (ellipse[EL_XRADIUS], ellipse[EL_YRADIUS]))
        else:
            if current_shape is None:
                current_shape = {
                    RE_LEFT: pixel_data.shape[1] / 4,
                    RE_TOP: pixel_data.shape[0] / 4,
                    RE_RIGHT: pixel_data.shape[1] * 3 / 4,
                    RE_BOTTOM: pixel_data.shape[0] * 3 / 4
                    }
            rectangle = current_shape
            shape = crop_rectangle((rectangle[RE_LEFT], rectangle[RE_TOP]),
                                   (rectangle[RE_RIGHT], rectangle[RE_BOTTOM]))
        for patch in shape.patches:
            axes.add_artist(patch)
            
        def on_mouse_down_event(event):
            axes.pick(event)
            
        def on_mouse_move_event(event):
            if current_handle[0] is not None:
                current_handle[0].handle_mouse_move_event(event)
                
        def on_mouse_up_event(event):
            if current_handle[0] is not None:
                current_handle[0].select(False)
            
        def on_pick_event(event):
            for h in shape.handles:
                if id(h) == id(event.artist):
                    h.handle_pick(event)
                    
        figure.canvas.mpl_connect('button_press_event', on_mouse_down_event)
        figure.canvas.mpl_connect('button_release_event', on_mouse_up_event)
        figure.canvas.mpl_connect('motion_notify_event', on_mouse_move_event)
        figure.canvas.mpl_connect('pick_event', on_pick_event)
        
        try:
            if dialog_box.ShowModal() != wx.ID_OK:
                raise ValueError("Cancelled by user")
        finally:
            dialog_box.Destroy()
        if self.shape == SH_RECTANGLE:
            return {
                RE_LEFT: shape.left,
                RE_TOP: shape.top,
                RE_RIGHT: shape.right,
                RE_BOTTOM: shape.bottom
                }
        else:
            return {
                EL_XCENTER: shape.center_x,
                EL_YCENTER: shape.center_y,
                EL_XRADIUS: shape.width / 2,
                EL_YRADIUS: shape.height / 2
                }