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
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
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