def __init__(self, scene, left=None, top=None, right=None, bottom=None, all=False): list.__init__(self) self.matrix = Matrix() self.scene = scene self.parent = None self.properties = ORIENTATION_RELATIVE if all: # contains all points self.left = -float('inf') self.top = -float('inf') self.right = float('inf') self.bottom = float('inf') else: # contains no points self.left = float('inf') self.top = float('inf') self.right = -float('inf') self.bottom = -float('inf') if left is not None: self.left = left if right is not None: self.right = right if top is not None: self.top = top if bottom is not None: self.bottom = bottom
def __init__(self): Module.__init__(self) self.hittable_elements = list() self.hit_chain = list() self.widget_root = SceneSpaceWidget(self) self.matrix_root = Matrix() self.process = self.animate_tick self.interval = 1.0 / 60.0 # 60fps self.last_position = None self.time = None self.distance = None
def update_step_image(self): element = self.image_element step_value = self.spin_step_size.GetValue() m = element.transform tx = m.e ty = m.f element.transform = Matrix.scale(float(step_value), float(step_value)) element.transform.post_translate(tx, ty) element.modified() if self.device is not None: self.device.signal('element_property_update', element) self.device.signal('refresh_scene')
def rebuild_hit_chain(self, current_widget, current_matrix=None): # If there is a matrix for the widget concatenate it. if current_widget.matrix is not None: matrix_within_scene = Matrix(current_widget.matrix) matrix_within_scene.post_cat(current_matrix) else: matrix_within_scene = Matrix(current_matrix) # Add to list and recurse for children based on response. response = current_widget.hit() if response == HITCHAIN_HIT: self.hittable_elements.append( (current_widget, matrix_within_scene)) elif response == HITCHAIN_DELEGATE: for w in current_widget: self.rebuild_hit_chain(w, matrix_within_scene) elif response == HITCHAIN_HIT_AND_DELEGATE: self.hittable_elements.append( (current_widget, matrix_within_scene)) for w in current_widget: self.rebuild_hit_chain(w, matrix_within_scene) elif response == HITCHAIN_DELEGATE_AND_HIT: for w in current_widget: self.rebuild_hit_chain(w, matrix_within_scene) self.hittable_elements.append( (current_widget, matrix_within_scene))
class Widget(list): def __init__(self, scene, left=None, top=None, right=None, bottom=None, all=False): list.__init__(self) self.matrix = Matrix() self.scene = scene self.parent = None self.properties = ORIENTATION_RELATIVE if all: # contains all points self.left = -float('inf') self.top = -float('inf') self.right = float('inf') self.bottom = float('inf') else: # contains no points self.left = float('inf') self.top = float('inf') self.right = -float('inf') self.bottom = -float('inf') if left is not None: self.left = left if right is not None: self.right = right if top is not None: self.top = top if bottom is not None: self.bottom = bottom def __str__(self): return 'Widget(%f, %f, %f, %f)' % (self.left, self.top, self.right, self.bottom) def __repr__(self): return '%s(%f, %f, %f, %f)' % (type(self).__name__, self.left, self.top, self.right, self.bottom) def hit(self): return HITCHAIN_DELEGATE def draw(self, gc): # Concat if this is a thing. m = self.matrix gc.PushState() gc.ConcatTransform(wx.GraphicsContext.CreateMatrix(gc, ZMatrix(m))) self.process_draw(gc) for i in range(len(self) - 1, -1, -1): widget = self[i] widget.draw(gc) gc.PopState() def process_draw(self, gc): pass def contains(self, x, y=None): if y is None: y = x.y x = x.x return self.left <= x <= self.right and \ self.top <= y <= self.bottom def event(self, window_pos=None, space_pos=None, event_type=None): return RESPONSE_CHAIN def notify_added_to_parent(self, parent): pass def notify_added_child(self, child): pass def notify_removed_from_parent(self, parent): pass def notify_removed_child(self, child): pass def notify_moved_child(self, child): pass def add_widget(self, index=-1, widget=None, properties=0): if len(self) == 0: last = None else: last = self[-1] if 0 <= index < len(self): self.insert(index, widget) else: self.append(widget) widget.parent = self self.layout_by_orientation(widget, last, properties) self.notify_added_to_parent(self) self.notify_added_child(widget) def translate(self, dx, dy): if dx == 0 and dy == 0: return if dx == float('nan'): return if dy == float('nan'): return if abs(dx) == float('inf'): return if abs(dy) == float('inf'): return self.translate_loop(dx, dy) def translate_loop(self, dx, dy): if self.properties & ORIENTATION_ABSOLUTE != 0: return # Do not translate absolute oriented widgets. self.translate_self(dx, dy) for w in self: w.translate_loop(dx, dy) def translate_self(self, dx, dy): self.left += dx self.right += dx self.top += dy self.bottom += dy if self.parent is not None: self.notify_moved_child(self) def union_children_bounds(self, bounds=None): if bounds is None: bounds = [self.left, self.top, self.right, self.bottom] else: if bounds[0] > self.left: bounds[0] = self.left if bounds[1] > self.top: bounds[1] = self.top if bounds[2] < self.right: bounds[2] = self.left if bounds[3] < self.bottom: bounds[3] = self.bottom for w in self: w.union_children_bounds(bounds) return bounds @property def height(self): return self.bottom - self.top @property def width(self): return self.right - self.left def layout_by_orientation(self, widget, last, properties): if properties & ORIENTATION_ABSOLUTE != 0: return if properties & ORIENTATION_NO_BUFFER != 0: buffer = 0 else: buffer = BUFFER if (properties & ORIENTATION_MODE_MASK) == ORIENTATION_RELATIVE: widget.translate(self.left, self.top) return elif last is None: # orientation = origin widget.translate(self.left - widget.left, self.top - widget.top) elif (properties & ORIENTATION_GRID) != 0: dim = properties & ORIENTATION_DIM_MASK if (properties & ORIENTATION_VERTICAL) != 0: if dim == 0: # Vertical if self.height >= last.bottom - self.top + widget.height: # add to line widget.translate(last.left - widget.left, last.bottom - widget.top) else: # line return widget.translate(last.right - widget.left + buffer, self.top - widget.top) else: if dim == 0: # Horizontal if self.width >= last.right - self.left + widget.width: # add to line widget.translate(last.right - widget.left + buffer, last.top - widget.top) else: # line return widget.translate(self.left - widget.left, last.bottom - widget.top + buffer) elif (properties & ORIENTATION_HORIZONTAL) != 0: widget.translate(last.right - widget.left + buffer, last.top - widget.top) elif (properties & ORIENTATION_VERTICAL) != 0: widget.translate(last.left - widget.left, last.bottom - widget.top + buffer) if properties & ORIENTATION_CENTERED: self.center_children() def center_children(self): child_bounds = self.union_children_bounds() dx = self.left - (child_bounds[0] + child_bounds[2]) / 2.0 dy = self.top - (child_bounds[1] + child_bounds[3]) / 2.0 if dx != 0 and dy != 0: for w in self: w.translate_loop(dx, dy) def center_widget(self, x, y=None): if y is None: y = x.y x = x.x child_bounds = self.union_children_bounds() cx = (child_bounds[0] + child_bounds[2]) / 2.0 cy = (child_bounds[1] + child_bounds[3]) / 2.0 self.translate(x - cx, y - cy) def set_position(self, x, y=None): if y is None: y = x.y x = x.x dx = x - self.left dy = y - self.top self.translate(dx, dy) def remove_all_widgets(self): for w in self: if w is None: continue w.parent = None w.notify_removed_from_parent(self) self.notify_removed_child(w) self.clear() try: self.scene.notify_tree_changed() except AttributeError: pass def remove_widget(self, widget=None): if widget is None: return if isinstance(widget, Widget): list.remove(widget) elif isinstance(widget, int): index = widget widget = self[index] list.remove(index) widget.parent = None widget.notify_removed_from_parent(self) self.notify_removed_child(widget) try: self.scene.notify_tree_changed() except AttributeError: pass def set_widget(self, index, widget): w = self[index] self[index] = widget widget.parent = self widget.notify_added_to_parent(self) self.notify_removed_child(w) try: self.scene.notify_tree_changed() except AttributeError: pass def on_matrix_change(self): pass def scene_matrix_reset(self): self.matrix.reset() self.on_matrix_change() def scene_post_scale(self, sx, sy=None, ax=0, ay=0): self.matrix.post_scale(sx, sy, ax, ay) self.on_matrix_change() def scene_post_pan(self, px, py): self.matrix.post_translate(px, py) self.on_matrix_change() def scene_post_rotate(self, angle, rx=0, ry=0): self.matrix.post_rotate(angle, rx, ry) self.on_matrix_change() def scene_pre_scale(self, sx, sy=None, ax=0, ay=0): self.matrix.pre_scale(sx, sy, ax, ay) self.on_matrix_change() def scene_pre_pan(self, px, py): self.matrix.pre_translate(px, py) self.on_matrix_change() def scene_pre_rotate(self, angle, rx=0, ry=0): self.matrix.pre_rotate(angle, rx, ry) self.on_matrix_change() def get_scale_x(self): return self.matrix.value_scale_x() def get_scale_y(self): return self.matrix.value_scale_y() def get_skew_x(self): return self.matrix.value_skew_x() def get_skew_y(self): return self.matrix.value_skew_y() def get_translate_x(self): return self.matrix.value_trans_x() def get_translate_y(self): return self.matrix.value_trans_y()
kernel.load(os.path.realpath(args.input.name)) if args.path is not None: # Force the inclusion of the path. from svgelements import Path try: path = Path(args.path) path.stroke = Color('blue') kernel.elements.add_elem(path) except Exception: print("SVG Path Exception to: %s" % ' '.join(sys.argv)) if args.transform: # Transform any data loaded data from svgelements import Matrix m = Matrix(args.transform) for e in kernel.elements.elems(): e *= m try: e.modified() except AttributeError: pass if args.set is not None: # Set the variables requested here. for v in args.set: attr = v[0] value = v[1] if hasattr(device, attr): v = getattr(device, attr) if isinstance(v, bool):
def wizard_image(svg_image, operations): image = svg_image.image matrix = Matrix(svg_image.transform) step = None mask = None from PIL import Image, ImageOps, ImageFilter, ImageEnhance for op in operations: name = op['name'] if name == 'crop': try: if op['enable'] and op['bounds'] is not None: crop = op['bounds'] left = int(crop[0]) upper = int(crop[1]) right = int(crop[2]) lower = int(crop[3]) image = image.crop((left, upper, right, lower)) except KeyError: pass if name == 'resample': try: if op['enable']: image, matrix = RasterScripts.actualize( image, matrix, step_level=op['step']) step = op['step'] except KeyError: pass if name == 'grayscale': try: if op['enable']: try: r = op['red'] * 0.299 g = op['green'] * 0.587 b = op['blue'] * 0.114 v = op['lightness'] c = r + g + b try: c /= v r = r / c g = g / c b = b / c except ZeroDivisionError: pass m = [r, g, b, 1.0] if image.mode != "L": if image.mode == "P" or image.mode == '1': image = image.convert('RGBA') if op['invert']: color = 0, 0, 0 c8 = 0 else: color = 255, 255, 255 c8 = 255 if image.mode == 'RGBA': background = Image.new( 'RGB', image.size, color) background.paste( image, mask=image.getchannel('A')) image = background image = image.convert("L", matrix=m) def mask_filter(e): if e == c8: return 0 else: return 255 mask = image.point( mask_filter ) # Makes a mask out of Alpha or pure mask color. if op['invert']: image = ImageOps.invert(image) except (KeyError, OSError): pass except KeyError: pass if name == 'edge_enhance': try: if op['enable']: if image.mode == 'P': image = image.convert('L') image = image.filter(filter=ImageFilter.EDGE_ENHANCE) except KeyError: pass if name == 'auto_contrast': try: if op['enable']: if image.mode != 'P': image = image.convert('L') image = ImageOps.autocontrast(image, cutoff=op['cutoff']) except KeyError: pass if name == 'tone': try: if op['enable'] and op['values'] is not None: if image.mode == 'L': image = image.convert('P') tone_values = op['values'] if op['type'] == 'spline': spline = RasterScripts.spline(tone_values) else: tone_values = [ q for q in tone_values if q is not None ] spline = RasterScripts.line(tone_values) if len(spline) < 256: spline.extend([255] * (256 - len(spline))) if len(spline) > 256: spline = spline[:256] image = image.point(spline) if image.mode != 'L': image = image.convert('L') except KeyError: pass if name == 'contrast': try: if op['enable']: if op['contrast'] is not None and op[ 'brightness'] is not None: contrast = ImageEnhance.Contrast(image) c = (op['contrast'] + 128.0) / 128.0 image = contrast.enhance(c) brightness = ImageEnhance.Brightness(image) b = (op['brightness'] + 128.0) / 128.0 image = brightness.enhance(b) except KeyError: pass if name == 'gamma': try: if op['enable'] and op['factor'] is not None: if image.mode == 'L': gamma_factor = float(op['factor']) def crimp(px): px = int(round(px)) if px < 0: return 0 if px > 255: return 255 return px if gamma_factor == 0: gamma_lut = [0] * 256 else: gamma_lut = [ crimp( pow(i / 255, (1.0 / gamma_factor)) * 255) for i in range(256) ] image = image.point(gamma_lut) if image.mode != 'L': image = image.convert('L') except KeyError: pass if name == 'unsharp_mask': try: if op['enable'] and \ op['percent'] is not None and \ op['radius'] is not None and \ op['threshold'] is not None: unsharp = ImageFilter.UnsharpMask( radius=op['radius'], percent=op['percent'], threshold=op['threshold']) image = image.filter(unsharp) except (KeyError, ValueError): # Value error if wrong type of image. pass if name == 'dither': try: if op['enable'] and op['type'] is not None: if mask is not None: background = Image.new(image.mode, image.size, 'white') background.paste(image, mask=mask) image = background # Mask exists use it to remove any pixels that were pure reject. if image.mode == 'RGBA': pixel_data = image.load() width, height = image.size for y in range(height): for x in range(width): if pixel_data[x, y][3] == 0: pixel_data[x, y] = (255, 255, 255, 255) image = image.convert("1") except KeyError: pass if name == 'halftone': try: if op['enable']: image = RasterScripts.halftone( image, sample=op['sample'], angle=op['angle'], oversample=op['oversample'], black=op['black']) except KeyError: pass return image, matrix, step
def __init__(self, *args, **kwds): # begin wxGlade: CameraInterface.__init__ kwds["style"] = kwds.get( "style", 0 ) | wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.TAB_TRAVERSAL wx.Frame.__init__(self, *args, **kwds) Module.__init__(self) self.SetSize((608, 549)) self.CameraInterface_menubar = wx.MenuBar() wxglade_tmp_menu = wx.Menu() item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Reset Perspective"), "") self.Bind(wx.EVT_MENU, self.reset_perspective, id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Reset Fisheye"), "") self.Bind(wx.EVT_MENU, self.reset_fisheye, id=item.GetId()) wxglade_tmp_menu.AppendSeparator() item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set IP Camera 1"), "", wx.ITEM_RADIO) self.camera_ip_menu1 = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(-1), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set IP Camera 2"), "", wx.ITEM_RADIO) self.camera_ip_menu2 = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(-2), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set IP Camera 3"), "", wx.ITEM_RADIO) self.camera_ip_menu3 = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(-3), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 0"), "", wx.ITEM_RADIO) self.camera_0_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(0), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 1"), "", wx.ITEM_RADIO) self.camera_1_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(1), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 2"), "", wx.ITEM_RADIO) self.camera_2_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(2), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 3"), "", wx.ITEM_RADIO) self.camera_3_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(3), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 4"), "", wx.ITEM_RADIO) self.camera_4_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(4), id=item.GetId()) self.CameraInterface_menubar.Append(wxglade_tmp_menu, _("Camera")) self.SetMenuBar(self.CameraInterface_menubar) # Menu Bar self.button_update = wx.BitmapButton(self, wx.ID_ANY, icons8_camera_50.GetBitmap()) self.button_export = wx.BitmapButton( self, wx.ID_ANY, icons8_picture_in_picture_alternative_50.GetBitmap()) self.check_fisheye = wx.CheckBox(self, wx.ID_ANY, _("Correct Fisheye")) self.check_perspective = wx.CheckBox(self, wx.ID_ANY, _("Correct Perspective")) self.slider_fps = wx.Slider(self, wx.ID_ANY, 1, 0, 24, style=wx.SL_AUTOTICKS | wx.SL_HORIZONTAL | wx.SL_LABELS) self.button_detect = wx.BitmapButton(self, wx.ID_ANY, icons8_detective_50.GetBitmap()) self.display_camera = wx.Panel(self, wx.ID_ANY) self.__set_properties() self.__do_layout() self.capture = None self.image_width = -1 self.image_height = -1 self._Buffer = None self.frame_bitmap = None self.fisheye_k = None self.fisheye_d = None # Used during calibration. self.objpoints = [] # 3d point in real world space self.imgpoints = [] # 2d points in image plane. # Perspective Points self.perspective = None self.previous_window_position = None self.previous_scene_position = None self.corner_drag = None self.matrix = Matrix() self.Bind(wx.EVT_BUTTON, self.on_button_update, self.button_update) self.Bind(wx.EVT_BUTTON, self.on_button_export, self.button_export) self.Bind(wx.EVT_CHECKBOX, self.on_check_fisheye, self.check_fisheye) self.Bind(wx.EVT_CHECKBOX, self.on_check_perspective, self.check_perspective) self.Bind(wx.EVT_SLIDER, self.on_slider_fps, self.slider_fps) self.Bind(wx.EVT_BUTTON, self.on_button_detect, self.button_detect) self.SetDoubleBuffered(True) # end wxGlade self.display_camera.Bind(wx.EVT_PAINT, self.on_paint) self.display_camera.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase) self.display_camera.Bind(wx.EVT_MOTION, self.on_mouse_move) self.display_camera.Bind(wx.EVT_MOUSEWHEEL, self.on_mousewheel) self.display_camera.Bind(wx.EVT_MIDDLE_UP, self.on_mouse_middle_up) self.display_camera.Bind(wx.EVT_MIDDLE_DOWN, self.on_mouse_middle_down) self.display_camera.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_left_down) self.display_camera.Bind(wx.EVT_LEFT_UP, self.on_mouse_left_up) self.display_camera.Bind(wx.EVT_ENTER_WINDOW, lambda event: self.display_camera.SetFocus( )) # Focus follows mouse. self.Bind(wx.EVT_CLOSE, self.on_close, self) self.on_size(None) self.Bind(wx.EVT_SIZE, self.on_size, self) self.camera_lock = threading.Lock() self.process = self.fetch_image self.camera_job = None self.fetch_job = None
class CameraInterface(wx.Frame, Module): def __init__(self, *args, **kwds): # begin wxGlade: CameraInterface.__init__ kwds["style"] = kwds.get( "style", 0 ) | wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.TAB_TRAVERSAL wx.Frame.__init__(self, *args, **kwds) Module.__init__(self) self.SetSize((608, 549)) self.CameraInterface_menubar = wx.MenuBar() wxglade_tmp_menu = wx.Menu() item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Reset Perspective"), "") self.Bind(wx.EVT_MENU, self.reset_perspective, id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Reset Fisheye"), "") self.Bind(wx.EVT_MENU, self.reset_fisheye, id=item.GetId()) wxglade_tmp_menu.AppendSeparator() item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set IP Camera 1"), "", wx.ITEM_RADIO) self.camera_ip_menu1 = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(-1), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set IP Camera 2"), "", wx.ITEM_RADIO) self.camera_ip_menu2 = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(-2), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set IP Camera 3"), "", wx.ITEM_RADIO) self.camera_ip_menu3 = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(-3), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 0"), "", wx.ITEM_RADIO) self.camera_0_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(0), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 1"), "", wx.ITEM_RADIO) self.camera_1_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(1), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 2"), "", wx.ITEM_RADIO) self.camera_2_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(2), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 3"), "", wx.ITEM_RADIO) self.camera_3_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(3), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 4"), "", wx.ITEM_RADIO) self.camera_4_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(4), id=item.GetId()) self.CameraInterface_menubar.Append(wxglade_tmp_menu, _("Camera")) self.SetMenuBar(self.CameraInterface_menubar) # Menu Bar self.button_update = wx.BitmapButton(self, wx.ID_ANY, icons8_camera_50.GetBitmap()) self.button_export = wx.BitmapButton( self, wx.ID_ANY, icons8_picture_in_picture_alternative_50.GetBitmap()) self.check_fisheye = wx.CheckBox(self, wx.ID_ANY, _("Correct Fisheye")) self.check_perspective = wx.CheckBox(self, wx.ID_ANY, _("Correct Perspective")) self.slider_fps = wx.Slider(self, wx.ID_ANY, 1, 0, 24, style=wx.SL_AUTOTICKS | wx.SL_HORIZONTAL | wx.SL_LABELS) self.button_detect = wx.BitmapButton(self, wx.ID_ANY, icons8_detective_50.GetBitmap()) self.display_camera = wx.Panel(self, wx.ID_ANY) self.__set_properties() self.__do_layout() self.capture = None self.image_width = -1 self.image_height = -1 self._Buffer = None self.frame_bitmap = None self.fisheye_k = None self.fisheye_d = None # Used during calibration. self.objpoints = [] # 3d point in real world space self.imgpoints = [] # 2d points in image plane. # Perspective Points self.perspective = None self.previous_window_position = None self.previous_scene_position = None self.corner_drag = None self.matrix = Matrix() self.Bind(wx.EVT_BUTTON, self.on_button_update, self.button_update) self.Bind(wx.EVT_BUTTON, self.on_button_export, self.button_export) self.Bind(wx.EVT_CHECKBOX, self.on_check_fisheye, self.check_fisheye) self.Bind(wx.EVT_CHECKBOX, self.on_check_perspective, self.check_perspective) self.Bind(wx.EVT_SLIDER, self.on_slider_fps, self.slider_fps) self.Bind(wx.EVT_BUTTON, self.on_button_detect, self.button_detect) self.SetDoubleBuffered(True) # end wxGlade self.display_camera.Bind(wx.EVT_PAINT, self.on_paint) self.display_camera.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase) self.display_camera.Bind(wx.EVT_MOTION, self.on_mouse_move) self.display_camera.Bind(wx.EVT_MOUSEWHEEL, self.on_mousewheel) self.display_camera.Bind(wx.EVT_MIDDLE_UP, self.on_mouse_middle_up) self.display_camera.Bind(wx.EVT_MIDDLE_DOWN, self.on_mouse_middle_down) self.display_camera.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_left_down) self.display_camera.Bind(wx.EVT_LEFT_UP, self.on_mouse_left_up) self.display_camera.Bind(wx.EVT_ENTER_WINDOW, lambda event: self.display_camera.SetFocus( )) # Focus follows mouse. self.Bind(wx.EVT_CLOSE, self.on_close, self) self.on_size(None) self.Bind(wx.EVT_SIZE, self.on_size, self) self.camera_lock = threading.Lock() self.process = self.fetch_image self.camera_job = None self.fetch_job = None def __do_layout(self): sizer_1 = wx.BoxSizer(wx.VERTICAL) sizer_2 = wx.BoxSizer(wx.HORIZONTAL) sizer_3 = wx.BoxSizer(wx.VERTICAL) sizer_2.Add(self.button_update, 0, 0, 0) sizer_2.Add(self.button_export, 0, 0, 0) sizer_3.Add(self.check_fisheye, 0, 0, 0) sizer_3.Add(self.check_perspective, 0, 0, 0) sizer_2.Add(sizer_3, 1, wx.EXPAND, 0) sizer_2.Add(self.slider_fps, 1, wx.EXPAND, 0) sizer_2.Add(self.button_detect, 0, 0, 0) sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) sizer_1.Add(self.display_camera, 10, wx.EXPAND, 0) self.SetSizer(sizer_1) self.Layout() def __set_properties(self): _icon = wx.NullIcon _icon.CopyFromBitmap(icons8_camera_50.GetBitmap()) self.SetIcon(_icon) # begin wxGlade: CameraInterface.__set_properties self.SetTitle("CameraInterface") self.button_update.SetToolTip(_("Update Image")) self.button_update.SetSize(self.button_update.GetBestSize()) self.button_export.SetToolTip(_("Export Snapsnot")) self.button_export.SetSize(self.button_export.GetBestSize()) self.button_detect.SetToolTip(_("Detect Distortions/Calibration")) self.button_detect.SetSize(self.button_detect.GetBestSize()) # end wxGlade def on_close(self, event): if self.state == 5: event.Veto() else: self.state = 5 self.device.close('window', self.name) event.Skip() # Call destroy as regular. def initialize(self, channel=None): self.device.close('window', self.name) self.Show() self.device.setting(int, 'draw_mode', 0) self.device.setting(int, 'camera_index', 0) self.device.setting(int, 'camera_fps', 1) self.device.setting(bool, 'mouse_zoom_invert', False) self.device.setting(bool, 'camera_correction_fisheye', False) self.device.setting(bool, 'camera_correction_perspective', False) self.device.setting(str, 'fisheye', '') self.device.setting(str, 'perspective', '') self.device.setting(str, 'camera_uri1', '') self.device.setting(str, 'camera_uri2', '') self.device.setting(str, 'camera_uri3', '') self.check_fisheye.SetValue(self.device.camera_correction_fisheye) self.check_perspective.SetValue( self.device.camera_correction_perspective) if self.device.fisheye is not None and len(self.device.fisheye) != 0: self.fisheye_k, self.fisheye_d = eval(self.device.fisheye) if self.device.perspective is not None and len( self.device.perspective) != 0: self.perspective = eval(self.device.perspective) self.slider_fps.SetValue(self.device.camera_fps) if self.device.camera_index == -3: self.camera_ip_menu3.Check(True) elif self.device.camera_index == -2: self.camera_ip_menu2.Check(True) elif self.device.camera_index == -1: self.camera_ip_menu1.Check(True) elif self.device.camera_index == 0: self.camera_0_menu.Check(True) elif self.device.camera_index == 1: self.camera_1_menu.Check(True) elif self.device.camera_index == 2: self.camera_2_menu.Check(True) elif self.device.camera_index == 3: self.camera_3_menu.Check(True) elif self.device.camera_index == 4: self.camera_4_menu.Check(True) if self.camera_job is not None: self.camera_job.cancel() self.camera_job = self.device.add_job(self.init_camera, times=1, interval=0.1) self.device.listen('camera_frame', self.on_camera_frame_update) self.device.listen('camera_frame_raw', self.on_camera_frame_raw) def finalize(self, channel=None): self.device.unlisten('camera_frame_raw', self.on_camera_frame_raw) self.device.unlisten('camera_frame', self.on_camera_frame_update) self.device.signal('camera_frame_raw', None) self.unschedule() self.camera_lock.acquire() self.close_camera() self.camera_lock.release() if self.camera_job is not None: self.camera_job.cancel() if self.fetch_job is not None: self.fetch_job.cancel() try: self.Close() except RuntimeError: pass def shutdown(self, channel=None): try: self.Close() except RuntimeError: pass def on_size(self, event): self.Layout() width, height = self.ClientSize if width <= 0: width = 1 if height <= 0: height = 1 self._Buffer = wx.Bitmap(width, height) self.update_in_gui_thread() def on_camera_frame_update(self, frame): self.on_camera_frame(frame) self.update_in_gui_thread() def on_camera_frame(self, frame): if frame is None: return bed_width = self.device.bed_width * 2 bed_height = self.device.bed_height * 2 self.image_height, self.image_width = frame.shape[:2] self.frame_bitmap = wx.Bitmap.FromBuffer(self.image_width, self.image_height, frame) if self.device.camera_correction_perspective: if bed_width != self.image_width or bed_height != self.image_height: self.image_width = bed_width self.image_height = bed_height self.display_camera.SetSize( (self.image_width, self.image_height)) def on_camera_frame_raw(self, frame): if frame is None: return try: import cv2 import numpy as np except: dlg = wx.MessageDialog(None, _("Could not import Camera requirements"), _("Imports Failed"), wx.OK) dlg.ShowModal() dlg.Destroy() CHECKERBOARD = (6, 9) subpix_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.1) calibration_flags = cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC + cv2.fisheye.CALIB_CHECK_COND + cv2.fisheye.CALIB_FIX_SKEW objp = np.zeros((1, CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) objp[0, :, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) img = frame gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Find the chess board corners ret, corners = cv2.findChessboardCorners( gray, CHECKERBOARD, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE) # If found, add object points, image points (after refining them) if ret: self.objpoints.append(objp) cv2.cornerSubPix(gray, corners, (3, 3), (-1, -1), subpix_criteria) self.imgpoints.append(corners) else: dlg = wx.MessageDialog(None, _("Checkerboard 6x9 pattern not found."), _("Pattern not found."), wx.OK) dlg.ShowModal() dlg.Destroy() return N_OK = len(self.objpoints) K = np.zeros((3, 3)) D = np.zeros((4, 1)) rvecs = [np.zeros((1, 1, 3), dtype=np.float64) for i in range(N_OK)] tvecs = [np.zeros((1, 1, 3), dtype=np.float64) for i in range(N_OK)] try: rms, a, b, c, d = \ cv2.fisheye.calibrate( self.objpoints, self.imgpoints, gray.shape[::-1], K, D, rvecs, tvecs, calibration_flags, (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6) ) except cv2.error: # Ill conditioned matrix for input values. self.objpoints = self.objpoints[:-1] # Deleting the last entry. self.imgpoints = self.imgpoints[:-1] dlg = wx.MessageDialog(None, _("Ill-conditioned Matrix. Keep trying."), _("Matrix."), wx.OK) dlg.ShowModal() dlg.Destroy() return dlg = wx.MessageDialog( None, _("Success. %d images so far." % len(self.objpoints)), _("Image Captured"), wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.device.fisheye = repr([K.tolist(), D.tolist()]) self.fisheye_k = K.tolist() self.fisheye_d = D.tolist() def swap_camera(self, camera_index=0): self.camera_lock.acquire() self.device.camera_index = camera_index self.close_camera() if self.camera_job is not None: self.camera_job.cancel() if camera_index < 0: self.camera_uri_request(abs(camera_index)) uri = self.get_camera_uri() if uri is None or len(uri) == 0: self.camera_lock.release() return self.camera_job = self.device.add_job(self.init_camera, times=1, interval=0.1) self.camera_lock.release() def close_camera(self): self.unschedule() if self.capture is not None: self.capture.release() self.capture = None def camera_error_requirement(self): try: for attr in dir(self): value = getattr(self, attr) if isinstance(value, wx.Control): value.Enable(False) dlg = wx.MessageDialog( None, _("If using a precompiled binary, this was requirement was not included.\nIf using pure Python, add it with: pip install opencv-python-headless" ), _("Interface Requires OpenCV."), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() except RuntimeError: pass def camera_error_webcam(self): try: for attr in dir(self): value = getattr(self, attr) if isinstance(value, wx.Control): value.Enable(False) dlg = wx.MessageDialog(None, _("No Webcam found."), _("Error"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() except RuntimeError: pass def camera_uri_request(self, index=None): if index is None: index = abs(self.device.camera_index) try: dlg = wx.TextEntryDialog( self, _("Enter the HTTP or RTSP uri for the webcam #%d") % index, _("Webcam URI Update"), '') dlg.SetValue(self.get_camera_uri()) modal = dlg.ShowModal() if modal == wx.ID_OK: self.set_camera_uri(str(dlg.GetValue())) wx.CallAfter(self.init_camera) else: wx.CallAfter(self.camera_error_webcam) dlg.Destroy() except RuntimeError: pass def camera_success(self): try: for attr in dir(self): value = getattr(self, attr) if isinstance(value, wx.Control): value.Enable(True) except RuntimeError: pass def set_camera_uri(self, uri): return setattr(self.device, "camera_uri%d" % abs(self.device.camera_index), uri) def get_camera_uri(self): index = self.device.camera_index if index >= 0: return index return getattr(self.device, "camera_uri%d" % abs(index)) def init_camera(self): if self.device is None: return if self.capture is not None: self.capture = None try: import cv2 except ImportError: wx.CallAfter(self.camera_error_requirement) return if self.device.camera_index >= 0: self.capture = cv2.VideoCapture(self.device.camera_index) else: uri = self.get_camera_uri() if uri is None or len(uri) == 0: return else: self.capture = cv2.VideoCapture(self.get_camera_uri()) wx.CallAfter(self.camera_success) try: self.interval = 1.0 / self.device.camera_fps except ZeroDivisionError: self.interval = 5 except AttributeError: return self.schedule() def reset_perspective(self, event): self.perspective = None self.device.perspective = '' def reset_fisheye(self, event): self.fisheye_k = None self.fisheye_d = None self.device.fisheye = '' def on_erase(self, event): pass def on_paint(self, event): try: wx.BufferedPaintDC(self.display_camera, self._Buffer) except RuntimeError: pass def on_update_buffer(self, event=None): if self.frame_bitmap is None: return # Need the bitmap to refresh. if self.device is None: return # No window to draw in. dm = self.device.draw_mode dc = wx.MemoryDC() dc.SelectObject(self._Buffer) dc.Clear() w, h = dc.Size if dm & DRAW_MODE_FLIPXY != 0: dc.SetUserScale(-1, -1) dc.SetLogicalOrigin(w, h) dc.SetBackground(wx.WHITE_BRUSH) gc = wx.GraphicsContext.Create(dc) gc.SetTransform( wx.GraphicsContext.CreateMatrix(gc, ZMatrix(self.matrix))) gc.PushState() gc.DrawBitmap(self.frame_bitmap, 0, 0, self.image_width, self.image_height) if not self.device.camera_correction_perspective: if self.perspective is None: self.perspective = [0, 0], \ [self.image_width, 0], \ [self.image_width, self.image_height], \ [0, self.image_height] gc.SetPen(wx.BLACK_DASHED_PEN) gc.StrokeLines(self.perspective) gc.StrokeLine(self.perspective[0][0], self.perspective[0][1], self.perspective[3][0], self.perspective[3][1]) gc.SetPen(wx.BLUE_PEN) for p in self.perspective: half = CORNER_SIZE / 2 gc.StrokeLine(p[0] - half, p[1], p[0] + half, p[1]) gc.StrokeLine(p[0], p[1] - half, p[0], p[1] + half) gc.DrawEllipse(p[0] - half, p[1] - half, CORNER_SIZE, CORNER_SIZE) gc.PopState() if dm & DRAW_MODE_INVERT != 0: dc.Blit(0, 0, w, h, dc, 0, 0, wx.SRC_INVERT) gc.Destroy() del dc def convert_scene_to_window(self, position): point = self.matrix.point_in_matrix_space(position) return point[0], point[1] def convert_window_to_scene(self, position): point = self.matrix.point_in_inverse_space(position) return point[0], point[1] def on_mouse_move(self, event): if not event.Dragging(): return else: self.SetCursor(wx.Cursor(wx.CURSOR_HAND)) if self.previous_window_position is None: return pos = event.GetPosition() window_position = pos.x, pos.y scene_position = self.convert_window_to_scene( [window_position[0], window_position[1]]) sdx = (scene_position[0] - self.previous_scene_position[0]) sdy = (scene_position[1] - self.previous_scene_position[1]) wdx = (window_position[0] - self.previous_window_position[0]) wdy = (window_position[1] - self.previous_window_position[1]) if self.corner_drag is None: self.scene_post_pan(wdx, wdy) else: self.perspective[self.corner_drag][0] += sdx self.perspective[self.corner_drag][1] += sdy self.device.perspective = repr(self.perspective) self.previous_window_position = window_position self.previous_scene_position = scene_position def on_mousewheel(self, event): rotation = event.GetWheelRotation() mouse = event.GetPosition() if self.device.device_root.mouse_zoom_invert: rotation = -rotation if rotation > 1: self.scene_post_scale(1.1, 1.1, mouse[0], mouse[1]) elif rotation < -1: self.scene_post_scale(0.9, 0.9, mouse[0], mouse[1]) def on_mouse_left_down(self, event): self.previous_window_position = event.GetPosition() self.previous_scene_position = self.convert_window_to_scene( self.previous_window_position) self.corner_drag = None if self.perspective is not None: for i, p in enumerate(self.perspective): half = CORNER_SIZE / 2 if Point.distance(self.previous_scene_position, p) < half: self.corner_drag = i break def on_mouse_left_up(self, event): self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) self.previous_window_position = None self.previous_scene_position = None self.corner_drag = None def on_mouse_middle_down(self, event): self.SetCursor(wx.Cursor(wx.CURSOR_HAND)) self.previous_window_position = event.GetPosition() self.previous_scene_position = self.convert_window_to_scene( self.previous_window_position) def on_mouse_middle_up(self, event): self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) self.previous_window_position = None self.previous_scene_position = None def scene_post_pan(self, px, py): self.matrix.post_translate(px, py) self.on_update_buffer() def scene_post_scale(self, sx, sy=None, ax=0, ay=0): self.matrix.post_scale(sx, sy, ax, ay) self.on_update_buffer() def fetch_image(self, raw=False): if self.device is None or self.capture is None: return try: import cv2 except ImportError: return if self.capture is None: return self.camera_lock.acquire() if self.device is None: self.camera_lock.release() return for i in range(10): ret = self.capture.grab() if not ret: if i >= 9: self.capture = None wx.CallAfter(self.camera_error_webcam) self.camera_lock.release() return time.sleep(0.05) else: break for i in range(10): ret, frame = self.capture.retrieve() if not ret or frame is None: if i >= 9: wx.CallAfter(self.camera_error_webcam) self.capture = None self.camera_lock.release() return time.sleep(0.05) else: break if not raw and \ self.fisheye_k is not None and \ self.fisheye_d is not None and \ self.device is not None and \ self.device.camera_correction_fisheye: # Unfisheye the drawing import numpy as np K = np.array(self.fisheye_k) D = np.array(self.fisheye_d) DIM = frame.shape[:2][::-1] map1, map2 = cv2.fisheye.initUndistortRectifyMap( K, D, np.eye(3), K, DIM, cv2.CV_16SC2) frame = cv2.remap(frame, map1, map2, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT) if not raw and self.device is not None and self.device.camera_correction_perspective: bed_width = self.device.bed_width * 2 bed_height = self.device.bed_height * 2 width, height = frame.shape[:2][::-1] import numpy as np if self.perspective is None: rect = np.array([[0, 0], [width - 1, 0], [width - 1, height - 1], [0, height - 1]], dtype="float32") else: rect = np.array(self.perspective, dtype="float32") dst = np.array( [[0, 0], [bed_width - 1, 0], [bed_width - 1, bed_height - 1], [0, bed_height - 1]], dtype="float32") M = cv2.getPerspectiveTransform(rect, dst) frame = cv2.warpPerspective(frame, M, (bed_width, bed_height)) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) if raw: self.device.signal('camera_frame_raw', frame) else: self.device.signal('camera_frame', frame) self.camera_lock.release() return frame def update_in_gui_thread(self): self.on_update_buffer() try: self.Refresh(True) self.Update() except RuntimeError: pass def on_check_perspective(self, event): self.device.camera_correction_perspective = self.check_perspective.GetValue( ) def on_check_fisheye(self, event): self.device.camera_correction_fisheye = self.check_fisheye.GetValue() def on_button_update(self, event): # wxGlade: CameraInterface.<event_handler> frame = self.device.last_signal('camera_frame') if frame is not None: frame = frame[0] buffer = wx.Bitmap.FromBuffer(self.image_width, self.image_height, frame) self.device.signal('background', buffer) def on_button_export(self, event): # wxGlade: CameraInterface.<event_handler> frame = self.device.last_signal('camera_frame') if frame is not None: elements = self.device.device_root.elements frame = frame[0] from PIL import Image img = Image.fromarray(frame) obj = SVGImage() obj.image = img obj.image_width = self.image_width obj.image_height = self.image_height elements.add_elem(obj) def on_slider_fps(self, event): # wxGlade: CameraInterface.<event_handler> fps = self.slider_fps.GetValue() if fps == 0: tick = 5 else: tick = 1.0 / fps self.device.camera_fps = fps self.interval = tick def on_button_detect(self, event): # wxGlade: CameraInterface.<event_handler> if self.fetch_job is not None: self.fetch_job.cancel() self.fetch_job = self.device.add_job(self.fetch_image, args=True, times=1, interval=0)
def renderLabel(self, inString): dwg = svgwrite.Drawing() # SVG drawing in memory strIdx = 0 # Used to iterate over inString xOffset = 100 # Cumulative character placement offset yOffset = 0 # Cumulative character placement offset charSizeX = 8 # Character size constant charSizeY = 8 # Character size constant baseline = 170 # Y value of text baseline glyphBounds = [ ] # List of boundingBox objects to track rendered character size finalSegments = [] # List of output paths escaped = False # Track whether the current character was preceded by a '\' lineover = False # Track whether the current character needs to be lined over lineoverList = [] # If we can't find the typeface that the user requested, we have to quit try: face = Face( os.path.dirname(os.path.abspath(__file__)) + '/typeface/' + self.fontName + '.ttf') face.set_char_size(charSizeX, charSizeY, 200, 200) except Exception as e: print(e) print("WARN: No Typeface found with the name " + self.fontName + ".ttf") sys.exit(0) # quit Python # If the typeface that the user requested exists, but there's no position table for it, we'll continue with a warning try: table = __import__( 'KiBuzzard.KiBuzzard.buzzard.typeface.' + self.fontName, globals(), locals(), ['glyphPos']) glyphPos = table.glyphPos spaceDistance = table.spaceDistance except: glyphPos = 0 spaceDistance = 60 print( "WARN: No Position Table found for this typeface. Composition will be haphazard at best." ) # If there's lineover text, drop the text down to make room for the line dropBaseline = False a = False x = 0 while x < len(inString): if x > 0 and inString[x] == '\\': a = True if x != len(inString) - 1: x += 1 if inString[x] == '!' and not a: dropBaseline = True a = False x += 1 if dropBaseline: baseline = 190 # Draw and compose the glyph portion of the tag for charIdx in range(len(inString)): # Check whether this character is a space if inString[charIdx] == ' ': glyphBounds.append(boundingBox(0, 0, 0, 0)) xOffset += spaceDistance continue # Check whether this character is a backslash that isn't escaped # and isn't the first character (denoting a backslash-shaped tag) if inString[charIdx] == '\\' and charIdx > 0 and not escaped: glyphBounds.append(boundingBox(0, 0, 0, 0)) escaped = True continue # If this is a non-escaped '!' mark the beginning of lineover if inString[charIdx] == '!' and not escaped: glyphBounds.append(boundingBox(0, 0, 0, 0)) lineover = True # If we've hit the end of the string but not the end of the lineover # go ahead and finish it out if charIdx == len(inString) - 1 and len(lineoverList) > 0: linePaths = [] linePaths.append( Line(start=complex(lineoverList[0], 10), end=complex(xOffset, 10))) linePaths.append( Line(start=complex(xOffset, 10), end=complex(xOffset, 30))) linePaths.append( Line(start=complex(xOffset, 30), end=complex(lineoverList[0], 30))) linePaths.append( Line(start=complex(lineoverList[0], 30), end=complex(lineoverList[0], 10))) linepath = Path(*linePaths) linepath = elPath(linepath.d()) finalSegments.append(linepath) lineover = False lineoverList.clear() continue # All special cases end in 'continue' so if we've gotten here we can clear our flags if escaped: escaped = False face.load_char( inString[charIdx]) # Load character curves from font outline = face.glyph.outline # Save character curves to var y = [t[1] for t in outline.points] # flip the points outline_points = [(p[0], max(y) - p[1]) for p in outline.points] start, end = 0, 0 paths = [] box = 0 yOffset = 0 for i in range(len(outline.contours)): end = outline.contours[i] points = outline_points[start:end + 1] points.append(points[0]) tags = outline.tags[start:end + 1] tags.append(tags[0]) segments = [ [ points[0], ], ] box = boundingBox(points[0][0], points[0][1], points[0][0], points[0][1]) for j in range(1, len(points)): if not tags[j]: # if this point is off-path if tags[j - 1]: # and the last point was on-path segments[-1].append( points[j]) # toss this point onto the segment elif not tags[j - 1]: # and the last point was off-path # get center point of two newPoint = ((points[j][0] + points[j - 1][0]) / 2.0, (points[j][1] + points[j - 1][1]) / 2.0) segments[-1].append( newPoint ) # toss this new point onto the segment segments.append( [ newPoint, points[j], ] ) # and start a new segment with the new point and this one elif tags[j]: # if this point is on-path segments[-1].append( points[j]) # toss this point onto the segment if j < (len(points) - 1): segments.append( [ points[j], ] ) # and start a new segment with this point if we're not at the end for segment in segments: if len(segment) == 2: paths.append( Line(start=tuple_to_imag(segment[0]), end=tuple_to_imag(segment[1]))) elif len(segment) == 3: paths.append( QuadraticBezier(start=tuple_to_imag(segment[0]), control=tuple_to_imag(segment[1]), end=tuple_to_imag(segment[2]))) start = end + 1 # Derive bounding box of character for segment in paths: i = 0 while i < 10: point = segment.point(0.1 * i) if point.real > box.xMax: box.xMax = point.real if point.imag > box.yMax: box.yMax = point.imag if point.real < box.xMin: box.xMin = point.real if point.imag < box.yMin: box.yMin = point.imag i += 1 glyphBounds.append(box) path = Path(*paths) if glyphPos != 0: try: xOffset += glyphPos[inString[charIdx]].real yOffset = glyphPos[inString[charIdx]].imag except: pass if lineover and len(lineoverList) == 0: lineoverList.append(xOffset) lineover = False if (lineover and len(lineoverList) > 0): linePaths = [] linePaths.append( Line(start=complex(lineoverList[0], 10), end=complex(xOffset, 10))) linePaths.append( Line(start=complex(xOffset, 10), end=complex(xOffset, 30))) linePaths.append( Line(start=complex(xOffset, 30), end=complex(lineoverList[0], 30))) linePaths.append( Line(start=complex(lineoverList[0], 30), end=complex(lineoverList[0], 10))) linepath = Path(*linePaths) linepath = elPath(linepath.d()) finalSegments.append(linepath) lineover = False lineoverList.clear() pathTransform = Matrix.translate(xOffset, baseline + yOffset - box.yMax) path = elPath(path.d()) * pathTransform path = elPath(path.d()) finalSegments.append(path) xOffset += 30 if glyphPos != 0: try: xOffset -= glyphPos[inString[charIdx]].real except: pass xOffset += (glyphBounds[charIdx].xMax - glyphBounds[charIdx].xMin) strIdx += 1 if self.leftCap == '' and self.rightCap == '': for i in range(len(finalSegments)): svgObj = dwg.add(dwg.path(finalSegments[i].d())) svgObj['fill'] = "#000000" else: #draw the outline of the label as a filled shape and #subtract each latter from it tagPaths = [] if self.rightCap == 'round': tagPaths.append( Line(start=complex(100, 0), end=complex(xOffset, 0))) tagPaths.append( Arc(start=complex(xOffset, 0), radius=complex(100, 100), rotation=180, large_arc=1, sweep=1, end=complex(xOffset, 200))) elif self.rightCap == 'square': tagPaths.append( Line(start=complex(100, 0), end=complex(xOffset, 0))) tagPaths.append( Line(start=complex(xOffset, 0), end=complex(xOffset + 50, 0))) tagPaths.append( Line(start=complex(xOffset + 50, 0), end=complex(xOffset + 50, 200))) tagPaths.append( Line(start=complex(xOffset + 50, 200), end=complex(xOffset, 200))) elif self.rightCap == 'pointer': tagPaths.append( Line(start=complex(100, 0), end=complex(xOffset, 0))) tagPaths.append( Line(start=complex(xOffset, 0), end=complex(xOffset + 50, 0))) tagPaths.append( Line(start=complex(xOffset + 50, 0), end=complex(xOffset + 100, 100))) tagPaths.append( Line(start=complex(xOffset + 100, 100), end=complex(xOffset + 50, 200))) tagPaths.append( Line(start=complex(xOffset + 50, 200), end=complex(xOffset, 200))) elif self.rightCap == 'flagtail': tagPaths.append( Line(start=complex(100, 0), end=complex(xOffset, 0))) tagPaths.append( Line(start=complex(xOffset, 0), end=complex(xOffset + 100, 0))) tagPaths.append( Line(start=complex(xOffset + 100, 0), end=complex(xOffset + 50, 100))) tagPaths.append( Line(start=complex(xOffset + 50, 100), end=complex(xOffset + 100, 200))) tagPaths.append( Line(start=complex(xOffset + 100, 200), end=complex(xOffset, 200))) elif self.rightCap == 'fslash': tagPaths.append( Line(start=complex(100, 0), end=complex(xOffset, 0))) tagPaths.append( Line(start=complex(xOffset, 0), end=complex(xOffset + 50, 0))) tagPaths.append( Line(start=complex(xOffset + 50, 0), end=complex(xOffset, 200))) elif self.rightCap == 'bslash': tagPaths.append( Line(start=complex(100, 0), end=complex(xOffset, 0))) tagPaths.append( Line(start=complex(xOffset, 0), end=complex(xOffset + 50, 200))) tagPaths.append( Line(start=complex(xOffset + 50, 200), end=complex(xOffset, 200))) elif self.rightCap == '' and self.leftCap != '': tagPaths.append( Line(start=complex(100, 0), end=complex(xOffset, 0))) tagPaths.append( Line(start=complex(xOffset, 0), end=complex(xOffset, 200))) if self.leftCap == 'round': tagPaths.append( Line(start=complex(xOffset, 200), end=complex(100, 200))) tagPaths.append( Arc(start=complex(100, 200), radius=complex(100, 100), rotation=180, large_arc=0, sweep=1, end=complex(100, 0))) elif self.leftCap == 'square': tagPaths.append( Line(start=complex(xOffset, 200), end=complex(100, 200))) tagPaths.append( Line(start=complex(100, 200), end=complex(50, 200))) tagPaths.append( Line(start=complex(50, 200), end=complex(50, 0))) tagPaths.append(Line(start=complex(50, 0), end=complex(100, 0))) elif self.leftCap == 'pointer': tagPaths.append( Line(start=complex(xOffset, 200), end=complex(100, 200))) tagPaths.append( Line(start=complex(100, 200), end=complex(50, 200))) tagPaths.append( Line(start=complex(50, 200), end=complex(0, 100))) tagPaths.append(Line(start=complex(0, 100), end=complex(50, 0))) tagPaths.append(Line(start=complex(50, 0), end=complex(100, 0))) elif self.leftCap == 'flagtail': tagPaths.append( Line(start=complex(xOffset, 200), end=complex(100, 200))) tagPaths.append( Line(start=complex(100, 200), end=complex(0, 200))) tagPaths.append( Line(start=complex(0, 200), end=complex(50, 100))) tagPaths.append(Line(start=complex(50, 100), end=complex(0, 0))) tagPaths.append(Line(start=complex(0, 0), end=complex(100, 0))) elif self.leftCap == 'fslash': tagPaths.append( Line(start=complex(xOffset, 200), end=complex(100, 200))) tagPaths.append( Line(start=complex(100, 200), end=complex(50, 200))) tagPaths.append( Line(start=complex(50, 200), end=complex(100, 0))) elif self.leftCap == 'bslash': tagPaths.append( Line(start=complex(xOffset, 200), end=complex(100, 200))) tagPaths.append( Line(start=complex(100, 200), end=complex(50, 0))) tagPaths.append(Line(start=complex(50, 0), end=complex(100, 0))) elif self.leftCap == '' and self.rightCap != '': tagPaths.append( Line(start=complex(xOffset, 200), end=complex(100, 200))) tagPaths.append( Line(start=complex(100, 200), end=complex(100, 0))) path = Path(*tagPaths) for i in range(len(finalSegments)): path = elPath(path.d() + " " + finalSegments[i].reverse()) tagObj = dwg.add(dwg.path(path.d())) tagObj['fill'] = "#000000" dwg['width'] = xOffset + 100 dwg['height'] = 250 #dwg.saveas('out.svg') print('create svg') return dwg
def drawSVG(self, svg_attributes, attributes, paths): global SCALE global SUBSAMPLING global SIMPLIFY global SIMPLIFYHQ global TRACEWIDTH out = '' svgWidth = 0 svgHeight = 0 if 'viewBox' in svg_attributes.keys(): if svg_attributes['viewBox'].split()[2] != '0': svgWidth = str( round(float(svg_attributes['viewBox'].split()[2]), 2)) svgHeight = str( round(float(svg_attributes['viewBox'].split()[3]), 2)) else: svgWidth = svg_attributes['width'] svgHeight = svg_attributes['height'] else: svgWidth = svg_attributes['width'] svgHeight = svg_attributes['height'] specifiedWidth = svg_attributes['width'] if 'mm' in specifiedWidth: specifiedWidth = float(specifiedWidth.replace('mm', '')) SCALE = specifiedWidth / float(svgWidth) if self.verbose: print("SVG width detected in mm \\o/") elif 'in' in specifiedWidth: specifiedWidth = float(specifiedWidth.replace('in', '')) * 25.4 SCALE = specifiedWidth / float(svgWidth) if self.verbose: print("SVG width detected in inches") else: SCALE = (self.scaleFactor * 25.4) / 150 if self.verbose: print("SVG width not found, guessing based on scale factor") self.exportHeight = float(svgHeight) * SCALE if len(paths) == 0: print("No paths found. Did you use 'Object to path' in Inkscape?") i = 0 out = [] while i < len(paths): if self.verbose: print('Translating Path ' + str(i + 1) + ' of ' + str(len(paths))) # Apply the tranform from this svg object to actually transform the points # We need the Matrix object from svgelements but we can only matrix multiply with # svgelements' version of the Path object so we're gonna do some dumb stuff # to launder the Path object from svgpathtools through a d-string into # svgelements' version of Path. Luckily, the Path object from svgelements has # backwards compatible .point methods pathTransform = Matrix('') if 'transform' in attributes[i].keys(): pathTransform = Matrix(attributes[i]['transform']) if self.verbose: print('...Applying Transforms') path = elPath(paths[i].d()) * pathTransform path = elPath(path.d()) # Another stage of transforms that gets applied to all paths # in order to shift the label around the origin tx = { 'l': 0, 'c': 0 - (float(svgWidth) / 2), 'r': 0 - float(svgWidth) } ty = {'t': 250, 'c': 150, 'b': 50} path = elPath(paths[i].d()) * Matrix.translate( tx[self.originPos[1]], ty[self.originPos[0]]) path = elPath(path.d()) style = 0 if 'style' in attributes[i].keys(): style = styleParse(attributes[i]['style']) if 'fill' in attributes[i].keys(): filled = attributes[i]['fill'] != 'none' and attributes[i][ 'fill'] != '' elif 'style' in attributes[i].keys(): filled = style['fill'] != 'none' and style['fill'] != '' else: filled = False if 'stroke' in attributes[i].keys(): stroked = attributes[i]['stroke'] != 'none' and attributes[i][ 'stroke'] != '' elif 'style' in attributes[i].keys(): stroked = style['stroke'] != 'none' and style['stroke'] != '' else: stroked = False if not filled and not stroked: i += 1 continue # not drawable (clip path?) SUBSAMPLING = self.subSampling TRACEWIDTH = str(self.traceWidth) l = path.length() divs = round(l * SUBSAMPLING) if divs < 3: divs = 3 maxLen = l * 2 * SCALE / divs p = path.point(0) p = complex(p.real * SCALE, p.imag * SCALE) last = p polys = [] points = [] s = 0 while s <= divs: p = path.point(s * 1 / divs) p = complex(p.real * SCALE, p.imag * SCALE) if dist(p, last) > maxLen: if len(points) > 1: points = simplify(points, SIMPLIFY, SIMPLIFYHQ) polys.append(points) points = [p] else: points.append(p) last = p s += 1 if len(points) > 1: points = simplify(points, SIMPLIFY, SIMPLIFYHQ) polys.append(points) if filled: polys = unpackPoly(polys) for points in polys: if len(points) < 2: return _points = [] if filled: points.append( points[0]) # re-add final point so we loop around for p in points: precisionX = round(p.real, 8) precisionY = round(p.imag - self.exportHeight, 8) _points += [(precisionX, precisionY)] out += [_points] i += 1 self.polys = out return out
class CameraInterface(wx.Frame, Module): def __init__(self, parent, *args, **kwds): wx.Frame.__init__(self, parent, -1, "", style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT | wx.TAB_TRAVERSAL) Module.__init__(self) if args >= 1: self.settings_value = args[0] else: self.settings_value = 0 self.SetSize((600, 600)) self.CameraInterface_menubar = wx.MenuBar() wxglade_tmp_menu = wx.Menu() item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Reset Perspective"), "") self.Bind(wx.EVT_MENU, self.reset_perspective, id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Reset Fisheye"), "") self.Bind(wx.EVT_MENU, self.reset_fisheye, id=item.GetId()) wxglade_tmp_menu.AppendSeparator() item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set URI"), "") self.Bind(wx.EVT_MENU, lambda e: self.ip_menu_edit(), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set IP Camera"), "", wx.ITEM_RADIO) self.ip_camera_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(-1), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 0"), "", wx.ITEM_RADIO) self.camera_0_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(0), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 1"), "", wx.ITEM_RADIO) self.camera_1_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(1), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 2"), "", wx.ITEM_RADIO) self.camera_2_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(2), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 3"), "", wx.ITEM_RADIO) self.camera_3_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(3), id=item.GetId()) item = wxglade_tmp_menu.Append(wx.ID_ANY, _("Set Camera 4"), "", wx.ITEM_RADIO) self.camera_4_menu = item self.Bind(wx.EVT_MENU, lambda e: self.swap_camera(4), id=item.GetId()) self.CameraInterface_menubar.Append(wxglade_tmp_menu, _("Camera")) self.SetMenuBar(self.CameraInterface_menubar) # Menu Bar self.button_update = wx.BitmapButton(self, wx.ID_ANY, icons8_camera_50.GetBitmap()) self.button_export = wx.BitmapButton( self, wx.ID_ANY, icons8_picture_in_picture_alternative_50.GetBitmap()) self.button_reconnect = wx.BitmapButton( self, wx.ID_ANY, icons8_connected_50.GetBitmap()) self.check_fisheye = wx.CheckBox(self, wx.ID_ANY, _("Correct Fisheye")) self.check_perspective = wx.CheckBox(self, wx.ID_ANY, _("Correct Perspective")) self.slider_fps = wx.Slider(self, wx.ID_ANY, 24, 0, 60, style=wx.SL_AUTOTICKS | wx.SL_HORIZONTAL | wx.SL_LABELS) self.button_detect = wx.BitmapButton(self, wx.ID_ANY, icons8_detective_50.GetBitmap()) self.display_camera = wx.Panel(self, wx.ID_ANY) self.__set_properties() self.__do_layout() self.camera_job = None self.fetch_job = None self.camera_setting = None self.setting = None self.current_frame = None self.last_frame = None self.current_raw = None self.last_raw = None self.capture = None self.image_width = -1 self.image_height = -1 self._Buffer = None self.frame_bitmap = None self.fisheye_k = None self.fisheye_d = None # Used during calibration. self.objpoints = [] # 3d point in real world space self.imgpoints = [] # 2d points in image plane. # Perspective Points self.perspective = None self.previous_window_position = None self.previous_scene_position = None self.corner_drag = None self.matrix = Matrix() self.Bind(wx.EVT_BUTTON, self.on_button_update, self.button_update) self.Bind(wx.EVT_BUTTON, self.on_button_export, self.button_export) self.Bind(wx.EVT_BUTTON, self.on_button_reconnect, self.button_reconnect) self.Bind(wx.EVT_CHECKBOX, self.on_check_fisheye, self.check_fisheye) self.Bind(wx.EVT_CHECKBOX, self.on_check_perspective, self.check_perspective) self.Bind(wx.EVT_SLIDER, self.on_slider_fps, self.slider_fps) self.Bind(wx.EVT_BUTTON, self.on_button_detect, self.button_detect) self.SetDoubleBuffered(True) # end wxGlade self.display_camera.Bind(wx.EVT_PAINT, self.on_paint) self.display_camera.Bind(wx.EVT_ERASE_BACKGROUND, self.on_erase) self.display_camera.Bind(wx.EVT_MOTION, self.on_mouse_move) self.display_camera.Bind(wx.EVT_MOUSEWHEEL, self.on_mousewheel) self.display_camera.Bind(wx.EVT_MIDDLE_UP, self.on_mouse_middle_up) self.display_camera.Bind(wx.EVT_MIDDLE_DOWN, self.on_mouse_middle_down) self.display_camera.Bind(wx.EVT_LEFT_DOWN, self.on_mouse_left_down) self.display_camera.Bind(wx.EVT_LEFT_UP, self.on_mouse_left_up) self.display_camera.Bind(wx.EVT_ENTER_WINDOW, lambda event: self.display_camera.SetFocus( )) # Focus follows mouse. self.Bind(wx.EVT_CLOSE, self.on_close, self) self.on_size(None) self.Bind(wx.EVT_SIZE, self.on_size, self) self.camera_lock = threading.Lock() self.process = self.update_view self.connection_attempts = 0 self.frame_attempts = 0 self.last_frame_index = -1 self.frame_index = 0 self.quit_thread = False def __do_layout(self): sizer_1 = wx.BoxSizer(wx.VERTICAL) sizer_2 = wx.BoxSizer(wx.HORIZONTAL) sizer_3 = wx.BoxSizer(wx.VERTICAL) sizer_2.Add(self.button_update, 0, 0, 0) sizer_2.Add(self.button_export, 0, 0, 0) sizer_2.Add(self.button_reconnect, 0, 0, 0) sizer_3.Add(self.check_fisheye, 0, 0, 0) sizer_3.Add(self.check_perspective, 0, 0, 0) sizer_2.Add(sizer_3, 1, wx.EXPAND, 0) sizer_2.Add(self.slider_fps, 1, wx.EXPAND, 0) sizer_2.Add(self.button_detect, 0, 0, 0) sizer_1.Add(sizer_2, 1, wx.EXPAND, 0) sizer_1.Add(self.display_camera, 10, wx.EXPAND, 0) self.SetSizer(sizer_1) self.Layout() def __set_properties(self): _icon = wx.NullIcon _icon.CopyFromBitmap(icons8_camera_50.GetBitmap()) self.SetIcon(_icon) # begin wxGlade: CameraInterface.__set_properties self.SetTitle("CameraInterface") self.button_update.SetToolTip(_("Update Image")) self.button_update.SetSize(self.button_update.GetBestSize()) self.button_export.SetToolTip(_("Export Snapsnot")) self.button_export.SetSize(self.button_export.GetBestSize()) self.button_reconnect.SetToolTip(_("Reconnect Camera")) self.button_reconnect.SetSize(self.button_reconnect.GetBestSize()) self.button_detect.SetToolTip(_("Detect Distortions/Calibration")) self.button_detect.SetSize(self.button_detect.GetBestSize()) # end wxGlade @staticmethod def sub_register(device): device.register('window', "CameraURI", CameraURI) def on_close(self, event): if self.state == 5: event.Veto() else: self.state = 5 self.device.close('window', self.name) event.Skip() # Call destroy as regular. def initialize(self, channel=None): self.device.close('window', self.name) self.Show() self.device.setting(bool, 'mouse_zoom_invert', False) self.device.setting(int, 'draw_mode', 0) self.device.setting(int, "bed_width", 320) # Default Value self.device.setting(int, "bed_height", 220) # Default Value self.camera_setting = self.device.device_root.derive('camera') self.setting = self.camera_setting.derive(str(self.settings_value)) self.setting.setting(int, 'index', 0) self.setting.setting(int, 'fps', 1) self.setting.setting(bool, 'correction_fisheye', False) self.setting.setting(bool, 'correction_perspective', False) self.setting.setting(str, 'fisheye', '') self.setting.setting(str, 'perspective', '') self.setting.setting(str, 'uri', '0') self.check_fisheye.SetValue(self.setting.correction_fisheye) self.check_perspective.SetValue(self.setting.correction_perspective) if self.setting.fisheye is not None and len(self.setting.fisheye) != 0: self.fisheye_k, self.fisheye_d = eval(self.setting.fisheye) if self.setting.perspective is not None and len( self.setting.perspective) != 0: self.perspective = eval(self.setting.perspective) self.slider_fps.SetValue(self.setting.fps) if self.camera_job is not None: self.camera_job.cancel() self.open_camera(self.setting.index) self.device.listen('camera_uri_changed', self.on_camera_uri_change) self.device.listen('camera_frame_raw', self.on_camera_frame_raw) self.set_camera_checks() def finalize(self, channel=None): self.setting.flush() self.camera_setting.flush() self.device.unlisten('camera_uri_changed', self.on_camera_uri_change) self.device.unlisten('camera_frame_raw', self.on_camera_frame_raw) self.quit_thread = True if self.camera_job is not None: self.camera_job.cancel() if self.fetch_job is not None: self.fetch_job.cancel() try: self.Close() except RuntimeError: pass def shutdown(self, channel=None): try: self.Close() self.quit_thread = True except RuntimeError: pass def on_size(self, event): self.Layout() width, height = self.ClientSize if width <= 0: width = 1 if height <= 0: height = 1 self._Buffer = wx.Bitmap(width, height) self.update_in_gui_thread() def on_camera_uri_change(self, *args): pass def ip_menu_edit(self): """ Select a particular URI. :param uri: :return: """ self.device.using( 'module', 'Console').write('window open CameraURI %s %s\n' % (self.settings_value, self.setting.uri)) def ip_menu_uri_change(self, uri): def function(event=None): self.ip_menu_uri(uri) return function def ip_menu_uri(self, uri): """ Select a particular URI. :param uri: :return: """ self.setting.uri = uri self.swap_camera(-1) def set_camera_checks(self): """ Set the checkmarks based on the index value. :return: """ if self.setting.index == 0: self.camera_0_menu.Check(True) elif self.setting.index == 1: self.camera_1_menu.Check(True) elif self.setting.index == 2: self.camera_2_menu.Check(True) elif self.setting.index == 3: self.camera_3_menu.Check(True) elif self.setting.index == 4: self.camera_4_menu.Check(True) else: self.ip_camera_menu.Check(True) def update_view(self): frame = self.last_frame if frame is None: return print("%d vs %d" % (self.last_frame_index, self.frame_index)) if self.frame_index == self.last_frame_index: return else: self.last_frame_index = self.frame_index bed_width = self.device.bed_width * 2 bed_height = self.device.bed_height * 2 self.image_height, self.image_width = frame.shape[:2] self.frame_bitmap = wx.Bitmap.FromBuffer(self.image_width, self.image_height, frame) if self.setting.correction_perspective: if bed_width != self.image_width or bed_height != self.image_height: self.image_width = bed_width self.image_height = bed_height self.display_camera.SetSize( (self.image_width, self.image_height)) self.update_in_gui_thread() def on_camera_frame_raw(self, *args): """ Raw Camera frame was requested and should be processed. This attempts to perform checkboard detection. :param frame: :return: """ frame = self.last_raw if frame is None: return try: import cv2 import numpy as np except: dlg = wx.MessageDialog(None, _("Could not import Camera requirements"), _("Imports Failed"), wx.OK) dlg.ShowModal() dlg.Destroy() CHECKERBOARD = (6, 9) subpix_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.1) calibration_flags = cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC + cv2.fisheye.CALIB_CHECK_COND + cv2.fisheye.CALIB_FIX_SKEW objp = np.zeros((1, CHECKERBOARD[0] * CHECKERBOARD[1], 3), np.float32) objp[0, :, :2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2) img = frame gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Find the chess board corners ret, corners = cv2.findChessboardCorners( gray, CHECKERBOARD, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE) # If found, add object points, image points (after refining them) if ret: self.objpoints.append(objp) cv2.cornerSubPix(gray, corners, (3, 3), (-1, -1), subpix_criteria) self.imgpoints.append(corners) else: dlg = wx.MessageDialog(None, _("Checkerboard 6x9 pattern not found."), _("Pattern not found."), wx.OK) dlg.ShowModal() dlg.Destroy() return N_OK = len(self.objpoints) K = np.zeros((3, 3)) D = np.zeros((4, 1)) rvecs = [np.zeros((1, 1, 3), dtype=np.float64) for i in range(N_OK)] tvecs = [np.zeros((1, 1, 3), dtype=np.float64) for i in range(N_OK)] try: rms, a, b, c, d = \ cv2.fisheye.calibrate( self.objpoints, self.imgpoints, gray.shape[::-1], K, D, rvecs, tvecs, calibration_flags, (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 1e-6) ) except cv2.error: # Ill conditioned matrix for input values. self.objpoints = self.objpoints[:-1] # Deleting the last entry. self.imgpoints = self.imgpoints[:-1] dlg = wx.MessageDialog(None, _("Ill-conditioned Matrix. Keep trying."), _("Matrix."), wx.OK) dlg.ShowModal() dlg.Destroy() return dlg = wx.MessageDialog( None, _("Success. %d images so far." % len(self.objpoints)), _("Image Captured"), wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() self.setting.fisheye = repr([K.tolist(), D.tolist()]) self.fisheye_k = K.tolist() self.fisheye_d = D.tolist() def swap_camera(self, camera_index=0): """ Disconnect the current connected camera. Connect to the provided camera index. :param camera_index: :return: """ with self.camera_lock: self.close_camera() self.open_camera(camera_index) self.set_camera_checks() def init_camera(self): """ Connect to Camera. :return: """ if self.device is None: return if self.capture is not None: self.capture = None try: import cv2 except ImportError: wx.CallAfter(self.camera_error_requirement) return wx.CallAfter(lambda: self.set_control_enable(True)) try: self.interval = 1.0 / self.setting.fps except ZeroDivisionError: self.interval = 5 except AttributeError: return self.device.threaded(self.threaded_image_fetcher) self.schedule() def open_camera(self, camera_index=0): """ Open Camera device. Prevents opening if already opening. :param camera_index: :return: """ if self.camera_job is not None: self.camera_job.cancel() self.setting.index = camera_index uri = self.get_camera_uri() if uri is not None: self.camera_job = self.device.add_job(self.init_camera, times=1, interval=0.1) def close_camera(self): """ Disconnect from the current camera. :return: """ self.unschedule() if self.capture is not None: self.capture.release() self.capture = None def set_control_enable(self, enable=False): """ Disable/Enable all the major controls within the GUI. :param enable: :return: """ try: for attr in dir(self): value = getattr(self, attr) if isinstance(value, wx.Control): if value is not self.button_reconnect: value.Enable(enable) except RuntimeError: pass def camera_error_requirement(self): """ Message Error that OpenCV is not installed and thus camera cannot run. Disable all Controls. :return: """ try: self.set_control_enable(False) dlg = wx.MessageDialog( None, _("If using a precompiled binary, this was requirement was not included.\nIf using pure Python, add it with: pip install opencv-python-headless" ), _("Interface Requires OpenCV."), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() except RuntimeError: pass def camera_error_webcam(self): """ Message error based on failure to connect to webcam. Disable all Controls. :return: """ try: self.set_control_enable(False) dlg = wx.MessageDialog(None, _("No Webcam found."), _("Error"), wx.OK | wx.ICON_ERROR) dlg.ShowModal() dlg.Destroy() except RuntimeError: pass def ip_menu_new(self): """ Requests and adds new IP Connection URI. :return: """ dlg = wx.TextEntryDialog(self, _("Enter the HTTP or RTSP uri"), _("Webcam URI Update"), '') dlg.SetValue(str(self.get_camera_uri())) modal = dlg.ShowModal() if modal == wx.ID_OK: uri = str(dlg.GetValue()) index = len(list(self.camera_setting.keylist())) setattr(self.camera_setting, 'uri' + str(index), uri) self.setting.uri = uri self.ip_menu_uri(uri) self.swap_camera(-1) self.set_camera_checks() def get_camera_uri(self): """ Get the current camera URI. If the URI :return: """ index = self.setting.index if index >= 0: return index uri = self.setting.uri if uri is None or len(uri) == 0: return None try: return int(uri) except ValueError: # URI is not a number. return uri def attempt_recovery(self): try: import cv2 except ImportError: return False if self.quit_thread: return False if self.device is None: return False self.frame_attempts += 1 if self.frame_attempts < 5: return True self.frame_attempts = 0 if self.capture is not None: with self.camera_lock: self.capture.release() self.capture = None uri = self.get_camera_uri() if uri is None: return False self.device.signal("camera_initialize") with self.camera_lock: self.capture = cv2.VideoCapture(uri) if self.capture is None: return False self.connection_attempts += 1 return True def threaded_image_fetcher(self): self.quit_thread = False try: import cv2 except ImportError: return raw = False uri = self.get_camera_uri() if uri is None: return self.device.signal("camera_initialize") with self.camera_lock: self.capture = cv2.VideoCapture(uri) while not self.quit_thread: if self.connection_attempts > 50: wx.CallAfter(self.camera_error_webcam) with self.camera_lock: if self.capture is None: # No capture the thread dies. return try: ret = self.capture.grab() except AttributeError: continue if not ret: if self.attempt_recovery(): continue else: return ret, frame = self.capture.retrieve() if not ret or frame is None: if self.attempt_recovery(): continue else: return self.frame_attempts = 0 self.connection_attempts = 0 self.last_raw = self.current_raw self.current_raw = frame if not raw and \ self.fisheye_k is not None and \ self.fisheye_d is not None and \ self.device is not None and \ self.setting.correction_fisheye: # Unfisheye the drawing import numpy as np K = np.array(self.fisheye_k) D = np.array(self.fisheye_d) DIM = frame.shape[:2][::-1] map1, map2 = cv2.fisheye.initUndistortRectifyMap( K, D, np.eye(3), K, DIM, cv2.CV_16SC2) frame = cv2.remap(frame, map1, map2, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT) if not raw and self.device is not None and self.setting.correction_perspective: # Perspective the drawing. bed_width = self.device.bed_width * 2 bed_height = self.device.bed_height * 2 width, height = frame.shape[:2][::-1] import numpy as np if self.perspective is None: rect = np.array([[0, 0], [width - 1, 0], [width - 1, height - 1], [0, height - 1]], dtype="float32") else: rect = np.array(self.perspective, dtype="float32") dst = np.array( [[0, 0], [bed_width - 1, 0], [bed_width - 1, bed_height - 1], [0, bed_height - 1]], dtype="float32") M = cv2.getPerspectiveTransform(rect, dst) frame = cv2.warpPerspective(frame, M, (bed_width, bed_height)) frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) self.last_frame = self.current_frame self.current_frame = frame self.frame_index += 1 if self.capture is not None: self.capture.release() self.capture = None if self.device is not None: self.device.signal("camera_finalize") def reset_perspective(self, event): """ Reset the perspective settings. :param event: :return: """ self.perspective = None self.setting.perspective = '' def reset_fisheye(self, event): """ Reset the fisheye settings. :param event: :return: """ self.fisheye_k = None self.fisheye_d = None self.setting.fisheye = '' def on_erase(self, event): """ Erase camera view. :param event: :return: """ pass def on_paint(self, event): """ Paint camera view. :param event: :return: """ try: wx.BufferedPaintDC(self.display_camera, self._Buffer) except RuntimeError: pass def on_update_buffer(self, event=None): """ Draw Camera view. :param event: :return: """ if self.frame_bitmap is None: return # Need the bitmap to refresh. dm = self.device.draw_mode dc = wx.MemoryDC() dc.SelectObject(self._Buffer) dc.Clear() w, h = dc.Size if dm & DRAW_MODE_FLIPXY != 0: dc.SetUserScale(-1, -1) dc.SetLogicalOrigin(w, h) dc.SetBackground(wx.WHITE_BRUSH) gc = wx.GraphicsContext.Create(dc) # Can crash at bitmap okay gc.SetTransform( wx.GraphicsContext.CreateMatrix(gc, ZMatrix(self.matrix))) gc.PushState() gc.DrawBitmap(self.frame_bitmap, 0, 0, self.image_width, self.image_height) if not self.setting.correction_perspective: if self.perspective is None: self.perspective = [0, 0], \ [self.image_width, 0], \ [self.image_width, self.image_height], \ [0, self.image_height] gc.SetPen(wx.BLACK_DASHED_PEN) gc.StrokeLines(self.perspective) gc.StrokeLine(self.perspective[0][0], self.perspective[0][1], self.perspective[3][0], self.perspective[3][1]) gc.SetPen(wx.BLUE_PEN) for p in self.perspective: half = CORNER_SIZE / 2 gc.StrokeLine(p[0] - half, p[1], p[0] + half, p[1]) gc.StrokeLine(p[0], p[1] - half, p[0], p[1] + half) gc.DrawEllipse(p[0] - half, p[1] - half, CORNER_SIZE, CORNER_SIZE) gc.PopState() if dm & DRAW_MODE_INVERT != 0: dc.Blit(0, 0, w, h, dc, 0, 0, wx.SRC_INVERT) gc.Destroy() del dc def convert_scene_to_window(self, position): """ Scene Matrix convert scene to window. :param position: :return: """ point = self.matrix.point_in_matrix_space(position) return point[0], point[1] def convert_window_to_scene(self, position): """ Scene Matrix convert window to scene. :param position: :return: """ point = self.matrix.point_in_inverse_space(position) return point[0], point[1] def on_mouse_move(self, event): """ Handle mouse movement. :param event: :return: """ if not event.Dragging(): return else: self.SetCursor(wx.Cursor(wx.CURSOR_HAND)) if self.previous_window_position is None: return pos = event.GetPosition() window_position = pos.x, pos.y scene_position = self.convert_window_to_scene( [window_position[0], window_position[1]]) sdx = (scene_position[0] - self.previous_scene_position[0]) sdy = (scene_position[1] - self.previous_scene_position[1]) wdx = (window_position[0] - self.previous_window_position[0]) wdy = (window_position[1] - self.previous_window_position[1]) if self.corner_drag is None: self.scene_post_pan(wdx, wdy) else: self.perspective[self.corner_drag][0] += sdx self.perspective[self.corner_drag][1] += sdy self.setting.perspective = repr(self.perspective) self.previous_window_position = window_position self.previous_scene_position = scene_position def on_mousewheel(self, event): """ Handle mouse wheel. Used for zooming. :param event: :return: """ rotation = event.GetWheelRotation() mouse = event.GetPosition() if self.device.device_root.mouse_zoom_invert: rotation = -rotation if rotation > 1: self.scene_post_scale(1.1, 1.1, mouse[0], mouse[1]) elif rotation < -1: self.scene_post_scale(0.9, 0.9, mouse[0], mouse[1]) def on_mouse_left_down(self, event): """ Handle mouse left down event. Used for adjusting perspective items. :param event: :return: """ self.previous_window_position = event.GetPosition() self.previous_scene_position = self.convert_window_to_scene( self.previous_window_position) self.corner_drag = None if self.perspective is not None: for i, p in enumerate(self.perspective): half = CORNER_SIZE / 2 if Point.distance(self.previous_scene_position, p) < half: self.corner_drag = i break def on_mouse_left_up(self, event): """ Handle Mouse Left Up. Drag Ends. :param event: :return: """ self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) self.previous_window_position = None self.previous_scene_position = None self.corner_drag = None def on_mouse_middle_down(self, event): """ Handle mouse middle down Panning. :param event: :return: """ self.SetCursor(wx.Cursor(wx.CURSOR_HAND)) self.previous_window_position = event.GetPosition() self.previous_scene_position = self.convert_window_to_scene( self.previous_window_position) def on_mouse_middle_up(self, event): """ Handle mouse middle up. Pan ends. :param event: :return: """ self.SetCursor(wx.Cursor(wx.CURSOR_ARROW)) self.previous_window_position = None self.previous_scene_position = None def scene_post_pan(self, px, py): """ Scene Pan. :param px: :param py: :return: """ self.matrix.post_translate(px, py) self.on_update_buffer() def scene_post_scale(self, sx, sy=None, ax=0, ay=0): """ Scene Zoom. :param sx: :param sy: :param ax: :param ay: :return: """ self.matrix.post_scale(sx, sy, ax, ay) self.on_update_buffer() def update_in_gui_thread(self): """ Redraw on the GUI thread. :return: """ self.on_update_buffer() try: self.Refresh(True) self.Update() except RuntimeError: pass def on_check_perspective(self, event): """ Perspective checked. Turns on/off :param event: :return: """ self.setting.correction_perspective = self.check_perspective.GetValue() def on_check_fisheye(self, event): """ Fisheye checked. Turns on/off. :param event: :return: """ self.setting.correction_fisheye = self.check_fisheye.GetValue() def on_button_update(self, event): # wxGlade: CameraInterface.<event_handler> """ Button update. Sets image background to main scene. :param event: :return: """ frame = self.device.last_signal('camera_frame') if frame is not None: frame = frame[0] buffer = wx.Bitmap.FromBuffer(self.image_width, self.image_height, frame) self.device.signal('background', buffer) def on_button_export(self, event): # wxGlade: CameraInterface.<event_handler> """ Button export. Sends an image to the scene as an exported object. :param event: :return: """ frame = self.device.last_signal('camera_frame') if frame is not None: elements = self.device.device_root.elements frame = frame[0] from PIL import Image img = Image.fromarray(frame) obj = SVGImage() obj.image = img obj.image_width = self.image_width obj.image_height = self.image_height elements.add_elem(obj) def on_button_reconnect(self, event): # wxGlade: CameraInterface.<event_handler> self.quit_thread = True def on_slider_fps(self, event): # wxGlade: CameraInterface.<event_handler> """ Adjusts the camera FPS. If set to 0, this will be a frame each 5 seconds. :param event: :return: """ fps = self.slider_fps.GetValue() if fps == 0: tick = 5 else: tick = 1.0 / fps self.setting.fps = fps self.interval = tick def on_button_detect(self, event): # wxGlade: CameraInterface.<event_handler> """ Attempts to locate 6x9 checkerboard pattern for OpenCV to correct the fisheye pattern. :param event: :return: """ if self.fetch_job is not None: self.fetch_job.cancel() # TODO: This won't work anymore. self.device.signal('camera_frame_raw', None) self.fetch_job = self.device.add_job(self.on_camera_frame_raw, args=True, times=1, interval=0)
def drawSVG(svg_attributes, attributes, paths): global SCALE global SUBSAMPLING global SIMPLIFY global SIMPLIFYHQ global TRACEWIDTH out = '' svgWidth = 0 svgHeight = 0 if 'viewBox' in svg_attributes.keys(): if svg_attributes['viewBox'].split()[2] != '0': svgWidth = str( round(float(svg_attributes['viewBox'].split()[2]), 2)) svgHeight = str( round(float(svg_attributes['viewBox'].split()[3]), 2)) else: svgWidth = svg_attributes['width'] svgHeight = svg_attributes['height'] else: svgWidth = svg_attributes['width'] svgHeight = svg_attributes['height'] specifiedWidth = svg_attributes.get('width') if specifiedWidth == None: specifiedWidth = "None" if 'mm' in specifiedWidth: specifiedWidth = float(specifiedWidth.replace('mm', '')) SCALE = specifiedWidth / float(svgWidth) if args.verbose: print("SVG width detected in mm \\o/") elif 'in' in specifiedWidth: specifiedWidth = float(specifiedWidth.replace('in', '')) * 25.4 SCALE = specifiedWidth / float(svgWidth) if args.verbose: print("SVG width detected in inches") else: SCALE = (args.scaleFactor * 25.4) / 150 if args.verbose: print("SVG width not found, guessing based on scale factor") exportHeight = float(svgHeight) * SCALE if args.outMode == "b": out += "CHANGE layer " + str(args.eagleLayerNumber) + \ "; CHANGE rank 3; CHANGE pour solid; SET WIRE_BEND 2;\n" if args.outMode == "ls": out += "CHANGE layer " + str(args.eagleLayerNumber) + \ "; CHANGE pour solid; Grid mm; SET WIRE_BEND 2;\n" if len(paths) == 0: print("No paths found. Did you use 'Object to path' in Inkscape?") anyVisiblePaths = False i = 0 while i < len(paths): if args.verbose: print('Translating Path ' + str(i + 1) + ' of ' + str(len(paths))) # Apply the tranform from this svg object to actually transform the points # We need the Matrix object from svgelements but we can only matrix multiply with # svgelements' version of the Path object so we're gonna do some dumb stuff # to launder the Path object from svgpathtools through a d-string into # svgelements' version of Path. Luckily, the Path object from svgelements has # backwards compatible .point methods pathTransform = Matrix('') if 'transform' in attributes[i].keys(): pathTransform = Matrix(attributes[i]['transform']) if args.verbose: print('...Applying Transforms') path = elPath(paths[i].d()) * pathTransform path = elPath(path.d()) # Another stage of transforms that gets applied to all paths # in order to shift the label around the origin tx = {'l': 0, 'c': 0 - (float(svgWidth) / 2), 'r': 0 - float(svgWidth)} ty = {'t': 250, 'c': 150, 'b': 50} path = elPath(paths[i].d()) * Matrix.translate(tx[args.originPos[1]], ty[args.originPos[0]]) path = elPath(path.d()) style = 0 if 'style' in attributes[i].keys(): style = styleParse(attributes[i].get('style')) if 'fill' in attributes[i].keys(): filled = attributes[i].get('fill') != 'none' and attributes[i].get( 'fill') != '' elif 'style' in attributes[i].keys(): filled = style.get('fill') != 'none' and style.get('fill') != '' else: filled = False if 'stroke' in attributes[i].keys(): stroked = attributes[i].get( 'stroke') != 'none' and attributes[i].get('stroke') != '' elif 'style' in attributes[i].keys(): stroked = style.get('stroke') != 'none' and style.get( 'stroke') != '' else: stroked = False if not filled and not stroked: i += 1 continue # not drawable (clip path?) SUBSAMPLING = args.subSampling TRACEWIDTH = str(args.traceWidth) anyVisiblePaths = True l = path.length() divs = round(l * SUBSAMPLING) if divs < 3: divs = 3 maxLen = l * 2 * SCALE / divs p = path.point(0) p = complex(p.real * SCALE, p.imag * SCALE) last = p polys = [] points = [] s = 0 while s <= divs: p = path.point(s * 1 / divs) p = complex(p.real * SCALE, p.imag * SCALE) if dist(p, last) > maxLen: if len(points) > 1: points = simplify(points, SIMPLIFY, SIMPLIFYHQ) polys.append(points) points = [p] else: points.append(p) last = p s += 1 if len(points) > 1: points = simplify(points, SIMPLIFY, SIMPLIFYHQ) polys.append(points) if filled: polys = unpackPoly(polys) for points in polys: if len(points) < 2: return scriptLine = '' if filled: points.append( points[0]) # re-add final point so we loop around if args.outMode != "lib": if args.outMode == "b": scriptLine += "polygon " + args.signalName + " " + TRACEWIDTH + "mm " if args.outMode == "ls": scriptLine += "polygon " + TRACEWIDTH + "mm " for p in points: precisionX = '{0:.2f}'.format(round(p.real, 6)) precisionY = '{0:.2f}'.format( round(exportHeight - p.imag, 6)) scriptLine += '(' + precisionX + 'mm ' + precisionY + 'mm) ' scriptLine += ';' else: scriptLine += "<polygon width=\"" + TRACEWIDTH + "\" layer=\"" + str( args.eagleLayerNumber) + "\">\n" for p in points: precisionX = '{0:.2f}'.format(round(p.real, 6)) precisionY = '{0:.2f}'.format( round(exportHeight - p.imag, 6)) scriptLine += "<vertex x=\"" + precisionX + "\" y=\"" + precisionY + "\"/>\n" scriptLine += "</polygon>" out += scriptLine + '\n' i += 1 if not anyVisiblePaths: print("No paths with fills or strokes found.") return out