示例#1
0
class DriverProcess(Process):
    """
    This class wraps a camera driver in a Process object. It also performs
    background and parameters estimation, as well as storing frames in a
    ring buffer and saving them to numpy files.
    """
    def __init__(self, nb_background_frame, driver_fps_window, buffer_size,
                 shared_variables, camera_height):
        """
        This function "unpacks" the shared_variables dictionary and call the
        Process's __init__ function.
        Params:
            nb_background_frame (int): Number of frames used for the first
                background estimation
            driver_fps_window (int): Number of frames used for FPS estimation
            shared_variables (dict): Contains all inter-process variables
        """
        super(DriverProcess, self).__init__()

        # Inter-process communication
        self.cancel = shared_variables["stop_event"]
        self.frame_available = shared_variables["ready_event"]
        self.shared_array = shared_variables["shared_array"]
        self.background_ready = False
        self.recording_trigger = shared_variables["recording_trigger"]
        self.frame_to_record = shared_variables["nb_frame_to_record"]
        self.record_delay = 2

        # Calculated parameters
        self.binarize_threshold = shared_variables["binarized_threshold"]
        self.min_area = shared_variables["min_area"]
        self.detect_threshold = shared_variables["detect_threshold"]
        self.percentile_stdev = 0

        # Parameters
        self.nb_background_frame = nb_background_frame
        self.middle_pixel_value = 0
        self.camera_height = camera_height

        # Variables
        self.buffer_size = buffer_size
        self.ring_buffer = None
        self.background = None
        self.driver_fps_window = driver_fps_window
        """
        OpenNI version selection
        True  ---> OpenNI1
        False ---> OpenNI2
        """
        #OpenNI2 used as default
        self.openni_version = hasattr(openni, "_openni2")

    def background_estimation(self,
                              frame_list):  ################ SOLO AL PRINCIPIO
        """
        This function estimates the background by taking the minimum value
        seen for each pixel in a set of frames. It replaces zero-values by
        the mean value of all frames.
        Params:
            frame_list (list): List of frames to estimate the background from
        Returns:
            Estimated background as an np.Array()
        """
        background_frame = np.ones_like(
            frame_list[0]) * 65535  ####### 65535 is maximum of uint16
        replace_value = int(
            np.sum(frame_list[0]) /
            np.sum(frame_list[0] != 0))  ######### TO BE CHECKED!
        #        replace_value = int(np.sum(frame_list[0]) / len(frame_list[0] != 0))       ######### CORRECT????
        print("[Driver] Replacing bad values with {}".format(replace_value))
        for i in range(self.nb_background_frame):
            for j in range(frame_list[0].shape[0]):
                for k in range(frame_list[0].shape[1]):
                    if frame_list[i][j][k] < background_frame[j][k] and \
                            frame_list[i][j][k] != 0:
                        background_frame[j][k] = frame_list[i][j][k]
                    elif frame_list[i][j][k] == 0:
                        background_frame[j][k] = replace_value
        return background_frame

    def compute_std_dev(self, frame_list):  ################ SOLO AL PRINCIPIO
        """
        This function extracts statistics from a list of frames. Extracts
        middle pixel average value and 95th percentile.
        Params:
            frame_list (list): List of frames to perform statistics on
        """
        # Compute mean frame
        mean = np.zeros(frame_list[0].shape)
        for i in range(len(frame_list)):
            mean += frame_list[i]
        mean = mean / len(frame_list)

        # Compute standard deviation frame
        stdev = np.zeros(frame_list[0].shape)
        for i in range(len(frame_list)):
            stdev += (frame_list[i] - mean)**2
        stdev = stdev / len(frame_list)
        stdev = np.round(np.sqrt(stdev), 0)

        # Extract values for parameters estimation
        self.middle_pixel_value = mean[int(self.height / 2)][int(self.width /
                                                                 2)]
        self.percentile_stdev = np.percentile(stdev, 97)
        print('	DRIVERRRR ---> self.middle_pixel_value = ',
              self.middle_pixel_value)
        print('	DRIVERRRR ---> self.percentile_stdev = ',
              self.percentile_stdev)

    def compute_parameters(self):  ################ SOLO AL PRINCIPIO
        """
        This function tries to estimate good people counting parameters from
        statistics extracted by "compute_std_dev". It estimates binarization
        threshold, minimal area and detection threshold.
        """
        # Auto mode
        # Compute binarization threshold from percentile
        self.binarize_threshold.value = int(
            round(self.percentile_stdev / 16.0 + 5, 0))

        # Compute minimal area from camera height.
        # Divided into two lines, because it is not linear.
        if self.camera_height < 2400:
            min_area = 110
        elif 2400 <= self.camera_height < 2850:
            min_area = -0.0875 * self.camera_height + 320
            min_area = 5 * round(min_area / 5)
        elif 2850 <= self.camera_height <= 3200:
            min_area = -0.0571 * self.camera_height + 223
            min_area = 5 * round(min_area / 5)
        else:
            min_area = 40
        self.min_area.value = int(min_area)
        print('	DRIVERRRR ---> self.min_area.value = ', self.min_area.value)
        # Adapt detect threshold from middle pixel
        # At 2400 => 9, at 3200 => 5
        self.detect_threshold.value = int(-(self.camera_height / 266) + 17)

        print("[Driver] Estimated parameters. Min area: {} | " \
              "Detect Threshold: {} | " \
              "Binarize Threshold: {}".format(
                self.min_area.value,
                self.detect_threshold.value,
                self.binarize_threshold.value
                ))

    def shutdown(self):
        """
        This function performs shutdown actions for this class.
        """
        self.cancel.value = True

    def middle_pixel_depth(self, depth_array):
        """
        This function wraps around the driver function that returns the
        center pixel value.
        Params:
            depth_array (np.Array): Frame to extract distance from
        Returns:
             middle pixel distance
        """
        if (self.openni_version == True):
            return OpenNi2Driver.middle_pixel_depth(depth_array)
        else:
            return OpenNiDriver.middle_pixel_depth(depth_array)

    def save_buffer(self):  ################ SOLO AL PRINCIPIO
        """
        This function saves frames that are in the ring buffer to a
        compressed numpy file. The number of saved frames depends on
        frame_to_record and record_delay values.

        """
        time.sleep(self.record_delay)
        record_length = int(self.frame_to_record.value +
                            self.record_delay * 30.0)
        if record_length > self.buffer_size:
            record_length = self.buffer_size
        elif record_length < 1:
            record_length = 1

        print("[Driver] Saving last {} frames".format(record_length))
        try:
            np.savez(time.strftime("data/detected_%Y-%m-%d_%Hh%Mm%Ss.npz"),
                     video_data=self.ring_buffer.data[:, :, -record_length:],
                     background=self.background)
            print("[Driver] Frames saved !")
        except (IOError, OSError) as error:
            print(error)

    def async_buffer_save(self):
        """
        This function wraps the asynchronous implementation of saving frames to
        a file.
        """
        threading.start_new_thread(self.save_buffer, ())

