【问题标题】:Drawing a cross on an image with OpenCV使用 OpenCV 在图像上绘制十字
【发布时间】:2018-07-21 11:31:18
【问题描述】:

上下文:我正在执行对象本地化并希望实现返回抑制机制(即在 红色 边界框位于trigger 行动。)

问题:我不知道如何根据原始输入 (init_input) 准确缩放边界框 (red)。如果理解了这种缩放,那么黑色十字应该准确地放置在红色边界框的中间。

我目前这个函数的代码如下:

def IoR(b, init_input, prev_coord):
    """
    Inhibition-of-Return mechanism.

    Marks the region of the image covered by
    the bounding box with a black cross.

    :param b:
        The current bounding box represented as [x1, y1, x2, y2].

    :param init_input:
        The initial input volume of the current episode.

    :param prev_coord:
        The previous state's bounding box coordinates (x1, y1, x2, y2)
    """
    x1, y1, x2, y2 = prev_coord
    width = 12
    x_mid = (b[2] + b[0]) // 2
    y_mid = (b[3] + b[1]) // 2

    # Define vertical rectangle coordinates
    ver_x1 = int(((x_mid) * IMG_SIZE / (x2 - x1)) - width)
    ver_x2 = int(((x_mid) * IMG_SIZE / (x2 - x1)) + width)
    ver_y1 = int((b[1]) * IMG_SIZE / (y2 - y1))
    ver_y2 = int((b[3]) * IMG_SIZE / (y2 - y1))

    # Define horizontal rectangle coordinates
    hor_x1 = int((b[0]) * IMG_SIZE / (x2 - x1))
    hor_x2 = int((b[2]) * IMG_SIZE / (x2 - x1))
    hor_y1 = int(((y_mid) * IMG_SIZE / (y2 - y1)) - width)
    hor_y2 = int(((y_mid) * IMG_SIZE / (y2 - y1)) + width)

    # Draw vertical rectangle
    cv2.rectangle(init_input, (ver_x1, ver_y1), (ver_x2, ver_y2), (0, 0, 0), -1)

    # Draw horizontal rectangle
    cv2.rectangle(init_input, (hor_x1, hor_y1), (hor_x2, hor_y2), (0, 0, 0), -1)

想要的效果如下图所示:

注意:我相信这个问题的复杂性是由于每次我采取行动(并因此进入下一个状态)时图像都会调整大小(到 224、224、3)。因此,确定缩放的“锚”必须从之前的状态缩放中提取出来,如下代码所示:

def next_state(init_input, b_prime, g):
    """
    Returns the observable region of the next state.

    Formats the next state's observable region, defined
    by b_prime, to be of dimension (224, 224, 3). Adding 16
    additional pixels of context around the original bounding box.
    The ground truth box must be reformatted according to the
    new observable region.

    IMG_SIZE = 224

    :param init_input:
        The initial input volume of the current episode.

    :param b_prime:
        The subsequent state's bounding box.

    :param g: (init_g)
        The initial ground truth box of the target object.
    """

    # Determine the pixel coordinates of the observable region for the following state
    context_pixels = 16
    x1 = max(b_prime[0] - context_pixels, 0)
    y1 = max(b_prime[1] - context_pixels, 0)
    x2 = min(b_prime[2] + context_pixels, IMG_SIZE)
    y2 = min(b_prime[3] + context_pixels, IMG_SIZE)

    # Determine observable region
    observable_region = cv2.resize(init_input[y1:y2, x1:x2], (224, 224), interpolation=cv2.INTER_AREA)

    # Resize ground truth box
    g[0] = int((g[0] - x1) * IMG_SIZE / (x2 - x1))  # x1
    g[1] = int((g[1] - y1) * IMG_SIZE / (y2 - y1))  # y1
    g[2] = int((g[2] - x1) * IMG_SIZE / (x2 - x1))  # x2
    g[3] = int((g[3] - y1) * IMG_SIZE / (y2 - y1))  # y2

    return observable_region, g, (b_prime[0], b_prime[1], b_prime[2], b_prime[3])

解释:

有一个状态t,代理正在预测目标对象的位置。目标对象有一个ground truth box(图像中的黄色,草图中的点),代理当前的“定位框”是红色的边界框。比如说,在状态t,代理决定最好向右移动。因此,边界框向右移动,然后下一个状态 t' 是通过在红色边界框周围添加额外的 16 像素上下文来确定的,裁剪 原始 图像相对于这个边界,然后将裁剪后的图像放大回 224、224 的尺寸。

假设代理现在确信其预测是准确的,因此它选择了trigger 操作。这基本上意味着,结束当前目标对象的定位事件,并在智能体预测对象所在的位置(即红色边界框的中间)放置一个黑色十字。现在,由于当前状态是在上一个动作之后被裁剪后放大的,因此必须相对于正常/原始/初始图像重新缩放边界框,然后才能在图像上准确地绘制黑色十字。

就我的问题而言,状态之间的第一次重新调整运行良好(本文中的第二个代码)。然而,缩小到正常水平并绘制黑色十字是我似乎无法理解的。

这是一张希望有助于解释的图片:

这是我当前解决方案的输出(请点击图片放大):

【问题讨论】:

  • 我认为你想要x_mid = (b[2] + b[0]) // 2 而不是减号。 y_mid 也是如此。
  • @MarkSetchell 谢谢!我已根据您的回复更新了帖子。
  • 有很多东西你没有展示。什么是 IMG_SIZE ?如何调用 IoR 函数?
  • @Sunreef IMG_SIZE = 224,图片的尺寸。 IoR 使用 red 区域的边界框 [x1, y1, x2, y2] 调用。 init_input 是原始 (224, 224, 3) 图像。即init_input = np.copy(init_region)
  • 问题是缩放问题。我相信我需要跟踪一个参考点,这是我接下来要弄清楚如何实现它的方法。

标签: python opencv image-processing


【解决方案1】:

我认为最好全局保存坐标,而不是使用一堆高档/低档。它们让我头疼,并且可能由于四舍五入而导致精度损失。

也就是说,每次检测到某些东西时,首先将其转换为全局(原始图像)坐标。我这里写了一个小demo,模仿你的检测和trigger的行为。

初步检测:

放大,另一个检测:

放大,另一个检测:

放大,另一个检测:

放大到原始比例,检测框位于正确位置

代码:

import cv2
import matplotlib.pyplot as plt

IMG_SIZE = 224

im = cv2.cvtColor(cv2.imread('lena.jpg'), cv2.COLOR_BGR2GRAY)
im = cv2.resize(im, (IMG_SIZE, IMG_SIZE))

# Your detector results
detected_region = [
    [(10, 20)   , (80, 100)],
    [(50, 0)    , (220, 190)],
    [(100, 143)  , (180, 200)],
    [(110, 45)  , (180, 150)]
]

# Global states
x_scale = 1.0
y_scale = 1.0
x_shift = 0
y_shift = 0

x1, y1 = 0, 0
x2, y2 = IMG_SIZE-1, IMG_SIZE-1
for region in detected_region:
    # Detection
    x_scale = IMG_SIZE / (x2-x1)
    y_scale = IMG_SIZE / (y2-y1)
    x_shift = x1
    y_shift = y1

    cur_im = cv2.resize(im[y1:y2, x1:x2], (IMG_SIZE, IMG_SIZE))

    # Assuming the detector return these results
    cv2.rectangle(cur_im, region[0], region[1], (255))

    plt.imshow(cur_im)
    plt.show()

    # Zooming in, using part of your code
    context_pixels = 16
    x1 = max(region[0][0] - context_pixels, 0) / x_scale + x_shift
    y1 = max(region[0][1] - context_pixels, 0) / y_scale + y_shift
    x2 = min(region[1][0] + context_pixels, IMG_SIZE) / x_scale + x_shift
    y2 = min(region[1][1] + context_pixels, IMG_SIZE) / y_scale + y_shift

    x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)


# Assuming the detector confirm its choice here
print('Confirmed detection: ', x1, y1, x2, y2)

# This time no padding
x1 = detected_region[-1][0][0] / x_scale + x_shift
y1 = detected_region[-1][0][1] / y_scale + y_shift
x2 = detected_region[-1][1][0] / x_scale + x_shift
y2 = detected_region[-1][1][1] / y_scale + y_shift
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)

cv2.rectangle(im, (x1, y1), (x2, y2), (255, 0, 0))
plt.imshow(im)
plt.show()

这还可以防止在调整大小的图像上调整大小,这可能会产生更多伪影并降低检测器的性能。

【讨论】:

  • 感谢您的详细解答。通过“全局”存储更改并使用您的重新缩放方法,它现在可以完美运行!再次感谢,以后我会按照你说的去做的。
【解决方案2】:

500x500 图像中想象一个点(x, y)。让它成为(100, 200)。 将其缩放到不同的大小后,例如250x250 - 缩放它的正确方法是只查看当前坐标并执行new_coord = old_coord * NEW_SIZE/OLD_SIZE

因此,(100,200) 将转换为 (50,100)

如果您使用 x2-x1 替换缩放并使用更简单的重新缩放公式,它应该可以解决您的问题。

更新:NEW_SIZEOLD_SIZE 根据原始图像和最终图像的形状,如果它们是矩形而不是正方形,这两个坐标可能会有所不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-19
    • 2012-06-15
    • 1970-01-01
    • 2019-07-25
    • 2011-04-03
    • 1970-01-01
    • 2021-11-13
    • 2013-01-14
    相关资源
    最近更新 更多