Unify canvas and sample shape for simulation, set flag for if repeating.
This commit is contained in:
parent
99f7802195
commit
159e2d14ab
1 changed files with 32 additions and 31 deletions
|
|
@ -79,37 +79,38 @@ class SimulatedCamera(BaseCamera):
|
||||||
self,
|
self,
|
||||||
thing_server_interface: lt.ThingServerInterface,
|
thing_server_interface: lt.ThingServerInterface,
|
||||||
shape: tuple[int, int, int] = (616, 820, 3),
|
shape: tuple[int, int, int] = (616, 820, 3),
|
||||||
glyph_size: int = 121,
|
canvas_shape: tuple[int, int, int] = (1500, 2000, 3),
|
||||||
canvas_shape: tuple[int, int, int] = (3000, 4000, 3),
|
repeating: bool = True,
|
||||||
sample_limits: Optional[tuple[int, int]] = (1000, 1500),
|
blob_density: int = 300,
|
||||||
frame_interval: float = 0.1,
|
frame_interval: float = 0.1,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialise the simulated with settings for how images are generated.
|
"""Initialise the simulated with settings for how images are generated.
|
||||||
|
|
||||||
:param shape: The shape (size) of the generated image.
|
:param shape: The shape (size) of the generated image.
|
||||||
:param glyph_size: The size randomly positioned glyphs.
|
|
||||||
:param canvas_shape: The shape (size) of the canvas generated on initialisation
|
:param canvas_shape: The shape (size) of the canvas generated on initialisation
|
||||||
that images are cropped from. If this is too large the it uses resources,
|
that images are cropped from. If this is too large the it uses resources,
|
||||||
but its size limits the range of motion of the simulation.
|
but its size limits the range of motion of the simulation.
|
||||||
:param sample_limits: The shape of the sample. Outside this range, the
|
:param repeating: If set True, outside the canvas, the
|
||||||
camera won't generate any blobs, preventing scanning from running
|
camera won't generate any blobs, preventing scanning from running
|
||||||
indefinitely and better demonstrating background detect.
|
indefinitely allowing testing of demonstrating background detect. If False
|
||||||
|
the canvas will repeat.
|
||||||
|
:param blob_density: The number of blobs per million pixels.
|
||||||
:param frame_interval: Nominally the time between frames on the MJPEG stream,
|
:param frame_interval: Nominally the time between frames on the MJPEG stream,
|
||||||
however the rate may be slower due to calculation time for focus.
|
however the rate may be slower due to calculation time for focus.
|
||||||
"""
|
"""
|
||||||
super().__init__(thing_server_interface)
|
super().__init__(thing_server_interface)
|
||||||
self.shape = shape
|
self.shape = shape
|
||||||
self.ds_shape = _downsample_shape(shape)
|
self.ds_shape = _downsample_shape(shape)
|
||||||
self.glyph_size = glyph_size // DOWNSAMPLE
|
self.glyph_size = 101 // DOWNSAMPLE
|
||||||
self.canvas_shape = _downsample_shape(canvas_shape)
|
self.canvas_shape = _downsample_shape(canvas_shape)
|
||||||
sample_limits = canvas_shape[:2] if sample_limits is None else sample_limits
|
self.repeating = repeating
|
||||||
self.sample_limits = _downsample_shape(sample_limits)
|
|
||||||
self.frame_interval = frame_interval
|
self.frame_interval = frame_interval
|
||||||
self._capture_thread: Optional[Thread] = None
|
self._capture_thread: Optional[Thread] = None
|
||||||
self._capture_enabled = False
|
self._capture_enabled = False
|
||||||
self.validate_inputs()
|
|
||||||
self.generate_sprites()
|
self.generate_sprites()
|
||||||
self.generate_blobs()
|
self.generate_blobs(
|
||||||
|
int(blob_density * 1e-6 * canvas_shape[0] * canvas_shape[1])
|
||||||
|
)
|
||||||
self.generate_canvas()
|
self.generate_canvas()
|
||||||
|
|
||||||
@lt.property
|
@lt.property
|
||||||
|
|
@ -117,18 +118,6 @@ class SimulatedCamera(BaseCamera):
|
||||||
"""Whether the camera needs calibrating."""
|
"""Whether the camera needs calibrating."""
|
||||||
return not self.background_detector_status.ready
|
return not self.background_detector_status.ready
|
||||||
|
|
||||||
def validate_inputs(self) -> None:
|
|
||||||
"""Validate the inputs passed to the simulation, and raises an error if invalid.
|
|
||||||
|
|
||||||
Currently only tests that the sample size is not greater than the canvas size in any dimension.
|
|
||||||
"""
|
|
||||||
# Iterate through elements in both tuples. As strict is False, will use the shorter of the two tuples
|
|
||||||
for a, b in zip(self.canvas_shape, self.sample_limits, strict=False):
|
|
||||||
if a < b:
|
|
||||||
raise ValueError(
|
|
||||||
"Canvas size must be bigger than or equal to canvas size"
|
|
||||||
)
|
|
||||||
|
|
||||||
def generate_sprites(self) -> None:
|
def generate_sprites(self) -> None:
|
||||||
"""Generate sprites to populate the image."""
|
"""Generate sprites to populate the image."""
|
||||||
sprite_sizes = [10, 21, 36, 40, 50]
|
sprite_sizes = [10, 21, 36, 40, 50]
|
||||||
|
|
@ -182,8 +171,8 @@ class SimulatedCamera(BaseCamera):
|
||||||
self.blobs = np.zeros((n_blobs, 3))
|
self.blobs = np.zeros((n_blobs, 3))
|
||||||
w = self.glyph_size
|
w = self.glyph_size
|
||||||
|
|
||||||
self.blobs[:, 0] = RNG.uniform(w // 2, self.sample_limits[1] - w // 2, n_blobs)
|
self.blobs[:, 0] = RNG.uniform(w // 2, self.canvas_shape[1] - w // 2, n_blobs)
|
||||||
self.blobs[:, 1] = RNG.uniform(w // 2, self.sample_limits[0] - w // 2, n_blobs)
|
self.blobs[:, 1] = RNG.uniform(w // 2, self.canvas_shape[0] - w // 2, n_blobs)
|
||||||
self.blobs[:, 2] = RNG.choice(len(self.sprites), n_blobs)
|
self.blobs[:, 2] = RNG.choice(len(self.sprites), n_blobs)
|
||||||
|
|
||||||
def generate_canvas(self) -> None:
|
def generate_canvas(self) -> None:
|
||||||
|
|
@ -242,13 +231,25 @@ class SimulatedCamera(BaseCamera):
|
||||||
)
|
)
|
||||||
|
|
||||||
top_left = (
|
top_left = (
|
||||||
int(im_pos[0]) - image_width // 2 + self.sample_limits[0] // 2,
|
int(im_pos[0]) - image_width // 2 + self.canvas_shape[0] // 2,
|
||||||
int(im_pos[1]) - image_height // 2 + self.sample_limits[1] // 2,
|
int(im_pos[1]) - image_height // 2 + self.canvas_shape[1] // 2,
|
||||||
)
|
)
|
||||||
# Create index list with modulo rather than slicing to handle wrapping at the
|
|
||||||
# canvas edge.
|
x_indices = np.arange(top_left[0], top_left[0] + image_width)
|
||||||
x_indices = (np.arange(top_left[0], top_left[0] + image_width)) % canvas_width
|
y_indices = np.arange(top_left[1], top_left[1] + image_height)
|
||||||
y_indices = (np.arange(top_left[1], top_left[1] + image_height)) % canvas_height
|
|
||||||
|
if self.repeating:
|
||||||
|
# Create index list with modulo rather than slicing to handle wrapping at the
|
||||||
|
# canvas edge.
|
||||||
|
x_indices = x_indices % canvas_width
|
||||||
|
y_indices = y_indices % canvas_height
|
||||||
|
else:
|
||||||
|
# No sprites are placed right at the edge of the image so that the whole
|
||||||
|
# sprite is on the canvas. For a non-repeating image just clip to the
|
||||||
|
# first or last pixel.
|
||||||
|
x_indices = np.clip(x_indices, 0, canvas_width - 1)
|
||||||
|
y_indices = np.clip(y_indices, 0, canvas_height - 1)
|
||||||
|
|
||||||
z_indices = np.arange(self.ds_shape[2])
|
z_indices = np.arange(self.ds_shape[2])
|
||||||
canvas = self.canvas if self._show_sample else self.blank_canvas
|
canvas = self.canvas if self._show_sample else self.blank_canvas
|
||||||
# Use npx to make each 1d index list 3D
|
# Use npx to make each 1d index list 3D
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue