diff --git a/bob/bio/face/script/display_face_annotations.py b/bob/bio/face/script/display_face_annotations.py
index cc2f40dfc102b380d32ec2594f86743522a3e1d5..79efc5771fc25e0c4c44a1fcfc86709a6516dc4b 100644
--- a/bob/bio/face/script/display_face_annotations.py
+++ b/bob/bio/face/script/display_face_annotations.py
@@ -1,7 +1,6 @@
 #!../bin/python
 
-"""This script displays the iamges 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``.
+"""This script displays the images with annotations provided by any face database.
 
 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``.
@@ -13,130 +12,241 @@ Note that this script can only be used with face image databases, not with video
 from __future__ import print_function
 
 import os
-import argparse
+import click
 import sys
-
-import bob.bio.base
-
-import bob.core
-logger = bob.core.log.setup("bob.bio.face")
-
-def command_line_arguments(command_line_parameters):
-  """Defines the command line parameters that are accepted."""
-
-  # create parser
-  parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.ArgumentDefaultsHelpFormatter)
-
-  # add parameters
-  parser.add_argument('-d', '--database', nargs = '+', help = 'Select the database for which the images plus annotations should be shown.')
-  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.')
-  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).')
-  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).')
-  parser.add_argument('-x', '--annotation-file-extension', default = '.pos', help = 'Annotation files have the given filename extension.')
-  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.')
-  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')
-  parser.add_argument('-C', '--font-color', default = 'b', help = 'Select the color for the annotation names')
-  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)')
-  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).")
-
-  parser.add_argument('--self-test', action='store_true', help=argparse.SUPPRESS)
-
-  bob.core.log.add_command_line_option(parser)
-  args = parser.parse_args(command_line_parameters)
-  bob.core.log.set_verbosity_level(logger, args.verbose)
-
-  return args
-
-
-def main(command_line_parameters=None):
-
-  args = command_line_arguments(command_line_parameters)
-
-  # load database
-  database = bob.bio.base.load_resource("".join(args.database), "database")
-  # replace directories
-  if isinstance(database, bob.bio.base.database.BioDatabase):
-    database.replace_directories(args.database_directories_file)
-
-  # get all files
-  if bob.bio.base.utils.is_argument_available('flat', database.all_files):
-    files = database.all_files(flat=True)
-  else:
-    files = database.all_files()
-
-  # filter file ids; convert them to str first
-  if args.file_ids is not None:
-    files = [f for f in files if str(f.id) in args.file_ids]
-
-  # open figure
-  if not args.self_test:
+import logging
+import json
+
+from bob.extension.scripts.click_helper import (
+    verbosity_option,
+    ConfigCommand,
+    ResourceOption,
+)
+
+import bob.ip.color
+
+logger = logging.getLogger(__name__)
+
+@click.command(
+    entry_point_group="bob.bio.config",
+    cls=ConfigCommand,
+    epilog="""\b
+Examples:
+
+    $ bob bio display-face-annotations -vvv -d <database> -a <annot_dir>
+""",
+)
+@click.option(
+    "-d",
+    "--database",
+    required=True,
+    cls=ResourceOption,
+    entry_point_group="bob.bio.database",
+    help="Select the database for which the images plus annotations should be shown.",
+)
+@click.option(
+    "-V",
+    "--video",
+    "is_video",
+    is_flag=True,
+    help="Provide this flag if your database is a video database. "
+        "For video databases, the annotations for the first frame is shown.",
+)
+@click.option(
+    "-a",
+    "--annotations-dir",
+    help="If given, use the annotations stored in this directory "
+        "(when annotated with `$ bob bio annnotate` for example).",
+)
+@click.option(
+    "-x",
+    "--annotations-extension",
+    default = ".json",
+    show_default=True,
+    help="Annotations files have the given filename extension.",
+)
+@click.option(
+    "-n",
+    "--display-names",
+    is_flag=True,
+    help="Plot the names of the annotations, too.",
+)
+@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
-    if args.output is None:
-      pyplot.ion()
-      pyplot.show()
+    if not self_test and not output_dir:
+        pyplot.ion()
+        pyplot.show()
     else:
-      pyplot.ioff()
+        pyplot.ioff()
     pyplot.figure()
 
-  for f in files:
-    # load image
-    logger.info("loading image for file %s", f.id)
-    image = f.load(database.original_directory, database.original_extension)
-    if args.video:
-      frame_id, image, _ = image[0]
-    # convert to color if it is not
-    if image.ndim == 2:
-      image = bob.ip.color.gray_to_rgb(image)
-
-    # get annotations
-    annotations = {}
-    if args.annotation_directory is not None:
-      # load annotation file
-      annotation_file = f.make_path(args.annotation_directory, args.annotation_file_extension)
-      if os.path.exists(annotation_file):
-        logger.info("Loading annotations from file %s", annotation_file)
-        annotations = bob.db.base.read_annotation_file(annotation_file, args.annotation_file_type)
-      else:
-        logger.warn("Could not find annotation file %s", annotation_file)
-    else:
-      # get annotations from database
-      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)
+    for sample in samples:
+        # load image
+        logger.info("loading image for sample %s", sample.key)
+        image = sample.data
+        if is_video:
+            frame_id, image, _ = image[0]
+        # convert to color if it is not
+        if image.ndim == 2:
+            image = bob.ip.color.gray_to_rgb(image)
+
+        # get annotations
+        annotations = {}
+        if annotations_dir is not None:
+            # Loads the corresponding annotations file
+            annotations_file = os.path.join(annotations_dir, sample.key + annotations_extension)
+            if os.path.exists(annotations_file):
+                logger.info("Loading annotations from file %s", annotations_file)
+                with open(annotations_file) as f: # TODO remove and use bob.db.base.read_annotations_file
+                    annotations=json.load(f)
+                # annotations = bob.db.base.read_annotation_file(annotations_file, args.annotations_type)
+            else:
+                logger.warn("Could not find annotation file %s", annotations_file)
         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=args.font_color, fontsize=args.font_size, ha='center', va='baseline')
-
-      pyplot.gca().set_aspect("equal")
-      pyplot.gca().autoscale(tight=True)
-      if args.output is None:
-        pyplot.pause(0.001)
-      else:
-        pyplot.savefig(args.output)
-
-      input_text = "Press Enter to continue to the next image (or Ctrl-C + Enter to exit)"
-      if sys.version_info >= (3, 0):
-        input(input_text)
-      else:
-        raw_input(input_text)
+            # get annotations from database
+            annotations = database.annotations(sample)
+
+        if not annotations:
+            logger.warn("Could not find annotations for file %s", sample.key)
+            continue
+
+        if is_video:
+            assert frame_id in annotations, annotations
+            annotations = annotations[frame_id]
+
+        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], 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)
+
diff --git a/bob/bio/face/test/data/annotations/s1/1.json b/bob/bio/face/test/data/annotations/s1/1.json
new file mode 100644
index 0000000000000000000000000000000000000000..09c8d47128fa13f9c317b47bdbe180e7c546c80e
--- /dev/null
+++ b/bob/bio/face/test/data/annotations/s1/1.json
@@ -0,0 +1 @@
+{"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
diff --git a/bob/bio/face/test/test_scripts.py b/bob/bio/face/test/test_scripts.py
index 37dcdd1d4d24e6e58996c0644937f34a3aa714e3..516258e01cfde4b3e3181189acb582b19fedd218 100644
--- a/bob/bio/face/test/test_scripts.py
+++ b/bob/bio/face/test/test_scripts.py
@@ -1,13 +1,41 @@
-import bob.bio.base.test.utils
-import bob.bio.face
+from click.testing import CliRunner
+import pkg_resources
+import tempfile
+import shutil
+import os
 
-# TODO: Disabling this test until we have bob.bio.base#146
-"""
 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():
-    parameters = ['-d', 'dummy', '-a', '/very/unlikely/directory', '--self-test']
-    main(parameters)
-"""
+        # Checks if an annotated sample exists
+        sample_1_path = os.path.join(tmp_dir, "s1","1.png")
+        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)
diff --git a/setup.py b/setup.py
index a5f1e4147cd022eea0bd58f2dd9850dbcc7dc8f5..b73abd8794d355a220e290140320ccef311e74ba 100644
--- a/setup.py
+++ b/setup.py
@@ -195,6 +195,10 @@ setup(
           'fargo  = bob.bio.face.config.database.fargo',
         ],
 
+        'bob.bio.cli': [
+          'display-face-annotations          = bob.bio.face.script.display_face_annotations:display_face_annotations',
+        ],
+
 
     },