예제 #1
0
def main():
    video_src = 0  # 0 means webcam; set file name if video
    cam = cv2.VideoCapture(video_src)
    # make_resolution(cam, 1200, 600)
    _, sample_frame = cam.read()  # get 1 sample frame to setup codes
    height, width = sample_frame.shape[:2]
    face_detector = FaceDetector()
    tracker = Tracker(FaceVar.IOU_THRESHOLD, img_size=(height, width))

    while True:
        frame_got, frame = cam.read()  # read an image from camera

        if frame_got is False:  # if not getting an image, just break
            print('Camera is not getting images')
            break

        # detect faces from the current image
        conf, boxes = face_detector.get_faceboxes(image=frame, threshold=0.9)

        # associate the current detection with existing tracks
        matches, unmatched_dets, unmatched_tracks = tracker.assign_detections_to_trackers(
            boxes)

        # update the tracking system
        tracker.update(frame, boxes, matches, unmatched_dets, unmatched_tracks)

        # annotate curent image (optional for bonding box, landmarks, head pose. all fater kalman filter)
        frame = tracker.annotate_BBox(frame)

        # choose if draw orignal detection bonding box on image, without kalman filter.
        if FaceVar.DRAW_ORIG_BBOX:
            frame = draw_BBox(image=frame, faceboxes=boxes, confidences=conf)

        cv2.imshow("preview", frame)
        if cv2.waitKey(10) == 27:
            break
예제 #2
0
def main():
    detector = FaceDetector()  # initialize face detector
    files = sorted(glob.glob('./data/face_sequence/*.jpg'))
    images_seq = [cv2.imread(file) for file in files]

    tracker = Tracker(FaceVar.IOU_THRESHOLD,
                      img_size=(720, 1280))  # initialize tracking system
    index = 1
    for image in images_seq:
        start = time.time()
        conf, boxes = detector.get_faceboxes(
            image=image, threshold=0.9)  # get detection box

        # assign detection to existing tracks
        matches, unmatched_dets, unmatched_tracks = tracker.assign_detections_to_trackers(
            boxes)

        # update tracking system.
        tracker.update(image, boxes, matches, unmatched_dets, unmatched_tracks)

        # annotate the image with current tracking status
        image = tracker.annotate_BBox(image)

        # if needed, could drawn original bonding box from detector.
        if FaceVar.DRAW_ORIG_BBOX:
            image = draw_BBox(image=image, faceboxes=boxes, confidences=conf)
        cv2.putText(image, 'Frame: ' + str(index), (15, 15),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0))
        #cv2.imwrite('image_seq'+str(index)+'.jpg', image)
        print(time.time() - start)
        cv2.imshow('image_seq' + str(index), image)

        cv2.waitKey(0)
        cv2.destroyAllWindows()

        index += 1
예제 #3
0
class MarkDetector:
    def __init__(self, mark_model='models/landmark_detector/frozen_inference_graph.pb'):
        '''initialize MarkDetector
        Input: mark_model --- path to the tensorflow model'''
        self.face_detector = FaceDetector()   # Initialize face detector
        self.cnn_input_size=128  # CNN input dimension
        self.marks = None
        
        # Get a tensorflow session ready for landmark detection
        # load a frozen tensorflow model into memory
        detection_graph = tf.Graph()
        with detection_graph.as_default():
            od_graph_def = tf.GraphDef()
            with tf.gfile.GFile(mark_model, 'rb') as fid:
                serialized_graph = fid.read()
                od_graph_def.ParseFromString(serialized_graph)
                tf.import_graph_def(od_graph_def, name='')
        self.graph=detection_graph
        self.sess = tf.Session(graph=detection_graph)
        
    @staticmethod
    def draw_box(image, boxes, box_color=(255,255,255)):
        '''Draw a list of boxes on a image,
        Input: image --- the image to be drawn on
                boxes --- n by 4 list of boxes. 
                box_color --- color of boxes
        '''
        for box in boxes:
            cv2.rectangle(image, (box[0], box[1]),
                          (box[2], box[3]), box_color)
            
    @staticmethod
    def move_box(box, offset):
        '''Move the boxt to direction specified by a vector offset. FaceDetector output bonding 
        boxes mainly contains the lower part (face part) of the boxes, but the Landmark detector is 
        trained on larger image including entire head, hence, need to offset and expend the box for CNN
        landmark detection.
        -------------------------------------------------------------------------------------------
        Input: box --- a list of 4 elements from a single bonding box
            offset --- the offset for the bonding box
        Output: [leftT_x, leftT_y, rightB_x, rightB_y] --- a list of new locations for a bonding box'''
        leftT_x = box[0] + offset[0]
        leftT_y = box[1]+offset[1]
        rightB_x = box[2]+offset[0]
        rightB_y = box[3] + offset[1]
        return [leftT_x, leftT_y, rightB_x, rightB_y]        
    
    @staticmethod
    def get_square_box(box):
        '''Get a square box out by expanding a given box. FaceDetector output bonding 
        boxes mainly contains the lower part (face part) of the boxes, but the Landmark detector is 
        trained on larger image including entire head, hence, need to offset and expend the box for CNN
        landmark detection.
        -----------------------------------------------------------------------------------------
        Input: box --- a list of 4 elements representing a single bonding box
        Output: [leftT_x, leftT_y, rightB_x, rightB_y] --- a list of new locations for an expanded bonding box'''
        leftT_x = box[0]
        leftT_y = box[1]
        rightB_x = box[2]
        rightB_y = box[3]
        
        # Get the width and height
        box_width = rightB_x - leftT_x
        box_height = rightB_y - leftT_y
        
        # check if it is already a square. if not, make it into a square
        diff = box_height-box_width
        delta = int(abs(diff)/2)
        
        if diff == 0:
            return box
        elif diff > 0: # width < height, expand width
            leftT_x -= delta
            rightB_x += delta
            if diff%2==1:
                rightB_x +=1
        else: # width > height, expand height
            leftT_y -= delta
            rightB_y += delta
            if diff%2==1:
                rightB_y +=1
        
        # make sure the box is always squre
        assert ((rightB_x-leftT_x) == (rightB_y - leftT_y)), 'Box is not square.'
        return [leftT_x, leftT_y, rightB_x, rightB_y]
    
    @staticmethod
    def box_in_image(box, image):
        '''check if the box could fit in the image
        Input: box --- a single bonding box
                image --- the input image for face and landmark detection
        Output: boolean results whether the offsetted and expanded box is inside image'''
        rows = image.shape[0]
        cols = image.shape[1]
        return box[0] >=0 and box[1] >=0 and box[2] <=cols and box[3] <=rows
    
    
    def extract_cnn_facebox(self, image):
        """Detect faces from a single image. Returns the box coordinates
        Input: image --- a single image for face dtection
        Output: boxes --- n by 4, list of bonding boxes after offsetting and expansion"""
        _, raw_boxes = self.face_detector.get_faceboxes(image, threshold=0.9)  # get the raw boxes from face detector
        boxes=[]
        for box in raw_boxes:
            diff_height_width = (box[3]-box[1]) - (box[2]-box[0]) # height-width difference
            offset_y = int(abs(diff_height_width/2)) 
            box_moved = self.move_box(box, [0, offset_y]) # offset the box
            
            # expand box to be a square
            facebox = self.get_square_box(box_moved)
            
            # if box is out of image
            ############# need to modify if detect multiple faces
           # if self.box_in_image(facebox, image):
            boxes.append(facebox)            
        return boxes
    
    def square_single_facebox(self, box):
        '''given a box from face detector, return a offsetted and squared box
        Input: box --- a raw bonding box from FaceDetector
        Return: facebox --- the offsetted and expanded bonding box'''
        
       
        #print('Box: ', box)
        diff_height_width = (box[3]-box[1]) - (box[2]-box[0])
        offset_y = int(abs(diff_height_width/2))
        box_moved = self.move_box(box, [0, offset_y])            
        # Make box square
        #print('box moved: ',box_moved)
        facebox = self.get_square_box(box_moved)        
        return facebox
    
    def detect_marks(self, images_np):
        '''Detect makrs from cropped face images. 
        Input: images_np --- a list of images
        Output: all_marks --- list of landmarks for all input face images, 
            all_marks[i] = marks for ith face,
            all_marks[i][j] = a 2D list of coordinate [x, y] for ith face and jth landmark
        '''
        #Get result tensor by its name
        logits_tensor = self.graph.get_tensor_by_name('logits/BiasAdd:0')
        all_marks = []
        for image in images_np:
            # Actual detection
            predictions = self.sess.run(
                logits_tensor,
                feed_dict={'input_image_tensor:0':image})
        
            # Convert preditions to landmarks
            marks = np.array(predictions).flatten()
            marks = np.reshape(marks, (-1, 2))
            all_marks.append(marks)
        #print(marks)
        return all_marks
    
    def detect_marks_on_single_image(self, image):
        '''Detect makrs from cropped face image, only 1 face 
        Returns the coordiate/width or length of cropped image 
        Input: image --- the cropped face image
        Output: a list of 2D face landmarks'''
        #Get result tensor by its name
        logits_tensor = self.graph.get_tensor_by_name('logits/BiasAdd:0')
        # Actual detection
        image = cv2.resize(image, (self.cnn_input_size, self.cnn_input_size))
        predictions = self.sess.run(
                logits_tensor,
                feed_dict={'input_image_tensor:0':image})
        
        # Convert preditions to landmarks
        marks = np.array(predictions).flatten()
        marks = np.reshape(marks, (-1, 2))
        return marks
    
    
    @staticmethod
    def draw_marks(image, marksFace, color=(0,255,0)):
        '''Draw mark a series of landmarks on a image with multiple faces
        Input: image -- image to be drawn on
                marksFace --- list of landmarks for all faces on an image, 
                marksFace[i] = marks for ith face,
                marksFace[i][j] = a 2D list of coordinate [x, y] for ith face and jth landmark'''
        for marks in marksFace:  # each face
            for mark in marks: # each mark  
                cv2.circle(image, (int(mark[0]), int(mark[1])), 
                       1, color, -1, cv2.LINE_AA)
    
    def get_face_for_boxes(self, image, boxes):
        '''crop the image and given boxes, crop the images with these boxes and 
        make them ready for marker detection
        Input: image --- the original image to crop faces from
                boxes --- the bonding boxes assciated with all faces on a single image
        Output: a list of cropped image based on the boxes'''
        
        ######### Problem to fix if face is out of box
        face_images = []
        for box in boxes:
            if not self.box_in_image(box, image):  
                # check if a boxing box is inside the image               
                # if not paddig the empty area by copying the pixels next to it 
                face_image = self.crop_at_image_boarder(image, box)         
            else: 
                face_image = image[box[1]:box[3], box[0]:box[2]]
            face_image = cv2.resize(face_image, (128,128))
            face_image = cv2.cvtColor(face_image, cv2.COLOR_BGR2RGB)
            face_images.append(face_image)
        return face_images
    
    def get_single_face_from_boxes(self, image, box):
        '''crop the image with a single given box, make it ready for marker detection
        Input: image --- the original image to crop faces from
                box --- a single bonding box to cromp the image
        Output: face_image --- a single face image cropped from input image'''        
       
        if not self.box_in_image(box, image):          
            # check if a boxing box is inside the image               
            # if not paddig the empty area by copying the pixels next to it 
            face_image = self.crop_at_image_boarder(image, box)
        else: 
            face_image = image[box[1]:box[3], box[0]:box[2]]
            face_image = cv2.resize(face_image, (128,128))
            face_image = cv2.cvtColor(face_image, cv2.COLOR_BGR2RGB)
        return face_image
    
    
    @staticmethod
    def fit_markers_in_image(markers, boxes):
        '''Landmarks are detected based on the face images sent into the CNN, which are
        cropped from the original image, therefore, the coordinates of the landmars are based on cropped
        face images. Here we fit the landmark cooridinates into the original (big images)
        Input: markers --- list of the coordinates of the landmarks on cropped image
                boxes --- list of the coordinate of the bonding boxes on original (un-cropped) image
        Output: markers -- list of the coordinates of markers based on original image'''
            
        for marker, box in zip(markers, boxes):
            marker *=(box[2]-box[0])
            marker[:, 0] += box[0]
            marker[:,1] += box[1]
        return markers    
    
    @staticmethod
    def fit_markers_in_single_image(marker, box):        
        '''Landmarks are detected based on the face images sent into the CNN, which are
        cropped from the original image, therefore, the coordinates of the landmars are based on cropped
        face images. Here we fit the landmark cooridinates into the original (big images)
        Input: marker --- the coordinates of the landmarks for a single face on cropped image
                boxes --- the coordinate of the bonding boxes for a single face on original (un-cropped) image
        Output: markers -- the coordinates of markers for a single face based on original image'''
        marker *=(box[2]-box[0])
        marker[:, 0] += box[0]
        marker[:,1] += box[1]
        return marker
    
    @staticmethod
    def crop_at_image_boarder(image, box):
        '''when cropping image with box, part of croped image could be out of
        image edge, so pad it with the pixels next to it.
        Input: image --- image to crop from
                box --- a bonding box to crop the face from original image
        Output: cropped -- the cropped images with padding'''
        rows = image.shape[0]
        cols = image.shape[1]
        delta_left =0
        delta_right =0
        delta_top = 0
        delta_bottom = 0
        # among the 4 edges, check which one needs to be pad, and how much to pad on each edge
        if box[0] < 0:
            delta_left = -box[0]
        if box[2] >= cols:
            delta_right = box[2] - cols + 1
        if box[1] < 0:
            delta_top = -box[1]
        if box[3] >= rows:
            delta_bottom = box[3] - rows +1        
        # crop the image without padding
        cropped = image[max(0, box[1]):min(rows-1, box[3]) ,
                        max(0, box[0]):min(cols-1, box[2])]
        # set the padding parameter
        pad_w = ((delta_top, delta_bottom), (delta_left, delta_right), (0, 0))
        # pad the cropped image
        cropped = np.lib.pad(cropped, pad_w, 'edge')       
        return cropped