Commit bd28dcc8 authored by André Anjos's avatar André Anjos 💬

Merge branch 'minor-fixes' into 'master'

Minor fixes

See merge request !9
parents 98e9668b 350d7ea2
Pipeline #35372 passed with stages
in 5 minutes and 17 seconds
include README.rst buildout.cfg LICENSE version.txt requirements.txt
include README.rst buildout.cfg COPYING version.txt requirements.txt
recursive-include doc *.rst *.png *.ico *.txt
......@@ -7,7 +7,8 @@ from bob.ip.binseg.data.imagefolderinference import ImageFolderInference
#### Config ####
# add your transforms below
transforms = Compose([
transforms = Compose([
ToRGB(),
CenterCrop((544,544))
,ToTensor()
])
......
......@@ -8,24 +8,45 @@ import torch
import torchvision.transforms.functional as VF
import bob.io.base
def get_file_lists(data_path):
def get_file_lists(data_path, glob):
"""
Recursively retrieves file lists from a given path, matching a given glob
This function will use :py:meth:`pathlib.Path.rglob`, together with the
provided glob pattern to search for anything the desired filename.
"""
data_path = Path(data_path)
image_file_names = np.array(sorted(list(data_path.glob('*'))))
image_file_names = np.array(sorted(list(data_path.rglob(glob))))
return image_file_names
class ImageFolderInference(Dataset):
"""
Generic ImageFolder containing images for inference
Notice that this implementation, contrary to its sister
:py:class:`.ImageFolder`, does not *automatically*
convert the input image to RGB, before passing it to the transforms, so it
is possible to accomodate a wider range of input types (e.g. 16-bit PNG
images).
Parameters
----------
path : str
full path to root of dataset
glob : str
glob that can be used to filter-down files to be loaded on the provided
path
transform : list
List of transformations to apply to every input sample
"""
def __init__(self, path, transform = None):
def __init__(self, path, glob='*', transform = None):
self.transform = transform
self.img_file_list = get_file_lists(path)
self.path = path
self.img_file_list = get_file_lists(path, glob)
def __len__(self):
"""
......@@ -35,27 +56,27 @@ class ImageFolderInference(Dataset):
size of the dataset
"""
return len(self.img_file_list)
def __getitem__(self,index):
"""
Parameters
----------
index : int
Returns
-------
list
dataitem [img_name, img]
"""
img_path = self.img_file_list[index]
img_name = img_path.name
img = Image.open(img_path).convert(mode='RGB')
img_name = img_path.relative_to(self.path).as_posix()
img = Image.open(img_path)
sample = [img]
if self.transform :
sample = self.transform(*sample)
sample.insert(0,img_name)
return sample
......@@ -11,6 +11,7 @@ import math
from math import floor
import warnings
import collections
import bob.core
_pil_interpolation_to_str = {
Image.NEAREST: 'PIL.Image.NEAREST',
......@@ -22,7 +23,7 @@ _pil_interpolation_to_str = {
}
Iterable = collections.abc.Iterable
# Compose
# Compose
class Compose:
"""Composes several transforms.
......@@ -62,7 +63,7 @@ class CenterCrop:
"""
def __init__(self, size):
self.size = size
def __call__(self, *args):
return [VF.center_crop(img, self.size) for img in args]
......@@ -70,24 +71,24 @@ class CenterCrop:
class Crop:
"""
Crop at the given coordinates.
Attributes
----------
i : int
i : int
upper pixel coordinate.
j : int
j : int
left pixel coordinate.
h : int
h : int
height of the cropped image.
w : int
w : int
width of the cropped image.
"""
def __init__(self, i, j, h, w):
self.i = i
self.j = j
self.h = h
self.w = w
self.h = h
self.w = w
def __call__(self, *args):
return [img.crop((self.j, self.i, self.j + self.w, self.i + self.h)) for img in args]
......@@ -97,34 +98,61 @@ class Pad:
Attributes
----------
padding : int or tuple
padding on each border. If a single int is provided this is used to pad all borders.
padding : int or tuple
padding on each border. If a single int is provided this is used to pad all borders.
If tuple of length 2 is provided this is the padding on left/right and top/bottom respectively.
If a tuple of length 4 is provided this is the padding for the left, top, right and bottom borders respectively.
fill : int
pixel fill value for constant fill. Default is 0. If a tuple of length 3, it is used to fill R, G, B channels respectively.
This value is only used when the padding_mode is constant
pixel fill value for constant fill. Default is 0. If a tuple of length 3, it is used to fill R, G, B channels respectively.
This value is only used when the padding_mode is constant
"""
def __init__(self, padding, fill=0):
self.padding = padding
self.fill = fill
def __call__(self, *args):
return [VF.pad(img, self.padding, self.fill, padding_mode='constant') for img in args]
class AutoLevel16to8:
"""Converts a 16-bit image to 8-bit representation using "auto-level"
This transform assumes that the input images are gray-scaled.
To auto-level, we calculate the maximum and the minimum of the image, and
consider such a range should be mapped to the [0,255] range of the
destination image.
"""
def _process_one(self, img):
return Image.fromarray(bob.core.convert(img, 'uint8', (0,255),
img.getextrema()))
def __call__(self, *args):
return [self._process_one(img) for img in args]
class ToRGB:
"""Converts from any input format to RGB, using an ADAPTIVE conversion.
This transform takes the input image and converts it to RGB using
py:method:`Image.Image.convert`, with `mode='RGB'` and using all other
defaults. This may be aggressive if applied to 16-bit images without
further considerations.
"""
def __call__(self, *args):
return [img.convert(mode="RGB") for img in args]
class ToTensor:
"""Converts :py:class:`PIL.Image.Image` to :py:class:`torch.Tensor` """
def __call__(self, *args):
return [VF.to_tensor(img) for img in args]
# Augmentations
class RandomHFlip:
"""
Flips horizontally
Attributes
----------
prob : float
......@@ -132,50 +160,50 @@ class RandomHFlip:
"""
def __init__(self, prob = 0.5):
self.prob = prob
def __call__(self, *args):
if random.random() < self.prob:
return [VF.hflip(img) for img in args]
else:
return args
class RandomVFlip:
"""
Flips vertically
Attributes
----------
prob : float
prob : float
probability at which imgage is flipped. Defaults to ``0.5``
"""
def __init__(self, prob = 0.5):
self.prob = prob
def __call__(self, *args):
if random.random() < self.prob:
return [VF.vflip(img) for img in args]
else:
return args
class RandomRotation:
"""
Rotates by degree
Attributes
----------
degree_range : tuple
range of degrees in which image and ground truth are rotated. Defaults to ``(-15, +15)``
prob : float
prob : float
probability at which imgage is rotated. Defaults to ``0.5``
"""
def __init__(self, degree_range = (-15, +15), prob = 0.5):
self.prob = prob
self.degree_range = degree_range
def __call__(self, *args):
if random.random() < self.prob:
degree = random.randint(*self.degree_range)
......@@ -184,21 +212,21 @@ class RandomRotation:
return args
class ColorJitter(object):
"""
"""
Randomly change the brightness, contrast, saturation and hue
Attributes
----------
brightness : float
brightness : float
how much to jitter brightness. brightness_factor
is chosen uniformly from ``[max(0, 1 - brightness), 1 + brightness]``.
contrast : float
how much to jitter contrast. contrast_factor
is chosen uniformly from ``[max(0, 1 - contrast), 1 + contrast]``.
saturation : float
saturation : float
how much to jitter saturation. saturation_factor
is chosen uniformly from ``[max(0, 1 - saturation), 1 + saturation]``.
hue : float
hue : float
how much to jitter hue. hue_factor is chosen uniformly from
``[-hue, hue]``. Should be >=0 and <= 0.5
prob : float
......@@ -247,21 +275,21 @@ class ColorJitter(object):
class RandomResizedCrop:
"""Crop to random size and aspect ratio.
A crop of random size of the original size and a random aspect ratio of
the original aspect ratio is made. This crop is finally resized to
A crop of random size of the original size and a random aspect ratio of
the original aspect ratio is made. This crop is finally resized to
given size. This is popularly used to train the Inception networks.
Attributes
----------
size : int
size : int
expected output size of each edge
scale : tuple
scale : tuple
range of size of the origin size cropped. Defaults to ``(0.08, 1.0)``
ratio : tuple
range of aspect ratio of the origin aspect ratio cropped. Defaults to ``(3. / 4., 4. / 3.)``
interpolation :
Defaults to ``PIL.Image.BILINEAR``
prob : float
prob : float
probability at which the operation is applied. Defaults to ``0.5``
"""
......@@ -332,7 +360,7 @@ class RandomResizedCrop:
class Resize:
"""Resize to given size.
Attributes
----------
size : tuple or int
......
This diff is collapsed.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import os
import logging
import time
import datetime
......@@ -24,7 +24,7 @@ def do_predict(
"""
Run inference and calculate metrics
Parameters
---------
model : :py:class:`torch.nn.Module`
......@@ -37,12 +37,12 @@ def do_predict(
logger = logging.getLogger("bob.ip.binseg.engine.inference")
logger.info("Start evaluation")
logger.info("Output folder: {}, Device: {}".format(output_folder, device))
results_subfolder = os.path.join(output_folder,'results')
results_subfolder = os.path.join(output_folder,'results')
os.makedirs(results_subfolder,exist_ok=True)
model.eval().to(device)
# Sigmoid for probabilities
sigmoid = torch.nn.Sigmoid()
# Sigmoid for probabilities
sigmoid = torch.nn.Sigmoid()
# Setup timers
start_total_time = time.time()
......@@ -55,24 +55,24 @@ def do_predict(
start_time = time.perf_counter()
outputs = model(images)
# necessary check for hed architecture that uses several outputs
# necessary check for hed architecture that uses several outputs
# for loss calculation instead of just the last concatfuse block
if isinstance(outputs,list):
outputs = outputs[-1]
probabilities = sigmoid(outputs)
batch_time = time.perf_counter() - start_time
times.append(batch_time)
logger.info("Batch time: {:.5f} s".format(batch_time))
# Create probability images
save_probability_images(probabilities, names, output_folder, logger)
# Save hdf5
save_hdf(probabilities, names, output_folder, logger)
# Report times
total_inference_time = str(datetime.timedelta(seconds=int(sum(times))))
average_batch_inference_time = np.mean(times)
......@@ -82,7 +82,7 @@ def do_predict(
times_file = "Times.txt"
logger.info("saving {}".format(times_file))
with open (os.path.join(results_subfolder,times_file), "w+") as outfile:
date = datetime.datetime.now()
outfile.write("Date: {} \n".format(date.strftime("%Y-%m-%d %H:%M:%S")))
......
This diff is collapsed.
......@@ -39,6 +39,7 @@ requirements:
- matplotlib
- tqdm
- tabulate
- bob.core
test:
imports:
......@@ -75,4 +76,3 @@ about:
home: https://www.idiap.ch/software/bob/
license: GNU General Public License v3 (GPLv3)
license_family: GPL
license_file: ../LICENSE
......@@ -17,9 +17,11 @@ PyTorch ImageFolder Dataset
===========================
.. automodule:: bob.ip.binseg.data.imagefolder
.. automodule:: bob.ip.binseg.data.imagefolderinference
Transforms
==========
.. note::
.. note::
All transforms work with :py:class:`PIL.Image.Image` objects. We make heavy use of the
`torchvision package`_
......
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