def put_calibrate_images(self, screen_image, cam_img):
     """calibrate(self, screen_image, cam_img) -> None
     Find the screen corners, transform matrix, and light state"""
 
     # find screen corners by comparing the difference between previous taken images
     if self._md is None:
         self._md = MotionDetector(N=2, shape=cam_img.shape)
 
     self._md.feed_image(cam_img.copy())
     
     gray_diff = cvtColor(self._md.diff, COLOR_BGR2GRAY)
     quadrangles = find_polygons(gray_diff, 4, 0.1, 1000, True, 10)
     
     if quadrangles != []:
         self.screens = quadrangles
 
     if self.screens is not None:
         self.compute_transform(screen_img)
class ScreenFinder(object):
    """This class find the location of the screen by checking
    if there is a quadrangle area that varies with respect to time"""

    def __init__(self, depth=2):
        self.screens = None
        self.screen_size = None
        self._md = None
        self._mapping_matrix = None
        self.real_screen_shape = None
        
    def put_calibrate_images(self, screen_image, cam_img):
        """calibrate(self, screen_image, cam_img) -> None
        Find the screen corners, transform matrix, and light state"""
    
        # find screen corners by comparing the difference between previous taken images
        if self._md is None:
            self._md = MotionDetector(N=2, shape=cam_img.shape)
    
        self._md.feed_image(cam_img.copy())
        
        gray_diff = cvtColor(self._md.diff, COLOR_BGR2GRAY)
        quadrangles = find_polygons(gray_diff, 4, 0.1, 1000, True, 10)
        
        if quadrangles != []:
            self.screens = quadrangles
    
        if self.screens is not None:
            self.compute_transform(screen_img)
        
        
    
    def compute_transform(self, screen_img):
        
        if self.screens is None:
            print 'Warning: calibration failed for the screen is not found yet'
            return
        
        # load the width and height of the real screen
        w, h = screen_img.shape[1], screen_img.shape[0]
        self.real_screen_shape = (w, h)
        
        # prepare the points
        src_pts = self.screens[0].astype(np.float)
        h, w = screen_img.shape[0], screen_img.shape[1]
        dst_pts = np.array([[0, 0], [0, h], [w, h], [w, 0]], dtype=np.float)
        
        # find transformation matrix
        self._mapping_matrix, mask = findHomography(src_pts, dst_pts, RANSAC, 5.0)
        
    
    def findTopView(self, cam_img):
        img = warpPerspective(cam_img, self._mapping_matrix, self.real_screen_shape)
        return img
    
    def find_laser_spots(self, screen_img, cam_img):
        # find location of green/red spots on camera image
        
        # transform the point and compute where it is
        pass
        
        
        
    def reset(self):
        self.screens = None
            
    def find(self):
        pass
Exemple #3
0
    def __init__(self,
                 test_res,
                 test_borders=None,
                 full_res=None,
                 localdir=None,
                 remotedir=None,
                 secs=5):
        self.test_res = test_res
        self.width = test_res[0]
        self.height = test_res[1]
        self.localdir = localdir
        self.remotedir = remotedir
        self.full_res = full_res
        self.millisecs = secs * 1000

        self.win = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.win.set_border_width(10)

        # Unfortunately delete and destroy events don't work on the RPi.
        # Not sure why, but most of the time the event never gets passed.
        # It works on other Linux platforms.
        self.win.connect("delete_event", self.quit)
        self.win.connect("destroy", self.quit)

        self.drawing_area = gtk.DrawingArea()
        self.drawing_area.set_size_request(self.width, self.height)
        self.win.add(self.drawing_area)

        self.drawing_area.set_events(
            gtk.gdk.EXPOSURE_MASK |
            # gtk.gdk.POINTER_MOTION_MASK |
            # gtk.gdk.POINTER_MOTION_HINT_MASK |
            gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
        self.drawing_area.connect("expose-event", self.expose_handler)
        self.drawing_area.connect("button_press_event", self.button_press)
        self.drawing_area.connect("button_release_event", self.button_release)
        self.drawing_area.connect("motion_notify_event", self.drag_handler)

        # Just for testing, temporarily
        self.num = 0

        gobject.timeout_add(self.millisecs, self.idle_handler,
                            self.drawing_area)

        self.gc = None
        self.pixbuf = None
        self.imgwidth = None
        self.imgheight = None
        self.cur_img = None
        self.drag_start_x = None
        self.drag_start_y = None

        self.win.show_all()

        # And the motion detecting parts,
        # which are infinitely simpler than the GTK garbage:
        self.md = MotionDetector(test_res=test_res,
                                 test_borders=test_borders,
                                 full_res=self.full_res,
                                 localdir=self.localdir,
                                 remotedir=self.localdir,
                                 verbose=2)
        self.buf1 = None
        self.buf2 = None
    def __init__(self,
                 test_res,
                 test_borders=None,
                 full_res=None,
                 localdir=None,
                 remotedir=None,
                 secs=5,
                 rangefinder=None):
        self.test_res = test_res
        self.width = test_res[0]
        self.height = test_res[1]
        self.localdir = localdir
        self.remotedir = remotedir
        self.full_res = full_res
        self.millisecs = secs * 1000

        self.use_tmp_file = True

        self.win = gtk.Window()
        self.win.set_border_width(10)

        # Unfortunately delete and destroy events don't work on the RPi.
        # Not sure why, but most of the time the event never gets passed.
        # It works on other Linux platforms.
        self.win.connect("delete_event", self.quit)
        self.win.connect("destroy", self.quit)

        vbox = gtk.VBox(spacing=3)
        self.win.add(vbox)

        self.drawing_area = gtk.DrawingArea()
        self.drawing_area.set_size_request(self.width, self.height)
        #self.win.add(self.drawing_area)
        vbox.pack_start(self.drawing_area)

        self.drawing_area.set_events(
            gtk.gdk.EXPOSURE_MASK |
            # gtk.gdk.POINTER_MOTION_MASK |
            # gtk.gdk.POINTER_MOTION_HINT_MASK |
            gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)

        try:
            # GTK2:
            self.drawing_area.connect("expose-event", self.expose_handler)
        except TypeError:
            # Python3/GI GTK3:
            self.drawing_area.connect('size-allocate', self.on_size_allocate)
            self.width = self.height = 0
            self.drawing_area.connect('draw', self.expose3)

        self.drawing_area.connect("button_press_event", self.button_press)
        self.drawing_area.connect("button_release_event", self.button_release)
        self.drawing_area.connect("motion_notify_event", self.drag_handler)

        # Just for testing, temporarily
        self.num = 0

        glib.timeout_add(self.millisecs, self.idle_handler, self.drawing_area)

        # GTK2: X GC
        self.gc = None

        self.redgc = None
        self.pixbuf = None
        self.imgwidth = None
        self.imgheight = None
        self.cur_img = None
        self.drag_start_x = None
        self.drag_start_y = None

        try:
            self.win.show_all()
        except:
            print("Couldn't show window: exiting")
            sys.exit(1)

        # And the motion detecting parts,
        # which are infinitely simpler than the GTK garbage:
        self.md = MotionDetector(test_res=test_res,
                                 test_borders=test_borders,
                                 full_res=self.full_res,
                                 sensitivity=400,
                                 threshold=30,
                                 localdir=self.localdir,
                                 remotedir=self.localdir,
                                 rangefinder=rangefinder,
                                 verbose=2)
        self.buf1 = None
        self.buf2 = None
class MotionDetectorViewer():
    '''
    '''
    def __init__(self,
                 test_res,
                 test_borders=None,
                 full_res=None,
                 localdir=None,
                 remotedir=None,
                 secs=5,
                 rangefinder=None):
        self.test_res = test_res
        self.width = test_res[0]
        self.height = test_res[1]
        self.localdir = localdir
        self.remotedir = remotedir
        self.full_res = full_res
        self.millisecs = secs * 1000

        self.use_tmp_file = True

        self.win = gtk.Window()
        self.win.set_border_width(10)

        # Unfortunately delete and destroy events don't work on the RPi.
        # Not sure why, but most of the time the event never gets passed.
        # It works on other Linux platforms.
        self.win.connect("delete_event", self.quit)
        self.win.connect("destroy", self.quit)

        vbox = gtk.VBox(spacing=3)
        self.win.add(vbox)

        self.drawing_area = gtk.DrawingArea()
        self.drawing_area.set_size_request(self.width, self.height)
        #self.win.add(self.drawing_area)
        vbox.pack_start(self.drawing_area)

        self.drawing_area.set_events(
            gtk.gdk.EXPOSURE_MASK |
            # gtk.gdk.POINTER_MOTION_MASK |
            # gtk.gdk.POINTER_MOTION_HINT_MASK |
            gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)

        try:
            # GTK2:
            self.drawing_area.connect("expose-event", self.expose_handler)
        except TypeError:
            # Python3/GI GTK3:
            self.drawing_area.connect('size-allocate', self.on_size_allocate)
            self.width = self.height = 0
            self.drawing_area.connect('draw', self.expose3)

        self.drawing_area.connect("button_press_event", self.button_press)
        self.drawing_area.connect("button_release_event", self.button_release)
        self.drawing_area.connect("motion_notify_event", self.drag_handler)

        # Just for testing, temporarily
        self.num = 0

        glib.timeout_add(self.millisecs, self.idle_handler, self.drawing_area)

        # GTK2: X GC
        self.gc = None

        self.redgc = None
        self.pixbuf = None
        self.imgwidth = None
        self.imgheight = None
        self.cur_img = None
        self.drag_start_x = None
        self.drag_start_y = None

        try:
            self.win.show_all()
        except:
            print("Couldn't show window: exiting")
            sys.exit(1)

        # And the motion detecting parts,
        # which are infinitely simpler than the GTK garbage:
        self.md = MotionDetector(test_res=test_res,
                                 test_borders=test_borders,
                                 full_res=self.full_res,
                                 sensitivity=400,
                                 threshold=30,
                                 localdir=self.localdir,
                                 remotedir=self.localdir,
                                 rangefinder=rangefinder,
                                 verbose=2)
        self.buf1 = None
        self.buf2 = None

    # There doesn't seem to be any way to predict reliably whether a
    # windowmanager delete event will pass 3 arguments or 2, so make
    # them all optional. We don't need them anyway.
    # Also, this is sometimes called twice (or, on the Pi, not at all).
    def quit(self, widget=None, event=None):
        print("Quitting")
        # Supposedly, return True if you don't want to close the window,
        # False if you do. In practice, neither one actually exits;
        # they just close the window and execution continues in such
        # a way that ctrl-C doesn't work and ctrl-\ and a core dump
        # is the only option.
        # return False

        # On the Pi, this crashes Python and dumps core:
        # gtk.main_quit()

        # This isn't what you're supposed to do, but it's the only
        # way I've found to actually exit without dumping core.
        sys.exit(0)

    def on_size_allocate(self, _unused, allocation):
        self.width = allocation.width
        self.height = allocation.height

    def expose3(self, _unused, _ctx):
        self.expose_handler(self.drawing_area, None)

    def expose_handler(self, widget, event):
        if not self.gc:
            try:
                self.gc = widget.window.new_gc()
                self.redgc = widget.window.new_gc()
                self.redgc.set_rgb_fg_color(gtk.gdk.Color(65535, 0, 0))
                self.redgc.line_width = 5
            except:
                pass
            #x, y, self.imgwidth, self.imgheight = self.get_allocation()

            # Have we had load_image called, but we weren't ready for it?
            # Now, theoretically, we are ... so call it again.
            if self.cur_img and not self.pixbuf:
                self.load_image(self.cur_img)

        self.show_image()

    def button_press(self, widget, event):
        print("Button press for button", event.button)
        if event.button != 1:
            return False
        # For some reason, button press events give float arguments
        # though draw_line won't accept floats
        self.drag_start_x = int(event.x)
        self.drag_start_y = int(event.y)
        return True

    def button_release(self, widget, event):
        print("Button release for button", event.button)
        x = int(event.x)
        y = int(event.y)
        self.md.test_borders = [[[self.drag_start_x, x],
                                 [self.drag_start_y, y]]]
        print("Reset test borders to", self.md.test_borders)
        self.drag_start_x = None
        self.drag_start_y = None
        return True

    def drag_handler(self, widget, event):
        return True

    def load_image(self, img):
        '''Load the image passed in, and show it.
           Image can be a PIL Image or a filename.
           Return True for success, False for error.
        '''
        print("load_image", img)
        self.cur_img = img
        if not img:
            print("No image to load, returning")
            return

        # Is this a PIL Image? Does it have a mode attribute?
        if hasattr(img, 'mode'):
            print("Displaying the image already in memory")
            has_alpha = img.mode == 'RGBA'
            newpb = gtk.gdk.pixbuf_new_from_data(
                img.tobytes(),  # data
                gtk.gdk.COLORSPACE_RGB,  # color mode
                has_alpha,
                8,  # bits
                img.size[0],  # width
                img.size[1],  # height
                (has_alpha and 4 or 3) * img.size[0]  # rowstride
            )
        # If it's not an image, assume it's a file.
        else:
            print("Reading an image in from", img)
            newpb = gtk.gdk.pixbuf_new_from_file(img)

        # Clean up memory from any existing pixbuf.
        # This still needs to be garbage collected before returning.
        if self.pixbuf:
            self.pixbuf = None

        try:
            self.pixbuf = newpb

        except glib.GError as e:
            print("glib error -- couldn't load")
            print(e)
            self.pixbuf = None

        # garbage collect the old pixbuf, if any, and the one we just read in:
        newpb = None
        gc.collect()

    def run(self):
        gtk.main()

    def clear(self):
        # Clear the drawing area
        self.drawing_area.window.draw_rectangle(self.gc, True, 0, 0,
                                                self.width, self.height)

    def show_image(self):
        if not self.pixbuf:
            print("No pixbuf!")
            return

        # GTK2?
        if self.gc:
            self.drawing_area.window.draw_pixbuf(self.gc, self.pixbuf, 0, 0, 0,
                                                 0)
            return

        # GTK3: all drawing is done through a Cairo context, cr.
        # It has to be created fresh each time.
        cr = self.drawing_area.get_window().cairo_create()

        # GTK3 pixbuf drawing seems to be almost completely undocumented.
        # The last two args of cairo_set_source_pixbuf are the point
        # on the canvas matching the (0, 0) point of the pixmap.
        gtk.gdk.cairo_set_source_pixbuf(cr, self.pixbuf, 0, 0)

        # Then these are the coordinates of the rectangle to draw
        # on the canvas.
        cr.rectangle(0, 0, self.width, self.height)

        # Some examples use paint() instead of fill();
        # I can't find any documentation explaining the difference,
        # but using paint() adds black borders around the images.
        cr.fill()

    # This is the function that actually takes and compares photos
    # every few seconds and does all the work.
    def idle_handler(self, widget):
        print()
        changed = False
        debugimage = None

        if not changed:
            if self.use_tmp_file:
                tmpfile = "/tmp/still.jpg"
                print("Snapping to", tmpfile)
                self.md.locam.take_still(outfile=tmpfile, res=self.test_res)
                im = Image.open(tmpfile)
            else:  # keep it all in memory, no temp files
                print("Snapping to memory")
                img_data = self.md.locam.take_still(outfile='-',
                                                    res=self.test_res)
                im = Image.open(img_data)

            changed, debugimage = self.md.compare_images(im)

        def red_frame():
            diff = 10
            if self.redgc:
                self.drawing_area.window.draw_rectangle(
                    self.redgc, False, diff, diff, self.width - diff * 2,
                    self.height - diff * 2)
            else:  # cairo way
                cr = self.drawing_area.get_window().cairo_create()
                cr.set_source_rgb(1., 0., 0.)
                cr.rectangle(float(diff), float(diff),
                             float(self.width - diff * 2),
                             float(self.height - diff * 2))
                cr.stroke()

        if changed:
            print("**** They're different!")
            red_frame()
            self.md.snap_full_res()

        if debugimage:
            # debugimage.load()
            self.load_image(debugimage)
            self.show_image()
            # We just overwrote the frame, so re-draw it:
            if changed:
                red_frame()

        self.buf1 = self.buf2

        return True
Exemple #6
0
    def __init__(self, test_res, test_borders=None,
                 full_res=None,
                 localdir=None, remotedir=None,
                 secs=5,
                 rangefinder=None):
        self.test_res = test_res
        self.width = test_res[0]
        self.height = test_res[1]
        self.localdir = localdir
        self.remotedir = remotedir
        self.full_res = full_res
        self.millisecs = secs * 1000

        self.use_tmp_file = True

        self.win = gtk.Window()
        self.win.set_border_width(10)

        # Unfortunately delete and destroy events don't work on the RPi.
        # Not sure why, but most of the time the event never gets passed.
        # It works on other Linux platforms.
        self.win.connect("delete_event", self.quit)
        self.win.connect("destroy", self.quit)

        vbox = gtk.VBox(spacing=3)
        self.win.add(vbox)

        self.drawing_area = gtk.DrawingArea()
        self.drawing_area.set_size_request(self.width, self.height)
        #self.win.add(self.drawing_area)
        vbox.pack_start(self.drawing_area)

        self.drawing_area.set_events(gtk.gdk.EXPOSURE_MASK |
                                     # gtk.gdk.POINTER_MOTION_MASK |
                                     # gtk.gdk.POINTER_MOTION_HINT_MASK |
                                     gtk.gdk.BUTTON_PRESS_MASK |
                                     gtk.gdk.BUTTON_RELEASE_MASK )

        try:
            # GTK2:
            self.drawing_area.connect("expose-event", self.expose_handler)
        except TypeError:
            # Python3/GI GTK3:
            self.drawing_area.connect('size-allocate', self.on_size_allocate)
            self.width = self.height = 0
            self.drawing_area.connect('draw', self.expose3)

        self.drawing_area.connect("button_press_event", self.button_press)
        self.drawing_area.connect("button_release_event", self.button_release)
        self.drawing_area.connect("motion_notify_event", self.drag_handler)

        # Just for testing, temporarily
        self.num = 0

        glib.timeout_add(self.millisecs, self.idle_handler,
                         self.drawing_area)

        # GTK2: X GC
        self.gc = None

        self.redgc = None
        self.pixbuf = None
        self.imgwidth = None
        self.imgheight = None
        self.cur_img = None
        self.drag_start_x = None
        self.drag_start_y = None

        try:
            self.win.show_all()
        except:
            print("Couldn't show window: exiting")
            sys.exit(1)

        # And the motion detecting parts,
        # which are infinitely simpler than the GTK garbage:
        self.md = MotionDetector(test_res=test_res, test_borders=test_borders,
                                 full_res=self.full_res,
                                 sensitivity=400,
                                 threshold=30,
                                 localdir=self.localdir,
                                 remotedir=self.localdir,
                                 rangefinder=rangefinder,
                                 verbose=2)
        self.buf1 = None
        self.buf2 = None
Exemple #7
0
class MotionDetectorViewer() :
    '''
    '''
    def __init__(self, test_res, test_borders=None,
                 full_res=None,
                 localdir=None, remotedir=None,
                 secs=5,
                 rangefinder=None):
        self.test_res = test_res
        self.width = test_res[0]
        self.height = test_res[1]
        self.localdir = localdir
        self.remotedir = remotedir
        self.full_res = full_res
        self.millisecs = secs * 1000

        self.use_tmp_file = True

        self.win = gtk.Window()
        self.win.set_border_width(10)

        # Unfortunately delete and destroy events don't work on the RPi.
        # Not sure why, but most of the time the event never gets passed.
        # It works on other Linux platforms.
        self.win.connect("delete_event", self.quit)
        self.win.connect("destroy", self.quit)

        vbox = gtk.VBox(spacing=3)
        self.win.add(vbox)

        self.drawing_area = gtk.DrawingArea()
        self.drawing_area.set_size_request(self.width, self.height)
        #self.win.add(self.drawing_area)
        vbox.pack_start(self.drawing_area)

        self.drawing_area.set_events(gtk.gdk.EXPOSURE_MASK |
                                     # gtk.gdk.POINTER_MOTION_MASK |
                                     # gtk.gdk.POINTER_MOTION_HINT_MASK |
                                     gtk.gdk.BUTTON_PRESS_MASK |
                                     gtk.gdk.BUTTON_RELEASE_MASK )

        try:
            # GTK2:
            self.drawing_area.connect("expose-event", self.expose_handler)
        except TypeError:
            # Python3/GI GTK3:
            self.drawing_area.connect('size-allocate', self.on_size_allocate)
            self.width = self.height = 0
            self.drawing_area.connect('draw', self.expose3)

        self.drawing_area.connect("button_press_event", self.button_press)
        self.drawing_area.connect("button_release_event", self.button_release)
        self.drawing_area.connect("motion_notify_event", self.drag_handler)

        # Just for testing, temporarily
        self.num = 0

        glib.timeout_add(self.millisecs, self.idle_handler,
                         self.drawing_area)

        # GTK2: X GC
        self.gc = None

        self.redgc = None
        self.pixbuf = None
        self.imgwidth = None
        self.imgheight = None
        self.cur_img = None
        self.drag_start_x = None
        self.drag_start_y = None

        try:
            self.win.show_all()
        except:
            print("Couldn't show window: exiting")
            sys.exit(1)

        # And the motion detecting parts,
        # which are infinitely simpler than the GTK garbage:
        self.md = MotionDetector(test_res=test_res, test_borders=test_borders,
                                 full_res=self.full_res,
                                 sensitivity=400,
                                 threshold=30,
                                 localdir=self.localdir,
                                 remotedir=self.localdir,
                                 rangefinder=rangefinder,
                                 verbose=2)
        self.buf1 = None
        self.buf2 = None

    # There doesn't seem to be any way to predict reliably whether a
    # windowmanager delete event will pass 3 arguments or 2, so make
    # them all optional. We don't need them anyway.
    # Also, this is sometimes called twice (or, on the Pi, not at all).
    def quit(self, widget=None, event=None):
        print("Quitting")
        # Supposedly, return True if you don't want to close the window,
        # False if you do. In practice, neither one actually exits;
        # they just close the window and execution continues in such
        # a way that ctrl-C doesn't work and ctrl-\ and a core dump
        # is the only option.
        # return False

        # On the Pi, this crashes Python and dumps core:
        # gtk.main_quit()

        # This isn't what you're supposed to do, but it's the only
        # way I've found to actually exit without dumping core.
        sys.exit(0)

    def on_size_allocate(self, _unused, allocation):
        self.width = allocation.width
        self.height = allocation.height

    def expose3(self, _unused, _ctx):
        self.expose_handler(self.drawing_area, None)

    def expose_handler(self, widget, event):
        if not self.gc:
            try:
                self.gc = widget.window.new_gc()
                self.redgc = widget.window.new_gc()
                self.redgc.set_rgb_fg_color(gtk.gdk.Color(65535, 0, 0))
                self.redgc.line_width = 5
            except:
                pass
            #x, y, self.imgwidth, self.imgheight = self.get_allocation()

            # Have we had load_image called, but we weren't ready for it?
            # Now, theoretically, we are ... so call it again.
            if self.cur_img and not self.pixbuf :
                self.load_image(self.cur_img)

        self.show_image()

    def button_press(self, widget, event):
        print("Button press for button", event.button)
        if event.button != 1 :
            return False
        # For some reason, button press events give float arguments
        # though draw_line won't accept floats
        self.drag_start_x = int(event.x)
        self.drag_start_y = int(event.y)
        return True

    def button_release(self, widget, event):
        print("Button release for button", event.button)
        x = int(event.x)
        y = int(event.y)
        self.md.test_borders = [[[self.drag_start_x, x],
                                 [self.drag_start_y, y]]]
        print("Reset test borders to", self.md.test_borders)
        self.drag_start_x = None
        self.drag_start_y = None
        return True

    def drag_handler(self, widget, event):
        return True

    def load_image(self, img):
        '''Load the image passed in, and show it.
           Image can be a PIL Image or a filename.
           Return True for success, False for error.
        '''
        print("load_image", img)
        self.cur_img = img
        if not img:
            print("No image to load, returning")
            return

        # Is this a PIL Image? Does it have a mode attribute?
        if hasattr(img, 'mode'):
            print("Displaying the image already in memory")
            has_alpha = img.mode == 'RGBA'
            newpb = gtk.gdk.pixbuf_new_from_data(
                img.tobytes(),          # data
                gtk.gdk.COLORSPACE_RGB, # color mode
                has_alpha,
                8,                      # bits
                img.size[0],            # width
                img.size[1],            # height
                (has_alpha and 4 or 3) * img.size[0] # rowstride
                )
        # If it's not an image, assume it's a file.
        else:
            print("Reading an image in from", img)
            newpb = gtk.gdk.pixbuf_new_from_file(img)

        # Clean up memory from any existing pixbuf.
        # This still needs to be garbage collected before returning.
        if self.pixbuf :
            self.pixbuf = None

        try :
            self.pixbuf = newpb

        except glib.GError as e :
            print("glib error -- couldn't load")
            print(e)
            self.pixbuf = None

        # garbage collect the old pixbuf, if any, and the one we just read in:
        newpb = None
        gc.collect()

    def run(self):
        gtk.main()

    def clear(self):
        # Clear the drawing area
        self.drawing_area.window.draw_rectangle(self.gc, True, 0, 0,
                                                self.width, self.height)

    def show_image(self):
        if not self.pixbuf:
            print("No pixbuf!")
            return

        # GTK2?
        if self.gc:
            self.drawing_area.window.draw_pixbuf(self.gc, self.pixbuf,
                                                 0, 0, 0, 0)
            return

        # GTK3: all drawing is done through a Cairo context, cr.
        # It has to be created fresh each time.
        cr = self.drawing_area.get_window().cairo_create()

        # GTK3 pixbuf drawing seems to be almost completely undocumented.
        # The last two args of cairo_set_source_pixbuf are the point
        # on the canvas matching the (0, 0) point of the pixmap.
        gtk.gdk.cairo_set_source_pixbuf(cr, self.pixbuf, 0, 0)

        # Then these are the coordinates of the rectangle to draw
        # on the canvas.
        cr.rectangle(0, 0, self.width, self.height)

        # Some examples use paint() instead of fill();
        # I can't find any documentation explaining the difference,
        # but using paint() adds black borders around the images.
        cr.fill()

    # This is the function that actually takes and compares photos
    # every few seconds and does all the work.
    def idle_handler(self, widget):
        print()
        changed = False
        debugimage = None

        if not changed:
            if self.use_tmp_file:
                tmpfile = "/tmp/still.jpg"
                print("Snapping to", tmpfile)
                self.md.locam.take_still(outfile=tmpfile, res=self.test_res)
                im = Image.open(tmpfile)
            else:   # keep it all in memory, no temp files
                print("Snapping to memory")
                img_data = self.md.locam.take_still(outfile='-',
                                                    res=self.test_res)
                im = Image.open(img_data)

            changed, debugimage = self.md.compare_images(im)

        def red_frame():
            diff = 10
            if self.redgc:
                self.drawing_area.window.draw_rectangle(self.redgc, False,
                                                        diff, diff,
                                                        self.width-diff*2,
                                                        self.height-diff*2)
            else:    # cairo way
                cr = self.drawing_area.get_window().cairo_create()
                cr.set_source_rgb(1., 0., 0.)
                cr.rectangle(float(diff), float(diff),
                             float(self.width-diff*2),
                             float(self.height-diff*2))
                cr.stroke()

        if changed:
            print("**** They're different!")
            red_frame()
            self.md.snap_full_res()

        if debugimage:
            # debugimage.load()
            self.load_image(debugimage)
            self.show_image()
            # We just overwrote the frame, so re-draw it:
            if changed:
                red_frame()

        self.buf1 = self.buf2

        return True