def predict_class(self, samples, sample_poses=None):
        """
        first use "is_guaranteed_new_class"!
        :param samples:
        :param sample_poses:
        :return: inconsistent, class, confidence
        """

        # disable sample weights
        sample_weight = None

        # individual classifier predictions (binary)
        predictions = {}
        true_pos_rate = 0.7
        true_pos_thresh = math.ceil(true_pos_rate*len(samples))
        false_pos_rate = 0.4
        false_pos_thresh = math.floor(false_pos_rate * len(samples))

        target_positive_classes = []
        true_positives_rates = []
        false_positives = []
        pos_matching_confidence = []
        neg_matching_confidence = []

        # select classes in range
        classes_in_range = self.data_controller.classes_in_range(samples=samples, metric='euclidean', thresh=1.25)

        if not classes_in_range:
            log.info('db', "No classes in range...")
            return True, -1, 1

        for class_id, cls in self.classifiers.iteritems():
            # only consider near classes
            if class_id in classes_in_range:
                # binary prediction
                predictions[class_id], matching_confidences = cls.predict(samples, samples_poses=sample_poses)
                true_positive_samples = np.count_nonzero(predictions[class_id] == 1)

                # count certain detections
                if true_positive_samples >= true_pos_thresh:
                    target_positive_classes.append(class_id)
                    true_positives_rates.append(true_positive_samples)
                    pos_matching_confidence.append(matching_confidences)
                elif true_positive_samples >= false_pos_thresh:
                    # not true class - check if too many false positives
                    false_positives.append(class_id)
                    neg_matching_confidence.append(matching_confidences)

                # else:
                #     false_positive_samples = np.count_nonzero(predictions[class_id] == -1)
                #     # count uncertain detections
                #     if false_positive_samples >= false_pos_thresh:
                #         false_positives.append(class_id)

        is_consistent = True
        target_class = None
        safe_weight = 7

        print "... T_fp: {}, T_tp: {} |  fp ids: {}, tp ids: {}".format(false_pos_thresh, true_pos_thresh, false_positives, target_positive_classes)


        confidence = 1.0
        decision_weights = np.repeat(99., len(samples))

        # check for inconsistent predictions
        if len(false_positives) == 0:
            if len(target_positive_classes) == 0:
                # new class!
                target_class = -1
                # TODO: confidence not implemented yet! Build additive score
            elif len(target_positive_classes) == 1:
                # single target class
                target_class = target_positive_classes[0]
                decision_weights = pos_matching_confidence[0]
            else:
                # multiple target classes
                is_consistent = False   # not a valid result
                best_index = np.argmax(true_positives_rates)
                target_class = target_positive_classes[best_index]
                decision_weights = pos_matching_confidence[best_index]
        else:
            is_consistent = False  # not a valid/safe result
            if len(target_positive_classes) == 0:
                # new class!
                target_class = -1
                # TODO: confidence not implemented yet! Build additive score
            elif len(target_positive_classes) == 1:
                # target class
                target_class = target_positive_classes[0]
                decision_weights = pos_matching_confidence[0]
            else:
                best_index = np.argmax(true_positives_rates)
                target_class = target_positive_classes[best_index]
                decision_weights = pos_matching_confidence[best_index]

        # TODO: not active right now
        # if is_consistent and False:
        #     # check for strong samples with inconsistent predictions
        #     mask = sample_weight > safe_weight
        #     if np.count_nonzero(mask):
        #         # check if safe samples predict wrong class
        #         for class_id, pred in predictions.iteritems():
        #             if class_id == target_class:
        #                 # false negative
        #                 if np.count_nonzero(pred[mask] < 0):
        #                     is_consistent = False
        #                     break
        #             else:
        #                 # false positive
        #                 if np.count_nonzero(pred[mask] > 0):
        #                     is_consistent = False
        #                     break

        # calculate confidence

        # print "decision_weights: ", decision_weights
        # print "pos_matching_confidence: ", pos_matching_confidence

        print "==== decision_weights: ", ["%0.1f" % i for i in decision_weights]

        if target_class == -1:
            # TODO: not implemented yet! Build additive score
            confidence = 1.0
        elif target_class > 0:
            # combine confidence with binary decision (weighted average)
            confidence = self.calc_normalized_positive_confidence(predictions[target_class], weights=decision_weights)
        else:
            confidence = 1.0

        print "---- Prediction: ", predictions
        print "---- Target class decision: {} / conf: {} / TP: {}, FP: {} / min. TP: {} max. FP: {}".format(target_class, confidence, len(target_positive_classes), len(false_positives), true_pos_thresh, false_pos_thresh)

        # confidence: 1...100 (full conf)
        return is_consistent, target_class, confidence
