본문 바로가기
목차
Python

[PyTorch] Conversion-Torchvision.transfroms.v2

by ds31x 2025. 6. 16.
728x90
반응형


Conversion
transforms은

  • 데이터 타입과 형식을 변환하며,
  • 일부는 값 범위 스케일링(예: uint8 [0,255] ↔ float32 [0,1])을 포함함.

https://docs.pytorch.org/vision/main/transforms.html#conversion

 

Transforming and augmenting images — Torchvision main documentation

Shortcuts

docs.pytorch.org

관련 gist

https://gist.github.com/dsaint31x/d67286ee5ad15db9b2981119e43f1695

 

dl_conversion-torchvision-transforms-v2.ipynb

dl_conversion-torchvision-transforms-v2.ipynb. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com


0. Prerequisites

사용할 이미지 다운로드등을 수행하는 다음의 코드를 수행하고 나서 예제를 실행할 것.

img_path = "assets/astronaut.jpg"
img_url = "https://raw.githubusercontent.com/pytorch/vision/main/gallery/assets/astronaut.jpg"

!mkdir -p assets/
!curl -o {img_path} {img_url}

from torchvision.io import decode_image

original_img = decode_image(img_path)

print(f" {type(original_img) = }\n \
{original_img.dtype = }\n \
{original_img.shape = }")

 


1. ToImage() - 주로 사용됨 **

1-1. 역할:

  • Tensor, ndarray, PIL Image를
  • torchvision의 Image TVTensor로 변환
  • 원본의 Meta Data가 조건부로 보존
    • Image TVTensor 의 경우 100% 보존.
    • 단, PIL 의 Image객체가 입력([W,H,C])인 경우는 [C,H,W]로 변환됨:
      • 엄밀히는 PIL의 Image 객체는  [W,H] + C  구조라고 볼 수 있음.
      • PIL의 Image객체의 mode, info, exif 는 보존되지 않음
    • 단, NumPy 의 ndarray객체가 입력([H,W,C])인 경우는 [C,H,W]로 변환됨.
ToImage는 ToDtype 과 함께,
가장 많이 애용
(과거의 ToTensor를 대체)

1-2. 파라미터:

ToImage()  # 파라미터 없음

1-3. 주요 특징:

  • Value Scaling 안함:
    • 픽셀 값을 그대로 유지
  • Meta Data 조건부 보존:
    • 이미지 크기,
    • dtype 등의 Meta Data 정보 조건부 보존
  • v2 호환성:
    • v2 transforms와 완전 호환

1-4. 성능 이슈:

  • 입력이 PyTorch의 텐서(특히 TVTensor의 Image)인 경우,
    • 매우 빠름:
      • 이 경우엔 단순 wrapping에 해당
      • 거의 오버헤드 없음
    • 메모리 효율:
      • 새로운 메모리 할당 없이 View 만 생성
    • 메타데이터 오버헤드:
      • TVTensor Wraping 으로 약간의 추가 메모리
  • 입력이 PIL Image 이거나 NumPy의 array인 경우,
    • PILToTensor()와 유사한 부하가 발생.
    • 이들 입력의 경우 새로운 Image TVTensor 생성이 이루어지기 때문임.

1-5. 사용 예시:

from torchvision.transforms.v2 import ToImage
import torch
import numpy as np
from PIL import Image
import time

transform = ToImage()

# 1. PyTorch 텐서 변환 (래핑만 - 빠름)
tensor = torch.randint(0, 255, (3, 224, 224), dtype=torch.uint8)
img_tensor = transform(tensor)
print(type(img_tensor))  # <class 'torchvision.datapoints._image.Image'>
print(torch.equal(tensor, img_tensor))  # True (동일한 값)

# 2. NumPy 배열 변환 (실제 변환 발생 - 중간 속도)
numpy_array = np.random.randint(0, 255, (224, 224, 3), dtype=np.uint8)
start = time.time()
img_from_numpy = transform(numpy_array)
numpy_time = time.time() - start
print(f"NumPy 변환 시간: {numpy_time:.6f}초")

# 3. PIL Image 변환 (실제 변환 발생 - 중간 속도)
pil_img = Image.fromarray(numpy_array)
start = time.time()
img_from_pil = transform(pil_img)
pil_time = time.time() - start
print(f"PIL 변환 시간: {pil_time:.6f}초")

# 4. 성능 비교 - 기존 텐서 vs PIL Image
start = time.time()
for _ in range(100):
    img_tensor = transform(tensor)  # 래핑만
tensor_time = time.time() - start

start = time.time()
for _ in range(100):
    img_from_pil = transform(pil_img)  # 실제 변환
pil_batch_time = time.time() - start

print(f"텐서 래핑 100회: {tensor_time:.4f}초")
print(f"PIL 변환 100회: {pil_batch_time:.4f}초")
print(f"성능 차이: {pil_batch_time/tensor_time:.1f}배")

 


2. ToPureTensor() - 메타데이터 제거

2-1. 역할:

  • 모든 TVTensor(Image, BoundingBoxes, Mask 등)를
  • 순수 PyTorch Tensor(텐서)로 변환 (사실 TVTensor만 Tensor로 변환)
  • Meta Data가 제거됨.
ToPUreTensor는
PIL Image나 NumPy의 array를 Tensor로 변환하지 못함.
CNN이나 Custom Module의 호환성 위해 사용.

 

2-2. 파라미터:

ToPureTensor()  # 파라미터 없음

2-3. 주요 특징:

  • 메타데이터 제거 (TVTensor의 메타데이터 제거됨):
    • TVTensor의 추가 정보 모두 삭제
    • Image TVTensor는 특별한 메타데이터가 없음에 유의할 것.
  • 순수 텐서 반환:
    • 표준 torch.Tensor로 변환
    • 정확히는 다음을 따름:
      • TVTensor → torch.Tensor (메타데이터 제거)
      • PIL Image → PIL Image (변환 안하고 그냥 통과)
      • NumPy array → NumPy array (변환 안하고 그냥 통과)
      • 순수 텐서 → 순수 텐서 (이미 순수하므로)
from torchvision.transforms.v2 import ToPureTensor, ToImage
from torchvision import tv_tensors
from PIL import Image
import torch

to_pure = ToPureTensor()

# TVTensor → 순수 텐서 변환
tv_tensor = tv_tensors.Image(torch.randn(3, 224, 224))
result2 = to_pure(tv_tensor)
print(f"TVTensor → ToPureTensor: {type(result2)}")
# 출력: <class 'torch.Tensor'> (변환됨!)

# PIL Image → 그대로 통과
pil_img = Image.open(img_path)
result1 = to_pure(pil_img)
print(f"PIL → ToPureTensor: {type(result1)}")
# 출력: <class 'PIL.JpegImagePlugin.JpegImageFile'> (변환 안됨!)


# 순수 텐서 → 그대로 통과
pure_tensor = torch.randn(3, 224, 224)
result3 = to_pure(pure_tensor)
print(f"Tensor → ToPureTensor: {type(result3)}")
# 출력: <class 'torch.Tensor'> (그대로 통과)
  • 호환성 위해 존재: 일반 PyTorch 연산에 필요한 경우 사용
    • 과거의 CNN 이나, Custom Module의 경우,
    • TVTensor가 유지되지 않기 쉽고, 예상치 못한 동작이 있을 수 있기 때문임.

2-4. 성능 이슈:

  • 매우 빠름: 메타데이터만 제거, 데이터는 그대로
  • 메모리 절약: 메타데이터 오버헤드 제거
  • 정보 손실: 원본 형태 정보 손실 (되돌릴 수 없음)

2-5. 사용 예시:

from torchvision.transforms.v2 import ToPureTensor, ToImage
from torchvision import tv_tensors
import torch

# Image TVTensor 생성
# Image TVTensor의 경우, 특별한 메타데이터 없음.
to_image = ToImage()
to_pure = ToPureTensor()
tensor = torch.randint(0, 255, (3, 224, 224), dtype=torch.uint8)
img_tensor = to_image(tensor)  # Image TVTensor

print(f"Image 원본: {type(img_tensor)}")  # Image TVTensor
print(f"Image shape: {img_tensor.shape}")  # torch.Size([3, 224, 224])
# Image를 순수 텐서로 변환
pure_tensor = to_pure(img_tensor)
print(f"변환후: {type(pure_tensor)}")   # torch.Tensor
print("="*10)

# BoundingBoxes의 메타데이터 예시
boxes = tv_tensors.BoundingBoxes(
    [[50, 50, 150, 150]], 
    format="XYXY", 
    canvas_size=(224, 224)
)
print(f"Boxes 메타데이터 - 포맷: {boxes.format}")        # XYXY
print(f"Boxes 메타데이터 - 캔버스: {boxes.canvas_size}")  # (224, 224)

# BoundingBoxes를 순수 텐서로 변환
pure_tensor = to_pure(img_tensor)
print(f"변환후: {type(pure_tensor)}")   # torch.Tensor
print(f"Boxes 메타데이터 - 포맷: {pure_tensor.format}") # error

3. PILToTensor() - PIL to 텐서 변환

3-1. 역할:

  • PIL Image를 PyTorch 텐서로 변환
  • 값 범위는 그대로 유지
torchvision 에서는 PILToTensro보다는
주로 ToImage를 보다 더 많이 사용함.

3-2. 파라미터:

PILToTensor()  # 파라미터 없음

3-3. 주요 특징:

  • 값 보존:
    • PIL의 [0, 255] 범위를 그대로 유지
  • 차원 변환:
    • (H, W, C) to (C, H, W) 자동 변환
  • 타입 변환:
    • PIL to uint8 텐서

3-4. 성능 이슈:

  • 빠름:
    • C++ 구현으로 최적화
  • 메모리 복사:
    • 새로운 텐서 생성 (메모리 사용량 증가)
  • CPU 전용:
    • CUDA 텐서로 직접 변환 안됨
    • PIL Image는 항상 cpu에서 처리됨.

3-5. 사용 예시:

from torchvision.transforms.v2 import PILToTensor
from PIL import Image
import torch

transform = PILToTensor()

# PIL Image 로드
pil_img = Image.open(img_path)  # RGB PIL Image
print(f"PIL 크기: {pil_img.size}")  # (width, height)
print(f"PIL 모드: {pil_img.mode}")  # RGB
print(f"PIL 범위: {pil_img.getextrema()}") 
print("-"*10)

# 텐서로 변환
tensor = transform(pil_img)
print(f"텐서 크기:   {tensor.shape}")   # torch.Size([3, height, width])
print(f"텐서 타입:   {tensor.dtype}")   # torch.uint8
print(f"텐서 값 범위: {tensor.min()}-{tensor.max()}")  # 0-255
print(f"{tensor.device = }") # device(type='cpu')
print("-"*10)

# 그레이스케일 이미지
gray_pil = Image.open(img_path).convert('L')
gray_tensor = transform(gray_pil)
print(f"그레이 크기: {gray_tensor.shape}")  # torch.Size([1, height, width])
print(f"{tensor.device = }") # device(type='cpu')
print("-"*10)

# 성능 비교
import time

# PIL to Tensor 변환 시간 측정
start = time.time()
for _ in range(100):
    tensor = transform(pil_img)
pil_time = time.time() - start
print(f"PILToTensor 100회: {pil_time:.4f}초")

 


4. ToPILImage([mode]) - 텐서 to PIL 변환

4-1. 역할:

  • PyTorch 텐서나 NumPy 배열을
  • PIL Image로 변환

