调整图像及其边界框大小

36

我有一张带有边界框的图像,想要调整图像大小。

img = cv2.imread("img.jpg",3)
x_ = img.shape[0]
y_ = img.shape[1]
img = cv2.resize(img,(416,416));

现在我想计算比例因子:

x_scale = ( 416 / x_)
y_scale = ( 416 / y_ )

绘制图像,这是原始边界框的代码:

( 128, 25, 447, 375 ) = ( xmin,ymin,xmax,ymax)
x = int(np.round(128*x_scale))
y = int(np.round(25*y_scale))
xmax= int(np.round  (447*(x_scale)))
ymax= int(np.round(375*y_scale))

然而,使用这个方法我得到了:

enter image description here

而原始的是:

enter image description here

我没有看到这个逻辑中有任何标志,出了什么问题?

整个代码:

imageToPredict = cv2.imread("img.jpg",3)
print(imageToPredict.shape)

x_ = imageToPredict.shape[0]
y_ = imageToPredict.shape[1]

x_scale = 416/x_
y_scale = 416/y_
print(x_scale,y_scale)
img = cv2.resize(imageToPredict,(416,416));
img = np.array(img);


x = int(np.round(128*x_scale))
y = int(np.round(25*y_scale))
xmax= int(np.round  (447*(x_scale)))
ymax= int(np.round(375*y_scale))
Box.drawBox([[1,0, x,y,xmax,ymax]],img)

并且绘制框

def drawBox(boxes, image):
    for i in range (0, len(boxes)):
        cv2.rectangle(image,(boxes[i][2],boxes[i][3]),(boxes[i][4],boxes[i][5]),(0,0,120),3)
    cv2.imshow("img",image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

图像和边界框的数据是分别加载的。 我在图像内部绘制边界框。 图像本身不包含边界框。


我删除了那行代码,这里与之无关。 - jejjejd
我不确定我理解了。您能否将代码调整为 MCVE 并发布?边界框是原始图像的一部分还是您单独绘制的内容? - cxw
好的,所以边界框不在图像中,你需要将边界框与图像分开调整大小。在这种情况下,drawBox 调用肯定是相关的 :) 。正如我所说,请给我一个 MCVE,我会看一下。现在,恐怕从代码片段中它并没有跳出来。谢谢! - cxw
编辑后的问题 - jejjejd
1
@jejjejd,我仍然看不到一段绘制框在原始图像上的代码。只有当您展示如何绘制**两个**框的代码时,我们才能谈论一致性。特别是在您的“原始”图像上,边框不是一个矩形(128,25)-(447,375)。实际左上角大约是(160,35) - SergGr
显示剩余4条评论
5个回答

30

我认为有两个问题:

  1. 你应该交换 x_y_,因为 shape[0] 实际上是 y 维度,shape[1] 是 x 维度。
  2. 你应该在原图和缩放后的图像上使用相同的坐标。在你的原始图片中矩形是 (160, 35) - (555, 470) 而不是你在代码中使用的 (128,25) - (447,375)

如果我使用以下代码:

import cv2
import numpy as np