Пример #2
0
 def print_embedding_status(self):
     log.info('db', "Current embeddings:")
     for user_id, embeddings in self.__class_samples.iteritems():
         log.info('db', "     User" + str(user_id) + ": " + str(len(embeddings)) + " representations")
Пример #3
0
    def get_weighted_score(self, test_samples, test_poses, ref_samples,
                           ref_poses):
        assert test_samples.ndim == 2
        assert ref_samples.ndim == 2

        dist_lookup = pairwise_distances(test_samples,
                                         ref_samples,
                                         metric='euclidean')

        # print np.shape(dist_lookup[0])
        factors = []
        sample_weights = []

        # if only one sample: cannot calculate abof
        if len(ref_samples) < 3:
            log.severe(
                'Cannot calculate ABOF with {} reference samples (variance calculation needs at least 3 reference points)'
                .format(len(ref_samples)))
            raise Exception

        for i_sample, A in enumerate(test_samples):
            factor_list = []
            weight_list = []
            for i in range(len(ref_samples)):
                # select first point in reference set
                B = ref_samples[i]
                # distance
                AB = dist_lookup[i_sample][i]
                for j in range(i + 1):
                    if j == i:  # ensure B != C
                        continue
                    # select second point in reference set
                    C = ref_samples[j]
                    # distance
                    AC = dist_lookup[i_sample][j]

                    if np.array_equal(B, C):
                        sys.exit(
                            "Points are equal: B == C! Reference Set contains two times the same samples"
                        )
                        factor_list.append(1000)
                        print "Bi/Cj: {}/{}".format(i, j)
                        # sys.exit('ERROR\tangleBAC\tmath domain ERROR, |cos<AB, AC>| <= 1')
                        continue

                    angle_BAC = ABOD.angleBAC(A, B, C, AB, AC)

                    w1 = self.weight_gen.get_pose_weight(
                        test_poses[i_sample], ref_poses[i])
                    w2 = self.weight_gen.get_pose_weight(
                        test_poses[i_sample], ref_poses[j])
                    weight_list.append(2. / float(w1 + w2))  # 1/(a+b)/2

                    # compute each element of variance list
                    try:
                        # apply weighting
                        if self.variant == 1:
                            tmp = angle_BAC / float(
                                math.pow(AB * AC, 2) * (w1 * w2))
                        elif self.variant == 2:
                            tmp = angle_BAC / float(
                                math.pow(AB * AC, 2) * (w1 + w2))
                        else:
                            tmp = angle_BAC / float(math.pow(AB * AC, 2))

                    except ZeroDivisionError:
                        log.severe(
                            "ERROR\tABOF\tfloat division by zero! Trying to predict training point?'"
                        )
                        tmp = 500
                        # sys.exit('ERROR\tABOF\tfloat division by zero! Trying to predict training point?')
                    factor_list.append(tmp)

            # calculate weighted variance
            if self.variant == 3:
                weighted_average = np.average(factor_list,
                                              weights=np.array(weight_list))
                var = np.average((factor_list - weighted_average)**2)
            elif self.variant == 4:
                var = WeightedABOD.biased_weighted_var(np.array(factor_list),
                                                       np.array(weight_list),
                                                       weighted_average=False)
            elif self.variant == 5:
                var = WeightedABOD.biased_weighted_var(np.array(factor_list),
                                                       np.array(weight_list))
            else:
                var = np.var(np.array(factor_list))

            factors.append(var)
            # weight_list = np.repeat(1, len(factors))
            sample_weights.append(np.average(weight_list))

        return np.array(factors), np.array(sample_weights)
