Skip to content

Real-time Fake Detection

Kano provides classes for simulating and visualizing object detection results, including smooth transitions between bounding boxes. The following classes are included:

  • FakeDetect: Simulates smooth transitions between two bounding boxes over a specified duration, ideal for testing object detection systems.
  • DetectGen: Manages and generates a sequence of fake detections with looping options like NoLoop, Reversed, and Replay for flexible simulation and testing.

kano.lab.box_gen.fake_detect.FakeDetect

Simulates an animated transition between two boxes over a specified duration.

Source code in kano\lab\box_gen\fake_detect.py
class FakeDetect:
    """
    Simulates an animated transition between two boxes over a specified duration.
    """

    def __init__(
        self,
        begin_box: Box,
        end_box: Box,
        duration: float,
        start_after: float = 0,
        from_bottom: bool = False,
    ) -> None:
        """
        Initializes the FakeDetect instance.

        Args:
            begin_box (Box): The starting box of the transition.
            end_box (Box): The ending box of the transition.
            duration (float): The duration of the transition in seconds.
            start_after (float): The delay before starting the transition. Default is 0.
            from_bottom (bool): Whether to use bottom-aligned coordinates. Default is False.
        """
        self.begin_box = begin_box
        self.end_box = end_box
        self.duration = duration
        self.start_after = start_after
        self.from_bottom = from_bottom
        self.start_time: float = None

    def reverse(self) -> None:
        """
        Reverses the transition by swapping the starting and ending boxes.
        """
        self.begin_box, self.end_box = self.end_box, self.begin_box

    def start(self, current_time: float) -> None:
        """
        Starts the transition at the specified current time.

        Args:
            current_time (float): The current time to start the transition.
        """
        self.start_time = current_time

    def _get_current_box(self, elapsed_time: float) -> Box:
        """
        Computes the intermediate box based on the elapsed time.

        Args:
            elapsed_time (float): The elapsed time since the transition started.

        Returns:
            Box: The interpolated box at the current state of the transition.
        """
        new_width = solve_equation(
            elapsed_time,
            self.duration,
            self.begin_box.width,
            self.end_box.width,
        )
        new_height = solve_equation(
            elapsed_time,
            self.duration,
            self.begin_box.height,
            self.end_box.height,
        )
        new_point = solve_equation(
            elapsed_time,
            self.duration,
            self.begin_box.point,
            self.end_box.point,
        )
        return Box(
            width=int(new_width),
            height=int(new_height),
            point=new_point,
        )

    def move(self, current_time: float) -> np.ndarray:
        """
        Computes the current box's coordinates in (xmin, ymin, xmax, ymax) format.

        Args:
            current_time (float): The current time used to compute the box's state.

        Returns:
            np.ndarray: The current box's coordinates, or None if the transition has not started or is finished.

        Raises:
            ValueError: If the start time is not set.
        """
        if self.start_time is None:
            raise ValueError(
                "Start time is not set. Call start() method first."
            )

        elapsed_time = current_time - self.start_time
        if (
            elapsed_time < self.start_after
            or elapsed_time > self.duration + self.start_after
        ):
            return None

        return self._get_current_box(elapsed_time).get_xyxy(self.from_bottom)

    def is_end(self, current_time: float) -> bool:
        """
        Determines whether the transition has finished.

        Args:
            current_time (float): The current time to check against the transition's end time.

        Returns:
            bool: True if the transition has ended, False otherwise.

        Raises:
            ValueError: If the start time is not set.
        """
        if self.start_time is None:
            raise ValueError(
                "Start time is not set. Call start() method first."
            )

        elapsed_time = current_time - self.start_time
        return elapsed_time > self.duration + self.start_after

__init__(begin_box, end_box, duration, start_after=0, from_bottom=False)

Initializes the FakeDetect instance.

Parameters:

Name Type Description Default
begin_box Box

The starting box of the transition.

required
end_box Box

The ending box of the transition.

required
duration float

The duration of the transition in seconds.

required
start_after float

The delay before starting the transition. Default is 0.

0
from_bottom bool

Whether to use bottom-aligned coordinates. Default is False.

False
Source code in kano\lab\box_gen\fake_detect.py
def __init__(
    self,
    begin_box: Box,
    end_box: Box,
    duration: float,
    start_after: float = 0,
    from_bottom: bool = False,
) -> None:
    """
    Initializes the FakeDetect instance.

    Args:
        begin_box (Box): The starting box of the transition.
        end_box (Box): The ending box of the transition.
        duration (float): The duration of the transition in seconds.
        start_after (float): The delay before starting the transition. Default is 0.
        from_bottom (bool): Whether to use bottom-aligned coordinates. Default is False.
    """
    self.begin_box = begin_box
    self.end_box = end_box
    self.duration = duration
    self.start_after = start_after
    self.from_bottom = from_bottom
    self.start_time: float = None

is_end(current_time)

Determines whether the transition has finished.

Parameters:

Name Type Description Default
current_time float

The current time to check against the transition's end time.

required

Returns:

Name Type Description
bool bool

True if the transition has ended, False otherwise.

Raises:

Type Description
ValueError

If the start time is not set.

Source code in kano\lab\box_gen\fake_detect.py
def is_end(self, current_time: float) -> bool:
    """
    Determines whether the transition has finished.

    Args:
        current_time (float): The current time to check against the transition's end time.

    Returns:
        bool: True if the transition has ended, False otherwise.

    Raises:
        ValueError: If the start time is not set.
    """
    if self.start_time is None:
        raise ValueError(
            "Start time is not set. Call start() method first."
        )

    elapsed_time = current_time - self.start_time
    return elapsed_time > self.duration + self.start_after

move(current_time)

Computes the current box's coordinates in (xmin, ymin, xmax, ymax) format.

Parameters:

Name Type Description Default
current_time float

The current time used to compute the box's state.

required

Returns:

Type Description
ndarray

np.ndarray: The current box's coordinates, or None if the transition has not started or is finished.

Raises:

Type Description
ValueError

If the start time is not set.

Source code in kano\lab\box_gen\fake_detect.py
def move(self, current_time: float) -> np.ndarray:
    """
    Computes the current box's coordinates in (xmin, ymin, xmax, ymax) format.

    Args:
        current_time (float): The current time used to compute the box's state.

    Returns:
        np.ndarray: The current box's coordinates, or None if the transition has not started or is finished.

    Raises:
        ValueError: If the start time is not set.
    """
    if self.start_time is None:
        raise ValueError(
            "Start time is not set. Call start() method first."
        )

    elapsed_time = current_time - self.start_time
    if (
        elapsed_time < self.start_after
        or elapsed_time > self.duration + self.start_after
    ):
        return None

    return self._get_current_box(elapsed_time).get_xyxy(self.from_bottom)

reverse()

Reverses the transition by swapping the starting and ending boxes.

Source code in kano\lab\box_gen\fake_detect.py
def reverse(self) -> None:
    """
    Reverses the transition by swapping the starting and ending boxes.
    """
    self.begin_box, self.end_box = self.end_box, self.begin_box

start(current_time)

Starts the transition at the specified current time.

Parameters:

Name Type Description Default
current_time float

The current time to start the transition.

required
Source code in kano\lab\box_gen\fake_detect.py
def start(self, current_time: float) -> None:
    """
    Starts the transition at the specified current time.

    Args:
        current_time (float): The current time to start the transition.
    """
    self.start_time = current_time

kano.lab.box_gen.detect_gen.DetectGen

Class to manage and generate a sequence of fake detections for animation or simulation purposes.

Source code in kano\lab\box_gen\detect_gen.py
class DetectGen:
    """
    Class to manage and generate a sequence of fake detections for animation or simulation purposes.
    """

    def __init__(
        self,
        boxes: List[Box],
        durations: List[float],
        start_after: float = 0,
        from_bottom: bool = False,
        loop_type: LoopType = LoopType.NoLoop,
    ) -> None:
        """
        Initializes the DetectGen object with the given boxes and durations.

        Args:
            boxes (List[Box]): A list of Box objects representing the sequence.
            durations (List[float]): Durations for transitions between boxes.
            start_after (float): Initial delay before starting the sequence. Default is 0.
            from_bottom (bool): Whether to use bottom-aligned coordinates. Default is False.
            loop_type (LoopType): Type of looping for the sequence. Default is NoLoop.

        Raises:
            InvalidEnumValueError: If the loop_type is not a valid LoopType.
        """
        if loop_type not in LoopType.__members__.values():
            raise InvalidEnumValueError(LoopType, loop_type)

        self.loop_type = loop_type
        self.fake_detects = self.get_sequence_detect(
            boxes, durations, start_after, from_bottom
        )
        self.start_time: float = None
        self.curr_i: int = 0
        self.stop: bool = False

    def start(self, current_time: float) -> None:
        """
        Starts the sequence at the specified current time.

        Args:
            current_time (float): The current time to start the sequence.
        """
        self.start_time = current_time
        self.fake_detects[0].start(current_time)

    def _update_i(self, current_time: float) -> None:
        """
        Updates the current index of the detection sequence based on the current time.

        Args:
            current_time (float): The current time used to update the sequence.
        """
        if not self.fake_detects[self.curr_i].is_end(current_time):
            return

        if self.curr_i < len(self.fake_detects) - 1:
            self.start_time += self.fake_detects[self.curr_i].duration
            self.curr_i += 1
            self.fake_detects[self.curr_i].start(self.start_time)
            return

        if self.loop_type == LoopType.NoLoop:
            self.stop = True
            return

        if self.loop_type == LoopType.Reversed:
            self.reverse_sequence()
            return

        if self.loop_type == LoopType.Replay:
            self.replay_sequence()
            return

    def reverse_sequence(self) -> None:
        """
        Reverses the sequence of detections.
        """
        self.fake_detects[0].start_after = 0
        self.start_time += self.fake_detects[self.curr_i].duration
        for fake_det in self.fake_detects:
            fake_det.reverse()
        self.fake_detects = self.fake_detects[::-1]
        self.curr_i = 0
        self.fake_detects[self.curr_i].start(self.start_time)

    def replay_sequence(self) -> None:
        """
        Replays the detection sequence from the beginning.
        """
        self.fake_detects[0].start_after = 0
        self.start_time += self.fake_detects[self.curr_i].duration
        self.curr_i = 0
        self.fake_detects[self.curr_i].start(self.start_time)

    def gen_xyxy(self, current_time: float) -> bool:
        """
        Generates the current (xmin, ymin, xmax, ymax) coordinates for the sequence.

        Args:
            current_time (float): The current time for coordinate generation.

        Returns:
            bool: True if the sequence has ended, otherwise False.
        """
        self._update_i(current_time)
        if self.stop:
            return True
        return self.fake_detects[self.curr_i].move(current_time)

    def get_sequence_detect(
        self,
        boxes: List[Box],
        durations: List[float],
        start_after: float = 0,
        from_bottom: bool = False,
    ) -> List[FakeDetect]:
        """
        Creates a sequence of FakeDetect objects based on the provided boxes and durations.

        Args:
            boxes (List[Box]): A list of Box objects representing the sequence.
            durations (List[float]): A list of durations for transitions between boxes.
            start_after (float): Initial delay before starting the sequence. Default is 0.
            from_bottom (bool): Whether to use bottom-aligned coordinates. Default is False.

        Returns:
            List[FakeDetect]: A list of FakeDetect objects for the sequence.

        Raises:
            ValueError: If there are fewer than 2 boxes or if the number of durations is invalid.
        """
        if len(boxes) < 2:
            raise ValueError("Need at least 2 boxes")

        if len(boxes) != len(durations) + 1:
            raise ValueError(
                f"The number of boxes must be exactly one more than the number of durations. "
                f"Got {len(boxes)} boxes and {len(durations)} durations."
            )

        fake_detects = []
        for i in range(len(boxes) - 1):
            fake_detects.append(
                FakeDetect(
                    begin_box=boxes[i],
                    end_box=boxes[i + 1],
                    duration=durations[i],
                    start_after=start_after,
                    from_bottom=from_bottom,
                )
            )
        return fake_detects

__init__(boxes, durations, start_after=0, from_bottom=False, loop_type=LoopType.NoLoop)

Initializes the DetectGen object with the given boxes and durations.

Parameters:

Name Type Description Default
boxes List[Box]

A list of Box objects representing the sequence.

required
durations List[float]

Durations for transitions between boxes.

required
start_after float

Initial delay before starting the sequence. Default is 0.

0
from_bottom bool

Whether to use bottom-aligned coordinates. Default is False.

False
loop_type LoopType

Type of looping for the sequence. Default is NoLoop.

NoLoop

Raises:

Type Description
InvalidEnumValueError

If the loop_type is not a valid LoopType.

Source code in kano\lab\box_gen\detect_gen.py
def __init__(
    self,
    boxes: List[Box],
    durations: List[float],
    start_after: float = 0,
    from_bottom: bool = False,
    loop_type: LoopType = LoopType.NoLoop,
) -> None:
    """
    Initializes the DetectGen object with the given boxes and durations.

    Args:
        boxes (List[Box]): A list of Box objects representing the sequence.
        durations (List[float]): Durations for transitions between boxes.
        start_after (float): Initial delay before starting the sequence. Default is 0.
        from_bottom (bool): Whether to use bottom-aligned coordinates. Default is False.
        loop_type (LoopType): Type of looping for the sequence. Default is NoLoop.

    Raises:
        InvalidEnumValueError: If the loop_type is not a valid LoopType.
    """
    if loop_type not in LoopType.__members__.values():
        raise InvalidEnumValueError(LoopType, loop_type)

    self.loop_type = loop_type
    self.fake_detects = self.get_sequence_detect(
        boxes, durations, start_after, from_bottom
    )
    self.start_time: float = None
    self.curr_i: int = 0
    self.stop: bool = False

gen_xyxy(current_time)

Generates the current (xmin, ymin, xmax, ymax) coordinates for the sequence.

Parameters:

Name Type Description Default
current_time float

The current time for coordinate generation.

required

Returns:

Name Type Description
bool bool

True if the sequence has ended, otherwise False.

Source code in kano\lab\box_gen\detect_gen.py
def gen_xyxy(self, current_time: float) -> bool:
    """
    Generates the current (xmin, ymin, xmax, ymax) coordinates for the sequence.

    Args:
        current_time (float): The current time for coordinate generation.

    Returns:
        bool: True if the sequence has ended, otherwise False.
    """
    self._update_i(current_time)
    if self.stop:
        return True
    return self.fake_detects[self.curr_i].move(current_time)

get_sequence_detect(boxes, durations, start_after=0, from_bottom=False)

Creates a sequence of FakeDetect objects based on the provided boxes and durations.

Parameters:

Name Type Description Default
boxes List[Box]

A list of Box objects representing the sequence.

required
durations List[float]

A list of durations for transitions between boxes.

required
start_after float

Initial delay before starting the sequence. Default is 0.

0
from_bottom bool

Whether to use bottom-aligned coordinates. Default is False.

False

Returns:

Type Description
List[FakeDetect]

List[FakeDetect]: A list of FakeDetect objects for the sequence.

Raises:

Type Description
ValueError

If there are fewer than 2 boxes or if the number of durations is invalid.

Source code in kano\lab\box_gen\detect_gen.py
def get_sequence_detect(
    self,
    boxes: List[Box],
    durations: List[float],
    start_after: float = 0,
    from_bottom: bool = False,
) -> List[FakeDetect]:
    """
    Creates a sequence of FakeDetect objects based on the provided boxes and durations.

    Args:
        boxes (List[Box]): A list of Box objects representing the sequence.
        durations (List[float]): A list of durations for transitions between boxes.
        start_after (float): Initial delay before starting the sequence. Default is 0.
        from_bottom (bool): Whether to use bottom-aligned coordinates. Default is False.

    Returns:
        List[FakeDetect]: A list of FakeDetect objects for the sequence.

    Raises:
        ValueError: If there are fewer than 2 boxes or if the number of durations is invalid.
    """
    if len(boxes) < 2:
        raise ValueError("Need at least 2 boxes")

    if len(boxes) != len(durations) + 1:
        raise ValueError(
            f"The number of boxes must be exactly one more than the number of durations. "
            f"Got {len(boxes)} boxes and {len(durations)} durations."
        )

    fake_detects = []
    for i in range(len(boxes) - 1):
        fake_detects.append(
            FakeDetect(
                begin_box=boxes[i],
                end_box=boxes[i + 1],
                duration=durations[i],
                start_after=start_after,
                from_bottom=from_bottom,
            )
        )
    return fake_detects

replay_sequence()

Replays the detection sequence from the beginning.

Source code in kano\lab\box_gen\detect_gen.py
def replay_sequence(self) -> None:
    """
    Replays the detection sequence from the beginning.
    """
    self.fake_detects[0].start_after = 0
    self.start_time += self.fake_detects[self.curr_i].duration
    self.curr_i = 0
    self.fake_detects[self.curr_i].start(self.start_time)

reverse_sequence()

Reverses the sequence of detections.

Source code in kano\lab\box_gen\detect_gen.py
def reverse_sequence(self) -> None:
    """
    Reverses the sequence of detections.
    """
    self.fake_detects[0].start_after = 0
    self.start_time += self.fake_detects[self.curr_i].duration
    for fake_det in self.fake_detects:
        fake_det.reverse()
    self.fake_detects = self.fake_detects[::-1]
    self.curr_i = 0
    self.fake_detects[self.curr_i].start(self.start_time)

start(current_time)

Starts the sequence at the specified current time.

Parameters:

Name Type Description Default
current_time float

The current time to start the sequence.

required
Source code in kano\lab\box_gen\detect_gen.py
def start(self, current_time: float) -> None:
    """
    Starts the sequence at the specified current time.

    Args:
        current_time (float): The current time to start the sequence.
    """
    self.start_time = current_time
    self.fake_detects[0].start(current_time)