diff --git a/bob/bio/vein/configurations/maximum_curvature.py b/bob/bio/vein/configurations/maximum_curvature.py index ba69e197996bc9a0424d72878531c7f0ca412db0..46c86ea792b568c4145d10de7cbe6852e1a96c27 100644 --- a/bob/bio/vein/configurations/maximum_curvature.py +++ b/bob/bio/vein/configurations/maximum_curvature.py @@ -20,8 +20,8 @@ or the attribute ``sub_directory`` in a configuration file loaded **after** this resource. """ -from ..preprocessor import Padder, TomesLeeMask, HuangNormalization, NoFilter -from ..preprocessor import Preprocessor +from ..preprocessor import NoCropper, Padder, TomesLeeMask, \ + HuangNormalization, NoFilter, Preprocessor # Filter sizes for the vertical "high-pass" filter FILTER_HEIGHT = 4 @@ -32,6 +32,7 @@ PAD_WIDTH = 5 PAD_CONST = 51 preprocessor = Preprocessor( + crop=NoCropper(), mask=TomesLeeMask(filter_height=FILTER_HEIGHT, filter_width=FILTER_WIDTH), normalize=HuangNormalization(padding_width=PAD_WIDTH, padding_constant=PAD_CONST), diff --git a/bob/bio/vein/configurations/repeated_line_tracking.py b/bob/bio/vein/configurations/repeated_line_tracking.py index 2050174a072c6250d38b0769a7ec429da2ee4fc9..accd87142e541857fce28986a01a43fa9cfeebc2 100644 --- a/bob/bio/vein/configurations/repeated_line_tracking.py +++ b/bob/bio/vein/configurations/repeated_line_tracking.py @@ -20,8 +20,8 @@ or the attribute ``sub_directory`` in a configuration file loaded **after** this resource. """ -from ..preprocessor import Padder, TomesLeeMask, HuangNormalization, NoFilter -from ..preprocessor import Preprocessor +from ..preprocessor import NoCropper, Padder, TomesLeeMask, \ + HuangNormalization, NoFilter, Preprocessor # Filter sizes for the vertical "high-pass" filter FILTER_HEIGHT = 4 @@ -32,6 +32,7 @@ PAD_WIDTH = 5 PAD_CONST = 51 preprocessor = Preprocessor( + crop=NoCropper(), mask=TomesLeeMask(filter_height=FILTER_HEIGHT, filter_width=FILTER_WIDTH), normalize=HuangNormalization(padding_width=PAD_WIDTH, padding_constant=PAD_CONST), diff --git a/bob/bio/vein/configurations/wide_line_detector.py b/bob/bio/vein/configurations/wide_line_detector.py index aef74c0e2f8f4cb5b6739ef53ecdd17b5762e185..b82cf420f1a819d8bbd2ffc31f2e314244ae42dc 100644 --- a/bob/bio/vein/configurations/wide_line_detector.py +++ b/bob/bio/vein/configurations/wide_line_detector.py @@ -20,8 +20,8 @@ or the attribute ``sub_directory`` in a configuration file loaded **after** this resource. """ -from ..preprocessor import Padder, TomesLeeMask, HuangNormalization, NoFilter -from ..preprocessor import Preprocessor +from ..preprocessor import NoCropper, Padder, TomesLeeMask, \ + HuangNormalization, NoFilter, Preprocessor # Filter sizes for the vertical "high-pass" filter FILTER_HEIGHT = 4 @@ -32,6 +32,7 @@ PAD_WIDTH = 5 PAD_CONST = 51 preprocessor = Preprocessor( + crop=NoCropper(), mask=TomesLeeMask(filter_height=FILTER_HEIGHT, filter_width=FILTER_WIDTH), normalize=HuangNormalization(padding_width=PAD_WIDTH, padding_constant=PAD_CONST), diff --git a/bob/bio/vein/preprocessor/__init__.py b/bob/bio/vein/preprocessor/__init__.py index 7d24970ca55a638d4f29a83364c96f9e873e98bf..d9cb613ded7d164d1aa6978f4557eaf35b027135 100644 --- a/bob/bio/vein/preprocessor/__init__.py +++ b/bob/bio/vein/preprocessor/__init__.py @@ -1,4 +1,5 @@ -from .mask import Padder, Masker, NoMask, AnnotatedRoIMask +from .cropper import Cropper, FixedCropper, NoCropper +from .mask import Padder, Masker, FixedMask, AnnotatedRoIMask from .mask import KonoMask, LeeMask, TomesLeeMask from .normalize import Normalizer, NoNormalization, HuangNormalization from .filters import Filter, NoFilter, HistogramEqualization @@ -19,9 +20,12 @@ def __appropriate__(*args): for obj in args: obj.__module__ = __name__ __appropriate__( + Cropper, + FixedCropper, + NoCropper, Padder, Masker, - NoMask, + FixedMask, AnnotatedRoIMask, KonoMask, LeeMask, diff --git a/bob/bio/vein/preprocessor/cropper.py b/bob/bio/vein/preprocessor/cropper.py new file mode 100644 index 0000000000000000000000000000000000000000..1dc545471e3f4005da55a6a16d2bde5aed86b45e --- /dev/null +++ b/bob/bio/vein/preprocessor/cropper.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : + + +'''Base utilities for pre-cropping images''' + +import numpy + + +class Cropper(object): + """This is the base class for all croppers + + It defines the minimum requirements for all derived cropper classes. + + + """ + + def __init__(self): + pass + + + def __call__(self, image): + """Overwrite this method to implement your masking method + + + Parameters: + + image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the + input image + + + Returns: + + numpy.ndarray: A 2D numpy array of the same type as the input, with + cropped rows and columns as per request + + """ + + raise NotImplemented('You must implement the __call__ slot') + + +class FixedCropper(object): + """Implements cropping using a fixed suppression of border pixels + + The defaults supress no lines from the image and returns an image like the + original. + + + .. note:: + + Before choosing values, note you're responsible for knowing what is the + orientation of images fed into this cropper. + + + Parameters: + + top (:py:class:`int`, optional): Number of lines to suppress from the top + of the image. The top of the image corresponds to ``y = 0``. + + bottom (:py:class:`int`, optional): Number of lines to suppress from the + bottom of the image. The bottom of the image corresponds to ``y = + height``. + + left (:py:class:`int`, optional): Number of lines to suppress from the left + of the image. The left of the image corresponds to ``x = 0``. + + right (:py:class:`int`, optional): Number of lines to suppress from the + right of the image. The right of the image corresponds to ``x = width``. + + """ + + def __init__(self, top=0, bottom=0, left=0, right=0): + self.top = top + self.bottom = bottom + self.left = left + self.right = right + + + def __call__(self, image): + """Returns a big mask + + + Parameters: + + image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the + input image + + + Returns: + + numpy.ndarray: A 2D numpy array of type boolean with the caculated + mask. ``True`` values correspond to regions where the finger is + situated + + + """ + + # this should work even if limits are zeros + h, w = image.shape + return image[self.top:h-self.bottom, self.left:w-self.right] + + +class NoCropper(FixedCropper): + """Convenience: same as FixedCropper()""" + + def __init__(self): + super(NoCropper, self).__init__(0, 0, 0, 0) diff --git a/bob/bio/vein/preprocessor/mask.py b/bob/bio/vein/preprocessor/mask.py index aa09d17f14edd183a56caccc4ef20131f2810750..b558ae69c22f296e8d9f5a7132b74e732744a47b 100644 --- a/bob/bio/vein/preprocessor/mask.py +++ b/bob/bio/vein/preprocessor/mask.py @@ -89,12 +89,41 @@ class Masker(object): raise NotImplemented('You must implement the __call__ slot') -class NoMask(object): - """Implements no masking - i.e. returns a mask the same size as input +class FixedMask(object): + """Implements masking using a fixed suppression of border pixels + + The defaults mask no lines from the image and returns a mask of the same size + of the original image where all values are ``True``. + + + .. note:: + + Before choosing values, note you're responsible for knowing what is the + orientation of images fed into this masker. + + + Parameters: + + top (:py:class:`int`, optional): Number of lines to suppress from the top + of the image. The top of the image corresponds to ``y = 0``. + + bottom (:py:class:`int`, optional): Number of lines to suppress from the + bottom of the image. The bottom of the image corresponds to ``y = + height``. + + left (:py:class:`int`, optional): Number of lines to suppress from the left + of the image. The left of the image corresponds to ``x = 0``. + + right (:py:class:`int`, optional): Number of lines to suppress from the + right of the image. The right of the image corresponds to ``x = width``. + """ - def __init__(self): - pass + def __init__(self, top=0, bottom=0, left=0, right=0): + self.top = top + self.bottom = bottom + self.left = left + self.right = right def __call__(self, image): @@ -115,7 +144,15 @@ class NoMask(object): """ - return numpy.ones(image.shape, dtype='bool') + retval = numpy.ones(image.shape, dtype='bool') + + # this should work even if limits are zeros + retval[:self.top] = False + retval[-self.bottom:] = False + retval[:,:self.left] = False + retval[:,-self.right:] = False + + return retval class AnnotatedRoIMask(object): @@ -428,3 +465,6 @@ class TomesLeeMask(Masker): else: w = self.padder.padding_width return finger_mask[w:-w,w:-w] + + + diff --git a/bob/bio/vein/preprocessor/preprocessor.py b/bob/bio/vein/preprocessor/preprocessor.py index f925ef051efcbd5baf9fb83387c81a361b2ec266..549877fbf5860dcc77aa74b420d57d6b083e1119 100644 --- a/bob/bio/vein/preprocessor/preprocessor.py +++ b/bob/bio/vein/preprocessor/preprocessor.py @@ -11,7 +11,8 @@ class Preprocessor (BasePreprocessor): In this implementation, the finger image is (in this order): - #. The mask is expolated from the image using one of our + #. The image is pre-cropped to remove obvious non-finger image parts + #. The mask is extrapolated from the image using one of our :py:class:`Masker`'s concrete implementations #. The image is normalized with one of our :py:class:`Normalizer`'s #. The image is filtered with one of our :py:class:`Filter`'s @@ -19,6 +20,11 @@ class Preprocessor (BasePreprocessor): Parameters: + crop (:py:class:`Cropper`): An object that will perform pre-cropping on + the input image before a mask can be estimated. It removes parts of the + image which are surely not part of the finger region you'll want to + consider for the next steps. + mask (:py:class:`Masker`): An object representing a Masker instance which will extrapolate the mask from the input image. @@ -34,15 +40,17 @@ class Preprocessor (BasePreprocessor): """ - def __init__(self, mask, normalize, filter, **kwargs): + def __init__(self, crop, mask, normalize, filter, **kwargs): BasePreprocessor.__init__(self, + crop = crop, mask = mask, normalize = normalize, filter = filter, **kwargs ) + self.crop = crop self.mask = mask self.normalize = normalize self.filter = filter @@ -66,7 +74,8 @@ class Preprocessor (BasePreprocessor): """ - mask = self.mask(data) + cropped = self.crop(data) + mask = self.mask(cropped) data, mask = self.normalize(data, mask) data = self.filter(data, mask) return data, mask diff --git a/bob/bio/vein/tests/test.py b/bob/bio/vein/tests/test.py index b9e1752b80291af52884755b470909957d594138..908d3fb8d01a1f95a1eece3d8fa2f773c5bd00f6 100644 --- a/bob/bio/vein/tests/test.py +++ b/bob/bio/vein/tests/test.py @@ -42,10 +42,11 @@ def test_finger_crop(): img = bob.io.base.load(input_filename) - from bob.bio.vein.preprocessor import Preprocessor, LeeMask, \ + from bob.bio.vein.preprocessor import Preprocessor, NoCropper, LeeMask, \ HuangNormalization, NoFilter processor = Preprocessor( + NoCropper(), LeeMask(filter_height=40, filter_width=4), HuangNormalization(padding_width=0, padding_constant=0), NoFilter(), @@ -112,9 +113,10 @@ def test_max_curvature_HE(): input_img = bob.io.base.load(input_img_filename) # Preprocess the data and apply Histogram Equalization postprocessing (same parameters as in maximum_curvature.py configuration file + postprocessing) - from bob.bio.vein.preprocessor import Preprocessor, LeeMask, \ + from bob.bio.vein.preprocessor import Preprocessor, NoCropper, LeeMask, \ HuangNormalization, HistogramEqualization processor = Preprocessor( + NoCropper(), LeeMask(filter_height=40, filter_width=4), HuangNormalization(padding_width=0, padding_constant=0), HistogramEqualization(), @@ -161,9 +163,10 @@ def test_repeated_line_tracking_HE(): input_img = bob.io.base.load(input_img_filename) # Preprocess the data and apply Histogram Equalization postprocessing (same parameters as in repeated_line_tracking.py configuration file + postprocessing) - from bob.bio.vein.preprocessor import Preprocessor, LeeMask, \ + from bob.bio.vein.preprocessor import Preprocessor, NoCropper, LeeMask, \ HuangNormalization, HistogramEqualization processor = Preprocessor( + NoCropper(), LeeMask(filter_height=40, filter_width=4), HuangNormalization(padding_width=0, padding_constant=0), HistogramEqualization(), @@ -214,9 +217,10 @@ def test_wide_line_detector_HE(): input_img = bob.io.base.load(input_img_filename) # Preprocess the data and apply Histogram Equalization postprocessing (same parameters as in wide_line_detector.py configuration file + postprocessing) - from bob.bio.vein.preprocessor import Preprocessor, LeeMask, \ + from bob.bio.vein.preprocessor import Preprocessor, NoCropper, LeeMask, \ HuangNormalization, HistogramEqualization processor = Preprocessor( + NoCropper(), LeeMask(filter_height=40, filter_width=4), HuangNormalization(padding_width=0, padding_constant=0), HistogramEqualization(),