Skip to content

Cameras

__init__(self, width=64, height=32, fov=30, colorsFadeWithDistance=1.0, sizeFadeWithDistance=0.8, reflectGround=True, reflectSky=False, max_range=1000, name='camera', samples=1, **kwargs) special

A camera device.

Parameters:

Name Type Description Default
width int

width of camera in pixels

64
height int

height of camera in pixels

32
fov float

width of camera field of view in degrees. Can be 180 or even 360 for wide angle cameras.

30
colorsFadeWithDistance float

colors get darker faster with larger value

1.0
sizeFadeWithDistance float

size gets smaller faster with with larger value

0.8
reflectGround bool

ground reflects for 3D point cloud

True
reflectSky bool

sky reflects for 3D point cloud

False
max_range int

maximum range of camera

1000
name str

the name of the camera

'camera'
samples int

how many pixels should it sample

1
Source code in robots/devices/cameras.py
def __init__(
    self,
    width=64,
    height=32,
    fov=30,
    colorsFadeWithDistance=1.0,
    sizeFadeWithDistance=0.8,
    reflectGround=True,
    reflectSky=False,
    max_range=1000,
    name="camera",
    samples=1,
    **kwargs
):
    """
    A camera device.

    Args:
        width (int): width of camera in pixels
        height (int): height of camera in pixels
        fov (float): width of camera field of view in degrees. Can be
            180 or even 360 for wide angle cameras.
        colorsFadeWithDistance (float): colors get darker
            faster with larger value
        sizeFadeWithDistance (float): size gets smaller faster
            with with larger value
        reflectGround (bool): ground reflects for 3D point cloud
        reflectSky (bool): sky reflects for 3D point cloud
        max_range (int): maximum range of camera
        name (str): the name of the camera
        samples (int): how many pixels should it sample

    Note: currently the camera faces forward.
    """
    config = {
        "width": width,
        "height": height,
        "fov": fov,
        "colorsFadeWithDistance": colorsFadeWithDistance,
        "sizeFadeWithDistance": sizeFadeWithDistance,
        "reflectGround": reflectGround,
        "reflectSky": reflectSky,
        "max_range": max_range,
        "name": name,
        "samples": samples,
    }
    config.update(kwargs)
    self._watcher = None
    self.robot = None
    self.initialize()
    self.from_json(config)

display(self, type='color')

Display an image of the camera.

Parameters:

Name Type Description Default
type str

the return type of image. Can be "color" or "depth"

'color'
Source code in robots/devices/cameras.py
def display(self, type="color"):
    """
    Display an image of the camera.

    Args:
        type (str): the return type of image. Can be "color"
            or "depth"
    """
    image = self.get_image(type=type)
    display(image)

draw(self, backend)

Draw the device on the backend.

Parameters:

Name Type Description Default
backend Backend

an aitk drawing backend

required
Source code in robots/devices/cameras.py
def draw(self, backend):
    """
    Draw the device on the backend.

    Args:
        backend (Backend): an aitk drawing backend

    NOTE: Currently, cameras are fixed at 0,0 and face forwards.
    """
    backend.set_fill(Color(0, 64, 0))
    backend.strokeStyle(None, 0)
    backend.draw_rect(5.0, -3.33, 1.33, 6.33)

    # Note angle in sim world is opposite in graphics:
    hits = self.robot.cast_ray(
        self.robot.x,
        self.robot.y,
        PI_OVER_2 - self.robot.a + self.fov / 2,
        self.max_range,
    )
    if hits:
        p = rotate_around(0, 0, hits[-1].distance, -self.fov / 2,)
    else:
        p = rotate_around(0, 0, self.max_range, -self.fov / 2,)
    backend.draw_line(0, 0, p[0], p[1])

    # Note angle in sim world is opposite in graphics:
    hits = self.robot.cast_ray(
        self.robot.x,
        self.robot.y,
        PI_OVER_2 - self.robot.a - self.fov / 2,
        self.max_range,
    )
    if hits:
        p = rotate_around(0, 0, hits[-1].distance, self.fov / 2,)
    else:
        p = rotate_around(0, 0, self.max_range, self.fov / 2,)
    backend.draw_line(0, 0, p[0], p[1])

from_json(self, config)

Set the settings from a device config.

Parameters:

Name Type Description Default
config dict

a config dictionary

required
Source code in robots/devices/cameras.py
def from_json(self, config):
    """
    Set the settings from a device config.

    Args:
        config (dict): a config dictionary
    """
    valid_keys = set([
        "width", "name", "height", "colorsFadeWithDistance",
        "sizeFadeWithDistance", "reflectGround", "reflectSky",
        "fov", "max_range", "samples", "position", "class"
    ])
    self.verify_config(valid_keys, config)

    if "width" in config:
        self.cameraShape[0] = config["width"]
    if "height" in config:
        self.cameraShape[1] = config["height"]

    if "colorsFadeWithDistance" in config:
        self.colorsFadeWithDistance = config["colorsFadeWithDistance"]
    if "sizeFadeWithDistance" in config:
        self.sizeFadeWithDistance = config["sizeFadeWithDistance"]
    if "reflectGround" in config:
        self.reflectGround = config["reflectGround"]
    if "reflectSky" in config:
        self.reflectSky = config["reflectSky"]
    if "fov" in config:
        self.set_fov(config["fov"])  # degrees
    if "max_range" in config:
        self.max_range = config["max_range"]
    if "samples" in config:
        self.samples = config["samples"]
    if "name" in config:
        self.name = config["name"]
    if "position" in config:
        self.position = config["position"]

get_fov(self)

Get the field of view angle in degrees.

Source code in robots/devices/cameras.py
def get_fov(self):
    """
    Get the field of view angle in degrees.
    """
    return self.fov * ONE80_OVER_PI

get_height(self)

Get the height in pixels of the camera.

Source code in robots/devices/cameras.py
def get_height(self):
    """
    Get the height in pixels of the camera.
    """
    return self.cameraShape[1]

get_image(self, type='color')

Get an image of the camera.

Parameters:

Name Type Description Default
type str

the return type of image. Can be "color" or "depth"

'color'
Source code in robots/devices/cameras.py
def get_image(self, type="color"):
    """
    Get an image of the camera.

    Args:
        type (str): the return type of image. Can be "color"
            or "depth"
    """
    try:
        from PIL import Image
    except ImportError:
        print("Pillow (PIL) module not available; get_image() unavailable")
        return

    # Lazy; only get the data when we need it:
    self._update()
    if self.robot.world._ground_image is not None:
        area = list(self._get_visible_area())
    else:
        area = None
    pic = Image.new("RGBA", (self.cameraShape[0], self.cameraShape[1]))
    pic_pixels = pic.load()
    # FIXME: probably should have a specific size rather than scale it to world
    size = max(self.robot.world.width, self.robot.world.height)
    hcolor = None
    # draw non-robot walls first:
    for i in range(self.cameraShape[0]):
        hits = [hit for hit in self.hits[i] if hit.height == 1.0]  # only walls
        if len(hits) == 0:
            continue
        hit = hits[-1]  # get closest
        high = None
        hcolor = None
        if hit:
            if self.fov < PI_OVER_2:
                # Orthoginal distance to camera:
                angle = hit.angle
                hit_distance = abs(hit.distance * math.sin(angle))
            else:
                hit_distance = hit.distance

            distance_ratio = 1.0 - hit_distance / size
            s = distance_ratio * self.sizeFadeWithDistance
            sc = distance_ratio * self.colorsFadeWithDistance
            if type == "color":
                r = hit.color.red * sc
                g = hit.color.green * sc
                b = hit.color.blue * sc
            elif type == "depth":
                r = 255 * distance_ratio
                g = 255 * distance_ratio
                b = 255 * distance_ratio
            else:
                avg = (hit.color.red + hit.color.green + hit.color.blue) / 3.0
                r = avg * sc
                g = avg * sc
                b = avg * sc
            hcolor = Color(r, g, b)
            high = (1.0 - s) * self.cameraShape[1]
        else:
            high = 0

        horizon = self.cameraShape[1] / 2
        for j in range(self.cameraShape[1]):
            dist = max(min(abs(j - horizon) / horizon, 1.0), 0.0)
            if j < high / 2:  # sky
                if type == "depth":
                    if self.reflectSky:
                        color = Color(255 * dist)
                    else:
                        color = Color(0)
                elif type == "color":
                    color = Color(0, 0, 128)
                else:
                    color = Color(128 / 3)
                pic_pixels[i, j] = color.to_tuple()
            elif j < self.cameraShape[1] - high / 2:  # hit
                if hcolor is not None:
                    pic_pixels[i, j] = hcolor.to_tuple()
            else:  # ground
                if type == "depth":
                    if self.reflectGround:
                        color = Color(255 * dist)
                    else:
                        color = Color(0)
                elif type == "color":
                    color = self._get_ground_color(area, i, j)
                else:
                    color = Color(128 / 3)
                pic_pixels[i, j] = color.to_tuple()

    # Other robots, draw on top of walls:
    self.obstacles = {}
    for i in range(self.cameraShape[0]):
        closest_wall_dist = self._find_closest_wall(self.hits[i])
        hits = [hit for hit in self.hits[i] if hit.height < 1.0]  # obstacles
        for hit in hits:
            if hit.distance > closest_wall_dist:
                # Behind wall, try next
                continue

            if self.fov < PI_OVER_2:
                angle = hit.angle
                hit_distance = abs(hit.distance * math.sin(angle))
            else: # perspective
                hit_distance = hit.distance

            distance_ratio = 4.0 - hit_distance / size
            s = distance_ratio * self.sizeFadeWithDistance
            sc = distance_ratio * self.colorsFadeWithDistance
            distance_to = self.cameraShape[1] / 2 * (4.0 - sc)
            # scribbler was 30, so 0.23 height ratio
            # height is ratio, 0 to 1
            height = round(hit.height * self.cameraShape[1] / 2.0 * s)
            if type == "color":
                r = hit.color.red * sc
                g = hit.color.green * sc
                b = hit.color.blue * sc
            elif type == "depth":
                r = 255 * distance_ratio
                g = 255 * distance_ratio
                b = 255 * distance_ratio
            else:
                avg = (hit.color.red + hit.color.green + hit.color.blue) / 3.0
                r = avg * sc
                g = avg * sc
                b = avg * sc
            hcolor = Color(r, g, b)
            horizon = self.cameraShape[1] / 2
            self._record_obstacle(
                hit.robot,
                i,
                self.cameraShape[1] - 1 - round(distance_to),
                self.cameraShape[1] - height - 1 - 1 - round(distance_to),
            )
            if not hit.robot.has_image():
                for j in range(height):
                    pic_pixels[
                        i, self.cameraShape[1] - j - 1 - round(distance_to)
                    ] = hcolor.to_tuple()
    if self.bulbs_are_visible:
        self._show_bulbs(pic)
    if self.food_is_visible:
        self._show_food(pic)
    self._show_obstacles(pic)
    return pic

get_max(self)

Get the maximum distance in CM the camera can see.

Source code in robots/devices/cameras.py
def get_max(self):
    """
    Get the maximum distance in CM the camera can see.
    """
    return self.max_range

get_name(self)

Get the name of the camera.

Source code in robots/devices/cameras.py
def get_name(self):
    """
    Get the name of the camera.
    """
    return self.name

get_point_cloud(self)

Get a 3D point cloud from the camera data.

Returns a list of [x, y, distance, red, green, blue] for each (x,y) of the camera.

Source code in robots/devices/cameras.py
def get_point_cloud(self):
    """
    Get a 3D point cloud from the camera data.

    Returns a list of [x, y, distance, red, green, blue]
    for each (x,y) of the camera.
    """
    depth_pic = self.get_image("depth")
    depth_pixels = depth_pic.load()
    color_pic = self.get_image("color")
    color_pixels = color_pic.load()
    points = []
    for x in range(self.cameraShape[0]):
        for y in range(self.cameraShape[1]):
            dist_color = depth_pixels[x, y]
            color = color_pixels[x, y]
            if dist_color[0] != 255:
                points.append(
                    [
                        self.cameraShape[0] - x - 1,
                        self.cameraShape[1] - y - 1,
                        dist_color[0],
                        color[0],
                        color[1],
                        color[2],
                    ]
                )
    return points

get_widget(self, **kwargs)

Return the dynamically updating widget.

Parameters:

Name Type Description Default
title str

title of device

required
Source code in robots/devices/cameras.py
def get_widget(self, **kwargs):
    """
    Return the dynamically updating widget.

    Args:
        title (str): title of device
    """
    from ..watchers import CameraWatcher

    if self._watcher is None:
        self._watcher = CameraWatcher(self, **kwargs)
        self.robot.world._watchers.append(self._watcher)
        return self._watcher.get_widget()
    else:
        return self._watcher.get_widget(**kwargs)

get_width(self)

Get the width in pixels of the camera.

Source code in robots/devices/cameras.py
def get_width(self):
    """
    Get the width in pixels of the camera.
    """
    return self.cameraShape[0]

initialize(self)

Internal method to set all settings to default values.