#    @profile

    def run(self):
        """
        This function is the main loop of the Process and is where driver
        data is received. The camera driver is created here as  the
        __init__() function is not run in this Process. This loop waits for
        data from the camera driver and either compute or pass frames to the
        shared buffer to be used by another process.
        """
        if (self.openni_version == True):
            device = OpenNi2Driver(fps_window=self.driver_fps_window)
            print('')
            print('')
            print('')
            print('OpenNi2Driver')
            print('')
            print('')
            print('')
        else:
            device = OpenNiDriver(fps_window=self.driver_fps_window)
            print('')
            print('')
            print('')
            print('OpenNiDriver')
            print('')
            print('')
            print('')
        self.height = device.height
        self.width = device.width
        self.ring_buffer = RingBuffer(self.height, self.width,
                                      self.buffer_size)
        frame_list = []

        try:
            while not self.cancel.value:
                # We wait for the consumer thread to handle the frame before
                # writing a new one
                while self.frame_available.is_set():
                    pass
                else:
                    frame = device.wait_and_get_depth_frame()
                    self.ring_buffer.extend(frame)

                if self.background_ready:
                    # Synchronized write access to shared array
                    # with self.shared_array.get_lock():
                    arr = np.frombuffer(self.shared_array.get_obj(),
                                        dtype='I').reshape(
                                            self.height, self.width)
                    arr[:] = frame
                    self.frame_available.set()

                else:
                    frame_list.append(frame)

                    if len(frame_list) >= self.nb_background_frame:
                        # Estimate background
                        print("[Driver] Estimating background from {} " \
                              "frames".format(self.nb_background_frame))
                        arr = np.frombuffer(self.shared_array.get_obj(),
                                            dtype='I').reshape(
                                                self.height, self.width)
                        self.background = self.background_estimation(
                            frame_list)
                        self.compute_std_dev(frame_list)
                        self.compute_parameters()
                        arr[:] = self.background
                        self.background_ready = True
                        print("[Driver] Background ready")
                        self.frame_available.set()
#                        del frame_list  ####################### adicionado por mi

                if self.recording_trigger.is_set():
                    self.async_buffer_save()
                    self.recording_trigger.clear()
            print("[Driver] Process closing")
            device.shutdown()

        except Exception:
            print("[Driver] Exception in process")
            device.shutdown()
            raise