影像尺寸變換scalepadding方法

wancy發表於2024-11-26

  在深度學習中,當需要將影像調整到特定尺寸時,直接resize可能會導致影像失真,特別是當目標尺寸與原始影像的寬高比不一致時。為了解決這個問題,一種最常見的方法是首先按照原始影像的寬高比將影像調整到與目標尺寸最接近的尺寸,然後在剩餘的空間中使用padding進行填充,以得到目標尺寸的影像。或者先使用填充,再使用resize,其最終結果是一樣的,但是往往使用前者。

1.先填充為正方形圖後縮放

  1. 確定長邊和短邊:

    • 首先,確定影像的長邊和短邊。長邊是影像的寬度和高度中較長的那個,短邊是較短的那個。
  2. 將影像變為正方形:

    • 以長邊的長度作為正方形的邊長,這意味著如果原始影像是長方形,那麼它的一邊將被擴充套件以形成一個正方形。
    • 較短的一邊將圍繞其周圍填充畫素,通常是白色或黑色畫素,以確保影像變為正方形。
  3. 填充畫素:

    • 填充操作會在短邊的兩側新增畫素,直到影像的寬度和高度相等。填充的畫素數量取決於原始影像的尺寸和目標正方形的尺寸。
  4. 縮放(如雙線性插值,最近鄰插值等):

    • 一旦影像被轉換為正方形,下一步是使用插值法將影像縮放到所需的目標尺寸。
    • 雙線性插值是一種平滑的縮放技術,它透過考慮周圍四個畫素的值來計算新畫素的值,這樣可以減少縮放過程中的失真。

示意圖如下:

  原始圖為寬為w,高為h,現在想要透過scalepadding這種方法變換為寬width,高為height的圖。(這裡假設w>h,h>w的情況與此類似)

第一步:確定長邊與短邊,然後將原始圖變為正方形,邊長長度為長邊長度,在短邊的外側填充,使得圖變為寬為w,高為w的圖。

第二步:縮放變換,將上一步中得到的寬為w,高為w的圖經過縮放變換後就變為了寬為width,高為height的目標大小的圖了。

  自此,變換就完成了,原始圖寬w,高h,以及填充的其中一個部分(w-h)/2變換為目標影像後,很容易得到縮放關係如下:

  w乘以(width/w)後,就變為了width,

  h乘以(height/w)後,就變為了(h*height)/w了,圖中未標註出來(第二步中間圖的紫色區域的高度),

  (w-h)/2乘以(height/w)後,就變為了((w-h)*height)/(2w)。

  

注意,如果目標的width與height不相等,也就是目標不為正方形就會造成w與h不是同等比例縮放,就會變形失真。所以這種方法只適用於width等於height的情況。

2.先縮放再填充(通用)

  1. 計算縮放比例:首先,根據目標尺寸和原始影像的寬高比,計算出影像在保持寬高比的情況下應該被縮放到的尺寸。這通常涉及到計算縮放比例,即目標尺寸與原始影像尺寸的比值,並選擇較小的那個比例作為最終的縮放比例,以確保影像不會被拉伸或壓縮。
  2. 進行resize操作:使用計算出的縮放比例對原始影像進行resize操作,得到一個新的影像。這個新影像的尺寸將小於或等於目標尺寸,但會保持原始影像的寬高比。
  3. 進行padding操作:最後,在新的影像的周圍新增padding,以使其達到目標尺寸。padding可以使用任何顏色或圖案,但通常選擇中性色(如灰色或白色)以避免對影像內容造成干擾。padding的大小將根據目標尺寸和新影像尺寸之間的差異來確。

  上圖中實際上需要根據width/w與height/h兩者的大小,決定ratio的值。為了防止在縮放過程中,某條邊超過目標的長度,應該選擇width/w與height/h的較小者作為ratio。看下面程式碼:

import cv2
import numpy as np
def resize_and_pad(image, target_height, target_width, padding_value=(0, 0, 0)):
    # 獲取原始影像的高度和寬度
    height, width = image.shape[:2]
    # 計算縮放比例,使得影像的長寬比例保持不變
    scale = min(target_width / width, target_height / height)

    # 計算縮放後的尺寸
    new_width = int(width * scale)
    new_height = int(height * scale)

    # 先對影像進行縮放
    resized_image = cv2.resize(image, (new_width, new_height))

    # 建立一個目標大小的空白影像(填充背景為自定義顏色)
    padded_image = np.full((target_height, target_width, 3), padding_value, dtype=np.uint8)

    # 計算填充的上下左右邊距
    top_padding = (target_height - new_height) // 2
    # bottom_padding = target_height - new_height - top_padding
    left_padding = (target_width - new_width) // 2
    # right_padding = target_width - new_width - left_padding

    # 將縮放後的影像放置到填充後的影像中心
    padded_image[top_padding:top_padding + new_height, left_padding:left_padding + new_width] = resized_image

    return padded_image, resized_image, left_padding, top_padding

if __name__ == '__main__':
    # 示例使用
    image = cv2.imread('./img/lena2.png',1)  # 讀取影像  (528, 532, 3)
    # image_resize=cv2.resize(image,None,fx=0.5,fy=0.5)
    # cv2.imwrite("img/lena_02.png", image_resize)
    print("image.shape",image.shape)#(400, 323, 3)
    target_height = 300  # 目標高度
    target_width = 400  # 目標寬度
    padding_value = (114, 114, 114)  # 自定義填充值(RGB)

    padded_image, resized_image, left_padding, top_padding = resize_and_pad(image, target_height, target_width,padding_value)
    print("padded_image.shape", padded_image.shape)

    # 儲存結果
    cv2.imwrite('result.jpg', padded_image)

  原圖

                                    目標圖

  如果將目標大小改為height=150,width=200,目標圖比原圖小,也是先縮放,再填充,如下:

小結:縮放填充是一種基本的操作,再影像預處理中,為了使得圖片不因形變失真,往往採用這種方式,比如後面會介紹的YOLOV5的圖片預處理的方法。

參考:

https://blog.csdn.net/weixin_51226647/article/details/139688302

相關文章