如何跟踪具有特定颜色的矩形

时间:2016-05-08 06:39:08

标签: python c++ image opencv python-imaging-library

我只找到了面部追踪的样本。

如何跟踪具有特定颜色的矩形?

我需要它的像素。

enter image description here

2 个答案:

答案 0 :(得分:1)

这是一个可以执行此操作的脚本:

from PIL import Image
from math import sqrt

RED = 0
GREEN = 1
BLUE = 2
COLORS = [RED, GREEN, BLUE]
RED_HEALTH = (234, 105, 112)
BLUE_HEALTH = (84, 165, 226)
HEALTH_BORDER = (0, 0, 0)
image = Image.open("image.jpg")

def close_enough_to(src, target, delta):
    diff = 0
    for color in COLORS:
        diff += (src[color] - target[color]) ** 2
    diff = sqrt(diff)
    return diff <= delta

class HealthBar:
    def __init__(self, team, health_percentage, length, pos):
        self.team = team
        self.health_percentage = health_percentage
        self.length = length
        self.pos = pos

    def __str__(self):
        return "team {}, percentage {}, length {}, pos {}".format(self.team,
                self.health_percentage,
                self.length,
                self.pos
                )

    def __repr__(self):
        return str(self)

def flood_fill_health_bar(image, pos, color, traversed):
    (x, y) = pos
    health_pixels = 0
    while close_enough_to(image.getpixel((x, y)), color) \
        and (x, y) not in traversed:
        health_pixels += 1
        traversed.add((x, y))
        x += 1
    black_pixels = 0
    while close_enough_to(image.getpixel((x, y)), HEALTH_BORDER, 50) \
        and (x, y) not in traversed:
        black_pixels += 1
        traversed.add((x, y))
        x += 1
    if black_pixels > 0:
        if color is RED_HEALTH:
            team = "red" 
        else:
            team = "blue"
        percent_health = health_pixels / (health_pixels + black_pixels)
        return HealthBar(team, percent_health, health_pixels + black_pixels, pos)

def in_bounds(image, pos):
    return pos[0] >= 0 and pos[1] >= 0 \
            and pos[0] < image.width and pos[1] < image.height

def flood_fill_image(image, start, delta):
    flood_fill_queue = [start]
    traversed = []
    color = image.getpixel(start)
    pos = start
    pix = image.load()
    while len(flood_fill_queue):
        (x, y) = flood_fill_queue.pop()
        positions = [(x+1, y), (x-1, y), (x, y+1), (x, y-1)]
        for position in positions:
            if in_bounds(image, position) \
                    and close_enough_to(image.getpixel(position), color, delta):
                if position not in traversed:
                    flood_fill_queue.append(position)
                    traversed.append(position)
                    (x, y) = position
                    pix[x, y] = (0, 0, 255)
    return traversed

def get_width(positions):
    return get_max_x(positions) - get_min_x(positions)

def get_height(positions):
    return get_max_y(positions) - get_min_y(positions)

def get_max_x(positions):
    return sorted(list(positions), key=lambda x: x[0])[-1][0]

def get_max_y(positions):
    return sorted(list(positions), key=lambda x: x[1])[-1][1]

def get_min_x(positions):
    return sorted(list(positions), key=lambda x: x[0])[0][0]

def get_min_y(positions):
    return sorted(list(positions), key=lambda x: x[1])[0][1]

def find_health_bars(image):
    traversed = set()
    health_bars = []
    pix = image.load()
    (width, height) = image.size
    for col in range(0, width):
        for row in range(0, height):
            #  pix = image.getpixel((col, row))
            if (col, row) in traversed:
                continue 
            for health_color in [RED_HEALTH, BLUE_HEALTH]:
                border_pixels = []
                if close_enough_to(image.getpixel((col, row)), health_color, 10):
                    health_pixels = flood_fill_image(image, (col, row), 100)
                    for pos in health_pixels:
                        (x, y) = pos
                        traversed.add(pos)
                        pix[x, y] = (255, 255, 0)
                    border_pixels = flood_fill_image(image, (col - 1, row - 1), 30)
                    if len(border_pixels) is 0:
                        continue
                    health_bar_width = get_width(border_pixels)
                    health_bar_height = get_height(border_pixels)
                    health_width = get_width(health_pixels)
                    if abs(health_bar_width / health_bar_height) - 10 <= 0.5:
                        team = "blue" if health_color == BLUE_HEALTH else "red"
                        percent_health = health_width / health_bar_width
                        health_bar = HealthBar(team, percent_health, health_bar_width, (col, row))
                        health_bars.append(health_bar)

                for pos in border_pixels:
                    (x, y) = pos
                    traversed.add(pos)
                    pix[x, y] = (0, 255, 255)
    health_bars = [health_bar for health_bar in health_bars if health_bar is not None]
    health_bars.sort(key=lambda x: x.length)
    return health_bars

health_bars = find_health_bars(image)

print(health_bars)

基本上这是算法:

  1. 遍历整个图像,查找健康栏的红色/蓝色
  2. 一旦我们找到,运行一个超级hacky泛洪填充功能,找到健康栏占用的坐标。
  3. 运行相同的洪水填充功能以获取运行状况栏的边框。
  4. 找到边框的宽度和健康的宽度,然后将它们除以另一个以获得健康百分比。
  5. 这是在计算洪水填充后的视觉效果(该功能与你的圈子不相称,我想虽然这不会有问题......): enter image description here

    黄色区域是健康栏的健康部分,青色是边界。正如你所看到的那样,它并不完美,但希望它足够接近。另外,我假设您将使用的图像将是png而不是jpg,这样可以消除一点点不准确。

    编辑:这是打印health_bars的输出:

    [team blue, percentage 1.0, length 20, pos (66, 433), team blue, percentage 1.0, length 34, pos (130, 436), team red, percentage 0.38095238095238093, length 63, pos (149, 357), team blue, percentage 0.953125, length 64, pos (27, 404), team red, percentage 0.6703296703296703, length 91, pos (480, 119), team red, percentage 0.5700934579439252, length 107, pos (500, 52)]

答案 1 :(得分:0)

列出你放入每个小兵的列表,然后遍历这个列表并查看它是红色还是蓝色。如果那可能......