Exemple #1
0
def match_track(gen_track, expected_tracks):
    score = None
    match = None
    MAX_ERROR = 8
    for track in expected_tracks:
        start_diff = abs(track.start - gen_track.start_s)

        gen_start = gen_track.bounds_history[0]
        distance = tools.eucl_distance(
            (track.start_pos.mid_x, track.start_pos.mid_y),
            (gen_start.mid_x, gen_start.mid_y),
        )
        distance += tools.eucl_distance(
            (track.start_pos.x, track.start_pos.y), (gen_start.x, gen_start.y)
        )
        distance += tools.eucl_distance(
            (track.start_pos.right, track.start_pos.bottom),
            (gen_start.right, gen_start.bottom),
        )
        distance /= 3.0
        distance = math.sqrt(distance)

        # makes it more comparable to start error
        distance /= 4.0
        new_score = distance + start_diff
        if new_score > MAX_ERROR:
            continue
        if score is None or new_score < score:
            match = track
            score = new_score
    return match
Exemple #2
0
    def get_track_region_score(self, region: Region, moving_vel_thresh):
        """
        Calculates a score between this track and a region of interest.  Regions that are close the the expected
        location for this track are given high scores, as are regions of a similar size.
        """

        if abs(self.vel_x) + abs(self.vel_y) >= moving_vel_thresh:
            expected_x = int(self.last_bound.mid_x + self.vel_x)
            expected_y = int(self.last_bound.mid_y + self.vel_y)
            distance = eucl_distance((expected_x, expected_y),
                                     (region.mid_x, region.mid_y))
        else:
            expected_x = int(self.last_bound.x + self.vel_x)
            expected_y = int(self.last_bound.y + self.vel_y)
            distance = eucl_distance((expected_x, expected_y),
                                     (region.x, region.y))
            distance += eucl_distance(
                (
                    expected_x + self.last_bound.width,
                    expected_y + self.last_bound.height,
                ),
                (region.x + region.width, region.y + region.height),
            )
            distance /= 2.0

        # ratio of 1.0 = 20 points, ratio of 2.0 = 10 points, ratio of 3.0 = 0 points.
        # area is padded with 50 pixels so small regions don't change too much
        size_difference = (abs(region.area - self.last_bound.area) /
                           (self.last_bound.area + 50)) * 100

        return distance, size_difference
Exemple #3
0
 def average_distance(self, other):
     """Calculates the distance between 2 regions by using the distance between
     (top, left), mid points and (bottom,right) of each region
     """
     expected_x = int(other.mid_x)
     expected_y = int(other.mid_y)
     distance = tools.eucl_distance(
         (expected_x, expected_y), (self.mid_x, self.mid_y)
     )
     expected_x = int(other.x)
     expected_y = int(other.y)
     distance += tools.eucl_distance((expected_x, expected_y), (self.x, self.y))
     distance += tools.eucl_distance(
         (
             other.right,
             other.bottom,
         ),
         (self.right, self.bottom),
     )
     distance /= 3.0
     return distance
    def get_stats(self):
        """
        Returns statistics for this track, including how much it moves, and a score indicating how likely it is
        that this is a good track.
        :return: a TrackMovementStatistics record
        """

        if len(self) <= 1:
            return TrackMovementStatistics()
        # get movement vectors only from non blank regions
        non_blank = [bound for bound in self.bounds_history if not bound.blank]
        mass_history = [int(bound.mass) for bound in non_blank]
        variance_history = [
            bound.pixel_variance for bound in non_blank if bound.pixel_variance
        ]
        movement = 0
        max_offset = 0

        frames_moved = 0
        avg_vel = 0
        first_point = self.bounds_history[0].mid
        for i, (vx, vy) in enumerate(zip(self.vel_x, self.vel_y)):
            region = self.bounds_history[i]
            if not region.blank:
                avg_vel += abs(vx) + abs(vy)
            if i == 0:
                continue

            if region.blank or self.bounds_history[i - 1].blank:
                continue
            if region.has_moved(
                    self.bounds_history[i - 1]) or region.is_along_border:
                distance = (vx**2 + vy**2)**0.5
                movement += distance
                offset = eucl_distance(first_point, region.mid)
                max_offset = max(max_offset, offset)
                frames_moved += 1
        avg_vel = avg_vel / len(mass_history)
        # the standard deviation is calculated by averaging the per frame variances.
        # this ends up being slightly different as I'm using /n rather than /(n-1) but that
        # shouldn't make a big difference as n = width*height*frames which is large.
        max_offset = math.sqrt(max_offset)
        delta_std = float(np.mean(variance_history))**0.5
        jitter_bigger = 0
        jitter_smaller = 0
        for i, bound in enumerate(self.bounds_history[1:]):
            prev_bound = self.bounds_history[i]
            if prev_bound.is_along_border or bound.is_along_border:
                continue
            height_diff = bound.height - prev_bound.height
            width_diff = prev_bound.width - bound.width
            thresh_h = max(Track.MIN_JITTER_CHANGE,
                           prev_bound.height * Track.JITTER_THRESHOLD)
            thresh_v = max(Track.MIN_JITTER_CHANGE,
                           prev_bound.width * Track.JITTER_THRESHOLD)
            if abs(height_diff) > thresh_h:
                if height_diff > 0:
                    jitter_bigger += 1
                else:
                    jitter_smaller += 1
            elif abs(width_diff) > thresh_v:
                if width_diff > 0:
                    jitter_bigger += 1
                else:
                    jitter_smaller += 1

        movement_points = (movement**0.5) + max_offset
        delta_points = delta_std * 25.0
        jitter_percent = int(
            round(100 * (jitter_bigger + jitter_smaller) / float(self.frames)))
        blank_percent = int(round(100.0 * self.blank_frames / self.frames))
        score = (min(movement_points, 100) + min(delta_points, 100) +
                 (100 - jitter_percent) + (100 - blank_percent))
        stats = TrackMovementStatistics(
            movement=float(movement),
            max_offset=float(max_offset),
            average_mass=float(np.mean(mass_history)),
            median_mass=float(np.median(mass_history)),
            delta_std=float(delta_std),
            score=float(score),
            region_jitter=jitter_percent,
            jitter_bigger=jitter_bigger,
            jitter_smaller=jitter_smaller,
            blank_percent=blank_percent,
            frames_moved=frames_moved,
            mass_std=float(np.std(mass_history)),
            average_velocity=float(avg_vel),
        )

        return stats
Exemple #5
0
def movement_images(
    frames,
    regions,
    dim,
    channel=TrackChannels.filtered,
    require_movement=False,
):
    """Return 2 images describing the movement, one has dots representing
    the centre of mass, the other is a collage of all frames
    """

    i = 0
    dots = np.zeros(dim)
    overlay = np.zeros(dim)

    prev = None
    prev_overlay = None
    line_colour = 60
    dot_colour = 120

    img = Image.fromarray(np.uint8(dots))

    d = ImageDraw.Draw(img)
    # draw movment lines and draw frame overlay
    center_distance = 0
    min_distance = 2
    for i, frame in enumerate(frames):
        region = regions[i]

        x = int(region.mid_x)
        y = int(region.mid_y)

        # writing dot image
        if prev is not None:
            d.line(prev + (x, y), fill=line_colour, width=1)
        prev = (x, y)

        # writing overlay image
        if require_movement and prev_overlay:
            center_distance = eucl_distance(
                prev_overlay,
                (
                    x,
                    y,
                ),
            )

        if (prev_overlay is None
                or center_distance > min_distance) or not require_movement:
            frame = frame[channel]
            subimage = region.subimage(overlay)
            subimage[:, :] += np.float32(frame)
            center_distance = 0
            min_distance = pow(region.width / 2.0, 2)
            prev_overlay = (x, y)

    # then draw dots to dot image so they go over the top
    for i, frame in enumerate(frames):
        region = regions[i]
        x = int(region.mid_x)
        y = int(region.mid_y)
        d.point([(x, y)], fill=dot_colour)

    return np.array(img), overlay