Source code in robots/devices/cameras.py
def initialize(self):
    """
    Internal method to set all settings to default values.
    """
    # FIXME: camera is fixed at (0,0) facing forward
    self.type = "camera"
    self.bulbs_are_visible = True
    self.food_is_visible = True
    self.time = 0.0
    self.cameraShape = [64, 32]
    self.position = [0, 0]
    self.max_range = 1000
    self.samples = 1
    self.name = "camera"
    # 0 = no fade, 1.0 = max fade
    self.colorsFadeWithDistance = 0.9
    self.sizeFadeWithDistance = 1.0
    self.reflectGround = True
    self.reflectSky = False
    self.set_fov(60)  # degrees
    self.reset()

reset(self)

Internal method to reset the camera data.

Source code in robots/devices/cameras.py
def reset(self):
    """
    Internal method to reset the camera data.
    """
    self.hits = [[] for i in range(self.cameraShape[0])]

set_fov(self, angle)

Set the field of view angle in degrees of the camera.

Parameters:

Name Type Description Default
angle float

angle in degrees of field of view

required
Source code in robots/devices/cameras.py
def set_fov(self, angle):
    """
    Set the field of view angle in degrees of the camera.

    Args:
        angle (float): angle in degrees of field of view
    """
    # given in degrees
    # save in radians
    # scale = min(max(angle / 6.0, 0.0), 1.0)
    self.fov = angle * PI_OVER_180
    # self.sizeFadeWithDistance = scale
    self.reset()

set_height(self, height)

Set the height of the camera in pixels.

Parameters:

Name Type Description Default
height int

height of camera in pixels

required
Source code in robots/devices/cameras.py
def set_height(self, height):
    """
    Set the height of the camera in pixels.

    Args:
        height (int): height of camera in pixels
    """
    self.cameraShape[1] = height
    self.reset()

set_max(self, max_range)

Set the maximum distance the camera can see.

Parameters:

Name Type Description Default
max_range float

distance (in CM) the camera can see

required
Source code in robots/devices/cameras.py
def set_max(self, max_range):
    """
    Set the maximum distance the camera can see.

    Args:
        max_range (float): distance (in CM) the camera can see
    """
    self.max_range = max_range

set_name(self, name)

Set the name of the camera.

Parameters:

Name Type Description Default
name str

the name of the camera

required
Source code in robots/devices/cameras.py
def set_name(self, name):
    """
    Set the name of the camera.

    Args:
        name (str): the name of the camera
    """
    self.name = name

set_size(self, width, height)

Set the height and width of the camera in pixels.

Parameters:

Name Type Description Default
width int

width of camera in pixels

required
height int

height of camera in pixels

required
Source code in robots/devices/cameras.py
def set_size(self, width, height):
    """
    Set the height and width of the camera in pixels.

    Args:
        width (int): width of camera in pixels
        height (int): height of camera in pixels
    """
    self.cameraShape[0] = width
    self.cameraShape[1] = height
    self.reset()

set_width(self, width)

Set the width of the camera in pixels.

Parameters:

Name Type Description Default
width int

width of camera in pixels

required
Source code in robots/devices/cameras.py
def set_width(self, width):
    """
    Set the width of the camera in pixels.

    Args:
        width (int): width of camera in pixels
    """
    self.cameraShape[0] = width
    self.reset()

to_json(self)

Save the internal settings to a config dictionary.

Source code in robots/devices/cameras.py
def to_json(self):
    """
    Save the internal settings to a config dictionary.
    """
    return {
        "class": self.__class__.__name__,
        "width": self.cameraShape[0],
        "height": self.cameraShape[1],
        "colorsFadeWithDistance": self.colorsFadeWithDistance,
        "sizeFadeWithDistance": self.sizeFadeWithDistance,
        "reflectGround": self.reflectGround,
        "reflectSky": self.reflectSky,
        "fov": self.fov * ONE80_OVER_PI,  # save in degrees
        "max_range": self.max_range,
        "samples": self.samples,
        "name": self.name,
        "position": self.position,
    }

update(self, draw_list=None)

Cameras operate in a lazy way: they don't actually update until needed because they are so expensive.

Parameters:

Name Type Description Default
draw_list list

optional. If given, then the method can add to it for drawing later.

None
Source code in robots/devices/cameras.py
def update(self, draw_list=None):
    """
    Cameras operate in a lazy way: they don't actually update
    until needed because they are so expensive.

    Args:
        draw_list (list): optional. If given, then the
            method can add to it for drawing later.
    """
    pass

watch(self, **kwargs)

Create a dynamically updating view of this device.

Parameters:

Name Type Description Default
title str

title of device

required
Source code in robots/devices/cameras.py
def watch(self, **kwargs):
    """
    Create a dynamically updating view
    of this device.

    Args:
        title (str): title of device
    """
    if self.robot is None or self.robot.world is None:
        raise Exception("can't watch device until added to robot, and robot is in world")

    # Make the watcher if doesn't exist:
    self.get_widget(**kwargs)
    self._watcher.watch()

__init__(self, width=15, height=15, name='ground-camera', **kwargs) special

A downward-facing camera device.

Parameters:

Name Type Description Default
width int

width of camera in pixels

15
height int

height of camera in pixels

15
name str

the name of the camera

'ground-camera'
Source code in robots/devices/cameras.py
def __init__(self, width=15, height=15, name="ground-camera", **kwargs):
    """
    A downward-facing camera device.

    Args:
        width (int): width of camera in pixels
        height (int): height of camera in pixels
        name (str): the name of the camera
    """
    config = {
        "width": width,
        "height": height,
        "name": name,
    }
    self._watcher = None
    self.robot = None
    self.initialize()
    self.from_json(config)

draw(self, backend)

Draw the device on the backend.

Parameters:

Name Type Description Default
backend Backend

an aitk drawing backend

required
Source code in robots/devices/cameras.py
def draw(self, backend):
    """
    Draw the device on the backend.

    Args:
        backend (Backend): an aitk drawing backend
    """
    left = -(self.cameraShape[0] // 2) / self.robot.world.scale
    upper = -(self.cameraShape[1] // 2) / self.robot.world.scale
    right = (self.cameraShape[0] // 2) / self.robot.world.scale
    lower = (self.cameraShape[1] // 2) / self.robot.world.scale
    backend.strokeStyle(Color(128), 1)
    backend.draw_line(left, upper, right, upper)
    backend.draw_line(right, upper, right, lower)
    backend.draw_line(right, lower, left, lower)
    backend.draw_line(left, lower, left, upper)

from_json(self, config)

Set the settings from a device config.

Parameters:

Name Type Description Default
config dict

a config dictionary

required
Source code in robots/devices/cameras.py
def from_json(self, config):
    """
    Set the settings from a device config.

    Args:
        config (dict): a config dictionary
    """
    if "width" in config:
        self.cameraShape[0] = config["width"]
    if "height" in config:
        self.cameraShape[1] = config["height"]
    if "name" in config:
        self.name = config["name"]

get_image(self, type=None)

Get an image of the camera.

Parameters:

Name Type Description Default
type str

the return type of image. Can be "color" or "depth"

None
Source code in robots/devices/cameras.py
def get_image(self, type=None):
    """
    Get an image of the camera.

    Args:
        type (str): the return type of image. Can be "color"
            or "depth"
    """
    # FIXME: would be faster to trim image down
    # before rotating
    center = (
        self.robot.x * self.robot.world.scale,
        self.robot.y * self.robot.world.scale,
    )
    rotated_image = self.robot.world._ground_image.rotate(
        (self.robot.a - math.pi / 4 * 6) * (ONE80_OVER_PI), center=center,
    )
    left = center[0] - self.cameraShape[0] // 2
    right = center[0] + self.cameraShape[0] // 2
    upper = center[1] - self.cameraShape[1] // 2
    lower = center[1] + self.cameraShape[1] // 2
    return rotated_image.crop((left, upper, right, lower))

initialize(self)

Internal method to set all settings to default values.

Source code in robots/devices/cameras.py
def initialize(self):
    """
    Internal method to set all settings to default values.
    """
    self.type = "ground-camera"
    self.time = 0.0
    self.cameraShape = [15, 15]
    self.name = "ground-camera"

to_json(self)

Save the internal settings to a config dictionary.

Source code in robots/devices/cameras.py
def to_json(self):
    """
    Save the internal settings to a config dictionary.
    """
    return {
        "class": self.__class__.__name__,
        "width": self.cameraShape[0],
        "height": self.cameraShape[1],
        "name": self.name,
    }

update(self, draw_list=None)

Update the device.

Parameters:

Name Type Description Default
draw_list list

optional. If given, then the method can add to it for drawing later.

None

Cameras operate in a lazy way: they don't actually update until needed because they are so expensive.

Source code in robots/devices/cameras.py
def update(self, draw_list=None):
    """
    Update the device.

    Args:
        draw_list (list): optional. If given, then the
            method can add to it for drawing later.

    Cameras operate in a lazy way: they don't actually update
    until needed because they are so expensive.
    """
    pass