def drawBox(boxes, image):
    for i in range(0, len(boxes)):
        # changed color and width to make it visible
        cv2.rectangle(image, (boxes[i][2], boxes[i][3]), (boxes[i][4], boxes[i][5]), (255, 0, 0), 1)
    cv2.imshow("img", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


def cvTest():
    # imageToPredict = cv2.imread("img.jpg", 3)
    imageToPredict = cv2.imread("49466033\\img.png ", 3)
    print(imageToPredict.shape)

    # Note: flipped comparing to your original code!
    # x_ = imageToPredict.shape[0]
    # y_ = imageToPredict.shape[1]
    y_ = imageToPredict.shape[0]
    x_ = imageToPredict.shape[1]

    targetSize = 416
    x_scale = targetSize / x_
    y_scale = targetSize / y_
    print(x_scale, y_scale)
    img = cv2.resize(imageToPredict, (targetSize, targetSize));
    print(img.shape)
    img = np.array(img);

    # original frame as named values
    (origLeft, origTop, origRight, origBottom) = (160, 35, 555, 470)

    x = int(np.round(origLeft * x_scale))
    y = int(np.round(origTop * y_scale))
    xmax = int(np.round(origRight * x_scale))
    ymax = int(np.round(origBottom * y_scale))
    # Box.drawBox([[1, 0, x, y, xmax, ymax]], img)
    drawBox([[1, 0, x, y, xmax, ymax]], img)


cvTest()
并使用您的“原始”图像作为“49466033\img.png”, 原始图像 我得到了以下图像 处理后的图像 正如您所看到的,我的细蓝线恰好位于您原始红线内部,并且无论您选择什么targetSize(因此缩放实际上是正确的),都会保持在那里。

5

另外一种做法是使用CHITRA

image = Chitra(img_path, box, label)
# Chitra can rescale your bounding box automatically based on the new image size.
image.resize_image_with_bbox((224, 224))

print('rescaled bbox:', image.bounding_boxes)
plt.imshow(image.draw_boxes())

https://chitra.readthedocs.io/zh_CN/latest/

使用pip安装chitra。


2
CHITRA看起来很不错。它的底层实现似乎在imgaug库中,使用imgaug进行此操作的示例在此处:https://imgaug.readthedocs.io/en/latest/source/examples_bounding_boxes.html?highlight=bbs#projection-of-bbs-onto-rescaled-images - Ryaminal

0

在使用TensorFlow.js和MobileNet-v2进行预测时,我在Angular中遇到了一个边界框坐标问题。这些坐标是基于视频帧的分辨率计算的。

但是,我将视频显示在一个具有固定高度和宽度的画布上。通过将坐标除以原始视频分辨率与画布分辨率之比,我解决了这个问题。

      const x = prediction.bbox[0] / (this.Owidth / 300);
      const y = prediction.bbox[1] / (this.Oheight / 300);
      const width = prediction.bbox[2] / (this.Owidth / 300);
      const height = prediction.bbox[3] / (this.Oheight / 300);
      // Draw the bounding box.
      ctx.strokeStyle = '#99ff00';
      ctx.lineWidth = 2;
      ctx.strokeRect(x, y, width, height);
  • this.Owidth & this.Oheight 是视频的原始分辨率。它是通过获取得到的。
this.video.addEventListener(
      'loadedmetadata',
      (e: any) => {
        this.Owidth = this.video.videoWidth;
        this.Oheight = this.video.videoHeight;
        console.log(this.Owidth, this.Oheight, ' pixels ');
      },
      false
    );
  • 300 X 300 是我的静态画布的宽度和高度。

0
使用imgaug库(感谢@Ryaminal的评论)
import imgaug as ia
from imgaug.augmentables.bbs import BoundingBox, BoundingBoxesOnImage
import cv2
import matplotlib.image

size=384  # we want to resize to 384x384
image = cv2.imread('xx.jpg')

# say the bounding box is given in (x,y,w,h) format
x=1493
y=1254
w=805
h=381

bbs = BoundingBoxesOnImage([BoundingBox(x1=x, x2=x+w, y1=y, y2=y+h)], shape=image.shape)

# Rescale image and bounding boxes
image_rescaled = ia.imresize_single_image(image, (size, size))
bbs_rescaled = bbs.on(image_rescaled)

new_x1 = round(bbs_rescaled[0].x1)
new_y1 = round(bbs_rescaled[0].y1)
new_x2 = round(bbs_rescaled[0].x2) - round(bbs_rescaled[0].x1)
new_y2 = round(bbs_rescaled[0].y2) - round(bbs_rescaled[0].y1)

new_w = new_x2 - new_x1
new_h = new_y2 - new_y1

matplotlib.image.imsave('rescaled.jpg', image_rescaled) # if you want to save the resized image

# Draw image before/after rescaling and with rescaled bounding boxes
image_bbs = bbs.draw_on_image(image, size=2)
image_rescaled_bbs = bbs_rescaled.draw_on_image(image_rescaled, size=2)

ia.imshow(image_rescaled_bbs)

-2

你可以使用resize_dataset_pascalvoc

这很容易使用python3 main.py -p <IMAGES_&_XML_PATH> --output <IMAGES_&_XML> --new_x <NEW_X_SIZE> --new_y <NEW_X_SIZE> --save_box_images <FLAG>,它会调整你的数据集并重新写入新的注释文件到调整大小的图像中。


你可能需要详细解释如何做到这一点。 - Priyank Pathak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接