Skip to content
Snippets Groups Projects
Commit 1d78c1bb authored by Amir MOHAMMADI's avatar Amir MOHAMMADI
Browse files

bob.learn.mlp is deprecated

parent 0c41d81c
Branches
Tags
1 merge request!50Remove WatershedMask since bob.learn.mlp is deprecated
Pipeline #50739 passed
from .crop import Cropper, FixedCrop, NoCrop
from .mask import Padder, Masker, FixedMask, NoMask, AnnotatedRoIMask
from .mask import KonoMask, LeeMask, TomesLeeMask, WatershedMask
from .mask import KonoMask, LeeMask, TomesLeeMask
from .normalize import Normalizer, NoNormalization, HuangNormalization
from .filters import Filter, NoFilter, HistogramEqualization
from .preprocessor import Preprocessor
......@@ -31,7 +31,6 @@ __appropriate__(
KonoMask,
LeeMask,
TomesLeeMask,
WatershedMask,
Normalizer,
NoNormalization,
HuangNormalization,
......
......@@ -477,193 +477,3 @@ class TomesLeeMask(Masker):
else:
w = self.padder.padding_width
return finger_mask[w:-w,w:-w]
class WatershedMask(Masker):
"""Estimates the finger region given an input NIR image using Watershedding
This method uses the `Watershedding Morphological Algorithm
<https://en.wikipedia.org/wiki/Watershed_(image_processing)>` for determining
the finger mask given an input image.
The masker works first by determining image edges using a simple 2-D Sobel
filter. The next step is to determine markers in the image for both the
finger region and background. Markers are set on the image by using a
pre-trained feed-forward neural network model (multi-layer perceptron or MLP)
learned from existing annotations. The model is trained in a separate
program and operates on 3x3 regions around the pixel to be predicted for
finger/background. The ``(y,x)`` location also is provided as input to the
classifier. The feature vector is then composed of 9 pixel values plus the
``y`` and ``x`` (normalized) coordinates of the pixel. The network then
provides a prediction that depends on these input parameters. The closer the
output is to ``1.0``, the more likely it is from within the finger region.
Values output by the network are thresholded in order to remove uncertain
markers. The ``threshold`` parameter is configurable.
A series of morphological opening operations is used to, given the neural net
markers, remove noise before watershedding the edges from the Sobel'ed
original image.
Parameters:
model (str): Path to the model file to be used for generating
finger/background markers. This model should be pre-trained using a
separate program.
foreground_threshold (float): Threshold given a logistic regression output
(interval :math:`[0, 1]`) for which we consider finger markers provided
by the network. The higher the value, the more selective the algorithm
will be and the less (foreground) markers will be used from the network
selection. This value should be a floating point number in the open-set
interval :math:`(0.0, 1.0)`. If ``background_threshold`` is not set,
values for background selection will be set to :math:`1.0-T`, where ``T``
represents this threshold.
background_threshold (float): Threshold given a logistic regression output
(interval :math:`[0, 1]`) for which we consider finger markers provided
by the network. The smaller the value, the more selective the algorithm
will be and the less (background) markers will be used from the network
selection. This value should be a floating point number in the open-set
interval :math:`(0.0, 1.0)`. If ``foreground_threshold`` is not set,
values for foreground selection will be set to :math:`1.0-T`, where ``T``
represents this threshold.
"""
def __init__(self, model, foreground_threshold, background_threshold):
import bob.io.base
import bob.learn.mlp
import bob.learn.activation
self.labeller = bob.learn.mlp.Machine((11,10,1))
h5f = bob.io.base.HDF5File(model)
self.labeller.load(h5f)
self.labeller.output_activation = bob.learn.activation.Logistic()
del h5f
# adjust threshold from background and foreground
if foreground_threshold is None and background_threshold is not None:
foreground_threshold = 1 - background_threshold
if background_threshold is None and foreground_threshold is not None:
background_threshold = 1 - foreground_threshold
if foreground_threshold is None and background_threshold is None:
foreground_threshold = 0.5
background_threshold = 0.5
self.foreground_threshold = foreground_threshold
self.background_threshold = background_threshold
class _filterfun(object):
'''Callable for filtering the input image with marker predictions'''
def __init__(self, image, labeller):
self.labeller = labeller
self.features = numpy.zeros(self.labeller.shape[0], dtype='float64')
self.output = numpy.zeros(self.labeller.shape[-1], dtype='float64')
# builds indexes before hand, based on image dimensions
idx = numpy.mgrid[:image.shape[0], :image.shape[1]]
self.indexes = numpy.array([idx[0].flatten(), idx[1].flatten()],
dtype='float64')
self.indexes[0,:] /= image.shape[0]
self.indexes[1,:] /= image.shape[1]
self.current = 0
def __call__(self, arr):
self.features[:9] = arr.astype('float64')/255
self.features[-2:] = self.indexes[:,self.current]
self.current += 1
return self.labeller(self.features, self.output)
def run(self, image):
'''Fully preprocesses the input image and returns intermediate results
Parameters:
image (numpy.ndarray): A 2D numpy array of type ``uint8`` with the
input image
Returns:
numpy.ndarray: A 2D numpy array of type ``uint8`` with the markers for
foreground and background, selected by the neural network model
numpy.ndarray: A 2D numpy array of type ``float64`` with the edges used
to define the borders of the watermasking process
numpy.ndarray: A 2D numpy array of type boolean with the caculated
mask. ``True`` values correspond to regions where the finger is
located
'''
# applies the pre-trained neural network model to get predictions about
# finger/background regions
function = WatershedMask._filterfun(image, self.labeller)
predictions = numpy.zeros(image.shape, 'float64')
scipy.ndimage.filters.generic_filter(image, function,
size=3, mode='nearest', output=predictions)
selector = skimage.morphology.disk(radius=5)
# applies a morphological "opening" operation
# (https://en.wikipedia.org/wiki/Opening_(morphology)) to remove outliers
markers_bg = numpy.where(predictions<self.background_threshold, 1, 0)
markers_bg = skimage.morphology.opening(markers_bg, selem=selector)
markers_fg = numpy.where(predictions>=self.foreground_threshold, 255, 0)
markers_fg = skimage.morphology.opening(markers_fg, selem=selector)
# avoids markers on finger borders
selector = skimage.morphology.disk(radius=2)
markers_fg = skimage.morphology.erosion(markers_fg, selem=selector)
# the final markers are a combination of foreground and background markers
markers = markers_fg | markers_bg
# this will determine the natural boundaries in the image where the
# flooding will be limited - dialation is applied on the output of the
# Sobel filter to well mark the finger boundaries
edges = skimage.filters.sobel(image)
edges = skimage.morphology.dilation(edges, selem=selector)
# applies watersheding to get a final estimate of the finger mask
segmentation = skimage.morphology.watershed(edges, markers)
# removes small perturbations and makes the finger region more uniform
segmentation[segmentation==1] = 0
mask = skimage.morphology.binary_opening(segmentation.astype('bool'),
selem=selector)
return markers, edges, mask
def __call__(self, image):
'''Inputs an image, returns a mask (numpy boolean array)
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
located
'''
markers, edges, mask = self.run(image)
return mask
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
"""Trains a new MLP to perform pre-watershed marker detection
Usage: %(prog)s [-v...] [--samples=N] [--model=PATH] [--points=N] [--hidden=N]
[--batch=N] [--iterations=N] <database> <protocol> <group>
%(prog)s --help
%(prog)s --version
Arguments:
<database> Name of the database to use for creating the model (options are:
"fv3d" or "verafinger")
<protocol> Name of the protocol to use for creating the model (options
depend on the database chosen)
<group> Name of the group to use on the database/protocol with the
samples to use for training the model (options are: "train",
"dev" or "eval")
Options:
-h, --help Shows this help message and exits
-V, --version Prints the version and exits
-v, --verbose Increases the output verbosity level. Using "-vv"
allows the program to output informational messages as
it goes along.
-m PATH, --model=PATH Path to the generated model file [default: model.hdf5]
-s N, --samples=N Maximum number of samples to use for training. If not
set, use all samples
-p N, --points=N Maximum number of samples to use for plotting
ground-truth and classification errors. The more
points, the less responsive the plot becomes
[default: 1000]
-H N, --hidden=N Number of neurons on the hidden layer of the
multi-layer perceptron [default: 5]
-b N, --batch=N Number of samples to use for every batch [default: 1]
-i N, --iterations=N Number of iterations to train the neural net for
[default: 2000]
Examples:
Trains on the 3D Fingervein database:
$ %(prog)s -vv fv3d central dev
Saves the model to a different file, use only 100 samples:
$ %(prog)s -vv -s 100 --model=/path/to/saved-model.hdf5 fv3d central dev
"""
import os
import sys
import schema
import docopt
import numpy
import skimage
def validate(args):
'''Validates command-line arguments, returns parsed values
This function uses :py:mod:`schema` for validating :py:mod:`docopt`
arguments. Logging level is not checked by this procedure (actually, it is
ignored) and must be previously setup as some of the elements here may use
logging for outputing information.
Parameters:
args (dict): Dictionary of arguments as defined by the help message and
returned by :py:mod:`docopt`
Returns
dict: Validate dictionary with the same keys as the input and with values
possibly transformed by the validation procedure
Raises:
schema.SchemaError: in case one of the checked options does not validate.
'''
from .validate import check_model_does_not_exist, validate_protocol, \
validate_group
sch = schema.Schema({
'--model': check_model_does_not_exist,
'--samples': schema.Or(schema.Use(int), None),
'--points': schema.Use(int),
'--hidden': schema.Use(int),
'--batch': schema.Use(int),
'--iterations': schema.Use(int),
'<database>': lambda n: n in ('fv3d', 'verafinger'),
'<protocol>': validate_protocol(args['<database>']),
'<group>': validate_group(args['<database>']),
str: object, #ignores strings we don't care about
}, ignore_extra_keys=True)
return sch.validate(args)
def main(user_input=None):
if user_input is not None:
argv = user_input
else:
argv = sys.argv[1:]
import pkg_resources
completions = dict(
prog=os.path.basename(sys.argv[0]),
version=pkg_resources.require('bob.bio.vein')[0].version
)
args = docopt.docopt(
__doc__ % completions,
argv=argv,
version=completions['version'],
)
try:
from .validate import setup_logger
logger = setup_logger('bob.bio.vein', args['--verbose'])
args = validate(args)
except schema.SchemaError as e:
sys.exit(e)
if args['<database>'] == 'fv3d':
from bob.bio.vein.config.fv3d import database as db
elif args['<database>'] == 'verafinger':
from bob.bio.vein.config.verafinger import database as db
else:
raise schema.SchemaError('Database %s is not supported' % \
args['<database>'])
database_replacement = "%s/.bob_bio_databases.txt" % os.environ["HOME"]
db.replace_directories(database_replacement)
objects = db.objects(protocol=args['<protocol>'], groups=args['<group>'])
if args['--samples'] is None:
args['--samples'] = len(objects)
from ..preprocessor.utils import poly_to_mask
features = None
target = None
loaded = 0
for k, sample in enumerate(objects):
if args['--samples'] is not None and loaded >= args['--samples']:
break
path = sample.make_path(directory=db.original_directory,
extension=db.original_extension)
logger.info('Loading sample %d/%d (%s)...', loaded, len(objects), path)
image = sample.load(directory=db.original_directory,
extension=db.original_extension)
if not (hasattr(image, 'metadata') and 'roi' in image.metadata):
logger.info('Skipping sample (no ROI)')
continue
loaded += 1
# copy() required by skimage.util.shape.view_as_windows()
image = image.copy().astype('float64') / 255.
windows = skimage.util.shape.view_as_windows(image, (3,3))
if features is None and target is None:
features = numpy.zeros(
(args['--samples']*windows.shape[0]*windows.shape[1],
windows.shape[2]*windows.shape[3]+2), dtype='float64')
target = numpy.zeros(args['--samples']*windows.shape[0]*windows.shape[1],
dtype='bool')
mask = poly_to_mask(image.shape, image.metadata['roi'])
mask = mask[1:-1, 1:-1]
for y in range(windows.shape[0]):
for x in range(windows.shape[1]):
idx = ((loaded-1)*windows.shape[0]*windows.shape[1]) + \
(y*windows.shape[1]) + x
features[idx,:-2] = windows[y,x].flatten()
features[idx,-2] = y+1
features[idx,-1] = x+1
target[idx] = mask[y,x]
# if number of loaded samples is smaller than expected, clip features array
features = features[:loaded*windows.shape[0]*windows.shape[1]]
target = target[:loaded*windows.shape[0]*windows.shape[1]]
# normalize w.r.t. dimensions
features[:,-2] /= image.shape[0]
features[:,-1] /= image.shape[1]
target_float = target.astype('float64')
target_float[~target] = -1.0
target_float = target_float.reshape(len(target), 1)
positives = features[target]
negatives = features[~target]
logger.info('There are %d samples on input dataset', len(target))
logger.info(' %d are negatives', len(negatives))
logger.info(' %d are positives', len(positives))
import bob.learn.mlp
# by default, machine uses hyperbolic tangent output
machine = bob.learn.mlp.Machine((features.shape[1], args['--hidden'], 1))
machine.randomize() #initialize weights randomly
loss = bob.learn.mlp.SquareError(machine.output_activation)
train_biases = True
trainer = bob.learn.mlp.RProp(args['--batch'], loss, machine, train_biases)
trainer.reset()
shuffler = bob.learn.mlp.DataShuffler([negatives, positives],
[[-1.0], [+1.0]])
# start cost
output = machine(features)
cost = loss.f(output, target_float)
logger.info('[initial] MSE = %g', cost.mean())
# trains the network until the error is near zero
for i in range(args['--iterations']):
try:
_feats, _tgts = shuffler.draw(args['--batch'])
trainer.train(machine, _feats, _tgts)
logger.info('[%d] MSE = %g', i, trainer.cost(_tgts))
except KeyboardInterrupt:
print() #avoids the ^C line
logger.info('Gracefully stopping training before limit (%d iterations)',
args['--batch'])
break
# describe errors
neg_output = machine(negatives)
pos_output = machine(positives)
neg_errors = neg_output >= 0
pos_errors = pos_output < 0
hter_train = ((sum(neg_errors) / float(len(negatives))) + \
(sum(pos_errors)) / float(len(positives))) / 2.0
logger.info('Training set HTER: %.2f%%', 100*hter_train)
logger.info(' Errors on negatives: %d / %d', sum(neg_errors), len(negatives))
logger.info(' Errors on positives: %d / %d', sum(pos_errors), len(positives))
threshold = 0.8
neg_errors = neg_output >= threshold
pos_errors = pos_output < -threshold
hter_train = ((sum(neg_errors) / float(len(negatives))) + \
(sum(pos_errors)) / float(len(positives))) / 2.0
logger.info('Training set HTER (threshold=%g): %.2f%%', threshold,
100*hter_train)
logger.info(' Errors on negatives: %d / %d', sum(neg_errors), len(negatives))
logger.info(' Errors on positives: %d / %d', sum(pos_errors), len(positives))
# plot separation threshold
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# only plot N random samples otherwise it makes it too slow
N = numpy.random.randint(min(len(negatives), len(positives)),
size=min(len(negatives), len(positives), args['--points']))
fig = plt.figure()
ax = fig.add_subplot(211, projection='3d')
ax.scatter(image.shape[1]*negatives[N,-1], image.shape[0]*negatives[N,-2],
255*negatives[N,4], label='negatives', color='blue', marker='.')
ax.scatter(image.shape[1]*positives[N,-1], image.shape[0]*positives[N,-2],
255*positives[N,4], label='positives', color='red', marker='.')
ax.set_xlabel('Width')
ax.set_xlim(0, image.shape[1])
ax.set_ylabel('Height')
ax.set_ylim(0, image.shape[0])
ax.set_zlabel('Intensity')
ax.set_zlim(0, 255)
ax.legend()
ax.grid()
ax.set_title('Ground Truth')
plt.tight_layout()
ax = fig.add_subplot(212, projection='3d')
neg_plot = negatives[neg_output[:,0]>=threshold]
pos_plot = positives[pos_output[:,0]<-threshold]
N = numpy.random.randint(min(len(neg_plot), len(pos_plot)),
size=min(len(neg_plot), len(pos_plot), args['--points']))
ax.scatter(image.shape[1]*neg_plot[N,-1], image.shape[0]*neg_plot[N,-2],
255*neg_plot[N,4], label='negatives', color='red', marker='.')
ax.scatter(image.shape[1]*pos_plot[N,-1], image.shape[0]*pos_plot[N,-2],
255*pos_plot[N,4], label='positives', color='blue', marker='.')
ax.set_xlabel('Width')
ax.set_xlim(0, image.shape[1])
ax.set_ylabel('Height')
ax.set_ylim(0, image.shape[0])
ax.set_zlabel('Intensity')
ax.set_zlim(0, 255)
ax.legend()
ax.grid()
ax.set_title('Classifier Errors')
plt.tight_layout()
print('Close plot window to save model and end program...')
plt.show()
import bob.io.base
h5f = bob.io.base.HDF5File(args['--model'], 'w')
machine.save(h5f)
del h5f
logger.info('Saved MLP model to %s', args['--model'])
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
# Wed 4 Oct 11:23:52 2017 CEST
"""Preprocesses a fingervein image with a watershed/neural-net seeded mask
Usage: %(prog)s [-v...] [-s <path>] [-f <float>] [-b <float>] [--scan]
<model> <database> [<stem>...]
%(prog)s --help
%(prog)s --version
Arguments:
<model> Path to model to use for find watershed markers
<database> Name of the database to use for creating the model (options are:
"fv3d" or "verafinger")
<stem> Name of the object on the database to display, without the root
or the extension. If none provided, run for all possible stems on
the database
Options:
-h, --help Shows this help message and exits
-V, --version Prints the version and exits
-v, --verbose Increases the output verbosity level
-f, --fg-threshold=<float> Foreground threshold value. Should be set to a
number that is between 0.5 and 1.0. The higher,
the less markers for the foreground watershed
process will be produced. [default: 0.7]
-b, --bg-threshold=<float> Background threshold value. Should be set to a
number that is between 0.0 and 0.5. The smaller,
the less markers for the foreground watershed
process will be produced. [default: 0.3]
-S, --scan If set, ignores settings for the threshold and
scans the whole range of threshold printing the
Jaccard, M1 and M2 merith figures
-s <path>, --save=<path> If set, saves individual image into files instead
of displaying the result of processing. Pass the
name of directory that will be created and
suffixed with the paths of original images.
Examples:
Visualize the preprocessing toolchain over a single image
$ %(prog)s model.hdf5 verafinger sample-stem
Save the results of the preprocessing to several files. In this case, the
program runs non-interactively:
$ %(prog)s -s graphics model.hdf5 verafinger sample-stem
Scans the set of possible thresholds printing Jaccard, M1 and M2 indexes:
$ %(prog)s --scan model.hdf5 verafinger sample-stem
"""
import os
import sys
import time
import numpy
import schema
import docopt
import bob.core
logger = bob.core.log.setup("bob.bio.vein")
import matplotlib.pyplot as plt
import bob.io.base
import bob.io.image
def validate(args):
'''Validates command-line arguments, returns parsed values
This function uses :py:mod:`schema` for validating :py:mod:`docopt`
arguments. Logging level is not checked by this procedure (actually, it is
ignored) and must be previously setup as some of the elements here may use
logging for outputing information.
Parameters:
args (dict): Dictionary of arguments as defined by the help message and
returned by :py:mod:`docopt`
Returns
dict: Validate dictionary with the same keys as the input and with values
possibly transformed by the validation procedure
Raises:
schema.SchemaError: in case one of the checked options does not validate.
'''
valid_databases = ('fv3d', 'verafinger')
sch = schema.Schema({
'<model>': schema.And(os.path.exists,
error='<model> should point to an existing path'),
'<database>': schema.And(lambda n: n in valid_databases,
error='<database> must be one of %s' % ', '.join(valid_databases)),
'--fg-threshold': schema.And(
schema.Use(float), lambda n: 0.5 < n < 1.0,
error='--fg-threshold should be a float between 0.5 and 1.0',
),
'--bg-threshold': schema.And(
schema.Use(float), lambda n: 0.0 < n < 0.5,
error='--bg-threshold should be a float between 0.0 and 0.5',
),
str: object, #ignores strings we don't care about
}, ignore_extra_keys=True)
return sch.validate(args)
def save_figures(title, image, markers, edges, mask):
'''Saves individual images on a directory
'''
dirname = os.path.dirname(title)
if not os.path.exists(dirname): os.makedirs(dirname)
bob.io.base.save(image, os.path.join(title, 'original.png'))
_ = markers.copy().astype('uint8')
_[_==1] = 128
bob.io.base.save(_, os.path.join(title, 'markers.png'))
bob.io.base.save((255*edges).astype('uint8'), os.path.join(title,'edges.png'))
bob.io.base.save(mask.astype('uint8')*255, os.path.join(title, 'mask.png'))
from ..preprocessor.utils import draw_mask_over_image
masked_image = draw_mask_over_image(image, mask)
masked_image.save(os.path.join(title, 'masked.png'))
def make_figure(image, markers, edges, mask):
'''Returns a matplotlib figure with the detailed processing result'''
plt.clf() #completely clears the current figure
figure = plt.gcf()
plt.subplot(2,2,1)
_ = markers.copy().astype('uint8')
_[_==1] = 128
plt.imshow(_, cmap='gray')
plt.title('Markers')
plt.subplot(2,2,2)
_ = numpy.dstack([
(_ | (255*edges).astype('uint8')),
_,
_,
])
plt.imshow(_)
plt.title('Edges')
plt.subplot(2,2,3)
plt.imshow(mask.astype('uint8')*255, cmap='gray')
plt.title('Mask')
plt.subplot(2,2,4)
plt.imshow(image, cmap='gray')
red_mask = numpy.dstack([
(~mask).astype('uint8')*255,
numpy.zeros_like(image),
numpy.zeros_like(image),
])
plt.imshow(red_mask, alpha=0.15)
plt.title('Image (masked)')
return figure
def process_one(args, image, path):
'''Processes a single image'''
from bob.bio.vein.preprocessor import WatershedMask, AnnotatedRoIMask
# loads the processor once - avoids re-reading weights from the disk
processor = WatershedMask(
model=args['<model>'],
foreground_threshold=args['--fg-threshold'],
background_threshold=args['--bg-threshold'],
)
annotator = AnnotatedRoIMask()
from bob.bio.vein.preprocessor.utils import \
jaccard_index, intersect_ratio, intersect_ratio_of_complement
start = time.time()
markers, edges, mask = processor.run(image)
total_time = time.time() - start
# error
annotated_mask = annotator(image)
ji = jaccard_index(annotated_mask, mask)
m1 = intersect_ratio(annotated_mask, mask)
m2 = intersect_ratio_of_complement(annotated_mask, mask)
logger.debug('%s, %.2f, %.2f, %.2f, %g, %g, %g', path, total_time,
args['--fg-threshold'], args['--bg-threshold'], ji, m1, m2)
if not args['--scan']:
if args['--save']:
dest = os.path.join(args['--save'], path)
save_figures(dest, image, markers, edges, mask)
else:
fig = make_figure(image, markers, edges, mask)
fig.suptitle('%s @ %s - JI=%.4f, M1=%.4f, M2=%.4f\n' \
'($\\tau_{FG}$ = %.2f - $\\tau_{BG}$ = %.2f)' % \
(path, args['<database>'], ji, m1, m2, args['--fg-threshold'],
args['--bg-threshold']), fontsize=12)
print('Close the figure to continue...')
plt.show()
return (path, total_time, args['--fg-threshold'], args['--bg-threshold'],
ji, m1, m2)
def eval_best_thresholds(results):
'''Evaluates the best thresholds taking into consideration various indexes'''
m1 = numpy.array([k[-2] for k in results])
m2 = numpy.array([k[-1] for k in results])
index = m1/m2
return index.argmax()
def main(user_input=None):
if user_input is not None:
argv = user_input
else:
argv = sys.argv[1:]
import pkg_resources
completions = dict(
prog=os.path.basename(sys.argv[0]),
version=pkg_resources.require('bob.bio.vein')[0].version
)
args = docopt.docopt(
__doc__ % completions,
argv=argv,
version=completions['version'],
)
try:
from .validate import setup_logger
logger = setup_logger('bob.bio.vein', args['--verbose'])
args = validate(args)
except schema.SchemaError as e:
sys.exit(e)
if args['<database>'] == 'fv3d':
from bob.bio.vein.config.fv3d import database as db
elif args['<database>'] == 'verafinger':
from bob.bio.vein.config.verafinger import database as db
database_replacement = "%s/.bob_bio_databases.txt" % os.environ["HOME"]
db.replace_directories(database_replacement)
all_files = db.objects()
# if a specific <stem> was not provided, run for all possible stems
if not args['<stem>']:
args['<stem>'] = [k.path for k in all_files]
# Loads the image, the mask and save it to a PNG file
for stem in args['<stem>']:
f = [k for k in all_files if k.path == stem]
if len(f) == 0:
raise RuntimeError('File with stem "%s" does not exist on "%s"' % \
stem, args['<database>'])
f = f[0]
image = f.load(db.original_directory, db.original_extension)
if args['--scan']:
results = []
logger.debug('stem, time, fg_thres, bg_thres, jaccard, m1, m2')
for fg_threshold in numpy.arange(0.6, 1.0, step=0.1):
for bg_threshold in numpy.arange(0.1, 0.5, step=0.1):
args['--fg-threshold'] = fg_threshold
args['--bg-threshold'] = bg_threshold
results.append(process_one(args, image, f.path))
best_thresholds = eval_best_thresholds(results)
logger.info('%s: FG = %.2f | BG = %.2f | M1/M2 = %.2f', f.path,
results[best_thresholds][2], results[best_thresholds][3],
results[best_thresholds][-2]/results[best_thresholds][-1])
else:
process_one(args, image, f.path)
......@@ -10,8 +10,6 @@ build:
- compare_rois.py = bob.bio.vein.script.compare_rois:main
- view_sample.py = bob.bio.vein.script.view_sample:main
- blame.py = bob.bio.vein.script.blame:main
- markdet.py = bob.bio.vein.script.markdet:main
- watershed_mask.py = bob.bio.vein.script.watershed_mask:main
number: {{ environ.get('BOB_BUILD_NUMBER', 0) }}
run_exports:
- {{ pin_subpackage(name) }}
......@@ -41,8 +39,6 @@ requirements:
- bob.ip.color
- bob.bio.base
- bob.learn.linear
- bob.learn.activation
- bob.learn.mlp
run:
- python
......@@ -62,8 +58,6 @@ test:
- compare_rois.py --help
- view_sample.py --help
- blame.py --help
- markdet.py --help
- watershed_mask.py --help
- nosetests --with-coverage --cover-package={{ name }} -sv {{ name }}
- sphinx-build -aEW {{ project_dir }}/doc {{ project_dir }}/sphinx
- sphinx-build -aEb doctest {{ project_dir }}/doc sphinx
......
......@@ -261,49 +261,6 @@ This package contains other resources that can be used to evaluate different
bits of the vein processing toolchain.
Training the Watershed Finger region detector
=============================================
The correct detection of the finger boundaries is an important step of many
algorithms for the recognition of finger veins. It allows to compensate for
eventual rotation and scaling issues one might find when comparing models and
probes. In this package, we propose a novel finger boundary detector based on
the `Watershedding Morphological Algorithm
<https://en.wikipedia.org/wiki/Watershed_(image_processing)>`. Watershedding
works in three steps:
1. Determine markers on the original image indicating the types of areas one
would like to detect (e.g. "finger" or "background")
2. Determine a 2D (gray-scale) surface representing the original image in which
darker spots (representing valleys) are more likely to be filled by
surrounding markers. This is normally achieved by filtering the image with a
high-pass filter like Sobel or using an edge detector such as Canny.
3. Run the watershed algorithm
In order to determine markers for step 1, we train a neural network which
outputs the likelihood of a point being part of a finger, given its coordinates
and values of surrounding pixels.
When used to run an experiment,
:py:class:`bob.bio.vein.preprocessor.WatershedMask` requires you provide a
*pre-trained* neural network model that presets the markers before
watershedding takes place. In order to create one, you can run the program
`bob_bio_vein_markdet.py`:
.. code-block:: sh
$ bob_bio_vein_markdet.py --hidden=20 --samples=500 fv3d central dev
You input, as arguments to this application, the database, protocol and subset
name you wish to use for training the network. The data is loaded observing a
total maximum number of samples from the dataset (passed with ``--samples=N``),
the network is trained and recorded into an HDF5 file (by default, the file is
called ``model.hdf5``, but the name can be changed with the option
``--model=``). Once you have a model, you can use the preprocessor mask by
constructing an object and attaching it to the
:py:class:`bob.bio.vein.preprocessor.Preprocessor` entry on your configuration.
Region of Interest Goodness of Fit
==================================
......
......@@ -21,5 +21,4 @@
.. _mailing list: https://www.idiap.ch/software/bob/discuss
.. _bob.bio.base: https://pypi.python.org/pypi/bob.bio.base
.. _jaccard index: https://en.wikipedia.org/wiki/Jaccard_index
.. _watershed: https://en.wikipedia.org/wiki/Watershed_(image_processing)
.. _gridtk: https://pypi.python.org/pypi/gridtk
......@@ -70,8 +70,6 @@ setup(
"bob_bio_vein_compare_rois.py = bob.bio.vein.script.compare_rois:main",
"bob_bio_vein_view_sample.py = bob.bio.vein.script.view_sample:main",
"bob_bio_vein_blame.py = bob.bio.vein.script.blame:main",
"bob_bio_vein_markdet.py = bob.bio.vein.script.markdet:main",
"bob_bio_vein_watershed_mask.py = bob.bio.vein.script.watershed_mask:main",
],
},
classifiers=[
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment