Commit fe2f04c1 authored by André Anjos's avatar André Anjos 💬 Committed by André Anjos

Add script to help debugging watershedding

parent 483e6b5b
#!/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 image into a file instead of
displaying it
Examples:
Visualize the preprocessing toolchain over a single image
$ %(prog)s model.hdf5 verafinger sample-stem
Save the results of the preprocessing to a file. In this case, the program
runs non-interactively:
$ %(prog)s -s graphics.png 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 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([
(_ | (2550*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']:
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)
if args['--save']:
fig.savefig(args['--save'])
else:
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 ..configurations.fv3d import database as db
elif args['<database>'] == 'verafinger':
from ..configurations.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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment