Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
2# coding=utf-8
4import glob
5import logging
6import os
8import click
9import numpy
10import skimage.color
11import skimage.io
12import skimage.measure
13import skimage.morphology
15from bob.extension import rc
16from bob.extension.scripts.click_helper import (
17 ConfigCommand,
18 ResourceOption,
19 verbosity_option,
20)
22logger = logging.getLogger(__name__)
25def threshold_and_closing(input_path, t, width=5):
26 """Creates a "rough" mask from the input image, returns binary equivalent
28 The mask will be created by running a simple threshold operation followed
29 by a morphological closing
32 Arguments
33 =========
35 input_path : str
36 The path leading to the image from where the mask needs to be extracted
38 t : int
39 Threshold to apply on the original image
41 width : int
42 Width of the disc to use for the closing operation
45 Returns
46 =======
48 mask : numpy.ndarray
49 A 2D array, with the same size as the input image, where ``True``
50 pixels correspond to the "valid" regions of the mask.
52 """
54 img = skimage.util.img_as_ubyte(skimage.io.imread(input_path, as_gray=True))
55 mask = img > t
56 return skimage.morphology.binary_opening(
57 mask, skimage.morphology.disk(width)
58 )
61def count_blobs(mask):
62 """Counts "white" blobs in a binary mask, outputs counts
65 Arguments
66 =========
68 mask : numpy.ndarray
69 A 2D array, with the same size as the input image, where ``255``
70 pixels correspond to the "valid" regions of the mask. ``0`` means
71 background.
74 Returns
75 =======
77 count : int
78 The number of connected blobs in the provided mask.
80 """
81 return skimage.measure.label(mask, return_num=True)[1]
84def process_glob(base_path, use_glob, output_path, threshold):
85 """Recursively process a set of images
87 Arguments
88 =========
90 base_path : str
91 The base directory where to look for files matching a certain name
92 patternrc.get("bob.ip.binseg." + dataset + ".datadir"):
94 use_glob : list
95 A list of globs to use for matching filenames inside ``base_path``
97 output_path : str
98 Where to place the results of procesing
100 """
102 files = []
103 for g in use_glob:
104 files += glob.glob(os.path.join(base_path, g))
105 for i, path in enumerate(files):
106 basename = os.path.relpath(path, base_path)
107 basename_without_extension = os.path.splitext(basename)[0]
108 logger.info(
109 f"Processing {basename_without_extension} ({i+1}/{len(files)})..."
110 )
111 dest = os.path.join(output_path, basename_without_extension + ".png")
112 destdir = os.path.dirname(dest)
113 if not os.path.exists(destdir):
114 os.makedirs(destdir)
115 mask = threshold_and_closing(path, threshold)
116 immask = mask.astype(numpy.uint8) * 255
117 nblobs = count_blobs(immask)
118 if nblobs != 1:
119 logger.warning(
120 f" -> WARNING: found {nblobs} blobs in the saved mask "
121 f"(should be one)"
122 )
123 skimage.io.imsave(dest, immask)
126@click.command(
127 cls=ConfigCommand,
128 epilog="""Examples:
130\b
131 1. Generate masks for supported dataset by bob. Ex: refuge.
132\b
133 $ bob binseg mkmask --dataset="refuge" --globs="Training400/*Glaucoma/*.jpg" --globs="Training400/*AMD/*.jpg" --threshold=5
134\b
135 Or you can generate the same results with this command
137\b
138 $ bob binseg mkmask -d "refuge" -g "Training400/*Glaucoma/*.jpg" -g "Training400/*AMD/*.jpg" -t 5
140\b
141 2. Generate masks for non supported dataset by bob
143\b
144 $ bob binseg mkmask -d "Path/to/dataset" -g "glob1" -g "glob2" -g glob3 -t 4
147""",
148)
149@click.option(
150 "--output-folder",
151 "-o",
152 help="Path where to store the generated model (created if does not exist)",
153 required=True,
154 type=click.Path(),
155 default="masks",
156 cls=ResourceOption,
157)
158@click.option(
159 "--dataset",
160 "-d",
161 help="""The base path to the dataset to which we want to generate the masks. \\
162 In case you have already configured the path for the datasets supported by bob, \\
163 you can just use the name of the dataset as written in the config. """,
164 required=True,
165 cls=ResourceOption,
166)
167@click.option(
168 "--globs",
169 "-g",
170 help="""The global path to the dataset to which we want to generate the masks.\\
171 We need to specify the path for the images ,\\
172 Ex : --globs="images/*.jpg"\\
173 It also can be used multiple time.
174 """,
175 required=True,
176 multiple=True,
177 cls=ResourceOption,
178)
179@click.option(
180 "--threshold",
181 "-t",
182 help=" Generating a mask needs a threshold to be fixed in order to transform the image to binary ",
183 required=True,
184 cls=ResourceOption,
185)
186@verbosity_option(cls=ResourceOption)
187def mkmask(dataset, globs, threshold, output_folder, **kwargs):
188 """
189 Commands for generating masks for images in a dataset.
191 """
192 if rc.get("bob.ip.binseg." + dataset + ".datadir"):
193 base_path = rc.get("bob.ip.binseg." + dataset + ".datadir")
194 else:
195 base_path = dataset
197 list_globs = []
198 for g in globs:
199 list_globs.append(g)
200 threshold = int(threshold)
201 process_glob(
202 base_path=base_path,
203 use_glob=list_globs,
204 output_path=output_folder,
205 threshold=threshold,
206 )