Spaces:
Running
Running
"""This code is taken from <https://github.com/alexandre01/deepsvg> | |
by Alexandre Carlier, Martin Danelljan, Alexandre Alahi and Radu Timofte | |
from the paper >https://arxiv.org/pdf/2007.11301.pdf> | |
""" | |
from src.preprocessing.deepsvg.deepsvg_config.config import _Config | |
from src.preprocessing.deepsvg.deepsvg_difflib.tensor import SVGTensor | |
from src.preprocessing.deepsvg.deepsvg_svglib.svg import SVG | |
from src.preprocessing.deepsvg.deepsvg_svglib.geom import Point | |
import math | |
import torch | |
import torch.utils.data | |
import random | |
from typing import List, Union | |
import pandas as pd | |
import os | |
import pickle | |
Num = Union[int, float] | |
class SVGDataset(torch.utils.data.Dataset): | |
def __init__(self, data_dir, meta_filepath, model_args, max_num_groups, max_seq_len, max_total_len=None, | |
filter_uni=None, filter_platform=None, filter_category=None, train_ratio=1.0, df=None, PAD_VAL=-1, | |
nb_augmentations=1, already_preprocessed=True): | |
self.data_dir = data_dir | |
self.already_preprocessed = already_preprocessed | |
self.MAX_NUM_GROUPS = max_num_groups | |
self.MAX_SEQ_LEN = max_seq_len | |
self.MAX_TOTAL_LEN = max_total_len | |
if max_total_len is None: | |
self.MAX_TOTAL_LEN = max_num_groups * max_seq_len | |
if df is None: | |
df = pd.read_csv(meta_filepath) | |
if len(df) > 0: | |
if filter_uni is not None: | |
df = df[df.uni.isin(filter_uni)] | |
if filter_platform is not None: | |
df = df[df.platform.isin(filter_platform)] | |
if filter_category is not None: | |
df = df[df.category.isin(filter_category)] | |
df = df[(df.nb_groups <= max_num_groups) & (df.max_len_group <= max_seq_len)] | |
if max_total_len is not None: | |
df = df[df.total_len <= max_total_len] | |
self.df = df.sample(frac=train_ratio) if train_ratio < 1.0 else df | |
self.model_args = model_args | |
self.PAD_VAL = PAD_VAL | |
self.nb_augmentations = nb_augmentations | |
def search_name(self, name): | |
return self.df[self.df.commonName.str.contains(name)] | |
def _filter_categories(self, filter_category): | |
self.df = self.df[self.df.category.isin(filter_category)] | |
def _uni_to_label(uni): | |
if 48 <= uni <= 57: | |
return uni - 48 | |
elif 65 <= uni <= 90: | |
return uni - 65 + 10 | |
return uni - 97 + 36 | |
def _label_to_uni(label_id): | |
if 0 <= label_id <= 9: | |
return label_id + 48 | |
elif 10 <= label_id <= 35: | |
return label_id + 65 - 10 | |
return label_id + 97 - 36 | |
def _category_to_label(category): | |
categories = ['characters', 'free-icons', 'logos', 'alphabet', 'animals', 'arrows', 'astrology', 'baby', 'beauty', | |
'business', 'cinema', 'city', 'clothing', 'computer-hardware', 'crime', 'cultures', 'data', 'diy', | |
'drinks', 'ecommerce', 'editing', 'files', 'finance', 'folders', 'food', 'gaming', 'hands', 'healthcare', | |
'holidays', 'household', 'industry', 'maps', 'media-controls', 'messaging', 'military', 'mobile', | |
'music', 'nature', 'network', 'photo-video', 'plants', 'printing', 'profile', 'programming', 'science', | |
'security', 'shopping', 'social-networks', 'sports', 'time-and-date', 'transport', 'travel', 'user-interface', | |
'users', 'weather', 'flags', 'emoji', 'men', 'women'] | |
return categories.index(category) | |
def get_label(self, idx=0, entry=None): | |
if entry is None: | |
entry = self.df.iloc[idx] | |
if "uni" in self.df.columns: # Font dataset | |
label = self._uni_to_label(entry.uni) | |
return torch.tensor(label) | |
elif "category" in self.df.columns: # Icons dataset | |
label = self._category_to_label(entry.category) | |
return torch.tensor(label) | |
return None | |
def idx_to_id(self, idx): | |
return self.df.iloc[idx].id | |
def entry_from_id(self, id): | |
return self.df[self.df.id == str(id)].iloc[0] | |
def _load_svg(self, icon_id): | |
svg = SVG.load_svg(os.path.join(self.data_dir, f"{icon_id}.svg")) | |
if not self.already_preprocessed: | |
svg.fill_(False) | |
svg.normalize().zoom(0.9) | |
svg.canonicalize() | |
svg = svg.simplify_heuristic() | |
return svg | |
def __len__(self): | |
return len(self.df) * self.nb_augmentations | |
def random_icon(self): | |
return self[random.randrange(0, len(self))] | |
def random_id(self): | |
idx = random.randrange(0, len(self)) % len(self.df) | |
return self.idx_to_id(idx) | |
def random_id_by_uni(self, uni): | |
df = self.df[self.df.uni == uni] | |
return df.id.sample().iloc[0] | |
def __getitem__(self, idx): | |
return self.get(idx, self.model_args) | |
def _augment(svg, mean=False): | |
dx, dy = (0, 0) if mean else (5 * random.random() - 2.5, 5 * random.random() - 2.5) | |
factor = 0.7 if mean else 0.2 * random.random() + 0.6 | |
return svg.zoom(factor).translate(Point(dx, dy)) | |
def simplify(svg, normalize=True): | |
svg.canonicalize(normalize=normalize) | |
svg = svg.simplify_heuristic() | |
return svg.normalize() | |
def preprocess(svg, augment=True, numericalize=True, mean=False): | |
if augment: | |
svg = SVGDataset._augment(svg, mean=mean) | |
if numericalize: | |
return svg.numericalize(256) | |
return svg | |
def get(self, idx=0, model_args=None, random_aug=True, id=None, svg: SVG=None): | |
if id is None: | |
idx = idx % len(self.df) | |
id = self.idx_to_id(idx) | |
if svg is None: | |
svg = self._load_svg(id) | |
svg = SVGDataset.preprocess(svg, augment=random_aug) | |
t_sep, fillings = svg.to_tensor(concat_groups=False, PAD_VAL=self.PAD_VAL), svg.to_fillings() | |
# Note: DeepSVG can only handle 8 paths in a SVG and 30 sequences per path | |
if len(t_sep) > 8: | |
#print(f"SVG {id} has more than 30 segments.") | |
t_sep = t_sep[0:8] | |
fillings = fillings[0:8] | |
for i in range(len(t_sep)): | |
if len(t_sep[i]) > 30: | |
#print(f"SVG {id}: Path nr {i} has more than 30 segments.") | |
t_sep[i] = t_sep[i][0:30] | |
label = self.get_label(idx) | |
return self.get_data(t_sep, fillings, model_args=model_args, label=label) | |
def get_data(self, t_sep, fillings, model_args=None, label=None): | |
res = {} | |
if model_args is None: | |
model_args = self.model_args | |
pad_len = max(self.MAX_NUM_GROUPS - len(t_sep), 0) | |
t_sep.extend([torch.empty(0, 14)] * pad_len) | |
fillings.extend([0] * pad_len) | |
t_grouped = [SVGTensor.from_data(torch.cat(t_sep, dim=0), PAD_VAL=self.PAD_VAL).add_eos().add_sos().pad( | |
seq_len=self.MAX_TOTAL_LEN + 2)] | |
t_sep = [SVGTensor.from_data(t, PAD_VAL=self.PAD_VAL, filling=f).add_eos().add_sos().pad(seq_len=self.MAX_SEQ_LEN + 2) for | |
t, f in zip(t_sep, fillings)] | |
for arg in set(model_args): | |
if "_grouped" in arg: | |
arg_ = arg.split("_grouped")[0] | |
t_list = t_grouped | |
else: | |
arg_ = arg | |
t_list = t_sep | |
if arg_ == "tensor": | |
res[arg] = t_list | |
if arg_ == "commands": | |
res[arg] = torch.stack([t.cmds() for t in t_list]) | |
if arg_ == "args_rel": | |
res[arg] = torch.stack([t.get_relative_args() for t in t_list]) | |
if arg_ == "args": | |
res[arg] = torch.stack([t.args() for t in t_list]) | |
if "filling" in model_args: | |
res["filling"] = torch.stack([torch.tensor(t.filling) for t in t_sep]).unsqueeze(-1) | |
if "label" in model_args: | |
res["label"] = label | |
return res | |
def load_dataset(cfg: _Config, already_preprocessed=True): | |
dataset = SVGDataset(cfg.data_dir, cfg.meta_filepath, cfg.model_args, cfg.max_num_groups, cfg.max_seq_len, cfg.max_total_len, | |
cfg.filter_uni, cfg.filter_platform, cfg.filter_category, cfg.train_ratio, | |
nb_augmentations=cfg.nb_augmentations, already_preprocessed=already_preprocessed) | |
return dataset | |