4-2. 파라미터:

ToPILImage(mode=None)
  • mode (str, optional): PIL 이미지 모드 ('RGB', 'L', 'P' 등)
    • None: 자동 추론 (기본값)
    • 'RGB': 3채널 컬러 이미지
    • 'L': 그레이스케일
    • 'P': 팔레트 모드

4-3. 주요 특징:

  • Automatic Value Scaling:
    • from [0,1] float
    • to [0,255] uint8
    • 자동 변환
  • 차원 변환:
    • from (C, H, W)
    • to (H, W, C)
    • 자동 변환
  • 타입 추론:
    • 텐서 shape 에 따라
    • PIL Image객체의 mode 자동 결정: Batch처리가 안됨 - 주의

4-4. 성능 이슈:

  • 느림: PIL 변환은 상대적으로 비용이 높음
  • 메모리 복사: 새로운 PIL 객체 생성
  • CPU 강제:
    • GPU 텐서는 CPU로 (자동) 이동 후 변환
    • 하지만 가급적 명시적 처리를 권장.

4-5. 사용 예시:

from torchvision.transforms.v2 import ToPILImage
import torch

transform = ToPILImage()

# 1. Float 텐서 (0-1 범위)
float_tensor = torch.rand(3, 224, 224)  # [0, 1] 범위
pil_img = transform(float_tensor)
print(f"RGB PIL 모드: {pil_img.mode}")  # RGB

# 2. Int 텐서 (0-255 범위)
int_tensor = torch.randint(0, 255, (3, 224, 224), dtype=torch.uint8)
pil_img2 = transform(int_tensor)
print(f"RGB PIL 모드: {pil_img2.mode} / {pil_img2.getextrema()=}")  # RGB

# 3. 그레이스케일
gray_tensor = torch.rand(1, 224, 224)
gray_pil = transform(gray_tensor)
print(f"그레이 PIL 모드: {gray_pil.mode} / {gray_pil.getextrema()=}")  # L

# 4. 명시적 모드 지정

# 명시적 RGB 변환도 채널 안 맞으면 에러.
# rgb_from_gray = ToPILImage(mode='RGB')(gray_tensor) # error!

gray_to_rgb_tensor = gray_tensor.repeat(3, 1, 1)  # 1채널 → 3채널 복제
rgb_from_gray = ToPILImage(mode='RGB')(gray_to_rgb_tensor)
print(f"그레이(3ch) PIL 모드: {rgb_from_gray.mode} / {rgb_from_gray.getextrema()=}")  # RGB

gray_to_rgb_tensor = gray_tensor.repeat(4, 1, 1)  # 1채널 → 4채널 복제
rgb_from_gray = ToPILImage()(gray_to_rgb_tensor)
print(f"그레이(4ch) PIL 모드: {rgb_from_gray.mode} / {rgb_from_gray.getextrema()=}")  # RGBA

# 5. GPU 텐서 처리
if torch.cuda.is_available():
    gpu_tensor = torch.rand(3, 224, 224).cuda()
    # ToPILImage는 자동으로 CPU로 이동
    pil_from_gpu = transform(gpu_tensor)
    print(f"from gpu PIL 모드: {pil_from_gpu.mode} / {pil_from_gpu.getextrema()=}")  # RGB

# 6. batch 텐서 (0-255 범위)
int_tensor = torch.randint(0, 255, (8, 3, 224, 224), dtype=torch.uint8)
# pil_img3 = transform(int_tensor) # error!
# print(f"RGB PIL 모드: {pil_img3.mode} / {pil_img3.getextrema()=}")  # RGB

# 성능 주의사항
import time

# 큰 이미지에서 성능 측정
large_tensor = torch.rand(3, 2048, 2048)
start = time.time()
for _ in range(10):
    pil = transform(large_tensor)
conversion_time = time.time() - start
print(f"큰 이미지 PIL 변환 10회: {conversion_time:.4f}초")

 


5. ToDtype(dtype[, scale]) - 타입 변환 ***

5-1. 역할:

  • 입력의 데이터 타입(dtype)을 conversion하며,
  • 이미지/비디오의 경우에만, 선택적으로 value range도 조정: scale 파라메터 이용.

5-2. 파라미터:

ToDtype(dtype, scale=False)
  • dtype (torch.dtype): 목표 데이터 타입
  • scale (bool): 값 범위 조정 여부
    • True: 타입에 맞는 범위로 자동 스케일링
    • False: 값 그대로 유지 (기본값)

5-3. 주요 특징:

  • 유연한 변환:
    • 모든 PyTorch dtype 지원
  •  Smart Value Scaling (다른 URL에서 보다 자세히 다룰 것):
    • Pure Tensor 스케일링 적용:
      • 단독 전달 인 경우: to_dtype(pure_tensor) → 변환 및 스케일링 적용
      • 첫 번째 요소 인 경우:
        • tv_tensors.Image나 tv_tensors.Video가 없으면: to_dtype([pure_tensor, labels]) → 첫 번째만 변환 및 스케일링
        • 그 외의 경우 패스스루 (변환안됨)
      • 나머지 요소 인 경우: 하위 호환을 위해 패스스루 (변환안됨)
    •  TVTensor 처리
      • 단일 dtype 지정시:
        • tv_tensors.Image, tv_tensors.Video 스케일링 처리,
        • 나머지 모든 TVTensor는 패스스루
      • dict로 dtype 인자 지정시
        • tv_tensors.Image, tv_tensors.Video → 항상 스케일링
        • tv_tensors.BoundingBoxes, tv_tensors.Mask → dict로 명시적 지정해도 조건부 처리.
          • Image/Video가 없으면 역시 그냥 패스스루
          • Image/Video가 있을 경우 dtype만 변환
    •  Image와 Video에 대해 선택적 scaling 적용 때문에 smart value scaling이라고 칭함.
      • 단일 dtype 지정: Image/Video (또는 첫 번째 Pure Tensor)만 특별 처리, 나머지 모두 패스
      • 딕셔너리 지정: Image/Video는 항상 처리, 나머지는 Image/Video 존재 여부에 따라 조건부 처리
      • 일반 Tensor 보호: Image/Video가 있으면 첫 번째 포함해서 모든 일반 Tensor 패스스루

5-4. 성능 이슈:

  • 빠름:
    • PyTorch 네이티브 연산 사용
  • 메모리:
    • 새로운 텐서 생성
    • in-place 아님
  • 정밀도 손실:
    • float to int 변환 시 정보 손실의 위험이 있음.

5-5. 사용 예시:

from torchvision.transforms.v2 import ToDtype
import torch
import numpy as np

print("=== 스마트 스케일링 데모 ===")

# 1. 기본 uint8 → float32 스케일링
print("1. uint8 → float32 스마트 스케일링 (기본):")
uint8_img = torch.randint(0, 255, (3, 4, 4), dtype=torch.uint8)
print(f"원본 uint8: 범위 [{uint8_img.min()}, {uint8_img.max()}]")
print(f"샘플 값 : {uint8_img[0, 0, :3]}")

to_float = ToDtype(torch.float32)
float_img = to_float(uint8_img)
print(f"변환 float32: 범위 [{float_img.min():.3f}, {float_img.max():.3f}]")
print(f"샘플 값 : {float_img[0, 0, :3]}")
print("\n기본은 scale=False 임.")

print("\n" + "="*10)

# 2. 기본 uint8 → float32 스케일링
print("2. uint8 → float32 스마트 스케일링 (scale=True):")
uint8_img = torch.randint(0, 255, (3, 4, 4), dtype=torch.uint8)
print(f"원본 uint8: 범위 [{uint8_img.min()}, {uint8_img.max()}]")
print(f"샘플 값 : {uint8_img[0, 0, :3]}")

to_float = ToDtype(torch.float32, scale=True)
float_img = to_float(uint8_img)
print(f"변환 float32: 범위 [{float_img.min():.3f}, {float_img.max():.3f}]")
print(f"샘플 값 : {float_img[0, 0, :3]}")
print("\nPure Tensor라도 단독인 경우엔 scale 가능.")

print("\n" + "="*10)

# 3. 역변환: float32 → uint8
print("3. float32 → uint8 역방향 스케일링:")
to_uint8 = ToDtype(torch.uint8, scale=True)
restored_uint8 = to_uint8(float_img)
print(f"복원 uint8: 범위 [{restored_uint8.min()}, {restored_uint8.max()}]")
print(f"샘플 값: {restored_uint8[0, 0, :3]}")

# 정확성 검증
diff = torch.abs(uint8_img.float() - restored_uint8.float())
print(f"복원 오차: 최대 {diff.max():.1f} (반올림 오차)")
print("[0,1] → [0,255] 역변환 + 반올림")


# Results
# === 스마트 스케일링 데모 ===
# 1. uint8 → float32 스마트 스케일링 (기본):
# 원본 uint8: 범위 [5, 248]
# 샘플 값 : tensor([ 62, 130, 126], dtype=torch.uint8)
# 변환 float32: 범위 [5.000, 248.000]
# 샘플 값 : tensor([ 62., 130., 126.])
# 
# 기본은 scale=False 임.
# 
# ==========
# 2. uint8 → float32 스마트 스케일링 (scale=True):
# 원본 uint8: 범위 [10, 248]
# 샘플 값 : tensor([108, 237,  26], dtype=torch.uint8)
# 변환 float32: 범위 [0.039, 0.973]
# 샘플 값 : tensor([0.4235, 0.9294, 0.1020])
# 
# Pure Tensor라도 단독인 경우엔 scale 가능.
# 
# ==========
# 3. float32 → uint8 역방향 스케일링:
# 복원 uint8: 범위 [10, 248]
# 샘플 값: tensor([108, 237,  26], dtype=torch.uint8)
# 복원 오차: 최대 0.0 (반올림 오차)
# [0,1] → [0,255] 역변환 + 반올림

 


6. ConvertBoundingBoxFormat(format) - 바운딩박스 포맷 변환

6-1. 역할:

  • 바운딩 박스 좌표를
  • 다른 포맷으로 변환
    • XYXY,
    • XYWH,
    • CXCYWH
    • 기타

6-2. 파라미터:

ConvertBoundingBoxFormat(format)
  • format (str): 목표 바운딩박스 포맷
    • 'XYXY':
      • (x1, y1, x2, y2)
      • 좌상단, 우하단 좌표
    • 'XYWH':
      • (x, y, width, height)
      • 좌상단 + 크기
    • 'CXCYWH':
      • (center_x, center_y, width, height)
      • 중심점 + 크기

6-3. 주요 특징:

  • 포맷 자동감지: 입력 포맷 자동 인식
  • 정확한 변환: 수학적으로 정확한 좌표 변환
  • 배치 처리: 여러 박스 동시 변환 가능

6-4. 성능 이슈:

  • 매우 빠름: 단순 수학 연산만 수행
  • 메모리 효율: in-place 연산 가능
  • GPU 지원: CUDA 텐서에서도 동작

6-5. 사용 예시:

from torchvision.transforms.v2 import ConvertBoundingBoxFormat
# from torchvision.datapoints import BoundingBoxes # not working
from torchvision.tv_tensors import BoundingBoxes # datapoints 대신 tv_tensors 사용

import torch

# 1. 바운딩박스 생성 (XYXY 포맷)
boxes_xyxy = torch.tensor([
    [10, 20, 100, 150],   # x1, y1, x2, y2
    [50, 30, 200, 180],
], dtype=torch.float32)

