Commit 1fe1af27 authored by wanglch's avatar wanglch
Browse files

Initial commit

parent 4cfcf972
"""
Donut
Copyright (c) 2022-present NAVER Corp.
MIT License
"""
from collections import OrderedDict
import numpy as np
from synthtiger import components
from elements.textbox import TextBox
from layouts import GridStack
class TextReader:
def __init__(self, path, cache_size=2 ** 28, block_size=2 ** 20):
self.fp = open(path, "r", encoding="utf-8")
self.length = 0
self.offsets = [0]
self.cache = OrderedDict()
self.cache_size = cache_size
self.block_size = block_size
self.bucket_size = cache_size // block_size
self.idx = 0
while True:
text = self.fp.read(self.block_size)
if not text:
break
self.length += len(text)
self.offsets.append(self.fp.tell())
def __len__(self):
return self.length
def __iter__(self):
return self
def __next__(self):
char = self.get()
self.next()
return char
def move(self, idx):
self.idx = idx
def next(self):
self.idx = (self.idx + 1) % self.length
def prev(self):
self.idx = (self.idx - 1) % self.length
def get(self):
key = self.idx // self.block_size
if key in self.cache:
text = self.cache[key]
else:
if len(self.cache) >= self.bucket_size:
self.cache.popitem(last=False)
offset = self.offsets[key]
self.fp.seek(offset, 0)
text = self.fp.read(self.block_size)
self.cache[key] = text
self.cache.move_to_end(key)
char = text[self.idx % self.block_size]
return char
class Content:
def __init__(self, config):
self.margin = config.get("margin", [0, 0.1])
self.reader = TextReader(**config.get("text", {}))
self.font = components.BaseFont(**config.get("font", {}))
self.layout = GridStack(config.get("layout", {}))
self.textbox = TextBox(config.get("textbox", {}))
self.textbox_color = components.Switch(components.Gray(), **config.get("textbox_color", {}))
self.content_color = components.Switch(components.Gray(), **config.get("content_color", {}))
def generate(self, size):
width, height = size
layout_left = width * np.random.uniform(self.margin[0], self.margin[1])
layout_top = height * np.random.uniform(self.margin[0], self.margin[1])
layout_width = max(width - layout_left * 2, 0)
layout_height = max(height - layout_top * 2, 0)
layout_bbox = [layout_left, layout_top, layout_width, layout_height]
text_layers, texts = [], []
layouts = self.layout.generate(layout_bbox)
self.reader.move(np.random.randint(len(self.reader)))
for layout in layouts:
font = self.font.sample()
for bbox, align in layout:
x, y, w, h = bbox
text_layer, text = self.textbox.generate((w, h), self.reader, font)
self.reader.prev()
if text_layer is None:
continue
text_layer.center = (x + w / 2, y + h / 2)
if align == "left":
text_layer.left = x
if align == "right":
text_layer.right = x + w
self.textbox_color.apply([text_layer])
text_layers.append(text_layer)
texts.append(text)
self.content_color.apply(text_layers)
return text_layers, texts
"""
Donut
Copyright (c) 2022-present NAVER Corp.
MIT License
"""
import numpy as np
from synthtiger import components
from elements.content import Content
from elements.paper import Paper
class Document:
def __init__(self, config):
self.fullscreen = config.get("fullscreen", 0.5)
self.landscape = config.get("landscape", 0.5)
self.short_size = config.get("short_size", [480, 1024])
self.aspect_ratio = config.get("aspect_ratio", [1, 2])
self.paper = Paper(config.get("paper", {}))
self.content = Content(config.get("content", {}))
self.effect = components.Iterator(
[
components.Switch(components.ElasticDistortion()),
components.Switch(components.AdditiveGaussianNoise()),
components.Switch(
components.Selector(
[
components.Perspective(),
components.Perspective(),
components.Perspective(),
components.Perspective(),
components.Perspective(),
components.Perspective(),
components.Perspective(),
components.Perspective(),
]
)
),
],
**config.get("effect", {}),
)
def generate(self, size):
width, height = size
fullscreen = np.random.rand() < self.fullscreen
if not fullscreen:
landscape = np.random.rand() < self.landscape
max_size = width if landscape else height
short_size = np.random.randint(
min(width, height, self.short_size[0]),
min(width, height, self.short_size[1]) + 1,
)
aspect_ratio = np.random.uniform(
min(max_size / short_size, self.aspect_ratio[0]),
min(max_size / short_size, self.aspect_ratio[1]),
)
long_size = int(short_size * aspect_ratio)
size = (long_size, short_size) if landscape else (short_size, long_size)
text_layers, texts = self.content.generate(size)
paper_layer = self.paper.generate(size)
self.effect.apply([*text_layers, paper_layer])
return paper_layer, text_layers, texts
"""
Donut
Copyright (c) 2022-present NAVER Corp.
MIT License
"""
from synthtiger import components, layers
class Paper:
def __init__(self, config):
self.image = components.BaseTexture(**config.get("image", {}))
def generate(self, size):
paper_layer = layers.RectLayer(size, (255, 255, 255, 255))
self.image.apply([paper_layer])
return paper_layer
"""
Donut
Copyright (c) 2022-present NAVER Corp.
MIT License
"""
import numpy as np
from synthtiger import layers
class TextBox:
def __init__(self, config):
self.fill = config.get("fill", [1, 1])
def generate(self, size, text, font):
width, height = size
char_layers, chars = [], []
fill = np.random.uniform(self.fill[0], self.fill[1])
width = np.clip(width * fill, height, width)
font = {**font, "size": int(height)}
left, top = 0, 0
for char in text:
if char in "\r\n":
continue
char_layer = layers.TextLayer(char, **font)
char_scale = height / char_layer.height
char_layer.bbox = [left, top, *(char_layer.size * char_scale)]
if char_layer.right > width:
break
char_layers.append(char_layer)
chars.append(char)
left = char_layer.right
text = "".join(chars).strip()
if len(char_layers) == 0 or len(text) == 0:
return None, None
text_layer = layers.Group(char_layers).merge()
return text_layer, text
"""
Donut
Copyright (c) 2022-present NAVER Corp.
MIT License
"""
from layouts.grid import Grid
from layouts.grid_stack import GridStack
__all__ = ["Grid", "GridStack"]
"""
Donut
Copyright (c) 2022-present NAVER Corp.
MIT License
"""
import numpy as np
class Grid:
def __init__(self, config):
self.text_scale = config.get("text_scale", [0.05, 0.1])
self.max_row = config.get("max_row", 5)
self.max_col = config.get("max_col", 3)
self.fill = config.get("fill", [0, 1])
self.full = config.get("full", 0)
self.align = config.get("align", ["left", "right", "center"])
def generate(self, bbox):
left, top, width, height = bbox
text_scale = np.random.uniform(self.text_scale[0], self.text_scale[1])
text_size = min(width, height) * text_scale
grids = np.random.permutation(self.max_row * self.max_col)
for grid in grids:
row = grid // self.max_col + 1
col = grid % self.max_col + 1
if text_size * (col * 2 - 1) <= width and text_size * row <= height:
break
else:
return None
bound = max(1 - text_size / width * (col - 1), 0)
full = np.random.rand() < self.full
fill = np.random.uniform(self.fill[0], self.fill[1])
fill = 1 if full else fill
fill = np.clip(fill, 0, bound)
padding = np.random.randint(4) if col > 1 else np.random.randint(1, 4)
padding = (bool(padding // 2), bool(padding % 2))
weights = np.zeros(col * 2 + 1)
weights[1:-1] = text_size / width
probs = 1 - np.random.rand(col * 2 + 1)
probs[0] = 0 if not padding[0] else probs[0]
probs[-1] = 0 if not padding[-1] else probs[-1]
probs[1::2] *= max(fill - sum(weights[1::2]), 0) / sum(probs[1::2])
probs[::2] *= max(1 - fill - sum(weights[::2]), 0) / sum(probs[::2])
weights += probs
widths = [width * weights[c] for c in range(col * 2 + 1)]
heights = [text_size for _ in range(row)]
xs = np.cumsum([0] + widths)
ys = np.cumsum([0] + heights)
layout = []
for c in range(col):
align = self.align[np.random.randint(len(self.align))]
for r in range(row):
x, y = xs[c * 2 + 1], ys[r]
w, h = xs[c * 2 + 2] - x, ys[r + 1] - y
bbox = [left + x, top + y, w, h]
layout.append((bbox, align))
return layout
"""
Donut
Copyright (c) 2022-present NAVER Corp.
MIT License
"""
import numpy as np
from layouts import Grid
class GridStack:
def __init__(self, config):
self.text_scale = config.get("text_scale", [0.05, 0.1])
self.max_row = config.get("max_row", 5)
self.max_col = config.get("max_col", 3)
self.fill = config.get("fill", [0, 1])
self.full = config.get("full", 0)
self.align = config.get("align", ["left", "right", "center"])
self.stack_spacing = config.get("stack_spacing", [0, 0.05])
self.stack_fill = config.get("stack_fill", [1, 1])
self.stack_full = config.get("stack_full", 0)
self._grid = Grid(
{
"text_scale": self.text_scale,
"max_row": self.max_row,
"max_col": self.max_col,
"align": self.align,
}
)
def generate(self, bbox):
left, top, width, height = bbox
stack_spacing = np.random.uniform(self.stack_spacing[0], self.stack_spacing[1])
stack_spacing *= min(width, height)
stack_full = np.random.rand() < self.stack_full
stack_fill = np.random.uniform(self.stack_fill[0], self.stack_fill[1])
stack_fill = 1 if stack_full else stack_fill
full = np.random.rand() < self.full
fill = np.random.uniform(self.fill[0], self.fill[1])
fill = 1 if full else fill
self._grid.fill = [fill, fill]
layouts = []
line = 0
while True:
grid_size = (width, height * stack_fill - line)
text_scale = np.random.uniform(self.text_scale[0], self.text_scale[1])
text_size = min(width, height) * text_scale
text_scale = text_size / min(grid_size)
self._grid.text_scale = [text_scale, text_scale]
layout = self._grid.generate([left, top + line, *grid_size])
if layout is None:
break
line = max(y + h - top for (_, y, _, h), _ in layout) + stack_spacing
layouts.append(layout)
line = max(line - stack_spacing, 0)
space = max(height - line, 0)
spaces = np.random.rand(len(layouts) + 1)
spaces *= space / sum(spaces) if sum(spaces) > 0 else 0
spaces = np.cumsum(spaces)
for layout, space in zip(layouts, spaces):
for bbox, _ in layout:
x, y, w, h = bbox
bbox[:] = [x, y + space, w, h]
return layouts
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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