Пример #4
0
    def get_score(test_samples, reference_set):

        assert test_samples.ndim == 2
        assert reference_set.ndim == 2

        dist_lookup = pairwise_distances(test_samples,
                                         reference_set,
                                         metric='euclidean')

        # print np.shape(dist_lookup[0])
        factors = []

        # if only one sample: cannot calculate abof
        if len(reference_set) < 3:
            log.severe(
                'Cannot calculate ABOF with {} reference samples (variance calculation needs at least 3 reference points)'
                .format(len(reference_set)))
            raise Exception

        for i_sample, A in enumerate(test_samples):
            factor_list = []
            for i in range(len(reference_set)):
                # select first point in reference set
                B = reference_set[i]
                # distance
                AB = dist_lookup[i_sample][i]

                for j in range(i + 1):
                    if j == i:  # ensure B != C
                        continue
                    # select second point in reference set
                    C = reference_set[j]
                    # distance
                    AC = dist_lookup[i_sample][j]

                    if np.array_equal(B, C):
                        print "Bi/Cj: {}/{}".format(i, j)
                        log.error(
                            "Points are equal: B == C! Assuming classification of training point"
                        )
                        sys.exit(
                            "Points are equal: B == C! Reference Set contains two times the same samples"
                        )
                        factor_list.append(1000)
                        # sys.exit('ERROR\tangleBAC\tmath domain ERROR, |cos<AB, AC>| <= 1')
                        continue

                    # angle_BAC = ABOD.angleBAC(A, B, C, AB, AC)
                    # angle_BAC = ABOD.angleFast(A-B, A-C)

                    vector_AB = B - A
                    vector_AC = C - A

                    # compute each element of variance list
                    try:
                        cos_similarity = np.dot(vector_AB,
                                                vector_AC) / (AB * AC)
                        # apply weighting
                        tmp = cos_similarity / float(math.pow(AB * AC, 2))
                    except ZeroDivisionError:
                        log.severe(
                            "ERROR\tABOF\tfloat division by zero! Trying to predict training point?'"
                        )
                        tmp = 500
                        # sys.exit('ERROR\tABOF\tfloat division by zero! Trying to predict training point?')
                    factor_list.append(tmp)
            factors.append(np.var(factor_list))
        return np.array(factors)
Пример #5
0
    def update(self, samples):

        # init - add all samples if no data yet
        if len(self.__data) == 0:
            self.__data = samples
            return

        # =======================================
        # 1.  Reduce data/sample data

        if self.dim_reduction > 0:
            basis, mean, var = ExtractMaxVarComponents(self.__data,
                                                       self.dim_reduction)
            self.log_expl_var.append(var)
        else:
            basis, mean = ExtractSubspace(self.__data, 0.8)

        cluster_reduced = ProjectOntoSubspace(self.__data, mean, basis)
        samples_reduced = ProjectOntoSubspace(samples, mean, basis)
        dims = np.shape(cluster_reduced)
        # select minimum data to build convex hull
        # min_nr_elems = dims[1] + 4
        if self.__verbose:
            print "Reducing dimension: {}->{}".format(
                np.shape(self.__data)[1], dims[1])

        # =======================================
        # 2.  Calculate Convex Hull in subspace

        data_hull = cluster_reduced  # take all samples of data
        hull = Delaunay(data_hull)
        if self.__verbose:
            print "Calculating data hull using {}/{} points".format(
                len(data_hull), len(self.__data))

        # =======================================
        # 3.  Select new samples from outside convex hull

        if not self.__inverted:
            inclusion_mask = np.array([
                False if hull.find_simplex(sample) >= 0 else True
                for sample in samples_reduced
            ])
            if self.__verbose:
                # Todo: use outside mask counting
                nr_elems_outside_hull = np.sum([
                    0 if hull.find_simplex(sample) >= 0 else 1
                    for sample in samples_reduced
                ])
                print "Elements OUTSIDE hull (to include): {}/{}".format(
                    nr_elems_outside_hull, len(samples))
        else:
            inclusion_mask = np.array([
                True if hull.find_simplex(sample) >= 0 else False
                for sample in samples_reduced
            ])

        # add samples (samples need to be np.array)
        self.__data = np.concatenate((self.__data, samples[inclusion_mask]))

        # =======================================
        # 4.  Recalculate hull with newly added points

        # If memory exceeded: Perform unrefinement process -
        # discharge sampling directions with lowest variance contribution
        if self.dim_reduction > 0:
            nr_comps = self.dim_reduction if len(
                self.__data) <= self.max_size else self.dim_removal
            if len(self.__data) > 150:
                nr_comps = self.dim_removal - 1
            basis, mean, var = ExtractMaxVarComponents(self.__data, nr_comps)
        else:
            # automatic dimension selection (based on containing certain variance)
            basis, mean = ExtractSubspace(self.__data, 0.75)

        cluster_reduced = ProjectOntoSubspace(self.__data, mean, basis)
        print "Recuding dimension: {}->{}".format(
            np.shape(self.__data)[1],
            np.shape(cluster_reduced)[1])
        hull = Delaunay(cluster_reduced)

        # =======================================
        # 5.  Discharge samples inside hull

        if not self.__inverted:
            # select samples inside hull
            cl_to_delete = np.array(
                list(
                    set(range(0, len(cluster_reduced))) -
                    set(np.unique(hull.convex_hull))))
            # set(range(len(data_hull))).difference(hull.convex_hull)
        else:
            cl_to_delete = np.array([])
            # select samples on hull
            if len(cluster_reduced) > self.max_size:
                hull_indices = list(np.unique(hull.convex_hull))
                if len(hull_indices) > 0:
                    nr_to_del = 5 if len(hull_indices) > 5 else 0
                    cl_to_delete = np.array(hull_indices[0:nr_to_del])

        # print "Points building convex hull: {}".format(set(np.unique(hull.convex_hull)))
        # print "To delete: {}".format(cl_to_delete)

        if len(cl_to_delete[cl_to_delete < 0]) > 0:
            print set(np.unique(hull.convex_hull))
            log.warning("Index elements smaller than 0: {}".format(
                cl_to_delete[cl_to_delete < 0]))

        if self.__log:
            self.log_intra_deleted.append(len(cl_to_delete))
            self.log_cl_size_orig.append(len(self.__data))

        print "Cleaning {} points from inside data".format(len(cl_to_delete))

        # Remove points from inside hull
        self.__data = np.delete(self.__data, cl_to_delete, axis=0)

        # =======================================
        # 6.  KNN point removal: remove similar points

        if self.knn_removal_thresh > 0:
            max_removal = 10 if len(
                self.__data) > self.knn_removal_thresh else 0
            if max_removal > 0:
                filter = KNFilter(self.__data, k=3, threshold=0.25)
                tmp = filter.filter_x_samples(max_removal)
                print "--- Removing {} knn points".format(
                    len(self.__data) - len(tmp))
                self.__data = tmp

            if self.__log:
                self.log_cl_size_reduced.append(len(self.__data))

        print "Cluster size: {}".format(len(self.__data))
Пример #6
0
    def __reduce_after(self, metric='euclidean', reverse=True):
        # TODO: clean samples with same pose but large variation (most likely erronomous)

        if len(self.data) < self.__max_size:
            return

        # prevent drift - keep frontal poses
        # keep at most 1/5 of the samples in the core to prevent drift
        # - filter only other samples
        if self.__prevent_drift:
            # take all non-frontal images (any rotation > 10 deg)
            m1 = abs(self.poses) > 10
            non_frontal_mask = np.any(m1, axis=1)
            frontal_mask = ~non_frontal_mask
            non_frontal_indices = np.flatnonzero(non_frontal_mask)
            frontal_indices = np.flatnonzero(frontal_mask)

            # indices to filter
            reduce_indices = non_frontal_indices
            save_indices = frontal_indices

            # take at minimum 4/5 non-frontal images
            save_size = int(self.__max_size * 4. / 5.)
            if len(non_frontal_indices) < save_size:
                # print reduce_indices
                # print save_indices
                # print "Save size: ", save_size
                # print "Set sizes: frontal: to add: {}, non-frontal: {}".format(save_size-len(non_frontal_indices), len(non_frontal_indices))
                reduce_indices = np.concatenate(
                    (reduce_indices,
                     frontal_indices[0:(save_size -
                                        len(non_frontal_indices))]))
                save_indices = frontal_indices[save_size -
                                               len(non_frontal_indices):]
                # print "Samples to reduce: ", len(reduce_indices)
                # print "Remaining save samples: ", len(save_indices)

            # temporary data
            data_tmp_save = self.data[save_indices]
            pose_tmp_save = self.poses[save_indices]
            data_tmp = self.data[reduce_indices]
            pose_tmp = self.poses[reduce_indices]

            # do filtering
            dist = pairwise_distances(self.data_mean, data_tmp, metric=metric)
            dist = dist[0]
            if metric == 'euclidean':
                dist = np.square(dist)
            to_remove = len(self.data) - self.__max_size
            indices = np.arange(0, len(data_tmp))
            dist_sorted, indices_sorted = zip(
                *sorted(zip(dist, indices), reverse=reverse))
            indices_to_delete = indices_sorted[0:to_remove]
            log.info(
                'cl', "Removing {} non-frontal points".format(
                    len(indices_to_delete)))

            data_tmp = np.delete(data_tmp, indices_to_delete, axis=0)
            pose_tmp = np.delete(pose_tmp, indices_to_delete, axis=0)

            # combine filtered with save data
            self.data = np.concatenate((data_tmp, data_tmp_save))
            self.poses = np.concatenate((pose_tmp, pose_tmp_save))

        else:
            # delete X samples which are most distant
            dist = pairwise_distances(self.data_mean, self.data, metric=metric)
            dist = dist[0]
            if metric == 'euclidean':
                dist = np.square(dist)
            to_remove = len(self.data) - self.__max_size
            indices = np.arange(0, len(self.data))
            dist_sorted, indices_sorted = zip(
                *sorted(zip(dist, indices), reverse=reverse))
            indices_to_delete = indices_sorted[0:to_remove]
            log.info('cl', "Removing {} points".format(len(indices_to_delete)))

            # delete
            self.data = np.delete(self.data, indices_to_delete, axis=0)
            self.poses = np.delete(self.poses, indices_to_delete, axis=0)
