使用 Python - 如何在不超过原始图像大小的情况下调整裁剪图像的大小以满足纵横比

Posted

技术标签:

【中文标题】使用 Python - 如何在不超过原始图像大小的情况下调整裁剪图像的大小以满足纵横比【英文标题】:Using Python- How to resize a cropped image to meet an aspect ratio while not exceeding original image size 【发布时间】:2022-01-22 16:52:21 【问题描述】:

我正在使用对象检测算法来检测图像中的对象。代码如下。使用的图片是一辆汽车,如下所示。

我想裁剪原始图像以仅保留在图像中检测到的对象以及将纵横比保持在 4/3 和 16/9 之间所需的一切。

汽车周围的框已经从下面的算法推导出来[变量是框],图像尺寸是[变量是高度,宽度]在下面的代码中。

如果我们手动执行此操作,由于需要多次迭代,会很麻烦,例如:我们必须确保调整大小不会超出原始图像大小。

下面包含 3 张图像,原始图像、检测到汽车的修改图像以及调整大小以满足宽高比范围的图像。(4/3 到 16/9)

python 中是否有现有的函数来完成此任务。因此,在保持纵横比的同时,将框尺寸从 [91, 90, 226, 158] 调整到最小必要量,使其在原始图像尺寸 183x275 的限制范围内

提前致谢。

代码:

import cv2
import matplotlib.pyplot as plt
import cvlib as cv
from cvlib.object_detection import draw_bbox
imagepath='/home/usr/Desktop/car.jpeg'
img = cv2.imread(imagepath)


####STEP 1
img1 = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

box, label, count = cv.detect_common_objects(img)
output = draw_bbox(img, box, label, count)    
output = cv2.cvtColor(output,cv2.COLOR_BGR2RGB)
plt.figure(figsize=(10,10))
plt.axis('off')
plt.imshow(output)
plt.show()

print("Number of objects in this image are " +str(len(label)))

height,width,_=img1.shape

print(height,width)

print(box)
#box size is [91, 90, 226, 158] (w1,h1,w2,h2)
#image size is 183x275 (heightxwidth)



#STEP2 (cropping original image to car dimensions as per box size)
crop_img = img[90:158, 91:226]
cv2.imshow("cropped", crop_img)
cv2.waitKey(0)

图片示例:

检测对象(步骤1)

裁剪图像(步骤 2)

预期结果(第三步)

【问题讨论】:

如果你有边界矩形的尺寸,你可以使用numpy slicing to crop the image。这可以满足您的第一个要求。我不清楚您是否想在之后调整裁剪图像的大小 - 将其调整为什么比例?还是您希望边界矩形保持一定的纵横比? 我添加了一张图片并修改了图片标签以便更清楚地解释,我想从第2步转到第3步。也就是说,增加图像大小,使其宽高比在 4/3 到 16/9 之间。 这听起来像是微不足道的计算。拿 bbox (不应该超出图像!),放大到所需的宽高比,如果在外面则移动到视图中(这将始终将原始 bbox 保留在其中)。 ——确切地说有什么问题?尽可能多地编写代码。 它适用于 1 张图像,但我想对许多图像执行此类操作,而无需手动评估每张图像。是否有适合此任务的可用功能。谢谢!! 呃,你只需要几个函数,然后你就可以将它们应用到你的每个bbox中,对吧? enlarge_bbox_to_aspect_ratio(bbox, aspect_ratio)move_bbox_into_view(bbox, width, height) 【参考方案1】:

以下代码将图像裁剪为所需的坐标,然后根据其 >16/9 或

import cv2
import math
import sys
imagepath=('/home/usr/Desktop/filename.jpeg')
img=cv2.imread(imagepath)
h,w,_=img.shape#height and width of original image


#Dimensions of car or object you want to crop (See step 2 in question)
crop_dimensions=[96, 56, 602, 686] #w1,h1,w2,h2

def cropimage(crop_dimensions,imgx):
    crop_img = imgx[crop_dimensions[1]:crop_dimensions[3], crop_dimensions[0]:crop_dimensions[2]]
    return crop_img

crop_img=cropimage(crop_dimensions,img)
height,width,_=crop_img.shape #height and width of cropped image



if width/height>16/9 or width/height<4/3:
    crop_centrepoint = ((crop_dimensions[2] - crop_dimensions[0])/2, (crop_dimensions[3] - crop_dimensions[1])/2) 
    print(crop_centrepoint) #Centre point of cropped image 

    if width/height<4/3:
        print('<4/3')

        newwidth=4/3*height
        newheight=height

        additionalwidth=newwidth-width

        w1maxadditional = crop_dimensions[0] - 0 #distance from cropped image to left edge (0) 
        w2maxadditional=w-crop_dimensions[2]#distance between cropped image and right end

        if w2maxadditional > additionalwidth/2:
            correction2=0
            w2=(additionalwidth/2)
        else:
            correction2=abs(w2maxadditional-(additionalwidth/2))
            w2=w2maxadditional

        if w1maxadditional > additionalwidth/2:
            correction1=0
            w1=(additionalwidth/2)+correction1
        else:
            correction1=abs(w2maxadditional-(additionalwidth/2))
            w1=w1maxadditional

        w1=w1+correction2
        w2 = w2+correction1
        if w1>w1maxadditional:
            w1=w1maxadditional
        if w2>w2maxadditional:
            w2=w2maxadditional

        w1 = crop_dimensions[0] - w1
        w2 = w2 + crop_dimensions[2]
        h1=crop_dimensions[1]
        h2=crop_dimensions[3]

    if width / height > 16/9:
        print('>16/9')

        newheight = width * 9 / 16
        newwidth = width

        additionalheight = newheight - height

        h1maxadditional = crop_dimensions[1] - 0  # distance from cropped image to top edge

        h2maxadditional = h - crop_dimensions[3]  # distance between cropped image to bottom end

        if h2maxadditional > additionalheight / 2:
            correction2 = 0
            h2 = (additionalheight / 2)
        else:
            correction2 = abs(h2maxadditional - (additionalheight / 2))
            h2 = h2maxadditional

        if h1maxadditional > additionalheight / 2:
            correction1 = 0
            h1 = (additionalheight / 2) + correction1
        else:
            correction1 = abs(h2maxadditional - (additionalheight / 2))
            h1 = h1maxadditional

        h1 = h1 + correction2
        h2 = h2 + correction1
        if h1 > h1maxadditional:
            h1 = h1maxadditional
        if h2 > h2maxadditional:
            h2 = h2maxadditional


        h1 = crop_dimensions[1] - h1
        h2 = h2 + crop_dimensions[3]

        w1 = crop_dimensions[0]
        w2 = crop_dimensions[2]

else:
    [w1,h1,w2,h2]=crop_dimensions 

#Rounding down because cropimage function takes integers
w1=math.trunc(w1)
h1=math.trunc(h1)
w2=math.trunc(w2)
h2=math.trunc(h2)




new_image=cropimage([w1,h1,w2,h2],img)

cv2.imshow('img',new_image)
cv2.waitKey(0)

【讨论】:

以上是关于使用 Python - 如何在不超过原始图像大小的情况下调整裁剪图像的大小以满足纵横比的主要内容,如果未能解决你的问题,请参考以下文章

python如何在不损失质量的情况下调整(缩小)图像的大小

如何在不本地存储的情况下知道图像文件大小

如何使用 PIL 减小图像文件大小

将图像重新调整为新的高度和宽度,但不要超过原始高度和宽度

如何在不保存原始图像的情况下保存图像 URL 的裁剪图像? (在 Rails 中使用 Paperclip 或其他插件)

在不改变原始纵横比的情况下将位图大小调整为 512x512