Update simulation canvas to have colour so it works with background detect.

This commit is contained in:
Julian Stirling 2025-07-18 21:21:25 +01:00
parent 37a28af04a
commit 5701225158

View file

@ -28,6 +28,9 @@ from ..stage import BaseStage
# higher related to a faster movement # higher related to a faster movement
RATIO = 0.2 RATIO = 0.2
# Some colour variation, for bg detect.
BG_COLOR = [220, 215, 217]
class SimulatedCamera(BaseCamera): class SimulatedCamera(BaseCamera):
"""A Thing that simulates a camera for testing.""" """A Thing that simulates a camera for testing."""
@ -38,7 +41,7 @@ class SimulatedCamera(BaseCamera):
def __init__( def __init__(
self, self,
shape: tuple[int, int, int] = (600, 800, 3), shape: tuple[int, int, int] = (600, 800, 3),
glyph_shape: tuple[int, int, int] = (51, 51, 3), glyph_shape: tuple[int, int, int] = (91, 91, 3),
canvas_shape: tuple[int, int, int] = (3000, 4000, 3), canvas_shape: tuple[int, int, int] = (3000, 4000, 3),
frame_interval: float = 0.1, frame_interval: float = 0.1,
): ):
@ -64,18 +67,42 @@ class SimulatedCamera(BaseCamera):
def generate_sprites(self): def generate_sprites(self):
"""Generate sprites to populate the image.""" """Generate sprites to populate the image."""
sprite_sizes = [5, 7, 10, 21, 36, 40]
self.sprites = [] self.sprites = []
black = np.zeros(self.glyph_shape, dtype=np.uint8)
x = np.arange(black.shape[0]) channel_block = np.zeros(self.glyph_shape[0:2])
y = np.arange(black.shape[1]) x = np.arange(channel_block.shape[0])
rr = np.sqrt((x[:, None] - np.mean(x)) ** 2 + (y[None, :] - np.mean(y)) ** 2) y = np.arange(channel_block.shape[1])
for i in [5, 7, 9, 11, 13, 15]: # 2D grid of radii
sprite = black.copy() r_coord = np.sqrt(
sprite[rr < i] = 255 (x[:, None] - np.mean(x)) ** 2 + (y[None, :] - np.mean(y)) ** 2
self.sprites.append(sprite) )
for sprite_size in sprite_sizes:
# Mask of where this sprite is
sprite_mask = r_coord < sprite_size
# Calculate a sharp edged circle with value varying from 0 in centre to 1
# at the edge
sprite_px = r_coord[sprite_mask]
sprite_px -= np.min(sprite_px)
sprite_px /= np.max(sprite_px)
# Create each channel. Note these will be subtracted from the white value.
sprite_r = channel_block.copy()
sprite_r[sprite_mask] = 70 * sprite_px
sprite_g = channel_block.copy()
sprite_g[sprite_mask] = 200 * sprite_px
sprite_b = channel_block.copy()
sprite_b[sprite_mask] = 70 * sprite_px
# Stack into a negative image of the sprite
sprite = np.stack([sprite_r, sprite_g, sprite_b], axis=2)
# Convert to uint8 and append to the list
self.sprites.append(sprite.astype(np.uint8))
def generate_blobs(self, n_blobs: int = 1000): def generate_blobs(self, n_blobs: int = 1000):
"""Generate coordinates of blobs. """Generate coordinates of blobs and their sizes.
A 1000x3 array is returned. Each row represents (x,y) coordinate
of the sprite and the index representing the size of the sprite.
Blobs are characterised by X, Y, sprite Blobs are characterised by X, Y, sprite
We also generate a KD tree to rapidly find blobs in an image We also generate a KD tree to rapidly find blobs in an image
@ -88,15 +115,20 @@ class SimulatedCamera(BaseCamera):
self.blobs[:, 2] = rng.choice(len(self.sprites), n_blobs) self.blobs[:, 2] = rng.choice(len(self.sprites), n_blobs)
def generate_canvas(self): def generate_canvas(self):
"""Generate a blank canvas.""" """Generate a canvas."""
self.canvas = np.zeros(self.canvas_shape, dtype=np.uint8) self.canvas = np.ones(self.canvas_shape, dtype=np.int16)
self.canvas[...] = 255 self.canvas[:, :, 0] *= BG_COLOR[0]
self.canvas[:, :, 1] *= BG_COLOR[1]
self.canvas[:, :, 2] *= BG_COLOR[2]
w, h, _ = self.glyph_shape w, h, _ = self.glyph_shape
for x, y, sprite in self.blobs: for x, y, sprite_size_index in self.blobs:
self.canvas[ self.canvas[
int(x) - w // 2 : int(x) - w // 2 + w, int(x) - w // 2 : int(x) - w // 2 + w,
int(y) - h // 2 : int(y) - h // 2 + h, int(y) - h // 2 : int(y) - h // 2 + h,
] -= self.sprites[int(sprite)] ] -= self.sprites[int(sprite_size_index)]
self.canvas[self.canvas < 0] = 0
self.canvas[self.canvas > 255] = 255
self.canvas = self.canvas.astype(np.uint8)
def generate_image(self, pos: tuple[int, int, int]): def generate_image(self, pos: tuple[int, int, int]):
"""Generate an image with blobs based on supplied coordinates.""" """Generate an image with blobs based on supplied coordinates."""