torchsight.datasets.flickr32
module
A dataset interface for the Flickr32 dataset.
Original web page for more information: http://www.multimedia-computing.de/flickrlogos/
Source code
"""A dataset interface for the Flickr32 dataset.
Original web page for more information:
http://www.multimedia-computing.de/flickrlogos/
"""
import os
import torch
from PIL import Image
from torchsight.utils import describe_boxes, visualize_boxes
from .mixins import VisualizeMixin
class Flickr32Dataset(torch.utils.data.Dataset, VisualizeMixin):
"""Dataset to get the images and annotations of the Flickr32 dataset.
Download the dataset:
- Request the dataset zip file in the original web page:
http://www.multimedia-computing.de/flickrlogos/
- Unzip the dataset in any directory.
- Provide the path to the root* directory of the dataset in the initialization.
*: The root directory is the one that contains the 'classes' and 'scripts' directories
and the `.txt` files with the split of the data (training, validation and test sets).
"""
def __init__(self, root, dataset='training', transform=None, brands=None, only_boxes=False):
"""Initialize the dataset.
Arguments:
root (str): The path to the root directory that contains the data.
dataset (str, optional): The dataset that you want to load.
Options available: 'training', 'validation', 'test', 'trainval'.
transform (callable, optional): A callable to transform the images and
bounding boxes.
brands (list, optional): A list with the brands to load. If None is provided it will load
all the classes.
only_boxes (bool, optional): If True, it will load images that only contains bounding boxes.
This is an option because in the validation and test set there are images without logos,
but for training we probably don't want to train with that images.
"""
self.root = self.validate_root(root)
self.dataset = self.validate_dataset(dataset)
self.transform = transform
self.brands = brands
self.only_boxes = only_boxes
self.paths = self.get_paths()
self.label_to_class, self.class_to_label = self.generate_labels()
if self.brands is None:
self.brands = list(self.class_to_label.keys())
def __len__(self):
"""Returns the length of the dataset.
Returns:
int: The length of the dataset.
"""
return len(self.paths)
def __getitem__(self, i):
"""Get the image and bounding boxes at the given index.
Arguments:
i (int): The index of the element that you want to get.
Returns:
any: The transformed image.
torch.Tensor: The bounding boxes with x1, y1, x2, y2, label.
Shape: `(num of boxes, 5)`
dict: A dict with more info about the item like the brand name and the
path to the image.
"""
brand, image, boxes = self.paths[i]
info = {'brand': brand, 'image': image}
image = Image.open(image)
boxes = self.get_boxes(boxes, brand)
if self.transform is not None:
image, boxes = self.transform({'image': image, 'boxes': boxes})
return image, boxes, info
################################
### VALIDATION ###
################################
@staticmethod
def validate_root(path):
"""Validate that the given path exists and return it.
Arguments:
path (str): The path to validate that exists.
Returns:
str: The given path if its valid.
Raises:
ValueError: if the given path does not exists.
"""
if not os.path.exists(path):
raise ValueError('"{}" does not exists.'.format(path))
return path
@staticmethod
def validate_dataset(name):
"""Check that the given name of the dataset is a correct one.
Arguments:
name (str): The name of the dataset to check.
Returns:
str: The name if its valid.
Raises:
ValueError: if the given name is not a valid one.
"""
if name not in ['training', 'validation', 'test', 'trainval']:
raise ValueError('"{}" is not a valid dataset name.'.format(name))
return name
############################
### GETTERS ###
############################
def get_paths(self):
"""Load the absolute paths to the files that are in the dataset.
Returns:
list of tuples of str: Each tuple containing the class' name, the
path to the image and the path to its bounding boxes.
Example:
('google',
'/datasets/flickr32/classes/jpg/google/2240784196.jpg',
'/datasets/flickr32/classes/masks/google/2240784196.jpg.bboxes.txt')
If the image has no brand it set the brand as 'no-logo'.
"""
if self.dataset == 'training':
file = 'trainset.txt'
if self.dataset == 'validation':
file = 'valset.txt'
if self.dataset == 'test':
file = 'testset.txt'
if self.dataset == 'trainval':
file = 'trainvalset.txt'
file = os.path.join(self.root, file)
with open(file, 'r') as file:
tuples = []
for line in file.readlines():
brand, image = line.split(',')
if self.brands is not None and brand not in self.brands:
continue
image = image.replace('\n', '')
boxes = os.path.join(
self.root, 'classes/masks/{}/{}.bboxes.txt'.format(brand if brand != 'HP' else 'hp', image))
image = os.path.join(self.root, 'classes/jpg/{}/{}'.format(brand, image))
if not os.path.exists(boxes) and self.only_boxes:
continue
tuples.append((brand, image, boxes))
return tuples
def generate_labels(self):
"""Generate the labels for the classes.
Returns:
dict: A dict with the label (int) -> brand (str) map.
dict: A dict with the brand (str) -> label (int) map.
"""
brands = list({brand for brand, *_ in self.paths if brand != 'no-logo'})
brands.sort()
label_to_class = {i: brand for i, brand in enumerate(brands)}
class_to_label = {brand: i for i, brand in enumerate(brands)}
label_to_class[-1] = 'no-logo'
class_to_label['no-logo'] = -1
return label_to_class, class_to_label
def get_boxes(self, file, brand):
"""Get the boxes from the given file.
Arguments:
file (str): The path to the file that contains the annotations.
brand (str): The name of the brand for the boxes.
Returns:
torch.Tensor: The bounding boxes for the given image.
Shape: `(num of boxes, 5)`
"""
label = self.class_to_label[brand]
try:
with open(file, 'r') as file:
boxes = []
for line in file.readlines()[1:]: # The first line contains "x y width height"
x, y, w, h = (int(val) for val in line.split())
x1, y1 = x - 1, y - 1
x2, y2 = x1 + w, y1 + h
boxes.append(torch.Tensor([x1, y1, x2, y2, label]))
return torch.stack(boxes)
except FileNotFoundError:
return torch.Tensor([])
##########################
### STATS ###
##########################
def describe_boxes(self):
"""Describe the boxes of the dataset.
See torchsight.utils.describe_boxes docs for more information.
"""
if self.transform is not None:
boxes = []
for i, (_, bxs, *_) in enumerate(self):
print('Loading boxes ... ({}/{})'.format(i + 1, len(self)))
boxes.append(bxs)
else:
boxes = [self.get_boxes(boxes, brand) for brand, _, boxes in self.paths]
return describe_boxes(torch.cat(boxes, dim=0))
Classes
class Flickr32Dataset (ancestors: torch.utils.data.dataset.Dataset, VisualizeMixin)
-
Dataset to get the images and annotations of the Flickr32 dataset.
Download the dataset: - Request the dataset zip file in the original web page: http://www.multimedia-computing.de/flickrlogos/ - Unzip the dataset in any directory. - Provide the path to the root* directory of the dataset in the initialization.
*
:The
root
directory
is
the
one
that
contains
the
'classes'
and
'scripts'
directories
and the
.txt
files with the split of the data (training, validation and test sets).Source code
class Flickr32Dataset(torch.utils.data.Dataset, VisualizeMixin): """Dataset to get the images and annotations of the Flickr32 dataset. Download the dataset: - Request the dataset zip file in the original web page: http://www.multimedia-computing.de/flickrlogos/ - Unzip the dataset in any directory. - Provide the path to the root* directory of the dataset in the initialization. *: The root directory is the one that contains the 'classes' and 'scripts' directories and the `.txt` files with the split of the data (training, validation and test sets). """ def __init__(self, root, dataset='training', transform=None, brands=None, only_boxes=False): """Initialize the dataset. Arguments: root (str): The path to the root directory that contains the data. dataset (str, optional): The dataset that you want to load. Options available: 'training', 'validation', 'test', 'trainval'. transform (callable, optional): A callable to transform the images and bounding boxes. brands (list, optional): A list with the brands to load. If None is provided it will load all the classes. only_boxes (bool, optional): If True, it will load images that only contains bounding boxes. This is an option because in the validation and test set there are images without logos, but for training we probably don't want to train with that images. """ self.root = self.validate_root(root) self.dataset = self.validate_dataset(dataset) self.transform = transform self.brands = brands self.only_boxes = only_boxes self.paths = self.get_paths() self.label_to_class, self.class_to_label = self.generate_labels() if self.brands is None: self.brands = list(self.class_to_label.keys()) def __len__(self): """Returns the length of the dataset. Returns: int: The length of the dataset. """ return len(self.paths) def __getitem__(self, i): """Get the image and bounding boxes at the given index. Arguments: i (int): The index of the element that you want to get. Returns: any: The transformed image. torch.Tensor: The bounding boxes with x1, y1, x2, y2, label. Shape: `(num of boxes, 5)` dict: A dict with more info about the item like the brand name and the path to the image. """ brand, image, boxes = self.paths[i] info = {'brand': brand, 'image': image} image = Image.open(image) boxes = self.get_boxes(boxes, brand) if self.transform is not None: image, boxes = self.transform({'image': image, 'boxes': boxes}) return image, boxes, info ################################ ### VALIDATION ### ################################ @staticmethod def validate_root(path): """Validate that the given path exists and return it. Arguments: path (str): The path to validate that exists. Returns: str: The given path if its valid. Raises: ValueError: if the given path does not exists. """ if not os.path.exists(path): raise ValueError('"{}" does not exists.'.format(path)) return path @staticmethod def validate_dataset(name): """Check that the given name of the dataset is a correct one. Arguments: name (str): The name of the dataset to check. Returns: str: The name if its valid. Raises: ValueError: if the given name is not a valid one. """ if name not in ['training', 'validation', 'test', 'trainval']: raise ValueError('"{}" is not a valid dataset name.'.format(name)) return name ############################ ### GETTERS ### ############################ def get_paths(self): """Load the absolute paths to the files that are in the dataset. Returns: list of tuples of str: Each tuple containing the class' name, the path to the image and the path to its bounding boxes. Example: ('google', '/datasets/flickr32/classes/jpg/google/2240784196.jpg', '/datasets/flickr32/classes/masks/google/2240784196.jpg.bboxes.txt') If the image has no brand it set the brand as 'no-logo'. """ if self.dataset == 'training': file = 'trainset.txt' if self.dataset == 'validation': file = 'valset.txt' if self.dataset == 'test': file = 'testset.txt' if self.dataset == 'trainval': file = 'trainvalset.txt' file = os.path.join(self.root, file) with open(file, 'r') as file: tuples = [] for line in file.readlines(): brand, image = line.split(',') if self.brands is not None and brand not in self.brands: continue image = image.replace('\n', '') boxes = os.path.join( self.root, 'classes/masks/{}/{}.bboxes.txt'.format(brand if brand != 'HP' else 'hp', image)) image = os.path.join(self.root, 'classes/jpg/{}/{}'.format(brand, image)) if not os.path.exists(boxes) and self.only_boxes: continue tuples.append((brand, image, boxes)) return tuples def generate_labels(self): """Generate the labels for the classes. Returns: dict: A dict with the label (int) -> brand (str) map. dict: A dict with the brand (str) -> label (int) map. """ brands = list({brand for brand, *_ in self.paths if brand != 'no-logo'}) brands.sort() label_to_class = {i: brand for i, brand in enumerate(brands)} class_to_label = {brand: i for i, brand in enumerate(brands)} label_to_class[-1] = 'no-logo' class_to_label['no-logo'] = -1 return label_to_class, class_to_label def get_boxes(self, file, brand): """Get the boxes from the given file. Arguments: file (str): The path to the file that contains the annotations. brand (str): The name of the brand for the boxes. Returns: torch.Tensor: The bounding boxes for the given image. Shape: `(num of boxes, 5)` """ label = self.class_to_label[brand] try: with open(file, 'r') as file: boxes = [] for line in file.readlines()[1:]: # The first line contains "x y width height" x, y, w, h = (int(val) for val in line.split()) x1, y1 = x - 1, y - 1 x2, y2 = x1 + w, y1 + h boxes.append(torch.Tensor([x1, y1, x2, y2, label])) return torch.stack(boxes) except FileNotFoundError: return torch.Tensor([]) ########################## ### STATS ### ########################## def describe_boxes(self): """Describe the boxes of the dataset. See torchsight.utils.describe_boxes docs for more information. """ if self.transform is not None: boxes = [] for i, (_, bxs, *_) in enumerate(self): print('Loading boxes ... ({}/{})'.format(i + 1, len(self))) boxes.append(bxs) else: boxes = [self.get_boxes(boxes, brand) for brand, _, boxes in self.paths] return describe_boxes(torch.cat(boxes, dim=0))
Static methods
def validate_dataset(name)
-
Check that the given name of the dataset is a correct one.
Arguments
name
:str
- The name of the dataset to check.
Returns
str
- The name if its valid.
Raises
ValueError
- if the given name is not a valid one.
Source code
@staticmethod def validate_dataset(name): """Check that the given name of the dataset is a correct one. Arguments: name (str): The name of the dataset to check. Returns: str: The name if its valid. Raises: ValueError: if the given name is not a valid one. """ if name not in ['training', 'validation', 'test', 'trainval']: raise ValueError('"{}" is not a valid dataset name.'.format(name)) return name
def validate_root(path)
-
Validate that the given path exists and return it.
Arguments
path
:str
- The path to validate that exists.
Returns
str
- The given path if its valid.
Raises
ValueError
- if the given path does not exists.
Source code
@staticmethod def validate_root(path): """Validate that the given path exists and return it. Arguments: path (str): The path to validate that exists. Returns: str: The given path if its valid. Raises: ValueError: if the given path does not exists. """ if not os.path.exists(path): raise ValueError('"{}" does not exists.'.format(path)) return path
Methods
def __init__(self, root, dataset='training', transform=None, brands=None, only_boxes=False)
-
Initialize the dataset.
Arguments
root
:str
- The path to the root directory that contains the data.
dataset
:str
, optional- The dataset that you want to load. Options available: 'training', 'validation', 'test', 'trainval'.
transform
:callable
, optional- A callable to transform the images and bounding boxes.
brands
:list
, optional- A list with the brands to load. If None is provided it will load all the classes.
only_boxes
:bool
, optional- If True, it will load images that only contains bounding boxes. This is an option because in the validation and test set there are images without logos, but for training we probably don't want to train with that images.
Source code
def __init__(self, root, dataset='training', transform=None, brands=None, only_boxes=False): """Initialize the dataset. Arguments: root (str): The path to the root directory that contains the data. dataset (str, optional): The dataset that you want to load. Options available: 'training', 'validation', 'test', 'trainval'. transform (callable, optional): A callable to transform the images and bounding boxes. brands (list, optional): A list with the brands to load. If None is provided it will load all the classes. only_boxes (bool, optional): If True, it will load images that only contains bounding boxes. This is an option because in the validation and test set there are images without logos, but for training we probably don't want to train with that images. """ self.root = self.validate_root(root) self.dataset = self.validate_dataset(dataset) self.transform = transform self.brands = brands self.only_boxes = only_boxes self.paths = self.get_paths() self.label_to_class, self.class_to_label = self.generate_labels() if self.brands is None: self.brands = list(self.class_to_label.keys())
def describe_boxes(self)
-
Describe the boxes of the dataset.
See torchsight.utils.describe_boxes docs for more information.
Source code
def describe_boxes(self): """Describe the boxes of the dataset. See torchsight.utils.describe_boxes docs for more information. """ if self.transform is not None: boxes = [] for i, (_, bxs, *_) in enumerate(self): print('Loading boxes ... ({}/{})'.format(i + 1, len(self))) boxes.append(bxs) else: boxes = [self.get_boxes(boxes, brand) for brand, _, boxes in self.paths] return describe_boxes(torch.cat(boxes, dim=0))
def generate_labels(self)
-
Generate the labels for the classes.
Returns
dict
- A dict with the label (int) -> brand (str) map.
dict
- A dict with the brand (str) -> label (int) map.
Source code
def generate_labels(self): """Generate the labels for the classes. Returns: dict: A dict with the label (int) -> brand (str) map. dict: A dict with the brand (str) -> label (int) map. """ brands = list({brand for brand, *_ in self.paths if brand != 'no-logo'}) brands.sort() label_to_class = {i: brand for i, brand in enumerate(brands)} class_to_label = {brand: i for i, brand in enumerate(brands)} label_to_class[-1] = 'no-logo' class_to_label['no-logo'] = -1 return label_to_class, class_to_label
def get_boxes(self, file, brand)
-
Get the boxes from the given file.
Arguments
file
:str
- The path to the file that contains the annotations.
brand
:str
- The name of the brand for the boxes.
Returns
torch.Tensor: The bounding boxes for the given image. Shape:
(num of boxes, 5)
Source code
def get_boxes(self, file, brand): """Get the boxes from the given file. Arguments: file (str): The path to the file that contains the annotations. brand (str): The name of the brand for the boxes. Returns: torch.Tensor: The bounding boxes for the given image. Shape: `(num of boxes, 5)` """ label = self.class_to_label[brand] try: with open(file, 'r') as file: boxes = [] for line in file.readlines()[1:]: # The first line contains "x y width height" x, y, w, h = (int(val) for val in line.split()) x1, y1 = x - 1, y - 1 x2, y2 = x1 + w, y1 + h boxes.append(torch.Tensor([x1, y1, x2, y2, label])) return torch.stack(boxes) except FileNotFoundError: return torch.Tensor([])
def get_paths(self)
-
Load the absolute paths to the files that are in the dataset.
Returns
list of tuples of str: Each tuple containing the class' name, the path to the image and the path to its bounding boxes. Example: ('google', '/datasets/flickr32/classes/jpg/google/2240784196.jpg', '/datasets/flickr32/classes/masks/google/2240784196.jpg.bboxes.txt') If the image has no brand it set the brand as 'no-logo'.
Source code
def get_paths(self): """Load the absolute paths to the files that are in the dataset. Returns: list of tuples of str: Each tuple containing the class' name, the path to the image and the path to its bounding boxes. Example: ('google', '/datasets/flickr32/classes/jpg/google/2240784196.jpg', '/datasets/flickr32/classes/masks/google/2240784196.jpg.bboxes.txt') If the image has no brand it set the brand as 'no-logo'. """ if self.dataset == 'training': file = 'trainset.txt' if self.dataset == 'validation': file = 'valset.txt' if self.dataset == 'test': file = 'testset.txt' if self.dataset == 'trainval': file = 'trainvalset.txt' file = os.path.join(self.root, file) with open(file, 'r') as file: tuples = [] for line in file.readlines(): brand, image = line.split(',') if self.brands is not None and brand not in self.brands: continue image = image.replace('\n', '') boxes = os.path.join( self.root, 'classes/masks/{}/{}.bboxes.txt'.format(brand if brand != 'HP' else 'hp', image)) image = os.path.join(self.root, 'classes/jpg/{}/{}'.format(brand, image)) if not os.path.exists(boxes) and self.only_boxes: continue tuples.append((brand, image, boxes)) return tuples
Inherited members