Пример #7
0
    def accumulate_samples(self,
                           tracking_id,
                           new_samples,
                           sample_weights=np.array([]),
                           sample_poses=np.array([])):

        # check for set inconsistency
        samples_ok = BaseMetaController.check_inter_sample_dist(
            new_samples, metric='euclidean')

        if not samples_ok:
            log.severe("Identification set is inconsistent - disposing...")
            # reset queue
            self.sample_queue.pop(tracking_id, None)
            self.sample_weight_queue.pop(tracking_id, None)
            self.sample_pose_queue.pop(tracking_id, None)
            return False, np.array([]), np.array([]), np.array([])

        # generate placeholder weights
        if sample_weights.size == 0:
            # 5 of 10
            sample_weights = np.repeat(5, len(new_samples))

        assert len(sample_weights) == len(new_samples)

        # add samples
        if tracking_id not in self.sample_queue:
            # initialize
            self.sample_queue[tracking_id] = new_samples
            self.sample_weight_queue[tracking_id] = sample_weights
            self.sample_pose_queue[tracking_id] = sample_poses
        else:
            # append
            self.sample_queue[tracking_id] = np.concatenate((self.sample_queue[tracking_id], new_samples))\
                                         if self.sample_queue[tracking_id].size \
                                         else new_samples
            self.sample_weight_queue[tracking_id] = np.concatenate((self.sample_weight_queue[tracking_id], sample_weights))\
                                         if self.sample_weight_queue[tracking_id].size \
                                         else sample_weights
            self.sample_pose_queue[tracking_id] = np.concatenate((self.sample_pose_queue[tracking_id], sample_poses))\
                                         if self.sample_pose_queue[tracking_id].size \
                                         else sample_poses

        is_save_set = False

        # if set has save sample or is long enough
        if len(self.sample_queue[tracking_id]) >= self.min_sample_length:
            if len(self.sample_queue[tracking_id]) >= self.save_sample_length\
                    or np.count_nonzero(self.sample_weight_queue[tracking_id] >= self.save_weight_thresh):

                # check set consistency
                samples_ok = BaseMetaController.check_inter_sample_dist(
                    self.sample_queue[tracking_id], metric='euclidean')

                if samples_ok:
                    # set is save - allow identification
                    is_save_set = True
                else:
                    # dispose all samples
                    self.sample_queue.pop(tracking_id, None)
                    self.sample_weight_queue.pop(tracking_id, None)
                    self.sample_pose_queue.pop(tracking_id, None)
                    log.severe("Set is inconsistent - disposing...")

        # TODO: return whole set or only last?
        current_samples = self.sample_queue.get(tracking_id, np.array([]))
        current_weights = self.sample_weight_queue.get(tracking_id,
                                                       np.array([]))
        current_poses = self.sample_pose_queue.get(tracking_id, np.array([]))

        # not enough save samples - return what we have so far
        return is_save_set, current_samples, current_weights, current_poses
Пример #8
0
    def accumulate_samples(self,
                           user_id,
                           new_samples,
                           sample_weights=np.array([]),
                           sample_poses=np.array([])):
        """

        :param user_id:
        :param new_samples:
        :param sample_weights:
        :return:
        array : save samples (save to integrate in any way)
        bool : reset user
        int : prediction of last section
        float : confidence of last section prediction
        """

        # check for set inconsistency
        samples_ok = BaseMetaController.check_inter_sample_dist(
            new_samples, metric='euclidean')

        if not samples_ok:
            # no return (queue is not filled up and thus we dont have a save section)
            log.severe("Update set is inconsistent - disposing...")
            # reset queue
            self.sample_queue.pop(user_id, None)
            self.sample_weight_queue.pop(user_id, None)
            self.sample_pose_queue.pop(user_id, None)
            return np.array([]), np.array([]), True, -1, 1.

        # generate placeholder weights
        if sample_weights.size == 0:
            # 5 of 10
            sample_weights = np.repeat(5, len(new_samples))

        assert len(sample_weights) == len(new_samples)

        # add samples
        if user_id not in self.sample_queue:
            # initialize
            self.sample_queue[user_id] = new_samples
            self.sample_weight_queue[user_id] = sample_weights
            self.sample_pose_queue[user_id] = sample_poses
        else:
            # append
            self.sample_queue[user_id] = np.concatenate((self.sample_queue[user_id], new_samples))\
                                         if self.sample_queue[user_id].size \
                                         else new_samples
            self.sample_weight_queue[user_id] = np.concatenate((self.sample_weight_queue[user_id], sample_weights))\
                                         if self.sample_weight_queue[user_id].size \
                                         else sample_weights
            self.sample_pose_queue[user_id] = np.concatenate((self.sample_pose_queue[user_id], sample_poses))\
                                         if self.sample_pose_queue[user_id].size \
                                         else sample_poses

        target_class = -1
        confidence = 1.
        forward = np.array([])
        forward_poses = np.array([])
        reset_user = False

        # do meta recognition
        # check set for inconsistencies - return only save section
        while len(self.sample_queue[user_id]) >= self.__queue_max_length:

            sample_batch = self.sample_queue[user_id][0:self.
                                                      __queue_max_length]
            weight_batch = self.sample_weight_queue[user_id][
                0:self.__queue_max_length]
            pose_batch = self.sample_pose_queue[user_id][0:self.
                                                         __queue_max_length]

            # check set consistency
            samples_ok = BaseMetaController.check_inter_sample_dist(
                sample_batch, metric='euclidean')

            # predict class
            is_consistent, target_class, confidence = self.__p_multicl.predict_class(
                sample_batch, sample_poses=pose_batch)

            if samples_ok and is_consistent:
                # add samples to forward
                forward = np.concatenate((forward, self.sample_queue[user_id][0:self.__inclusion_range])) \
                    if forward.size \
                    else self.sample_queue[user_id][0:self.__inclusion_range]
                forward_poses = np.concatenate((forward_poses, self.sample_pose_queue[user_id][0:self.__inclusion_range])) \
                    if forward_poses.size \
                    else self.sample_pose_queue[user_id][0:self.__inclusion_range]

                # remove first x samples
                self.sample_queue[user_id] = self.sample_queue[user_id][
                    self.__inclusion_range:]
                self.sample_weight_queue[user_id] = self.sample_weight_queue[
                    user_id][self.__inclusion_range:]
                self.sample_pose_queue[user_id] = self.sample_pose_queue[
                    user_id][self.__inclusion_range:]
            else:
                # dispose all samples! Whole queue!
                self.sample_queue.pop(user_id, None)
                self.sample_weight_queue.pop(user_id, None)
                self.sample_pose_queue.pop(user_id, None)
                log.severe("Set is inconsistent - disposing...")
                reset_user = True
                break

        # predict user if not enough samples
        if not forward.size and reset_user is False:
            is_consistent, target_class, confidence = self.__p_multicl.predict_class(
                self.sample_queue[user_id],
                sample_poses=self.sample_pose_queue[user_id])
            print "Not enough to forward but predict...", is_consistent, target_class, confidence

        return forward, forward_poses, reset_user, target_class, confidence
Пример #9
0
    def __init__(self, server, conn, handle):

        # receive tracking id
        tracking_id = server.receive_uint(conn)

        # receive images
        images = server.receive_image_batch_squared_same_size(
            conn, switch_rgb_bgr=True)

        # get sample poses
        sample_poses = []
        for x in range(0, len(images)):
            pitch = server.receive_char(conn)
            yaw = server.receive_char(conn)
            sample_poses.append([pitch, yaw])
        sample_poses = np.array(sample_poses)

        # generate embedding (from rgb images)
        embeddings = server.embedding_gen.get_embeddings(rgb_images=images,
                                                         align=False)

        if not embeddings.any():
            r.Error(server, conn, "Could not generate face embeddings.")
            return

        # accumulate samples
        is_save_set, current_samples, _weights_placeholder, current_poses = \
            server.classifier.id_controller.accumulate_samples(
                tracking_id, new_samples=embeddings, sample_poses=sample_poses
            )

        log.info('server', "tracking id: {}".format(tracking_id))

        if len(current_samples) == 0:
            # queue has just been resetted
            r.Error(
                server, conn,
                "Samples are inconsistent - starting accumulation again...")
            return

        # predict class similarities
        new_class_guaranteed = server.classifier.is_guaranteed_new_class(
            current_samples)

        # at least 3 samples needed to generate classifier
        if new_class_guaranteed and len(current_samples) > 2:
            id_pred = -1
            confidence = 100
            is_consistent = True  # yes - inter-sample distance checked
            is_save_set = True
        else:
            # do meta recognition and predict the user id from the cls scores
            is_consistent, id_pred, confidence = server.classifier.predict_class(
                current_samples, sample_poses=current_poses)

            # convert to integer
            confidence = int(confidence * 100.0)

        # get user nice name
        user_name = server.user_db.get_name_from_id(id_pred)

        print ".... is_save: {}, is_consistent: {}, id_pred: {}, confidence: {}".format(
            is_save_set, is_consistent, id_pred, confidence)

        if user_name is None:
            user_name = "unnamed"

        # save images
        if DEBUG_IMAGES:
            for i in images:
                # save from RGB order
                scipy.misc.imsave(
                    "identification_{}.png".format(current_milli_time()), i)

        if is_save_set:
            # SAVE SET - TAKE ACTION
            profile_picture = None

            if is_consistent:
                # new identity
                if id_pred < 0:
                    # unknown user
                    print "--- creating new user"
                    log.info('db', "Creating new User")
                    user_id = server.user_db.create_new_user("a_user")
                    server.user_db.print_users()
                    # add classifier
                    server.classifier.init_new_class(
                        user_id, current_samples, sample_poses=current_poses)
                    id_pred = user_id
                else:
                    # for s in current_samples:
                    #     print "s: {:.2f}".format(s[0])
                    # add data for training and return identification
                    # add to data model
                    server.classifier.data_controller.add_samples(
                        user_id=id_pred,
                        new_samples=current_samples,
                        new_poses=current_poses)
                    # add to classifier training queue
                    server.classifier.add_training_data(
                        id_pred, current_samples)

                    # get profile picture
                    profile_picture = server.user_db.get_profile_picture(
                        id_pred)

                # cleanup
                server.classifier.id_controller.drop_samples(tracking_id)

                # valid identification
                log.info(
                    'server',
                    "User identification complete: {} [ID], {} [Username]".
                    format(id_pred, user_name))
                r.Identification(server,
                                 conn,
                                 int(id_pred),
                                 user_name,
                                 confidence=confidence,
                                 profile_picture=profile_picture)
            else:
                # inconsistent prediction - samples might be bad. dump and take new samples

                server.classifier.id_controller.drop_samples(tracking_id)
                r.Error(
                    server, conn,
                    "Samples are inconsistent - starting accumulation again..."
                )
                return
                # TODO: is feedback useful here?

        else:
            # UNSAVE SET - WAIT TILL SAVE SET IS ACCUMULATED

            # identification progress in percent
            id_progress = len(current_samples) / float(
                server.classifier.id_controller.save_sample_length)
            id_progress = int(id_progress * 100)

            # return prediction and confidence - but no identification
            r.PredictionFeedback(server,
                                 conn,
                                 int(id_pred),
                                 user_name,
                                 confidence=confidence,
                                 progress=id_progress)