bbox_xyxy = BoundingBoxes(
    boxes_xyxy, 
    format='XYXY', 
    canvas_size=(300, 400)  # 이미지 크기
)

print(f"원본 XYXY: {bbox_xyxy}")
print(f"포맷: {bbox_xyxy.format}")

# 2. XYXY to XYWH 변환
to_xywh = ConvertBoundingBoxFormat('XYWH')
bbox_xywh = to_xywh(bbox_xyxy)
print(f"XYWH 변환: {bbox_xywh}")
print(f"새 포맷: {bbox_xywh.format}")

# 3. XYXY to CXCYWH 변환
to_cxcywh = ConvertBoundingBoxFormat('CXCYWH')
bbox_cxcywh = to_cxcywh(bbox_xyxy)
print(f"CXCYWH 변환: {bbox_cxcywh}")

# 4. 포맷별 의미 해석
print("\n=== 포맷별 해석 ===")
print("XYXY:", bbox_xyxy[0])  # [x1, y1, x2, y2]
print("XYWH:", bbox_xywh[0])  # [x, y, width, height] 
print("CXCYWH:", bbox_cxcywh[0])  # [center_x, center_y, width, height]

# 5. 연쇄 변환
chain_transform = ConvertBoundingBoxFormat('CXCYWH')
final_boxes = chain_transform(bbox_xywh)  # XYWH to CXCYWH
print(f"연쇄 변환 결과: {final_boxes}")

# 6. Detection 파이프라인에서 활용
from torchvision.transforms.v2 import Compose, RandomHorizontalFlip

detection_pipeline = Compose([
    RandomHorizontalFlip(p=0.5),
    ConvertBoundingBoxFormat('CXCYWH'),  # YOLO 포맷으로 변환
])

# 7. 성능 테스트
import time

# 대량 박스 변환 성능
large_boxes = torch.rand(1000, 4) * 500  # 1000개 박스
large_bbox = BoundingBoxes(large_boxes, format='XYXY', canvas_size=(1000, 1000))

start = time.time()
for _ in range(100):
    converted = to_cxcywh(large_bbox)
conversion_time = time.time() - start
print(f"1000개 박스 변환 100회: {conversion_time:.4f}초")

# 8. 유효성 검증
def validate_conversion(original, converted, original_format, target_format):
    """변환 정확성 검증"""
    if original_format == 'XYXY' and target_format == 'XYWH':
        # XYXY to XYWH 검증
        x1, y1, x2, y2 = original[0]
        x, y, w, h = converted[0]
        assert torch.isclose(x, x1)
        assert torch.isclose(y, y1) 
        assert torch.isclose(w, x2 - x1)
        assert torch.isclose(h, y2 - y1)
        print("XYXY to XYWH 변환 정확")

validate_conversion(bbox_xyxy, bbox_xywh, 'XYXY', 'XYWH')

7. 정리:

7-1. 성능 순위:

  1. ConvertBoundingBoxFormat - 가장 빠름 (단순 수학 연산)
  2. ToPureTensor - 매우 빠름 (메타데이터 제거만)
  3. ToImage (기존 텐서 입력) - 빠름 (TVTensor 래핑만)
  4. ToDtype - 빠름 (PyTorch 네이티브 연산)
  5. ToImage (PIL/NumPy 입력), PILToTensor - 중간 (실제 변환 발생)
  6. ToPILImage - 상대적으로 느림 (PIL 변환 비용)

7-2. 메모리 사용 패턴:

  • ToImage:
    • ToImage: 입력에 따라 다름
    • 기존 텐서는 래핑만, PIL/NumPy는 새 텐서 생성
  • ToPureTensor:
    • 메모리 절약 (메타데이터 제거)
    • 호환성 향상.
  • PILToTensor/ToPILImage:
    • 새로운 객체 생성
  • ToDtype:
    • 타입에 따라 4배까지 증가 가능: uint8 에서 float32 의 경우.
728x90