diff --git a/bob/pad/face/config/lbp_svm.py b/bob/pad/face/config/lbp_svm.py index 3261e8ff815e23c2edd3609ce32d2a0b9f31a1af..ecaf7d031519998fe75a8c0d4fe3a9fd0c5e6979 100644 --- a/bob/pad/face/config/lbp_svm.py +++ b/bob/pad/face/config/lbp_svm.py @@ -20,35 +20,35 @@ this resource. #======================================================================================= # define preprocessor: -from ..preprocessor import VideoFaceCrop - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 # Minimal possible size of the face -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -COLOR_CHANNEL = 'gray' # Convert image to gray-scale format - -preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - color_channel=COLOR_CHANNEL) +from ..preprocessor import FaceCropAlign + +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = False # Gray-scale output +USE_FACE_ALIGNMENT = False # use annotations +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = None # use annotations +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +preprocessor = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) """ In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. -The size of the face is normalized to ``cropped_image_size`` dimensions. The faces with the size -below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in -[CAM12]_, which is defined by ``use_local_cropper_flag = True``. +The size of the face is normalized to ``FACE_SIZE`` dimensions. The faces with the size +below ``MIN_FACE_SIZE`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``FACE_DETECTION_METHOD = None``. """ #======================================================================================= diff --git a/bob/pad/face/config/lbp_svm_aggregated_db.py b/bob/pad/face/config/lbp_svm_aggregated_db.py index 778364e8c7d69047cc4218cbec6136312e05161d..27313a4cc8f889bd997f9571034915f3d2846c8b 100644 --- a/bob/pad/face/config/lbp_svm_aggregated_db.py +++ b/bob/pad/face/config/lbp_svm_aggregated_db.py @@ -22,35 +22,35 @@ this resource. #======================================================================================= # define preprocessor: -from ..preprocessor import VideoFaceCrop - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 # Minimal possible size of the face -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -COLOR_CHANNEL = 'gray' # Convert image to gray-scale format - -preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - color_channel=COLOR_CHANNEL) +from ..preprocessor import FaceCropAlign + +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = False # Gray-scale output +USE_FACE_ALIGNMENT = False # use annotations +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = None # use annotations +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +preprocessor = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) """ In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. -The size of the face is normalized to ``cropped_image_size`` dimensions. The faces with the size -below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in -[CAM12]_, which is defined by ``use_local_cropper_flag = True``. +The size of the face is normalized to ``FACE_SIZE`` dimensions. The faces with the size +below ``MIN_FACE_SIZE`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``FACE_DETECTION_METHOD = None``. """ #======================================================================================= diff --git a/bob/pad/face/config/preprocessor/video_face_crop.py b/bob/pad/face/config/preprocessor/video_face_crop.py index 08ed1448ed1240d7ce60ef81de560ad336a0ba5d..924ada95ba8eec4ccc4d1bb6dd5120d8d450e655 100644 --- a/bob/pad/face/config/preprocessor/video_face_crop.py +++ b/bob/pad/face/config/preprocessor/video_face_crop.py @@ -1,61 +1,44 @@ #!/usr/bin/env python2 # -*- coding: utf-8 -*- -from bob.pad.face.preprocessor import VideoFaceCrop +from ..preprocessor import FaceCropAlign -#======================================================================================= +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +# ======================================================================================= # Define instances here: -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper -DETECT_FACES_FLAG = True # find annotations locally replacing the database annotations -FACE_DETECTION_METHOD = "dlib" - -rgb_face_detector_dlib = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG, - detect_faces_flag=DETECT_FACES_FLAG, - face_detection_method=FACE_DETECTION_METHOD) - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper -DETECT_FACES_FLAG = True # find annotations locally replacing the database annotations -FACE_DETECTION_METHOD = "mtcnn" - -rgb_face_detector_mtcnn = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG, - detect_faces_flag=DETECT_FACES_FLAG, - face_detection_method=FACE_DETECTION_METHOD) + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = True # RGB output +USE_FACE_ALIGNMENT = False # +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = "dlib" # use dlib face detection +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size=FACE_SIZE, + rgb_output_flag=RGB_OUTPUT_FLAG, + use_face_alignment=USE_FACE_ALIGNMENT, + max_image_size=MAX_IMAGE_SIZE, + face_detection_method=FACE_DETECTION_METHOD, + min_face_size=MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +rgb_face_detector_dlib = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) + +# ======================================================================================= +FACE_DETECTION_METHOD = "mtcnn" # use mtcnn face detection + +_image_preprocessor = FaceCropAlign(face_size=FACE_SIZE, + rgb_output_flag=RGB_OUTPUT_FLAG, + use_face_alignment=USE_FACE_ALIGNMENT, + max_image_size=MAX_IMAGE_SIZE, + face_detection_method=FACE_DETECTION_METHOD, + min_face_size=MIN_FACE_SIZE) + +rgb_face_detector_mtcnn = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) diff --git a/bob/pad/face/config/qm_lr.py b/bob/pad/face/config/qm_lr.py index 19a5d4221f0156c01e32d78a09ed848468416ff3..f936d2a5ebfa2a409c70e60db3a2b91989255c5b 100644 --- a/bob/pad/face/config/qm_lr.py +++ b/bob/pad/face/config/qm_lr.py @@ -19,35 +19,35 @@ this resource. #======================================================================================= # define preprocessor: -from ..preprocessor import VideoFaceCrop - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper - -preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG) +from ..preprocessor import FaceCropAlign + +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = True # RGB output +USE_FACE_ALIGNMENT = False # use annotations +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = None # use annotations +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +preprocessor = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) """ In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. -The size of the face is normalized to ``cropped_image_size`` dimensions. The faces of the size -below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in -[CAM12]_, which is defined by ``use_local_cropper_flag = True``. The preprocessed frame is the RGB +The size of the face is normalized to ``FACE_SIZE`` dimensions. The faces of the size +below ``MIN_FACE_SIZE`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``FACE_DETECTION_METHOD = None``. The preprocessed frame is the RGB facial image, which is defined by ``RGB_OUTPUT_FLAG = True``. """ diff --git a/bob/pad/face/config/qm_one_class_gmm.py b/bob/pad/face/config/qm_one_class_gmm.py index 182c71bbbe8ef8783c81c9d42a2467e6371d5d0c..cce5c5a1d5e9f4d11a9ce97cfb3411728c7365eb 100644 --- a/bob/pad/face/config/qm_one_class_gmm.py +++ b/bob/pad/face/config/qm_one_class_gmm.py @@ -19,35 +19,35 @@ this resource. #======================================================================================= # define preprocessor: -from ..preprocessor import VideoFaceCrop - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper - -preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG) +from ..preprocessor import FaceCropAlign + +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = True # RGB output +USE_FACE_ALIGNMENT = False # use annotations +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = None # use annotations +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +preprocessor = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) """ In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. -The size of the face is normalized to ``cropped_image_size`` dimensions. The faces of the size -below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in -[CAM12]_, which is defined by ``use_local_cropper_flag = True``. The preprocessed frame is the RGB +The size of the face is normalized to ``FACE_SIZE`` dimensions. The faces of the size +below ``MIN_FACE_SIZE`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``FACE_DETECTION_METHOD = None``. The preprocessed frame is the RGB facial image, which is defined by ``RGB_OUTPUT_FLAG = True``. """ diff --git a/bob/pad/face/config/qm_one_class_svm_aggregated_db.py b/bob/pad/face/config/qm_one_class_svm_aggregated_db.py index 1216d66bf9af9cf324c0b8cc0c947eb342a78ecf..3f010944feea8d5ebeb0c0cd05345d70aad65efd 100644 --- a/bob/pad/face/config/qm_one_class_svm_aggregated_db.py +++ b/bob/pad/face/config/qm_one_class_svm_aggregated_db.py @@ -21,35 +21,35 @@ this resource. #======================================================================================= # define preprocessor: -from ..preprocessor import VideoFaceCrop - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper - -preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG) +from ..preprocessor import FaceCropAlign + +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = True # RGB output +USE_FACE_ALIGNMENT = False # use annotations +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = None # use annotations +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +preprocessor = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) """ In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. -The size of the face is normalized to ``cropped_image_size`` dimensions. The faces of the size -below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in -[CAM12]_, which is defined by ``use_local_cropper_flag = True``. The preprocessed frame is the RGB +The size of the face is normalized to ``FACE_SIZE`` dimensions. The faces of the size +below ``MIN_FACE_SIZE`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``FACE_DETECTION_METHOD = None``. The preprocessed frame is the RGB facial image, which is defined by ``RGB_OUTPUT_FLAG = True``. """ diff --git a/bob/pad/face/config/qm_one_class_svm_cascade_aggregated_db.py b/bob/pad/face/config/qm_one_class_svm_cascade_aggregated_db.py index 7fcc6122020478a7828f70e357e37cd5fa1e082f..f0f04b21dc343ac5bf958012b019120bfe029b61 100644 --- a/bob/pad/face/config/qm_one_class_svm_cascade_aggregated_db.py +++ b/bob/pad/face/config/qm_one_class_svm_cascade_aggregated_db.py @@ -21,35 +21,35 @@ this resource. #======================================================================================= # define preprocessor: -from ..preprocessor import VideoFaceCrop - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper - -preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG) +from ..preprocessor import FaceCropAlign + +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = True # RGB output +USE_FACE_ALIGNMENT = False # use annotations +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = None # use annotations +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +preprocessor = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) """ In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. -The size of the face is normalized to ``cropped_image_size`` dimensions. The faces of the size -below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in -[CAM12]_, which is defined by ``use_local_cropper_flag = True``. The preprocessed frame is the RGB +The size of the face is normalized to ``FACE_SIZE`` dimensions. The faces of the size +below ``MIN_FACE_SIZE`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``FACE_DETECTION_METHOD = None``. The preprocessed frame is the RGB facial image, which is defined by ``RGB_OUTPUT_FLAG = True``. """ diff --git a/bob/pad/face/config/qm_svm.py b/bob/pad/face/config/qm_svm.py index 961a413fdbbc294ac325b2d5a694af3689cfae48..8f9b8e2563d6bcc51d3a1f11a5dd58236480c6ad 100644 --- a/bob/pad/face/config/qm_svm.py +++ b/bob/pad/face/config/qm_svm.py @@ -19,35 +19,35 @@ this resource. #======================================================================================= # define preprocessor: -from ..preprocessor import VideoFaceCrop - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper - -preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG) +from ..preprocessor import FaceCropAlign + +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = True # RGB output +USE_FACE_ALIGNMENT = False # use annotations +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = None # use annotations +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +preprocessor = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) """ In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. -The size of the face is normalized to ``cropped_image_size`` dimensions. The faces of the size -below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in -[CAM12]_, which is defined by ``use_local_cropper_flag = True``. The preprocessed frame is the RGB +The size of the face is normalized to ``FACE_SIZE`` dimensions. The faces of the size +below ``MIN_FACE_SIZE`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``FACE_DETECTION_METHOD = None``. The preprocessed frame is the RGB facial image, which is defined by ``RGB_OUTPUT_FLAG = True``. """ diff --git a/bob/pad/face/config/qm_svm_aggregated_db.py b/bob/pad/face/config/qm_svm_aggregated_db.py index 481c50b85b7ec42eaf5a1e4f59999d48bd38f605..8fafbbd5f277116557f4ec22517acb9832e6751b 100644 --- a/bob/pad/face/config/qm_svm_aggregated_db.py +++ b/bob/pad/face/config/qm_svm_aggregated_db.py @@ -21,35 +21,35 @@ this resource. #======================================================================================= # define preprocessor: -from ..preprocessor import VideoFaceCrop - -CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face -CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} -FIXED_POSITIONS = None -MASK_SIGMA = None # The sigma for random values areas outside image -MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating -MASK_SEED = None # The seed for generating random values during extrapolation -CHECK_FACE_SIZE_FLAG = True # Check the size of the face -MIN_FACE_SIZE = 50 -USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) -RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper - -preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG) +from ..preprocessor import FaceCropAlign + +from bob.bio.video.preprocessor import Wrapper + +from bob.bio.video.utils import FrameSelector + +FACE_SIZE = 64 # The size of the resulting face +RGB_OUTPUT_FLAG = True # RGB output +USE_FACE_ALIGNMENT = False # use annotations +MAX_IMAGE_SIZE = None # no limiting here +FACE_DETECTION_METHOD = None # use annotations +MIN_FACE_SIZE = 50 # skip small faces + +_image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + +_frame_selector = FrameSelector(selection_style = "all") + +preprocessor = Wrapper(preprocessor = _image_preprocessor, + frame_selector = _frame_selector) """ In the preprocessing stage the face is cropped in each frame of the input video given facial annotations. -The size of the face is normalized to ``cropped_image_size`` dimensions. The faces of the size -below ``min_face_size`` threshold are discarded. The preprocessor is similar to the one introduced in -[CAM12]_, which is defined by ``use_local_cropper_flag = True``. The preprocessed frame is the RGB +The size of the face is normalized to ``FACE_SIZE`` dimensions. The faces of the size +below ``MIN_FACE_SIZE`` threshold are discarded. The preprocessor is similar to the one introduced in +[CAM12]_, which is defined by ``FACE_DETECTION_METHOD = None``. The preprocessed frame is the RGB facial image, which is defined by ``RGB_OUTPUT_FLAG = True``. """ diff --git a/bob/pad/face/database/aggregated_db.py b/bob/pad/face/database/aggregated_db.py index cc584490fcdd20d16f2caaa6c1c89122b8c8a67c..6a30de5e72864cbc6d00590e3f927eb286ae4187 100644 --- a/bob/pad/face/database/aggregated_db.py +++ b/bob/pad/face/database/aggregated_db.py @@ -187,7 +187,8 @@ class AggregatedDbPadFile(VideoPadFile): return file_path # ========================================================================= - def load(self, directory=None, extension='.mov'): + def load(self, directory=None, extension='.mov', + frame_selector=FrameSelector(selection_style='all')): """ Overridden version of the load method defined in the ``VideoPadFile``. @@ -252,9 +253,6 @@ class AggregatedDbPadFile(VideoPadFile): if isinstance(db_pad_file, bob.bio.video.database.mobio.MobioBioFile): - frame_selector = FrameSelector( - selection_style='all') # select all frames of the file - video_data = db_pad_file.load( directory=directory, extension='.mp4', @@ -263,7 +261,9 @@ class AggregatedDbPadFile(VideoPadFile): else: video_data = db_pad_file.load( - directory=directory, extension=extension) + directory=directory, + extension=extension, + frame_selector=frame_selector) return video_data # video data diff --git a/bob/pad/face/preprocessor/FaceCropAlign.py b/bob/pad/face/preprocessor/FaceCropAlign.py new file mode 100644 index 0000000000000000000000000000000000000000..acbd819b8b6bf650b082511ef7fa1e7ad4fc98f3 --- /dev/null +++ b/bob/pad/face/preprocessor/FaceCropAlign.py @@ -0,0 +1,474 @@ +#!/usr/bin/env python2 +# -*- coding: utf-8 -*- +""" +Created on Tue May 30 14:11:16 2017 + +@author: Olegs Nikisins +""" + +# ============================================================================== +# Import what is needed here: + +from bob.bio.base.preprocessor import Preprocessor + +import numpy as np + +import bob.ip.color + +import bob.ip.base + +import importlib + + +# ============================================================================== +def get_eye_pos(lm): + """ + This function returns the locations of left and right eyes + + **Parameters:** + + ``lm`` : :py:class:`numpy.ndarray` + A numpy array containing the coordinates of facial landmarks, (68X2) + + **Returns:** + + ``right_eye`` + A tuple containing the location of right eye, + + ``left_eye`` + A tuple containing the location of left eye + + """ + + # Mean position of eye corners as eye centers , casted to int() + + left_eye_t = (lm[36, :] + lm[39, :]) / 2.0 + right_eye_t = (lm[42, :] + lm[45, :]) / 2.0 + + right_eye = (int(left_eye_t[1]), int(left_eye_t[0])) + left_eye = (int(right_eye_t[1]), int(right_eye_t[0])) + + return right_eye, left_eye + + +# ============================================================================== +def detect_face_landmarks_in_image(image, method="dlib"): + """ + This function detects a face in the input image. Two oprions for face detector , but landmark detector is always the same + + **Parameters:** + + ``image`` : 3D :py:class:`numpy.ndarray` + A color image to detect the face in. + + ``method`` : :py:class:`str` + A package to be used for face detection. Options supported by this + package: "dlib" (dlib is a dependency of this package). If bob.ip.mtcnn + is installed in your system you can use it as-well (bob.ip.mtcnn is NOT + a dependency of this package). + + **Returns:** + + ``annotations`` : :py:class:`dict` + A dictionary containing annotations of the face bounding box, eye locations and facial landmarks. + Dictionary must be as follows ``{'topleft': (row, col), 'bottomright': (row, col), 'left_eye': (row, col), 'right_eye': (row, col), 'landmarks': [(col1,row1), (col2,row2), ...]}``. + If no annotations found an empty dictionary is returned. + Where (rowK,colK) is the location of Kth facial landmark (K=0,...,67). + """ + + ### Face detector + + try: + face_detection_module = importlib.import_module("bob.ip." + method) + + except ImportError: + + print("No module named bob.ip." + method + + " trying to use default method!") + + try: + face_detection_module = importlib.import_module("bob.ip.dlib") + method = "dlib" + except ImportError: + raise ImportError("No module named bob.ip.dlib") + + if not hasattr(face_detection_module, 'FaceDetector'): + raise AttributeError( + "bob.ip." + method + " module has no attribute FaceDetector!") + + #### Landmark detector + + try: + landmark_detection_module = importlib.import_module( + "bob.ip.facelandmarks") + except ImportError: + raise ImportError("No module named bob.ip.facelandmarks!!") + + if not hasattr(landmark_detection_module, + 'detect_landmarks_on_boundingbox'): + raise AttributeError( + "bob.ip.facelandmarksmodule has no attribute detect_landmarks_on_boundingbox!" + ) + + face_detector = face_detection_module.FaceDetector() + + data = face_detector.detect_single_face(image) + + annotations = {} + + if (data is not None) and (not all([x is None for x in data])): + + bounding_box = data[0] + + bounding_box_scaled = bounding_box.scale(0.95, True) # is ok for dlib + + lm = landmark_detection_module.detect_landmarks_on_boundingbox( + image, bounding_box_scaled) + + if lm is not None: + + lm = np.array(lm) + + lm = np.vstack((lm[:, 1], lm[:, 0])).T + + #print("LM",lm) + + right_eye, left_eye = get_eye_pos(lm) + + points = [] + + for i in range(lm.shape[0]): + + points.append((int(lm[i, 0]), int(lm[i, 1]))) + + annotations['topleft'] = bounding_box.topleft + + annotations['bottomright'] = bounding_box.bottomright + + annotations['landmarks'] = points + + annotations['left_eye'] = left_eye + + annotations['right_eye'] = right_eye + + return annotations + + +# ========================================================================== +def normalize_image_size_in_grayscale(image, annotations, + face_size, use_face_alignment): + """ + This function crops the face in the input Gray-scale image given annotations + defining the face bounding box, and eye positions. + The size of the face is also normalized to the pre-defined dimensions. + + Two normalization options are available, which are controlled by + ``use_face_alignment`` flag, see below. + + **Parameters:** + + ``image`` : 2D :py:class:`numpy.ndarray` + Gray-scale input image. + + ``annotations`` : :py:class:`dict` + A dictionary containing annotations of the face bounding box, + eye locations and facial landmarks. + Dictionary must be as follows: ``{'topleft': (row, col), 'bottomright': (row, col), + 'left_eye': (row, col), 'right_eye': (row, col)}``. + + ``face_size`` : :py:class:`int` + The size of the face after normalization. + + ``use_face_alignment`` : :py:class:`bool` + If ``False``, the re-sizing from this publication is used: + "On the Effectiveness of Local Binary Patterns in Face Anti-spoofing" + If ``True`` the facial image is both re-sized and aligned using + positions of the eyes, which are given in the annotations. + + **Returns:** + + ``normbbx`` : 2D :py:class:`numpy.ndarray` + An image of the cropped face of the size (face_size, face_size). + """ + + if use_face_alignment: + + face_eyes_norm = bob.ip.base.FaceEyesNorm( + eyes_distance=((face_size + 1) / 2.), + crop_size=(face_size, face_size), + eyes_center=(face_size / 4., (face_size - 0.5) / 2.)) + + right_eye, left_eye = annotations['right_eye'], annotations['left_eye'] + + normalized_image = face_eyes_norm( image, right_eye = right_eye, left_eye = left_eye ) + + normbbx=normalized_image.astype('uint8') + + else: + + cutframe = image[annotations['topleft'][0]:annotations['bottomright'][ + 0], annotations['topleft'][1]:annotations['bottomright'][1]] + + tempbbx = np.ndarray((face_size, face_size), 'float64') + normbbx = np.ndarray((face_size, face_size), 'uint8') + bob.ip.base.scale(cutframe, tempbbx) # normalization + tempbbx_ = tempbbx + 0.5 + tempbbx_ = np.floor(tempbbx_) + normbbx = np.cast['uint8'](tempbbx_) + + return normbbx + + +# ========================================================================== +def normalize_image_size(image, annotations, face_size, + rgb_output_flag, use_face_alignment): + """ + This function crops the face in the input image given annotations defining + the face bounding box. The size of the face is also normalized to the + pre-defined dimensions. For RGB inputs it is possible to return both + color and gray-scale outputs. This option is controlled by ``rgb_output_flag``. + + Two normalization options are available, which are controlled by + ``use_face_alignment`` flag, see below. + + **Parameters:** + + ``image`` : 2D or 3D :py:class:`numpy.ndarray` + Input image (RGB or gray-scale). + + ``annotations`` : :py:class:`dict` + A dictionary containing annotations of the face bounding box, + eye locations and facial landmarks. + Dictionary must be as follows: ``{'topleft': (row, col), 'bottomright': (row, col), + 'left_eye': (row, col), 'right_eye': (row, col)}``. + + ``face_size`` : :py:class:`int` + The size of the face after normalization. + + ``rgb_output_flag`` : :py:class:`bool` + Return RGB cropped face if ``True``, otherwise a gray-scale image is + returned. Default: ``False``. + + ``use_face_alignment`` : :py:class:`bool` + If ``False``, the facial image re-sizing from this publication is used: + "On the Effectiveness of Local Binary Patterns in Face Anti-spoofing" + If ``True`` the facial image is both re-sized, and aligned, using + positions of the eyes, which are given in the annotations. + + **Returns:** + + ``face`` : 2D or 3D :py:class:`numpy.ndarray` + An image of the cropped face of the size (face_size, face_size), + RGB 3D or gray-scale 2D. + """ + + if len(image.shape) == 3: + + if not (rgb_output_flag): + + image = bob.ip.color.rgb_to_gray(image) + + if len(image.shape) == 2: + + image = [image] # make gray-scale image an iterable + + result = [] + + for image_channel in image: # for all color channels in the input image + + cropped_face = normalize_image_size_in_grayscale( + image_channel, annotations, face_size, use_face_alignment) + + result.append(cropped_face) + + face = np.stack(result, axis=0) + + face = np.squeeze(face) # squeeze 1-st dimension for gray-scale images + + return face + + +# ========================================================================== +class FaceCropAlign(Preprocessor): + """ + This function is designed to crop / size-normalize / align face + in the input image. + + The size of the output face is ``3 x face_size x face_size`` pixels, if + ``rgb_output_flag = True``, or ``face_size x face_size`` if + ``rgb_output_flag = False``. + + The face can also be aligned using positions of the eyes, only when + ``use_face_alignment = True`` and ``face_detection_method is not None``. + + Both input annotations, and automatically determined are supported. + + If ``face_detection_method is not None``, the annotations returned by + face detector will be used in the cropping. + Currently supported face detectors are listed in + ``supported_face_detection_method`` argument of this class. + + If ``face_detection_method is None`` (Default), the input annotations are + used for cropping. + + A few quality checks are supported in this function. + The quality checks are controlled by these arguments: + ``max_image_size``, ``min_face_size``. More details below. + Note: ``max_image_size`` is only supported when + ``face_detection_method is not None``. + + **Parameters:** + + ``face_size`` : :py:class:`int` + The size of the face after normalization. + + ``rgb_output_flag`` : :py:class:`bool` + Return RGB cropped face if ``True``, otherwise a gray-scale image is + returned. + + ``use_face_alignment`` : :py:class:`bool` + If set to ``True`` the face will be aligned aligned, + using the facial landmarks detected locally. + Works only when ``face_detection_method is not None``. + + ``max_image_size`` : :py:class:`int` + The maximum size of the image to be processed. + ``max_image_size`` is only supported when + ``face_detection_method is not None``. + Default: ``None``. + + ``face_detection_method`` : :py:class:`str` + A package to be used for face detection and landmark detection. + Options supported by this class: + "dlib" and "mtcnn", which are listed in + ``self.supported_face_detection_method`` argument. + Default: ``None``. + + ``min_face_size`` : :py:class:`int` + The minimal size of the face in pixels to be processed. + Default: None. + """ + + # ========================================================================== + def __init__(self, face_size, + rgb_output_flag, + use_face_alignment, + max_image_size=None, + face_detection_method=None, + min_face_size=None): + + Preprocessor.__init__(self, face_size=face_size, + rgb_output_flag=rgb_output_flag, + use_face_alignment=use_face_alignment, + max_image_size=max_image_size, + face_detection_method=face_detection_method, + min_face_size=min_face_size) + + self.face_size = face_size + self.rgb_output_flag = rgb_output_flag + self.use_face_alignment = use_face_alignment + + self.max_image_size = max_image_size + self.face_detection_method = face_detection_method + self.min_face_size = min_face_size + + self.supported_face_detection_method = ["dlib", "mtcnn"] + + if self.face_detection_method is not None: + + if self.face_detection_method not in self.supported_face_detection_method: + + raise ValueError('The {0} face detection method is not supported by this class. ' + 'Currently supported face detectors are: bob.ip.{1}, bob.ip.{2}'. + format(face_detection_method, self.supported_face_detection_method[0], self.supported_face_detection_method[1])) + + # ========================================================================== + def __call__(self, image, annotations=None): + """ + This function is designed to crop / size-normalize / align face + in the input image. + + The size of the output face is ``3 x face_size x face_size`` pixels, if + ``rgb_output_flag = True``, or ``face_size x face_size`` if + ``rgb_output_flag = False``. + + The face can also be aligned using positions of the eyes, only when + ``use_face_alignment = True`` and ``face_detection_method is not None``. + + Both input annotations, and automatically determined are supported. + + If ``face_detection_method is not None``, the annotations returned by + face detector will be used in the cropping. + Currently supported face detectors are listed in + ``supported_face_detection_method`` argument of this class. + + If ``face_detection_method is None`` (Default), the input annotations are + used for cropping. + + A few quality checks are supported in this function. + The quality checks are controlled by these arguments: + ``max_image_size``, ``min_face_size``. More details below. + Note: ``max_image_size`` is only supported when + ``face_detection_method is not None``. + + **Parameters:** + + ``image`` : 2D or 3D :py:class:`numpy.ndarray` + Input image (RGB or gray-scale) or None. + + ``annotations`` : :py:class:`dict` or None + A dictionary containing annotations of the face bounding box. + Dictionary must be as follows: + ``{'topleft': (row, col), 'bottomright': (row, col)}`` + Default: None . + + **Returns:** + + ``norm_face_image`` : 2D or 3D :py:class:`numpy.ndarray` or None + An image of the cropped / aligned face, of the size: + (self.face_size, self.face_size), RGB 3D or gray-scale 2D. + """ + + if self.face_detection_method is not None: + + if self.max_image_size is not None: # max_image_size = 1920, for example + + if np.max(image.shape) > self.max_image_size: # quality check + + return None + + try: + + annotations = detect_face_landmarks_in_image(image=image, + method=self.face_detection_method) + + except: + + return None + + if not annotations: # if empty dictionary is returned + + return None + + if annotations is None: # annotations are missing for this image + + return None + + if self.min_face_size is not None: # quality check + + # size of the face + original_face_size = np.min( + np.array(annotations['bottomright']) - + np.array(annotations['topleft'])) + + if original_face_size < self.min_face_size: # check if face size is above the threshold + + return None + + norm_face_image = normalize_image_size(image=image, + annotations=annotations, + face_size=self.face_size, + rgb_output_flag=self.rgb_output_flag, + use_face_alignment=self.use_face_alignment) + + return norm_face_image diff --git a/bob/pad/face/preprocessor/ImageFaceCrop.py b/bob/pad/face/preprocessor/ImageFaceCrop.py deleted file mode 100644 index 2b3fefb7c706a911643ee842ddba88ecbb3a81a2..0000000000000000000000000000000000000000 --- a/bob/pad/face/preprocessor/ImageFaceCrop.py +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -""" -Created on Tue May 30 14:11:16 2017 - -@author: Olegs Nikisins -""" - -#============================================================================== -# Import what is needed here: - -from bob.bio.base.preprocessor import Preprocessor - -import numpy as np - -import bob.ip.color - -import bob.ip.base - -#============================================================================== -# Main body: - - -class ImageFaceCrop(Preprocessor): - """ - This class crops the face in the input image given annotations defining - the face bounding box. The size of the face is also normalized to the - pre-defined dimensions. For RGB inputs it is possible to return both - color and gray-scale outputs. This option is controlled by ``rgb_output_flag``. - - The algorithm is identical to the following paper: - "On the Effectiveness of Local Binary Patterns in Face Anti-spoofing" - - **Parameters:** - - ``face_size`` : :py:class:`int` - The size of the face after normalization. - - ``rgb_output_flag`` : :py:class:`bool` - Return RGB cropped face if ``True``, otherwise a gray-scale image is - returned. Default: ``False``. - """ - - #========================================================================== - def __init__(self, face_size, rgb_output_flag=False): - - Preprocessor.__init__( - self, face_size=face_size, rgb_output_flag=rgb_output_flag) - - self.face_size = face_size - self.rgb_output_flag = rgb_output_flag - - #========================================================================== - def normalize_image_size_in_grayscale(self, image, annotations, face_size): - """ - This function crops the face in the input Gray-scale image given annotations - defining the face bounding box. The size of the face is also normalized to the - pre-defined dimensions. - - The algorithm is identical to the following paper: - "On the Effectiveness of Local Binary Patterns in Face Anti-spoofing" - - **Parameters:** - - ``image`` : 2D :py:class:`numpy.ndarray` - Gray-scale input image. - - ``annotations`` : :py:class:`dict` - A dictionary containing annotations of the face bounding box. - Dictionary must be as follows ``{'topleft': (row, col), 'bottomright': (row, col)}`` - - ``face_size`` : :py:class:`int` - The size of the face after normalization. - - **Returns:** - - ``normbbx`` : 2D :py:class:`numpy.ndarray` - An image of the cropped face of the size (self.face_size, self.face_size). - """ - - cutframe = image[annotations['topleft'][0]:annotations['bottomright'][ - 0], annotations['topleft'][1]:annotations['bottomright'][1]] - - tempbbx = np.ndarray((face_size, face_size), 'float64') - normbbx = np.ndarray((face_size, face_size), 'uint8') - bob.ip.base.scale(cutframe, tempbbx) # normalization - tempbbx_ = tempbbx + 0.5 - tempbbx_ = np.floor(tempbbx_) - normbbx = np.cast['uint8'](tempbbx_) - - return normbbx - - #========================================================================== - def normalize_image_size(self, image, annotations, face_size, - rgb_output_flag): - """ - This function crops the face in the input image given annotations defining - the face bounding box. The size of the face is also normalized to the - pre-defined dimensions. For RGB inputs it is possible to return both - color and gray-scale outputs. This option is controlled by ``rgb_output_flag``. - - The algorithm is identical to the following paper: - "On the Effectiveness of Local Binary Patterns in Face Anti-spoofing" - - **Parameters:** - - ``image`` : 2D or 3D :py:class:`numpy.ndarray` - Input image (RGB or gray-scale). - - ``annotations`` : :py:class:`dict` - A dictionary containing annotations of the face bounding box. - Dictionary must be as follows ``{'topleft': (row, col), 'bottomright': (row, col)}`` - - ``face_size`` : :py:class:`int` - The size of the face after normalization. - - ``rgb_output_flag`` : :py:class:`bool` - Return RGB cropped face if ``True``, otherwise a gray-scale image is - returned. Default: ``False``. - - **Returns:** - - ``face`` : 2D or 3D :py:class:`numpy.ndarray` - An image of the cropped face of the size (self.face_size, self.face_size), - rgb 3D or gray-scale 2D. - """ - - if len(image.shape) == 3: - - if not (rgb_output_flag): - - image = bob.ip.color.rgb_to_gray(image) - - if len(image.shape) == 2: - - image = [image] # make gray-scale image an iterable - - result = [] - - for image_channel in image: # for all color channels in the input image - - cropped_face = self.normalize_image_size_in_grayscale( - image_channel, annotations, face_size) - - result.append(cropped_face) - - face = np.stack(result, axis=0) - - face = np.squeeze(face) # squeeze 1-st dimension for gray-scale images - - return face - - #========================================================================== - def __call__(self, image, annotations): - """ - Call the ``normalize_image_size()`` method of this class. - - **Parameters:** - - ``image`` : 2D or 3D :py:class:`numpy.ndarray` - Input image (RGB or gray-scale). - - ``annotations`` : :py:class:`dict` - A dictionary containing annotations of the face bounding box. - Dictionary must be as follows ``{'topleft': (row, col), 'bottomright': (row, col)}`` - - **Returns:** - - ``norm_face_image`` : 2D or 3D :py:class:`numpy.ndarray` - An image of the cropped face of the size (self.face_size, self.face_size), - rgb 3D or gray-scale 2D. - """ - - norm_face_image = self.normalize_image_size( - image, annotations, self.face_size, self.rgb_output_flag) - - return norm_face_image diff --git a/bob/pad/face/preprocessor/VideoFaceCrop.py b/bob/pad/face/preprocessor/VideoFaceCrop.py deleted file mode 100644 index b5433657427f8a7b91e59b5e84aa6055cdb8e057..0000000000000000000000000000000000000000 --- a/bob/pad/face/preprocessor/VideoFaceCrop.py +++ /dev/null @@ -1,363 +0,0 @@ -#!/usr/bin/env python2 -# -*- coding: utf-8 -*- -""" -Created on Fri May 12 14:14:23 2017 - -@author: Olegs Nikisins -""" -#============================================================================== -# Import what is needed here: - -from bob.bio.base.preprocessor import Preprocessor - -from bob.bio.face.preprocessor import FaceCrop - -import bob.bio.video - -import numpy as np - -from bob.pad.face.preprocessor.ImageFaceCrop import ImageFaceCrop - -from ..utils.face_detection_utils import detect_faces_in_video - -#============================================================================== -# Main body: - - -class VideoFaceCrop(Preprocessor, object): - """ - This class is designed to crop faces in each frame of the input video given - annotations defining the position of the face. - - **Parameters:** - - ``cropped_image_size`` : (int, int) - The size of the resulting cropped images. - - ``cropped_positions`` : :py:class:`dict` - The coordinates in the cropped image, where the annotated points should be put to. - This parameter is a dictionary with usually two elements, e.g., ``{'reye':(RIGHT_EYE_Y, RIGHT_EYE_X) , 'leye':(LEFT_EYE_Y, LEFT_EYE_X)}``. - However, also other parameters, such as ``{'topleft' : ..., 'bottomright' : ...}`` are supported, as long as the ``annotations`` in the `__call__` function are present. - - ``fixed_positions`` : :py:class:`dict` - Or None. - If specified, ignore the annotations from the database and use these fixed positions throughout. - - ``mask_sigma`` : :py:class:`float` - Or None - Fill the area outside of image boundaries with random pixels from the border, by adding noise to the pixel values. - To disable extrapolation, set this value to ``None``. - To disable adding random noise, set it to a negative value or 0. - - ``mask_neighbors`` : :py:class:`int` - The number of neighbors used during mask extrapolation. - See :py:func:`bob.ip.base.extrapolate_mask` for details. - - ``mask_seed`` : :py:class:`int` - Or None. - The random seed to apply for mask extrapolation. - - .. warning:: - When run in parallel, the same random seed will be applied to all parallel processes. - Hence, results of parallel execution will differ from the results in serial execution. - - ``check_face_size_flag`` : :py:class:`bool` - If True, only return the frames containing faces of the size above the - specified threshold ``min_face_size``. Default: False. - - ``min_face_size`` : :py:class:`int` - The minimal size of the face in pixels. Only valid when ``check_face_size_flag`` - is set to True. Default: 50. - - ``use_local_cropper_flag`` : :py:class:`bool` - If True, use the local ImageFaceCrop class to crop faces in the frames. - Otherwise, the FaceCrop preprocessor from bob.bio.face is used. - Default: False. - - ``rgb_output_flag`` : :py:class:`bool` - Return RGB cropped face if ``True``, otherwise a gray-scale image is - returned. This flag is only valid when ``use_local_cropper_flag = True``. - Default: ``False``. - - ``detect_faces_flag`` : :py:class:`bool` - If set to ``True`` the facial annotations will be generated using - face detection. Otherwise, annotations of the database are used for - cropping. - Default: ``False``. - - ``face_detection_method`` : :py:class:`str` - A package to be used for face detection. Options supported by this - package: "dlib" (dlib is a dependency of this package). If bob.ip.mtcnn - is installed in your system you can use it as-well (bob.ip.mtcnn is NOT - a dependency of this package). - - ``kwargs`` - Remaining keyword parameters passed to the Base constructor, such as ``color_channel`` or ``dtype``. - """ - - #========================================================================== - def __init__(self, - cropped_image_size, - cropped_positions, - fixed_positions=None, - mask_sigma=None, - mask_neighbors=5, - mask_seed=None, - check_face_size_flag=False, - min_face_size=50, - use_local_cropper_flag=False, - rgb_output_flag=False, - detect_faces_flag=False, - face_detection_method="dlib", - **kwargs): - - super(VideoFaceCrop, self).__init__( - cropped_image_size=cropped_image_size, - cropped_positions=cropped_positions, - fixed_positions=fixed_positions, - mask_sigma=mask_sigma, - mask_neighbors=mask_neighbors, - mask_seed=mask_seed, - check_face_size_flag=check_face_size_flag, - min_face_size=min_face_size, - use_local_cropper_flag=use_local_cropper_flag, - rgb_output_flag=rgb_output_flag, - detect_faces_flag=detect_faces_flag, - face_detection_method=face_detection_method, - **kwargs) - - self.cropped_image_size = cropped_image_size - self.cropped_positions = cropped_positions - self.fixed_positions = fixed_positions - self.mask_sigma = mask_sigma - self.mask_neighbors = mask_neighbors - self.mask_seed = mask_seed - self.check_face_size_flag = check_face_size_flag - self.min_face_size = min_face_size - self.use_local_cropper_flag = use_local_cropper_flag - self.rgb_output_flag = rgb_output_flag - self.detect_faces_flag = detect_faces_flag - self.face_detection_method = face_detection_method - - # Save also the data stored in the kwargs: - for (k, v) in kwargs.items(): - setattr(self, k, v) - - if self.use_local_cropper_flag: - - preprocessor = ImageFaceCrop( - face_size=self.cropped_image_size[0], - rgb_output_flag=self.rgb_output_flag) - - else: - - preprocessor = FaceCrop( - cropped_image_size=self.cropped_image_size, - cropped_positions=self.cropped_positions, - fixed_positions=self.fixed_positions, - mask_sigma=self.mask_sigma, - mask_neighbors=self.mask_neighbors, - mask_seed=self.mask_seed, - **kwargs) - - self.video_preprocessor = bob.bio.video.preprocessor.Wrapper( - preprocessor) - - #========================================================================== - def check_face_size(self, frame_container, annotations, min_face_size): - """ - Return the FrameContainer containing the frames with faces of the - size overcoming the specified threshold. - - **Parameters:** - - ``frame_container`` : FrameContainer - Video data stored in the FrameContainer, see ``bob.bio.video.utils.FrameContainer`` - for further details. - - ``annotations`` : :py:class:`dict` - A dictionary containing the annotations for each frame in the video. - Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. - Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}`` - is the dictionary defining the coordinates of the face bounding box in frame N. - - ``min_face_size`` : :py:class:`int` - The minimal size of the face in pixels. - - **Returns:** - - ``cleaned_frame_container`` : FrameContainer - FrameContainer containing the frames with faces of the size - overcoming the specified threshold. - """ - - cleaned_frame_container = bob.bio.video.FrameContainer( - ) # initialize the FrameContainer - - selected_frame_idx = 0 - - for idx in range(0, - np.min([len(annotations), - len(frame_container)])): # idx - frame index - - frame_annotations = annotations[str( - idx)] # annotations for particular frame - - # size of current face - face_size = np.min( - np.array(frame_annotations['bottomright']) - - np.array(frame_annotations['topleft'])) - - if face_size >= min_face_size: # check if face size is above the threshold - - selected_frame = frame_container[idx][1] # get current frame - - cleaned_frame_container.add( - selected_frame_idx, - selected_frame) # add current frame to FrameContainer - - selected_frame_idx = selected_frame_idx + 1 - - return cleaned_frame_container - - #========================================================================== - def select_annotated_frames(self, frames, annotations): - """ - Select only annotated frames in the input FrameContainer ``frames``. - - **Parameters:** - - ``frames`` : FrameContainer - Video data stored in the FrameContainer, see ``bob.bio.video.utils.FrameContainer`` - for further details. - - ``annotations`` : :py:class:`dict` - A dictionary containing the annotations for each frame in the video. - Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. - Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}`` - is the dictionary defining the coordinates of the face bounding box in frame N. - - **Returns:** - - ``cleaned_frame_container`` : FrameContainer - FrameContainer containing the annotated frames only. - - ``cleaned_annotations`` : :py:class:`dict` - A dictionary containing the annotations for each frame in the output video. - Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. - Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}`` - is the dictionary defining the coordinates of the face bounding box in frame N. - """ - - annotated_frames = np.sort([ - np.int(item) for item in annotations.keys() - ]) # annotated frame numbers - - available_frames = range( - 0, len(frames)) # frame numbers in the input video - - valid_frames = list( - set(annotated_frames).intersection( - available_frames)) # valid and annotated frames - - cleaned_frame_container = bob.bio.video.FrameContainer( - ) # initialize the FrameContainer - - cleaned_annotations = {} - - for idx, valid_frame_num in enumerate(valid_frames): - ## valid_frame_num - is the number of the original frame having annotations - - cleaned_annotations[str(idx)] = annotations[str( - valid_frame_num)] # correct the frame numbers - - selected_frame = frames[valid_frame_num][1] # get current frame - - cleaned_frame_container.add( - idx, selected_frame) # add current frame to FrameContainer - - return cleaned_frame_container, cleaned_annotations - - #========================================================================== - def __call__(self, frames, annotations): - """ - Crop the face in the input video frames given annotations for each frame. - - **Parameters:** - - ``frames`` : FrameContainer - Video data stored in the FrameContainer, see ``bob.bio.video.utils.FrameContainer`` - for further details. - - ``annotations`` : :py:class:`dict` - A dictionary containing the annotations for each frame in the video. - Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. - Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}`` - is the dictionary defining the coordinates of the face bounding box in frame N. - - **Returns:** - - ``preprocessed_video`` : FrameContainer - Cropped faces stored in the FrameContainer. - """ - - if self.detect_faces_flag: - - annotations = detect_faces_in_video(frames, - self.face_detection_method) - - if len(frames) != len(annotations): # if some annotations are missing - - ## Select only annotated frames: - frames, annotations = self.select_annotated_frames( - frames, annotations) - - preprocessed_video = self.video_preprocessor( - frames=frames, annotations=annotations) - - if self.check_face_size_flag: - - preprocessed_video = self.check_face_size( - preprocessed_video, annotations, self.min_face_size) - - return preprocessed_video - - #========================================================================== - def write_data(self, frames, file_name): - """ - Writes the given data (that has been generated using the __call__ function of this class) to file. - This method overwrites the write_data() method of the Preprocessor class. - - **Parameters:** - - ``frames`` : - data returned by the __call__ method of the class. - - ``file_name`` : :py:class:`str` - name of the file. - """ - - if frames: # save file if FrameContainer is not empty, otherwise do nothing. - - self.video_preprocessor.write_data(frames, file_name) - - #========================================================================== - def read_data(self, file_name): - """ - Reads the preprocessed data from file. - This method overwrites the read_data() method of the Preprocessor class. - - **Parameters:** - - ``file_name`` : :py:class:`str` - name of the file. - - **Returns:** - - ``frames`` : :py:class:`bob.bio.video.FrameContainer` - Frames stored in the frame container. - """ - - frames = self.video_preprocessor.read_data(file_name) - - return frames diff --git a/bob/pad/face/preprocessor/__init__.py b/bob/pad/face/preprocessor/__init__.py index d2b91b800873183e0a4df1d230eaf46773e372d5..5f43d3e2fcdacb09d1feed125c727d2c0a9f7234 100644 --- a/bob/pad/face/preprocessor/__init__.py +++ b/bob/pad/face/preprocessor/__init__.py @@ -1,5 +1,4 @@ -from .VideoFaceCrop import VideoFaceCrop -from .ImageFaceCrop import ImageFaceCrop +from .FaceCropAlign import FaceCropAlign from .FrameDifference import FrameDifference from .VideoSparseCoding import VideoSparseCoding @@ -23,8 +22,7 @@ def __appropriate__(*args): __appropriate__( - VideoFaceCrop, - ImageFaceCrop, + FaceCropAlign, FrameDifference, VideoSparseCoding, ) diff --git a/bob/pad/face/test/test.py b/bob/pad/face/test/test.py index 5693bb7fa89f2f775de938ee60d79d78fadf93f7..d6afe44fe18c17b3aaf306e19bec9fd9766fd8de 100644 --- a/bob/pad/face/test/test.py +++ b/bob/pad/face/test/test.py @@ -18,9 +18,7 @@ from bob.ip.color import rgb_to_gray from ..extractor import LBPHistogram -from ..preprocessor import ImageFaceCrop - -from ..preprocessor import VideoFaceCrop +from ..preprocessor import FaceCropAlign from ..preprocessor import FrameDifference @@ -30,16 +28,18 @@ from ..extractor import LBPHistogram from ..extractor import ImageQualityMeasure -from ..utils import face_detection_utils - import random +from ..preprocessor.FaceCropAlign import detect_face_landmarks_in_image + +from bob.bio.video.preprocessor import Wrapper + def test_detect_face_landmarks_in_image_mtcnn(): img = load(datafile('testimage.jpg', 'bob.bio.face.test')) assert len(img) == 3 - annotations = face_detection_utils.detect_face_landmarks_in_image( + annotations = detect_face_landmarks_in_image( img, method='mtcnn') assert len(annotations['landmarks']) == 68 assert len(annotations['left_eye']) == 2 @@ -50,25 +50,11 @@ def test_detect_face_landmarks_in_image_mtcnn(): #assert len(annotations['left_eye']) == (176, 220) -def test_detect_face_landmarks_in_video_mtcnn(): - - img = load(datafile('testimage.jpg', 'bob.bio.face.test')) - assert len(img) == 3 - frame_container = bob.bio.video.FrameContainer() - frame_container.add(1, img) - frame_container.add(2, img) - - annotations = face_detection_utils.detect_face_landmarks_in_video( - frame_container, method='mtcnn') - assert len(annotations) == 2 - assert len(annotations['1']['landmarks']) == 68 - - def test_detect_face_landmarks_in_image_dlib(): img = load(datafile('testimage.jpg', 'bob.bio.face.test')) assert len(img) == 3 - annotations = face_detection_utils.detect_face_landmarks_in_image( + annotations = detect_face_landmarks_in_image( img, method='dlib') assert len(annotations['landmarks']) == 68 assert len(annotations['left_eye']) == 2 @@ -79,20 +65,6 @@ def test_detect_face_landmarks_in_image_dlib(): #assert len(annotations['left_eye']) == (176, 220) -def test_detect_face_landmarks_in_video_dlib(): - - img = load(datafile('testimage.jpg', 'bob.bio.face.test')) - assert len(img) == 3 - frame_container = bob.bio.video.FrameContainer() - frame_container.add(1, img) - frame_container.add(2, img) - - annotations = face_detection_utils.detect_face_landmarks_in_video( - frame_container, method='dlib') - assert len(annotations) == 2 - assert len(annotations['1']['landmarks']) == 68 - - #============================================================================== def test_lbp_histogram(): lbp = LBPHistogram() @@ -104,21 +76,21 @@ def test_lbp_histogram(): #============================================================================== -def test_image_face_crop(): +def test_face_crop_align(): """ - Test ImageFaceCrop preprocessor, which is designed to crop faces in the images. + Test FaceCropAlign preprocessor, which is designed to crop faces in the images. """ image = load(datafile('test_image.png', 'bob.pad.face.test')) annotations = {'topleft': (95, 155), 'bottomright': (215, 265)} - preprocessor = ImageFaceCrop(face_size=64, rgb_output_flag=False) + preprocessor = FaceCropAlign(face_size=64, rgb_output_flag=False, use_face_alignment=False) face = preprocessor(image, annotations) assert face.shape == (64, 64) assert np.sum(face) == 429158 - preprocessor = ImageFaceCrop(face_size=64, rgb_output_flag=True) + preprocessor = FaceCropAlign(face_size=64, rgb_output_flag=True, use_face_alignment=False) face = preprocessor(image, annotations) assert face.shape == (3, 64, 64) @@ -172,35 +144,28 @@ def convert_image_to_video_data(image, annotations, n_frames): #============================================================================== def test_video_face_crop(): """ - Test VideoFaceCrop preprocessor, which is designed to crop faces in the video. + Test FaceCropAlign preprocessor with Wrapper, which is designed to crop faces in the video. """ + FACE_SIZE = 64 # The size of the resulting face + RGB_OUTPUT_FLAG = False # Gray-scale output + USE_FACE_ALIGNMENT = False # use annotations + MAX_IMAGE_SIZE = None # no limiting here + FACE_DETECTION_METHOD = None # use annotations + MIN_FACE_SIZE = 50 # skip small faces + + image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + + preprocessor = Wrapper(image_preprocessor) + image = load(datafile('test_image.png', 'bob.pad.face.test')) annotations = {'topleft': (95, 155), 'bottomright': (215, 265)} - CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face - CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} - FIXED_POSITIONS = None - MASK_SIGMA = None # The sigma for random values areas outside image - MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating - MASK_SEED = None # The seed for generating random values during extrapolation - CHECK_FACE_SIZE_FLAG = True # Check the size of the face - MIN_FACE_SIZE = 50 # Minimal possible size of the face - USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) - COLOR_CHANNEL = 'gray' # Convert image to gray-scale format - - preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=MASK_SEED, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - color_channel=COLOR_CHANNEL) - video, annotations = convert_image_to_video_data(image, annotations, 20) faces = preprocessor(frames=video, annotations=annotations) @@ -212,34 +177,23 @@ def test_video_face_crop(): assert np.sum(faces[-1][1]) == 429158 #========================================================================== - # test another configuration of the VideoFaceCrop preprocessor: - - CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face - CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} - FIXED_POSITIONS = None - MASK_SIGMA = None # The sigma for random values areas outside image - MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating - MASK_SEED = None # The seed for generating random values during extrapolation - CHECK_FACE_SIZE_FLAG = True # Check the size of the face - MIN_FACE_SIZE = 50 - USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) - RGB_OUTPUT_FLAG = True # Return RGB cropped face using local cropper - DETECT_FACES_FLAG = True # find annotations locally replacing the database annotations - FACE_DETECTION_METHOD = "dlib" - - preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=None, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG, - detect_faces_flag=DETECT_FACES_FLAG, - face_detection_method=FACE_DETECTION_METHOD) + # test another configuration of the preprocessor: + + FACE_SIZE = 64 # The size of the resulting face + RGB_OUTPUT_FLAG = True # Gray-scale output + USE_FACE_ALIGNMENT = False # use annotations + MAX_IMAGE_SIZE = None # no limiting here + FACE_DETECTION_METHOD = "dlib" # use annotations + MIN_FACE_SIZE = 50 # skip small faces + + image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + + preprocessor = Wrapper(image_preprocessor) video, _ = convert_image_to_video_data(image, annotations, 3) @@ -310,34 +264,29 @@ def test_video_lbp_histogram(): Test LBPHistogram with Wrapper extractor. """ + from ..preprocessor import FaceCropAlign + + from bob.bio.video.preprocessor import Wrapper + + FACE_SIZE = 64 # The size of the resulting face + RGB_OUTPUT_FLAG = False # Gray-scale output + USE_FACE_ALIGNMENT = False # use annotations + MAX_IMAGE_SIZE = None # no limiting here + FACE_DETECTION_METHOD = None # use annotations + MIN_FACE_SIZE = 50 # skip small faces + + image_preprocessor = FaceCropAlign(face_size = FACE_SIZE, + rgb_output_flag = RGB_OUTPUT_FLAG, + use_face_alignment = USE_FACE_ALIGNMENT, + max_image_size = MAX_IMAGE_SIZE, + face_detection_method = FACE_DETECTION_METHOD, + min_face_size = MIN_FACE_SIZE) + + preprocessor = Wrapper(image_preprocessor) + image = load(datafile('test_image.png', 'bob.pad.face.test')) annotations = {'topleft': (95, 155), 'bottomright': (215, 265)} - CROPPED_IMAGE_SIZE = (64, 64) # The size of the resulting face - CROPPED_POSITIONS = {'topleft': (0, 0), 'bottomright': CROPPED_IMAGE_SIZE} - FIXED_POSITIONS = None - MASK_SIGMA = None # The sigma for random values areas outside image - MASK_NEIGHBORS = 5 # The number of neighbors to consider while extrapolating - MASK_SEED = None # The seed for generating random values during extrapolation - CHECK_FACE_SIZE_FLAG = True # Check the size of the face - MIN_FACE_SIZE = 50 # Minimal possible size of the face - USE_LOCAL_CROPPER_FLAG = True # Use the local face cropping class (identical to Ivana's paper) - RGB_OUTPUT_FLAG = False # The output is gray-scale - COLOR_CHANNEL = 'gray' # Convert image to gray-scale format - - preprocessor = VideoFaceCrop( - cropped_image_size=CROPPED_IMAGE_SIZE, - cropped_positions=CROPPED_POSITIONS, - fixed_positions=FIXED_POSITIONS, - mask_sigma=MASK_SIGMA, - mask_neighbors=MASK_NEIGHBORS, - mask_seed=MASK_SEED, - check_face_size_flag=CHECK_FACE_SIZE_FLAG, - min_face_size=MIN_FACE_SIZE, - use_local_cropper_flag=USE_LOCAL_CROPPER_FLAG, - rgb_output_flag=RGB_OUTPUT_FLAG, - color_channel=COLOR_CHANNEL) - video, annotations = convert_image_to_video_data(image, annotations, 20) faces = preprocessor(frames=video, annotations=annotations) diff --git a/bob/pad/face/utils/__init__.py b/bob/pad/face/utils/__init__.py index 4444a68057622b8ebef09f5f12fd586aeca7fb82..fe90985c44f031beee8771dcc8ec134836f5907b 100644 --- a/bob/pad/face/utils/__init__.py +++ b/bob/pad/face/utils/__init__.py @@ -1,6 +1,3 @@ -from .face_detection_utils import (detect_face_in_image, detect_faces_in_video, - detect_face_landmarks_in_image, - detect_face_landmarks_in_video, get_eye_pos) from .load_utils import (frames, number_of_frames, yield_frames, normalize_detections, yield_faces, scale_face, blocks) diff --git a/bob/pad/face/utils/face_detection_utils.py b/bob/pad/face/utils/face_detection_utils.py deleted file mode 100644 index 034ca9abe1d3d56f880c173f78f7043bc8238c67..0000000000000000000000000000000000000000 --- a/bob/pad/face/utils/face_detection_utils.py +++ /dev/null @@ -1,274 +0,0 @@ -#!/usr/bin/env python -# vim: set fileencoding=utf-8 : -""" -This file contains face detection utils. -""" -#============================================================================== -# Import here: - -import importlib -import numpy as np - - -#============================================================================== -def get_eye_pos(lm): - """ - This function returns the locations of left and right eyes - - **Parameters:** - - ``lm`` : :py:class:`numpy.ndarray` - A numpy array containing the coordinates of facial landmarks, (68X2) - - **Returns:** - - ``right_eye`` - A tuple containing the location of right eye, - - ``left_eye`` - A tuple containing the location of left eye - - """ - - # Mean position of eye corners as eye centers , casted to int() - - left_eye_t = (lm[36, :] + lm[39, :]) / 2.0 - right_eye_t = (lm[42, :] + lm[45, :]) / 2.0 - - right_eye = (int(left_eye_t[1]), int(left_eye_t[0])) - left_eye = (int(right_eye_t[1]), int(right_eye_t[0])) - - return right_eye, left_eye - - -#============================================================================== -def detect_face_in_image(image, method="dlib"): - """ - This function detects a face in the input image. - - **Parameters:** - - ``image`` : 3D :py:class:`numpy.ndarray` - A color image to detect the face in. - - ``method`` : :py:class:`str` - A package to be used for face detection. Options supported by this - package: "dlib" (dlib is a dependency of this package). If bob.ip.mtcnn - is installed in your system you can use it as-well (bob.ip.mtcnn is NOT - a dependency of this package). - - **Returns:** - - ``annotations`` : :py:class:`dict` - A dictionary containing annotations of the face bounding box. - Dictionary must be as follows ``{'topleft': (row, col), 'bottomright': (row, col)}``. - If no annotations found an empty dictionary is returned. - """ - - try: - face_detection_module = importlib.import_module("bob.ip." + method) - except ImportError: - raise ImportError("No module named bob.ip." + method) - - if not hasattr(face_detection_module, 'FaceDetector'): - raise AttributeError( - "bob.ip." + method + " module has no attribute FaceDetector") - - data = face_detection_module.FaceDetector().detect_single_face(image) - - annotations = {} - - if (data is not None) and (not all([x is None for x in data])): - - bounding_box = data[0] - - annotations['topleft'] = bounding_box.topleft - - annotations['bottomright'] = bounding_box.bottomright - - return annotations - - -#============================================================================== -def detect_faces_in_video(frame_container, method="dlib"): - """ - This function detects a face in each farme of the input video. - - **Parameters:** - - ``frame_container`` : FrameContainer - FrameContainer containing the frames data. - - ``method`` : :py:class:`str` - A package to be used for face detection. Options supported by this - package: "dlib" (dlib is a dependency of this package). If bob.ip.mtcnn - is installed in your system you can use it as-well (bob.ip.mtcnn is NOT - a dependency of this package). - - **Returns:** - - ``annotations`` : :py:class:`dict` - A dictionary containing the annotations for each frame in the video. - Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. - Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col)}`` - is the dictionary defining the coordinates of the face bounding box in frame N. - If no annotations found an empty dictionary is returned. - """ - - annotations = {} - - for idx, frame in enumerate(frame_container): - - image = frame[1] - - frame_annotations = detect_face_in_image(image, method) - - if frame_annotations: - - annotations[str(idx)] = frame_annotations - - return annotations - - -#============================================================================== -def detect_face_landmarks_in_image(image, method="dlib"): - """ - This function detects a face in the input image. Two oprions for face detector , but landmark detector is always the same - - **Parameters:** - - ``image`` : 3D :py:class:`numpy.ndarray` - A color image to detect the face in. - - ``method`` : :py:class:`str` - A package to be used for face detection. Options supported by this - package: "dlib" (dlib is a dependency of this package). If bob.ip.mtcnn - is installed in your system you can use it as-well (bob.ip.mtcnn is NOT - a dependency of this package). - - **Returns:** - - ``annotations`` : :py:class:`dict` - A dictionary containing annotations of the face bounding box, eye locations and facial landmarks. - Dictionary must be as follows ``{'topleft': (row, col), 'bottomright': (row, col), 'left_eye': (row, col), 'right_eye': (row, col), 'landmarks': [(col1,row1), (col2,row2), ...]}``. - If no annotations found an empty dictionary is returned. - Where (rowK,colK) is the location of Kth facial landmark (K=0,...,67). - """ - - ### Face detector - - try: - face_detection_module = importlib.import_module("bob.ip." + method) - - except ImportError: - - print("No module named bob.ip." + method + - " trying to use default method!") - - try: - face_detection_module = importlib.import_module("bob.ip.dlib") - method = "dlib" - except ImportError: - raise ImportError("No module named bob.ip.dlib") - - if not hasattr(face_detection_module, 'FaceDetector'): - raise AttributeError( - "bob.ip." + method + " module has no attribute FaceDetector!") - - #### Landmark detector - - try: - landmark_detection_module = importlib.import_module( - "bob.ip.facelandmarks") - except ImportError: - raise ImportError("No module named bob.ip.facelandmarks!!") - - if not hasattr(landmark_detection_module, - 'detect_landmarks_on_boundingbox'): - raise AttributeError( - "bob.ip.facelandmarksmodule has no attribute detect_landmarks_on_boundingbox!" - ) - - face_detector = face_detection_module.FaceDetector() - - data = face_detector.detect_single_face(image) - - annotations = {} - - if (data is not None) and (not all([x is None for x in data])): - - bounding_box = data[0] - - bounding_box_scaled = bounding_box.scale(0.95, True) # is ok for dlib - - lm = landmark_detection_module.detect_landmarks_on_boundingbox( - image, bounding_box_scaled) - - if lm is not None: - - lm = np.array(lm) - - lm = np.vstack((lm[:, 1], lm[:, 0])).T - - #print("LM",lm) - - right_eye, left_eye = get_eye_pos(lm) - - points = [] - - for i in range(lm.shape[0]): - - points.append((int(lm[i, 0]), int(lm[i, 1]))) - - annotations['topleft'] = bounding_box.topleft - - annotations['bottomright'] = bounding_box.bottomright - - annotations['landmarks'] = points - - annotations['left_eye'] = left_eye - - annotations['right_eye'] = right_eye - - return annotations - - -#============================================================================== -def detect_face_landmarks_in_video(frame_container, method="dlib"): - """ - This function detects a face and face landmarks in each farme of the input video. - - **Parameters:** - - ``frame_container`` : FrameContainer - FrameContainer containing the frames data. - - ``method`` : :py:class:`str` - A package to be used for face detection. Options supported by this - package: "dlib" (dlib is a dependency of this package) and "mtcnn". - If its found it will be used else defaults to "dlib" - - **Returns:** - - ``annotations`` : :py:class:`dict` - A dictionary containing the annotations for each frame in the video. - Dictionary structure: ``annotations = {'1': frame1_dict, '2': frame1_dict, ...}``. - Where ``frameN_dict = {'topleft': (row, col), 'bottomright': (row, col), 'left_eye': (row, col), 'right_eye': (row, col), 'landmarks': [(col1,row1), (col2,row2), ...]}`` - is the dictionary defining the coordinates of the face bounding box in frame N. - Where (colK,rowK) is the location of Kth facial landmark (K=0,...,67). - If no annotations found an empty dictionary is returned. - """ - - annotations = {} - - for idx, frame in enumerate(frame_container): - - image = frame[1] - - frame_annotations = detect_face_landmarks_in_image(image, method) - - if frame_annotations: - - annotations[str(idx)] = frame_annotations - - return annotations diff --git a/doc/api.rst b/doc/api.rst index 8a763821a21241a62193fe21a0d2a2db457bc402..e085abd029c89a2c9a42f2fe0096009d5b015e66 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -72,12 +72,7 @@ Utilities .. autosummary:: bob.pad.face.utils.blocks - bob.pad.face.utils.detect_face_in_image - bob.pad.face.utils.detect_face_landmarks_in_image - bob.pad.face.utils.detect_face_landmarks_in_video - bob.pad.face.utils.detect_faces_in_video bob.pad.face.utils.frames - bob.pad.face.utils.get_eye_pos bob.pad.face.utils.normalize_detections bob.pad.face.utils.number_of_frames bob.pad.face.utils.scale_face