Пример #10
0
    def __init__(self, server, conn, handle):
        # receive user id
        user_id = server.receive_uint(conn)

        log.info('server',
                 'User Update (Aligned, Robust) for ID {}'.format(user_id))

        # receive images
        images = server.receive_image_batch_squared_same_size(
            conn, switch_rgb_bgr=True)

        # save images
        if DEBUG_IMAGES:
            for i in images:
                # save from RGB order
                scipy.misc.imsave("update_{}.png".format(current_milli_time()),
                                  i)

        # get sample poses
        sample_poses = []
        for x in range(0, len(images)):
            pitch = server.receive_char(conn)
            yaw = server.receive_char(conn)
            sample_poses.append([pitch, yaw])
        sample_poses = np.array(sample_poses)

        # generate embedding
        embeddings = server.embedding_gen.get_embeddings(rgb_images=images,
                                                         align=False)

        if not embeddings.any():
            r.Error(server, conn, "Could not generate face embeddings.")
            return

        # TODO: calculate weights
        weights = np.repeat(10, len(images))

        # accumulate samples - check for inconsistencies
        verified_data, verified_poses, reset_user, id_pred, confidence = server.classifier.update_controller.accumulate_samples(
            user_id,
            embeddings,
            sample_weights=weights,
            sample_poses=sample_poses)

        log.info(
            'cl',
            "verified_data (len: {}), reset_user: {}: ID {}, conf {}".format(
                len(verified_data), reset_user, id_pred, confidence))

        # forward save part of data
        if verified_data.size:
            # for s in embeddings:
            #     print "new: {:.8f}".format(s[0])
            # print "------------------"
            # for s in verified_data:
            #     print "s: {:.5f}".format(s[0])

            # add to data model
            server.classifier.data_controller.add_samples(
                user_id=user_id,
                new_samples=verified_data,
                new_poses=verified_poses)
            # add to classifier training queue
            server.classifier.add_training_data(user_id, verified_data)

        # reset user if queue has become inconsistent or wrong user is predicted
        if reset_user:
            log.severe("USER VERIFICATION FAILED - FORCE REIDENTIFICATION")
            r.Reidentification(server, conn)
            return

        # return prediction feedback
        user_name = server.user_db.get_name_from_id(id_pred)
        if user_name is None:
            user_name = "unnamed"
        r.PredictionFeedback(server,
                             conn,
                             id_pred,
                             user_name,
                             confidence=int(confidence * 100.0))
Пример #11
0
    def predict(self, samples):
        """
        Prediction cases:
        - Only target class is identified with ratio X (high): Class
        - Target and other class is identified with ration X (high) and Y (small): Class with small confusion
        - Multiple classes are identified with small ratios Ys: Novelty
        - No classes identified: Novelty
        :param samples:
        :return: Class ID, -1 (Novelty), None invalid samples (multiple detections)
        """

        # no classifiers yet, predict novelty
        if not self.classifiers:
            # 100% confidence
            self.__decision_function = np.array([len(samples)]), np.array([-1])
            return -1

        predictions, class_ids = self.__predict(samples)

        if len(predictions) == 0:
            # no class in reach - classify as novel class
            self.__decision_function = np.array([len(samples)]), np.array([-1])
            return -1

        # calc nr of positive class detections
        cls_scores = (predictions > 0).sum(axis=1)
        self.__decision_function = cls_scores, class_ids
        nr_samples = len(samples)
        self.__decision_nr_samples = nr_samples

        log.info(
            'cl',
            "Classifier scores: {} | max: {}".format(cls_scores, nr_samples))

        # no classes detected at all - novelty
        if len(cls_scores[cls_scores <= self.__novelty_thresh *
                          nr_samples]) == len(cls_scores):
            return -1

        identification_mask = cls_scores >= self.__class_thresh * nr_samples
        ids = class_ids[identification_mask]
        if len(ids) > 0:

            # multiple possible detection - invalid samples
            if len(ids) > 1:

                # use average to-class-distance to select best choice
                mean_dist_cosine = []
                mean_dist_euclidean = []

                # todo: mean dist or mean dist to cluster mean
                for class_id in ids:
                    mean_dist_cosine.append(
                        self.classifiers[class_id].mean_dist(samples))
                    mean_dist_euclidean.append(
                        self.classifiers[class_id].mean_dist(
                            samples, 'euclidean'))

                id_index_cosine = mean_dist_cosine.index(min(mean_dist_cosine))
                id_index_euclidean = mean_dist_euclidean.index(
                    min(mean_dist_euclidean))

                log.severe("Samples are inambiguous. Classes: {}".format(ids))
                log.severe("IDCOS: {} | meandist cosine: {}".format(
                    int(ids[id_index_cosine]), mean_dist_cosine))
                log.severe("IDEUC: {} | meandist euclidean: {}".format(
                    int(ids[id_index_euclidean]), mean_dist_euclidean))

                for class_id in ids:
                    print self.classifiers[class_id].class_mean_dist(
                        samples, 'cosine')

                mean_dist_cosine = np.array(mean_dist_cosine)

                if np.sum(
                    (mean_dist_cosine - min(mean_dist_cosine)) < 0.05) > 1:
                    log.severe(
                        "SAMPLES DISCARGED: Average distance to data inambiguous"
                    )
                    return None

                return int(ids[id_index_cosine])
                # return None

            # single person identified - return id
            return int(ids[0])
        else:
            # samples unclear
            return None