示例#1
0
 def __init__(self):
     self.mp_array = SyncableMPArray((GRADIENT_HEIGHT, *MAP_DIMS[1:]))
     # Constants
     self.label_y = 15
     self.label_xmin = (3, self.label_y)
     self.label_xmax_shift = MAP_DIMS[1] - 10
     # Main thread vars
     self.last_min, self.last_max = -1, -1
示例#2
0
 def __init__(self):
     self.mp_array = SyncableMPArray(MAP_DIMS)
     # Constants
     self.num_rows = 12
     self.num_cols = 16
     self.row_scale = int(MAP_DIMS[0] / self.num_rows)
     self.col_scale = int(MAP_DIMS[1] / self.num_cols)
     # Main thread vars
     self.bins = np.zeros((self.num_rows, self.num_cols), dtype='uint32')
     self.empty = np.zeros((self.num_rows, self.num_cols), dtype='uint8')
示例#3
0
 def __init__(self, initial_duration):
     self.mp_array = SyncableMPArray((PROGBAR_HEIGHT, *VID_DIM_RGB[1:]))
     # -- Constants -- #
     # Total segments in progress bar (= horizontal length)
     self.num_steps = VID_DIM_RGB[1]
     # Text Constants
     self.font = cv2.FONT_HERSHEY_SIMPLEX
     self.text_vloc = 15
     self.text_size = 0.45
     self.text_thickness = 1
     self.text_dims = cv2.getTextSize('00:00.000', self.font,
                                      self.text_size,
                                      self.text_thickness)[0]
     spacing = self.text_dims[0] // 2
     self.txt_left_lmt = spacing
     self.txt_right_lmt = self.num_steps - spacing
     # -- Main thread vars -- #
     # Operation Params
     self.curr_loc = -1
     self.text_hloc = 0
     self.start_time = None
     self.output_array = None
     self.image = None
     self.displaying_error_image = False
     self.targ_perim = CV2TargetAreaPerimeter()
     # Progress bar segments for each element
     self.pbar_slice = None
     self.mouse_in_targ_slice = None
     self.mouse_stim_slice = None
     # Mouse Status
     self.mouse_in_target = False  # is mouse inside target region?
     self.mouse_recv_stim = False  # does mouse receive stimulation?
     self.mouse_stim_timer = None  # timer to make sure mouse receives STIM_ON secs stim, max every STIM_TOTAL secs
     self.in_targ_stopwatch = StopWatch(
     )  # total time spent in target region
     self.get_stim_stopwatch = StopWatch(
     )  # total time spent receiving stimulation
     self.mouse_n_entries = 0  # num entries into target region
     self.mouse_n_stims = 0  # num stimulations received
     # -- Modifier vars (read-only for main thread) -- #
     # Operation Params
     self._running = False
     self._duration = initial_duration
示例#4
0
class Pathing(object):
    """Generates pathing map from coordinates"""
    def __init__(self):
        self.mp_array = SyncableMPArray(MAP_DIMS)
        # Main thread vars
        self.last_coord = None

    # Initializing functions. Call once once new process starts
    def init_unpickleable_objs(self):
        """These objects must be created in the process they will run in"""
        self.output_array = self.mp_array.generate_np_array()
        self.output_array.set_can_recv_img()

    # Modifier Functions. Can call from other threads
    # *** Non-underscored variables are READ ONLY
    def reset(self):
        self.last_coord = None
        self.output_array.fill(0)

    # Main Update Function. Run in Main Thread. Do NOT call from any other thread
    # *** Underscored variables are READ ONLY
    def update(self, coord):
        """Draw new pathing segment on pathing array"""
        col, row = coord
        if col is not None and row is not None:
            # Scale coords
            if MAP_DOWNSCALE > 1:
                coord = round(col / MAP_DOWNSCALE), round(row / MAP_DOWNSCALE)
            # Draw new path segment
            if self.last_coord:
                cv2.line(self.output_array, coord, self.last_coord,
                         (0, 255, 0), 1)
            self.last_coord = coord

    # Generate Output from List of Coords
    @staticmethod
    def get_pathmap(coord_list):
        """Provided a full list of coords, generate a full size map"""
        last_path_coord = None
        pathmap = np.zeros(VID_DIM_RGB, dtype='uint8')
        for coord in coord_list:
            if coord != (None, None):
                if last_path_coord:
                    cv2.line(pathmap, coord, last_path_coord, (0, 255, 0), 1)
                last_path_coord = coord
        return pathmap
示例#5
0
class Gradient(object):
    """Generates a gradient with variable labels from coordinates"""
    def __init__(self):
        self.mp_array = SyncableMPArray((GRADIENT_HEIGHT, *MAP_DIMS[1:]))
        # Constants
        self.label_y = 15
        self.label_xmin = (3, self.label_y)
        self.label_xmax_shift = MAP_DIMS[1] - 10
        # Main thread vars
        self.last_min, self.last_max = -1, -1

    # Initializing functions. Call once once new process starts
    def init_unpickleable_objs(self):
        """These objects must be created in the process they will run in"""
        self.output_array = self.mp_array.generate_np_array()
        self.init_gradient()
        self.output_array.set_can_recv_img()

    def init_gradient(self):
        """Create gradient indicator"""
        num_grads = 32
        raw = np.zeros((1, num_grads))
        for col in range(num_grads):
            raw[:, col] = col
        empty = np.zeros((1, num_grads), dtype='uint8')
        # Create red and green channels.
        red = raw.copy()
        green = raw.copy()
        # Generate gradient. we use a black-yellow-red gradient.
        red = (red / red.max()) * 2 * 255
        red = np.clip(red, 0, 255)
        green = (green / green.max()) * 2 * 255
        green[green > 255] = 255 - (green[green > 255] - 255)
        # Retype into 8 bits
        red = red.astype('uint8')
        green = green.astype('uint8')
        # Create image
        image = np.dstack((red, green, empty))
        col_scale = int(MAP_DIMS[1] / num_grads)
        self.gradient = np.kron(
            image, np.ones((GRADIENT_HEIGHT, col_scale, 1), dtype='uint8'))
        self.text_slice = self.gradient[(GRADIENT_HEIGHT // 2 -
                                         10):(GRADIENT_HEIGHT // 2 + 10), :, :]
        # Send gradient image
        self.output_array.send_img(self.gradient)

    # Modifier Functions. Can call from other threads
    # *** Non-underscored variables are READ ONLY
    def reset(self):
        self.last_min, self.last_max = -1, -1
        self.update(0, 0)

    # Main Update Function. Run in Main Thread. Do NOT call from any other thread
    # *** Underscored variables are READ ONLY
    def update(self, minimum, maximum):
        """Update scale on gradient"""
        if minimum == self.last_min and maximum == self.last_max:
            return
        # Reset text
        self.text_slice[:] = self.gradient[:20, :, :]
        # Find x location of maximum label
        label_xmax = self.label_xmax_shift - (len(str(maximum)) * 5)
        label_xmax = (label_xmax, self.label_y)
        # Label gradient
        cv2.putText(self.text_slice, str(minimum), self.label_xmin,
                    cv2.FONT_HERSHEY_SIMPLEX, 0.35, (255, 255, 255), 1)
        cv2.putText(self.text_slice, str(maximum), label_xmax,
                    cv2.FONT_HERSHEY_SIMPLEX, 0.35, (0, 0, 0), 1)
        # Send to shared array
        self.output_array.send_img(self.gradient)
        # Remember min and max
        self.last_min, self.last_max = minimum, maximum

    # Append a gradient to a heatmap, given min and max
    @staticmethod
    def append_gradient(minimum, maximum, heatmap):
        """Append gradient"""
        num_grads = 64
        raw = np.zeros((1, num_grads))
        for col in range(num_grads):
            raw[:, col] = col
        empty = np.zeros((1, num_grads), dtype='uint8')
        # Create red and green channels.
        red = raw.copy()
        green = raw.copy()
        # Generate gradient. we use a black-yellow-red gradient.
        red = (red / red.max()) * 2 * 255
        red = np.clip(red, 0, 255)
        green = (green / green.max()) * 2 * 255
        green[green > 255] = 255 - (green[green > 255] - 255)
        # Retype into 8 bits
        red = red.astype('uint8')
        green = green.astype('uint8')
        # Create gradient (cv2 imwrite takes BGR)
        image = np.dstack((empty, green, red))
        col_scale = int(VID_DIM_RGB[1] / num_grads)
        gradient = np.kron(
            image, np.ones((GRADIENT_HEIGHT // 2, col_scale, 1),
                           dtype='uint8'))
        # Add min max labels
        y = 30
        xmax = VID_DIM_RGB[1] - 8 - len(str(maximum) * 10), y
        xmin = 3, y
        # Label gradient
        cv2.putText(gradient, str(minimum), xmin, cv2.FONT_HERSHEY_SIMPLEX,
                    0.5, (255, 255, 255), 1)
        cv2.putText(gradient, str(maximum), xmax, cv2.FONT_HERSHEY_SIMPLEX,
                    0.5, (0, 0, 0), 1)
        # Append to heatmap
        shape = heatmap.shape[0] + GRADIENT_HEIGHT // 2, heatmap.shape[1], 3
        output = np.zeros(shape, dtype='uint8')
        output[:heatmap.shape[0], :, :] = heatmap
        output[heatmap.shape[0]:, :, :] = gradient
        return output
示例#6
0
 def __init__(self):
     self.mp_array = SyncableMPArray(MAP_DIMS)
     # Main thread vars
     self.last_coord = None
示例#7
0
class Heatmap(object):
    """Generates heatmap from coordinates"""
    def __init__(self):
        self.mp_array = SyncableMPArray(MAP_DIMS)
        # Constants
        self.num_rows = 12
        self.num_cols = 16
        self.row_scale = int(MAP_DIMS[0] / self.num_rows)
        self.col_scale = int(MAP_DIMS[1] / self.num_cols)
        # Main thread vars
        self.bins = np.zeros((self.num_rows, self.num_cols), dtype='uint32')
        self.empty = np.zeros((self.num_rows, self.num_cols), dtype='uint8')

    # Initializing functions. Call once once new process starts
    def init_unpickleable_objs(self):
        """These objects must be created in the process they will run in"""
        self.output_array = self.mp_array.generate_np_array()
        self.output_array.set_can_recv_img()

    # Modifier Functions. Can call from other threads
    # *** Non-underscored variables are READ ONLY
    def reset(self):
        self.bins.fill(0)
        self.output_array.fill(0)

    # Main Update Function. Run in Main Thread. Do NOT call from any other thread
    # *** Underscored variables are READ ONLY
    def update(self, coord):
        """Update heatmap with supplied coord"""
        col, row = coord
        # Find bins this coord belongs to, and add to bin
        if row is not None and col is not None:
            rowbin = int(row / (self.row_scale * MAP_DOWNSCALE))
            colbin = int(col / (self.col_scale * MAP_DOWNSCALE))
            self.bins[rowbin, colbin] += 1
            # We use a black-yellow-red gradient.
            red = self.bins.copy()
            green = self.bins.copy()
            # Create Gradient
            red = (red / red.max()) * 2 * 255
            red = np.clip(red, 0, 255)
            green = (green / green.max()) * 2 * 255
            green[green > 255] = 255 - (green[green > 255] - 255)
            # Retype into 8 bits
            red = red.astype('uint8')
            green = green.astype('uint8')
            # Create Image; blue is empty.
            bins = np.dstack((red, green, self.empty))
            heatmap = np.kron(
                bins,
                np.ones((self.row_scale, self.col_scale, 1), dtype='uint8'))
            self.output_array.send_img(heatmap)
        # Update Gradient
        return self.bins.min(), self.bins.max()

    # Generate Output from List of Coords
    def get_heatmap(self, coord_list):
        """Provided a full list of coords, generate a full size map"""
        bins = np.zeros((self.num_rows, self.num_cols), dtype='uint32')
        empty = np.zeros((self.num_rows, self.num_cols), dtype='uint8')
        row_scale = int(VID_DIM_RGB[0] / self.num_rows)
        col_scale = int(VID_DIM_RGB[1] / self.num_cols)
        for col, row in coord_list:
            if row is not None and col is not None:
                rowbin = int(row / row_scale)
                colbin = int(col / col_scale)
                bins[rowbin, colbin] += 1
        red = bins.copy()
        green = bins.copy()
        # Create Gradient (black yellow red)
        if bins.max() > 0:
            red = (red / red.max()) * 2 * 255
            red = np.clip(red, 0, 255)
            green = (green / green.max()) * 2 * 255
            green[green > 255] = 255 - (green[green > 255] - 255)
        # Retype into 8 bits
        red = red.astype('uint8')
        green = green.astype('uint8')
        # Create BGR Image (cv2 imwrite takes BGR)
        stacked = np.dstack((empty, green, red))
        heatmap = np.kron(stacked,
                          np.ones((row_scale, col_scale, 1), dtype='uint8'))
        # Add bin text
        for row in range(self.num_rows):
            for col in range(self.num_cols):
                num = int(bins[row, col])
                if num < bins.max() / 3:
                    color = (255, 255, 255)
                else:
                    color = (0, 0, 0)
                cv2.putText(heatmap, str(num),
                            (col * col_scale + 5, row * row_scale + 25),
                            cv2.FONT_HERSHEY_SIMPLEX, 0.35, color, 1)
        return bins.min(), bins.max(), heatmap
示例#8
0
class ProgressBar(object):
    """Numpy Array based progress bar"""
    def __init__(self, initial_duration):
        self.mp_array = SyncableMPArray((PROGBAR_HEIGHT, *VID_DIM_RGB[1:]))
        # -- Constants -- #
        # Total segments in progress bar (= horizontal length)
        self.num_steps = VID_DIM_RGB[1]
        # Text Constants
        self.font = cv2.FONT_HERSHEY_SIMPLEX
        self.text_vloc = 15
        self.text_size = 0.45
        self.text_thickness = 1
        self.text_dims = cv2.getTextSize('00:00.000', self.font,
                                         self.text_size,
                                         self.text_thickness)[0]
        spacing = self.text_dims[0] // 2
        self.txt_left_lmt = spacing
        self.txt_right_lmt = self.num_steps - spacing
        # -- Main thread vars -- #
        # Operation Params
        self.curr_loc = -1
        self.text_hloc = 0
        self.start_time = None
        self.output_array = None
        self.image = None
        self.displaying_error_image = False
        self.targ_perim = CV2TargetAreaPerimeter()
        # Progress bar segments for each element
        self.pbar_slice = None
        self.mouse_in_targ_slice = None
        self.mouse_stim_slice = None
        # Mouse Status
        self.mouse_in_target = False  # is mouse inside target region?
        self.mouse_recv_stim = False  # does mouse receive stimulation?
        self.mouse_stim_timer = None  # timer to make sure mouse receives STIM_ON secs stim, max every STIM_TOTAL secs
        self.in_targ_stopwatch = StopWatch(
        )  # total time spent in target region
        self.get_stim_stopwatch = StopWatch(
        )  # total time spent receiving stimulation
        self.mouse_n_entries = 0  # num entries into target region
        self.mouse_n_stims = 0  # num stimulations received
        # -- Modifier vars (read-only for main thread) -- #
        # Operation Params
        self._running = False
        self._duration = initial_duration

    # Initializing functions. Call once once new process starts
    def init_unpickleable_objs(self):
        """These objects must be created in the process they will run in"""
        self.output_array = self.mp_array.generate_np_array()
        self.image = self.output_array.copy()
        # Progress bar slices
        self.pbar_slice = self.image[20:60, :, :1]
        self.mouse_in_targ_slice = self.image[20:40, :, 1:2]
        self.mouse_stim_slice = self.image[40:60, :, 2:3]
        # Text slices
        w, h = self.text_dims
        self.main_timer_slice = self.image[:self.text_vloc + 5, :, :]
        self.targ_timer_slice = self.image[92 - h:92 + 5, 95:95 + w, :]
        self.stim_timer_slice = self.image[92 - h:92 + 5, 411:411 + w, :]
        self.targ_count_slice = self.image[92 - h:92 + 5,
                                           256:256 + int(w * 3 / 4), :]
        self.stim_count_slice = self.image[92 - h:92 + 5,
                                           563:563 + int(w * 3 / 4), :]
        # Arduino object
        cv2.putText(self.output_array, 'CONNECTING TO ARDUINO...', (30, 63),
                    cv2.FONT_HERSHEY_SIMPLEX, 1.3, (255, 255, 255), 1)
        self.output_array.set_can_recv_img()
        self.arduino = ArduinoDevice()
        self.arduino.connect()  # this step takes a few seconds
        self.output_array.fill(0)
        # Set Progress bar to initial conditions
        self.reset_bar()

    # Modifier Functions. Can call from other threads
    # *** Non-underscored variables are READ ONLY
    def set_duration(self, duration_in_secs):
        self._duration = float(duration_in_secs)

    def set_start(self):
        self._running = True
        self.reset_bar()

    def set_stop(self):
        self._running = False

    # Main Update Function. Run in Main Thread. Do NOT call from any other thread
    # *** Underscored variables are READ ONLY
    def set_timer_text(self, reset):
        """Places cv2 text on output array"""
        if reset:
            main_timer = '00:00.000'
            mouse_in_region_timer = '00:00.000'
            mouse_recv_stim_timer = '00:00.000'
            num_entries = '0)'
            num_stims = '0)'
        else:
            main_timer = format_secs(time.perf_counter() - self.start_time,
                                     'with_ms')
            mouse_in_region_timer = format_secs(
                self.in_targ_stopwatch.elapsed(), 'with_ms')
            mouse_recv_stim_timer = format_secs(
                self.get_stim_stopwatch.elapsed(), 'with_ms')
            num_entries = '{})'.format(self.mouse_n_entries)
            num_stims = '{})'.format(self.mouse_n_stims)
        self.main_timer_slice.fill(0)
        self.targ_timer_slice.fill(0)
        self.stim_timer_slice.fill(0)
        self.targ_count_slice.fill(0)
        self.stim_count_slice.fill(0)
        cv2.putText(self.main_timer_slice,
                    main_timer, (self.text_hloc, self.text_vloc),
                    fontFace=self.font,
                    fontScale=self.text_size,
                    color=(255, 255, 255))
        cv2.putText(self.targ_timer_slice,
                    mouse_in_region_timer, (0, self.text_dims[1]),
                    fontFace=self.font,
                    fontScale=self.text_size,
                    color=(255, 255, 255))
        cv2.putText(self.stim_timer_slice,
                    mouse_recv_stim_timer, (0, self.text_dims[1]),
                    fontFace=self.font,
                    fontScale=self.text_size,
                    color=(255, 255, 255))
        cv2.putText(self.targ_count_slice,
                    num_entries, (0, self.text_dims[1]),
                    fontFace=self.font,
                    fontScale=self.text_size,
                    color=(255, 255, 255))
        cv2.putText(self.stim_count_slice,
                    num_stims, (0, self.text_dims[1]),
                    fontFace=self.font,
                    fontScale=self.text_size,
                    color=(255, 255, 255))
        # Image is now fully prepared, send
        self.output_array.send_img(self.image)

    def check_mouse_inside_target(self, coord):
        """Checks if mouse is inside target region"""
        x, y = coord
        self.mouse_in_target = False
        if not self.targ_perim.draw or coord == (None, None):
            return
        x1, x2, y1, y2 = self.targ_perim.x1, self.targ_perim.x2, self.targ_perim.y1, self.targ_perim.y2
        if x1 <= x <= x2 and y1 <= y <= y2:
            self.mouse_in_target = True

    def send_stim_to_mouse(self):
        """Stim mouse if inside region"""
        if self.mouse_stim_timer:
            elapsed = time.perf_counter() - self.mouse_stim_timer
            if STIM_ON > elapsed:
                return
            elif STIM_ON <= elapsed < STIM_TOTAL:
                self.mouse_recv_stim = False
                return
            elif STIM_TOTAL <= elapsed:
                if self.mouse_in_target:
                    self.mouse_recv_stim = True
                    self.mouse_stim_timer = time.perf_counter()
                    return
                elif not self.mouse_in_target:
                    self.mouse_recv_stim = False
                    self.mouse_stim_timer = None
                    return
        else:
            if self.mouse_in_target:
                self.mouse_recv_stim = True
                self.mouse_stim_timer = time.perf_counter()

    def reset_bar(self):
        """Resets to initial conditions"""
        # Reset Locations
        self.curr_loc = -1
        self.text_hloc = 0
        # Reset Mouse Location timers and counters
        self.in_targ_stopwatch.reset()
        self.get_stim_stopwatch.reset()
        self.mouse_n_entries = 0
        self.mouse_n_stims = 0
        self.mouse_stim_timer = None
        # Reset Progress Bar Image
        self.reset_progbar_img()
        # Send Image
        self.output_array.set_can_recv_img()

    def reset_progbar_img(self):
        """Reset progressbar to initial image"""
        self.image.fill(0)
        # Add new progress bar at origin
        self.pbar_slice[:, :1, :] = 255
        # Add time indicators
        self.image[61:62, :, :] = 255
        num_chunks = int(self._duration / 30)
        num_chunks = min([12, num_chunks])
        num_chunks = max([2, num_chunks])
        seg_size = int(self.image.shape[1] / num_chunks)
        time_chunk = (self._duration / num_chunks)
        for i in np.arange(1, num_chunks):
            loc = seg_size * i
            tloc = format_secs(time_chunk * i)
            self.image[62:65, loc - 1:loc, :] = 255
            cv2.putText(self.image, tloc, (loc - 15, 74),
                        cv2.FONT_HERSHEY_SIMPLEX, 0.3, (255, 255, 255))
        # Add legends
        self.image[79:96, 319:320, :] = 255
        self.image[80:95, 3:18, 1] = 255
        cv2.putText(self.image, 'In Region:', (19, 92),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.45, (255, 255, 255))
        cv2.putText(self.image, '(# Entries:', (175, 92),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.45, (255, 255, 255))
        self.image[80:95, 323:338, 2] = 255
        cv2.putText(self.image, 'Get Stim:', (340, 92),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.45, (255, 255, 255))
        cv2.putText(self.image, '(# Stims:', (491, 92),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.45, (255, 255, 255))
        # add initializing timer text
        self.set_timer_text(reset=True)

    def can_update(self):
        """Checks we are allowed to proceed"""
        finished = self.curr_loc >= self.num_steps
        if finished or not self._running:
            return False
        return True

    def update(self):
        """Draws next frame of progress bar"""
        # Get elapsed time, get expected location, check mouse location/stim status
        elapsed = time.perf_counter() - self.start_time
        loc = int((elapsed / self._duration) * self.num_steps)
        # Check if mouse in target region; also calculate total time inside
        if self.mouse_in_target:
            self.mouse_in_targ_slice[:, loc - 1:loc, :] = 255
            if not self.in_targ_stopwatch.started:
                self.mouse_n_entries += 1
                self.in_targ_stopwatch.start()
        else:
            if self.in_targ_stopwatch.started:
                self.in_targ_stopwatch.stop()
        # Check if mouse receive stimulation; calculate total time receive
        if self.mouse_recv_stim:
            self.mouse_stim_slice[:, loc - 1:loc, :] = 255
            if not self.get_stim_stopwatch.started:
                self.mouse_n_stims += 1
                self.get_stim_stopwatch.start()
                if not self.arduino.manual_mode:
                    self.arduino.send_signal()
        else:
            if self.get_stim_stopwatch.started:
                self.get_stim_stopwatch.stop()
        # If enough time elapsed, update current location to expected location
        if loc != self.curr_loc:
            self.pbar_slice[:, self.curr_loc - 1:self.curr_loc, :] = 0
            self.pbar_slice[:, loc - 1:loc + 1, :] = 255
            if self.txt_left_lmt <= loc <= self.txt_right_lmt:
                self.text_hloc = loc - self.txt_left_lmt
            self.curr_loc = loc
        # Test for arduino connection; send new progbar frame; attempt to reconnect lost devices
        self.ping_arduino(updating=True)

    def ping_arduino(self, updating):
        """Pings arduino, displays errors, attempt to reconnect"""
        self.arduino.ping()
        if self.output_array.can_send_img():
            if not self.arduino.connected:
                self.display_error_img()
            elif updating:
                self.set_timer_text(reset=False)
            elif not updating and self.displaying_error_image:
                self.displaying_error_image = False
                self.output_array[:] = self.image
            self.output_array.set_can_recv_img()
        if not self.arduino.connected:
            self.arduino.connect()

    def display_error_img(self):
        if not self.displaying_error_image:
            self.displaying_error_image = True
            self.output_array.fill(0)
            cv2.putText(self.output_array, 'ARDUINO ERROR. RECONNECT DEVICE',
                        (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255),
                        2)