Skip to content
Snippets Groups Projects
Commit 028f5093 authored by Yannick DAYER's avatar Yannick DAYER
Browse files

[click] Port display-annotations script and tests.

parent d4fbeef1
Branches
Tags
1 merge request!80Port display-face-annotations script
#!../bin/python #!../bin/python
"""This script displays the iamges with annotations provided by any face database. """This script displays the images with annotations provided by any face database.
Basically, anything that can be used as a --database for verify.py can be specified here as well, including configuration files and ``database`` resources: ``resources.py -d database``.
By default, all images and their corresponding annotations are displayed, and you have to press ``Enter`` after each image. By default, all images and their corresponding annotations are displayed, and you have to press ``Enter`` after each image.
If the database does not include annotations, or you want to display a different set of annotations, you can specify the ``--annotation-directory`` (and if required modify the ``--annotation-file-extension`` and ``--annotation-file-type``. If the database does not include annotations, or you want to display a different set of annotations, you can specify the ``--annotation-directory`` (and if required modify the ``--annotation-file-extension`` and ``--annotation-file-type``.
...@@ -13,130 +12,241 @@ Note that this script can only be used with face image databases, not with video ...@@ -13,130 +12,241 @@ Note that this script can only be used with face image databases, not with video
from __future__ import print_function from __future__ import print_function
import os import os
import argparse import click
import sys import sys
import logging
import bob.bio.base import json
import bob.core from bob.extension.scripts.click_helper import (
logger = bob.core.log.setup("bob.bio.face") verbosity_option,
ConfigCommand,
def command_line_arguments(command_line_parameters): ResourceOption,
"""Defines the command line parameters that are accepted.""" )
# create parser import bob.ip.color
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
logger = logging.getLogger(__name__)
# add parameters
parser.add_argument('-d', '--database', nargs = '+', help = 'Select the database for which the images plus annotations should be shown.') @click.command(
parser.add_argument('-V', '--video', action = 'store_true', help = 'Provide this flag if your database is a video database. For video databases, the annotations for the first frame is shown.') entry_point_group="bob.bio.config",
parser.add_argument('-f', '--file-ids', nargs = '+', help = 'If given, only the images of the --database with the given file id are shown (non-existing IDs will be silently skipped).') cls=ConfigCommand,
parser.add_argument('-a', '--annotation-directory', help = 'If given, use the annotations stored in the given annotation directory (this might be required for some databases).') epilog="""\b
parser.add_argument('-x', '--annotation-file-extension', default = '.pos', help = 'Annotation files have the given filename extension.') Examples:
parser.add_argument('-t', '--annotation-file-type', default = 'named', help = 'Select the annotation file style, see bob.db.base documentation for valid types.')
parser.add_argument('-n', '--annotate-names', action = 'store_true', help = 'Plot the names of the annotations, too.') $ bob bio display-face-annotations -vvv -d <database> -a <annot_dir>
parser.add_argument('-m', '--marker-style', default='rx', help = 'Select the marker style') """,
parser.add_argument('-M', '--marker-size', type=float, default=10., help = 'Select the marker size') )
parser.add_argument('-F', '--font-size', type=int, default=16, help = 'Select the font size for the annotation names') @click.option(
parser.add_argument('-C', '--font-color', default = 'b', help = 'Select the color for the annotation names') "-d",
parser.add_argument('--database-directories-file', metavar = 'FILE', default = "%s/.bob_bio_databases.txt" % os.environ["HOME"], help = 'An optional file, where database directories are stored (to avoid changing the database configurations)') "--database",
parser.add_argument('-o', '--output', help = "If given, it will save the plots in this output file instead of showing them. This option is useful when you don't have a display server (ssh).") required=True,
cls=ResourceOption,
parser.add_argument('--self-test', action='store_true', help=argparse.SUPPRESS) entry_point_group="bob.bio.database",
help="Select the database for which the images plus annotations should be shown.",
bob.core.log.add_command_line_option(parser) )
args = parser.parse_args(command_line_parameters) @click.option(
bob.core.log.set_verbosity_level(logger, args.verbose) "-V",
"--video",
return args "is_video",
is_flag=True,
help="Provide this flag if your database is a video database. "
def main(command_line_parameters=None): "For video databases, the annotations for the first frame is shown.",
)
args = command_line_arguments(command_line_parameters) @click.option(
"-a",
# load database "--annotations-dir",
database = bob.bio.base.load_resource("".join(args.database), "database") help="If given, use the annotations stored in this directory "
# replace directories "(when annotated with `$ bob bio annnotate` for example).",
if isinstance(database, bob.bio.base.database.BioDatabase): )
database.replace_directories(args.database_directories_file) @click.option(
"-x",
# get all files "--annotations-extension",
if bob.bio.base.utils.is_argument_available('flat', database.all_files): default = ".json",
files = database.all_files(flat=True) show_default=True,
else: help="Annotations files have the given filename extension.",
files = database.all_files() )
@click.option(
# filter file ids; convert them to str first "-n",
if args.file_ids is not None: "--display-names",
files = [f for f in files if str(f.id) in args.file_ids] is_flag=True,
help="Plot the names of the annotations, too.",
# open figure )
if not args.self_test: @click.option(
"-m",
"--marker-style",
default="rx",
show_default=True,
help="Select the marker style",
)
@click.option(
"-M",
"--marker-size",
type=float,
default=10.,
show_default=True,
help="Select the marker size",
)
@click.option(
"-C",
"--font-color",
default="b",
show_default=True,
help="Select the color for the annotations names",
)
@click.option(
"-F",
"--font-size",
type=int,
default=16,
show_default=True,
help="Select the font size for the annotations names",
)
@click.option(
"-o",
"--output-dir",
help="If given, it will save the plots in this output file instead of showing them. This option is useful when you don't have a display server (ssh).",
)
@click.option(
"-k",
"--keep-all",
is_flag=True,
help="When -o is given: keeps every annotated samples instead of just one.",
)
@click.option(
"--self-test",
is_flag=True,
help="Prevents outputing to the screen and waiting for user input.",
)
@click.option(
"--groups",
"-g",
multiple=True,
default=["dev", "eval"],
show_default=True,
help="Biometric Database group that will be displayed.",
)
@verbosity_option(cls=ResourceOption)
def display_face_annotations(
database,
is_video,
annotations_dir,
annotations_extension,
marker_style,
marker_size,
display_names,
font_color,
font_size,
output_dir,
keep_all,
self_test,
groups,
**kwargs
):
"""
Plots annotations on the corresponding face picture.
"""
logger.debug("Retrieving background model samples from database.")
background_model_samples = database.background_model_samples()
logger.debug("Retrieving references and probes samples from database.")
references_samplesets = []
probes_samplesets = []
for group in groups:
references_samplesets.extend(database.references(group=group))
probes_samplesets.extend(database.probes(group=group))
# Unravels all samples in one list (no SampleSets)
samples = background_model_samples
samples.extend([
sample
for r in references_samplesets
for sample in r.samples
])
samples.extend([
sample
for p in probes_samplesets
for sample in p.samples
])
logger.debug(f"{len(samples)} samples loaded from database.")
# open figure
from matplotlib import pyplot from matplotlib import pyplot
if args.output is None: if not self_test and not output_dir:
pyplot.ion() pyplot.ion()
pyplot.show() pyplot.show()
else: else:
pyplot.ioff() pyplot.ioff()
pyplot.figure() pyplot.figure()
for f in files: for sample in samples:
# load image # load image
logger.info("loading image for file %s", f.id) logger.info("loading image for sample %s", sample.key)
image = f.load(database.original_directory, database.original_extension) image = sample.data
if args.video: if is_video:
frame_id, image, _ = image[0] frame_id, image, _ = image[0]
# convert to color if it is not # convert to color if it is not
if image.ndim == 2: if image.ndim == 2:
image = bob.ip.color.gray_to_rgb(image) image = bob.ip.color.gray_to_rgb(image)
# get annotations # get annotations
annotations = {} annotations = {}
if args.annotation_directory is not None: if annotations_dir is not None:
# load annotation file # Loads the corresponding annotations file
annotation_file = f.make_path(args.annotation_directory, args.annotation_file_extension) annotations_file = os.path.join(annotations_dir, sample.key + annotations_extension)
if os.path.exists(annotation_file): if os.path.exists(annotations_file):
logger.info("Loading annotations from file %s", annotation_file) logger.info("Loading annotations from file %s", annotations_file)
annotations = bob.db.base.read_annotation_file(annotation_file, args.annotation_file_type) with open(annotations_file) as f: # TODO remove and use bob.db.base.read_annotations_file
else: annotations=json.load(f)
logger.warn("Could not find annotation file %s", annotation_file) # annotations = bob.db.base.read_annotation_file(annotations_file, args.annotations_type)
else: else:
# get annotations from database logger.warn("Could not find annotation file %s", annotations_file)
annotations = database.annotations(f)
if not annotations:
logger.warn("Could not find annotations for file %s", f.id)
if args.video:
assert frame_id in annotations, annotations
annotations = annotations[frame_id]
if not args.self_test:
pyplot.clf()
pyplot.imshow(image.transpose(1,2,0))
global_annotation = []
for n,a in annotations.items():
if isinstance(a, (list,tuple)) and len(a) == 2:
pyplot.plot(a[1], a[0], args.marker_style, ms = args.marker_size, mew = args.marker_size / 5.)
if args.annotate_names:
pyplot.annotate(n, (a[1], a[0]), color=args.font_color, fontsize=args.font_size)
else: else:
global_annotation.append("%s=%s"%(n,a)) # get annotations from database
annotations = database.annotations(sample)
# plot all global annotations, at the top center of the image
pyplot.annotate(";".join(global_annotation), (image.shape[-1]/2, 0), color=args.font_color, fontsize=args.font_size, ha='center', va='baseline') if not annotations:
logger.warn("Could not find annotations for file %s", sample.key)
pyplot.gca().set_aspect("equal") continue
pyplot.gca().autoscale(tight=True)
if args.output is None: if is_video:
pyplot.pause(0.001) assert frame_id in annotations, annotations
else: annotations = annotations[frame_id]
pyplot.savefig(args.output)
pyplot.clf()
input_text = "Press Enter to continue to the next image (or Ctrl-C + Enter to exit)" pyplot.imshow(image.transpose(1,2,0))
if sys.version_info >= (3, 0):
input(input_text) global_annotation = []
else: for n,a in annotations.items():
raw_input(input_text) if isinstance(a, (list,tuple)) and len(a) == 2:
pyplot.plot(a[1], a[0], marker_style, ms = marker_size, mew = marker_size / 5.)
if display_names:
pyplot.annotate(n, (a[1], a[0]), color=font_color, fontsize=font_size)
else:
global_annotation.append("%s=%s"%(n,a))
# plot all global annotations, at the top center of the image
pyplot.annotate(";".join(global_annotation), (image.shape[-1]/2, 0), color=font_color, fontsize=font_size, ha='center', va='baseline')
pyplot.gca().set_aspect("equal")
pyplot.gca().autoscale(tight=True)
if output_dir is None:
if self_test:
raise RuntimeError("Do not run self_test without --output_dir.")
pyplot.pause(0.001)
else:
if keep_all:
output_path = os.path.join(output_dir, sample.key + ".png")
else:
output_path = os.path.join(output_dir, "annotated.png")
os.makedirs(os.path.dirname(output_path), exist_ok=True)
pyplot.savefig(output_path)
if not self_test:
input_text = "Press Enter to continue to the next image (or Ctrl-C to exit)"
if sys.version_info >= (3, 0):
input(input_text)
else:
raw_input(input_text)
{"topleft": [5.476375102996826, 11.880301475524902], "bottomright": [78.71327209472656, 111.0], "reye": [27.06552505493164, 52.629173278808594], "leye": [61.69809341430664, 51.46814727783203], "nose": [45.439552307128906, 72.81857299804688], "mouthright": [30.882678985595703, 89.54910278320312], "mouthleft": [59.577816009521484, 89.41582489013672], "quality": 0.999476969242096}
\ No newline at end of file
import bob.bio.base.test.utils from click.testing import CliRunner
import bob.bio.face import pkg_resources
import tempfile
import shutil
import os
# TODO: Disabling this test until we have bob.bio.base#146
"""
def test_display_annotations(): def test_display_annotations():
from bob.bio.face.script.display_face_annotations import main from bob.bio.face.script.display_face_annotations import display_face_annotations
try:
tmp_dir = tempfile.mkdtemp(prefix="bobtest_")
annotations_dir = pkg_resources.resource_filename(
"bob.bio.face.test",
"data/annotations/"
)
runner = CliRunner()
result = runner.invoke(
display_face_annotations,
args=(
'--database', 'dummy',
'--groups', 'dev',
'--annotations-dir', annotations_dir,
'--output-dir', tmp_dir, '--keep-all',
'--self-test',
)
)
assertion_error_message = (
'Command exited with this output: `{}\' \n'
'If the output is empty, you can run this script locally to see '
'what is wrong:\n'
'$ bob bio display_face_annotations -vvv -d dummy -g dev -a ./annotations/ -o /tmp/temp_annotated'
''.format(result.output))
assert result.exit_code == 0, assertion_error_message
with bob.bio.base.test.utils.Quiet(): # Checks if an annotated sample exists
parameters = ['-d', 'dummy', '-a', '/very/unlikely/directory', '--self-test'] sample_1_path = os.path.join(tmp_dir, "s1","1.png")
main(parameters) assertion_error_message = "File '{}' not created.".format(sample_1_path)
""" assert os.path.isfile(sample_1_path), assertion_error_message
finally:
shutil.rmtree(tmp_dir)
...@@ -195,6 +195,10 @@ setup( ...@@ -195,6 +195,10 @@ setup(
'fargo = bob.bio.face.config.database.fargo', 'fargo = bob.bio.face.config.database.fargo',
], ],
'bob.bio.cli': [
'display-face-annotations = bob.bio.face.script.display_face_annotations:display_face_annotations',
],
}, },
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment