///|
pub enum SamplingStrategy {
Uniform
Grid
Jittered
} derive(Debug, Eq)
///|
pub struct SamplingConfig {
count : Int
seed : UInt
strategy : SamplingStrategy
margin : Int
} derive(Debug, Eq)
///|
pub fn SamplingConfig::new(
count~ : Int,
seed~ : UInt,
strategy? : SamplingStrategy = Uniform,
margin? : Int = 0,
) -> SamplingConfig {
{ count: max_i(0, count), seed, strategy, margin: max_i(0, margin) }
}
///|
pub fn SamplingConfig::jittered(
count : Int,
seed : UInt,
margin : Int,
) -> SamplingConfig {
SamplingConfig::new(count~, seed~, strategy=Jittered, margin~)
}
///|
pub fn sample_points(size : Size, config : SamplingConfig) -> Array[Point] {
let out : Array[Point] = []
let count = config.count
let margin_x = min_i(config.margin, max_i(0, size.width / 2))
let margin_y = min_i(config.margin, max_i(0, size.height / 2))
match config.strategy {
Uniform => {
let rng = Rng::new(config.seed)
for _ in 0.. {
let columns = max_i(1, (count * 2 + 1) / max_i(1, size.height))
let rows = max_i(1, (count + columns - 1) / columns)
for y in 0.. {
let base = sample_points(
size,
SamplingConfig::new(
count~,
seed=config.seed,
strategy=Grid,
margin=config.margin,
),
)
let rng = Rng::new(config.seed + 17)
for point in base {
out.push(
Point::new(
x=clamp(
point.x + rng.signed(2),
low=0,
high=max_i(0, size.width - 1),
),
y=clamp(
point.y + rng.signed(2),
low=0,
high=max_i(0, size.height - 1),
),
),
)
}
}
}
out
}
///|
pub fn sample_rects(
size : Size,
config : SamplingConfig,
min_size : Size,
max_size : Size,
) -> Array[Rect] {
let out : Array[Rect] = []
let rng = Rng::new(config.seed)
let min_width = max_i(1, min_size.width)
let min_height = max_i(1, min_size.height)
let max_width = max_i(min_width, min_i(max_size.width, size.width))
let max_height = max_i(min_height, min_i(max_size.height, size.height))
for _ in 0.. Rect {
let dx = rng.signed(amount)
let dy = rng.signed(amount)
clamp_rect(rect.translate(dx, dy), size)
}
///|
pub fn jitter_boxes(
boxes : Array[Box2D],
seed : UInt,
amount : Int,
size : Size,
) -> Array[Box2D] {
let rng = Rng::new(seed)
let out : Array[Box2D] = []
for box in boxes {
out.push(
Box2D::new(
label=box.label,
rect=jitter_rect(box.rect, rng, amount, size),
occluded=box.occluded,
),
)
}
out
}
///|
pub fn boxes_from_rects(
rects : Array[Rect],
label : Label,
occluded? : Bool = false,
) -> Array[Box2D] {
let out : Array[Box2D] = []
for rect in rects {
out.push(Box2D::new(label~, rect~, occluded~))
}
out
}
///|
pub fn render_sampled_detection_scene(
size : Size,
config : SamplingConfig,
) -> SceneOutput {
let plan = ScenePlan::new(
name="sampled_detection",
kind=Detection,
size~,
seed=config.seed,
)
let image = gradient(size, horizontal=false)
let rects = sample_rects(
size,
config,
Size::new(width=4, height=4),
Size::new(width=max_i(4, size.width / 3), height=max_i(4, size.height / 3)),
)
let label = Label::new(id=1, name="sampled")
let boxes = boxes_from_rects(rects, label)
for box in boxes {
image.fill_rect(box.rect, Gray::new(80))
image.draw_rect(box.rect, Gray::white())
}
SceneOutput::new(plan~, image~, boxes~)
}
///|
pub fn sampling_manifest(
size : Size,
configs : Array[SamplingConfig],
prefix : String,
) -> DatasetManifest {
let manifest = DatasetManifest::new(name="sampled-vision-fixtures")
for i, config in configs {
manifest.add(
Asset::new(
name="sampled_\{i}_\{size.width}x\{size.height}",
image_path="\{prefix}/sampled_\{i}.pgm",
annotation_path="\{prefix}/sampled_\{i}.json",
seed=config.seed,
tags=["detection", "sampled", "synthetic"],
),
)
}
manifest
}