Image Resize and Padding

2022. 12. 17. 12:34· 개발
목차
  1. 이미지와 바운딩박스를 리사이즈, 패딩해주는 소스
  2. 함수 설명
  3. 소스
  4. 실행
  5. 참고

이미지와 바운딩박스를 리사이즈, 패딩해주는 소스

 

* 640 x 480 -> 480 x 480 으로 변환하는 것 같이 height가 같은 경우는 처리했지만, 필요 시 width가 같은 경우 추가 처리가 필요하다.

함수 설명

SetImagePadding

이미지 패딩처리

  • image_path: 원본 이미지 경로
  • save_image_path: 저장할 이미지 경로
  • finalTargetSize: 패딩 처리한 이후의 이미지 사이즈

SetImageResizeNPadding

이미지 리사이즈 및 패딩 처리

  • image_path : 원본 이미지 경로
  • save_image_path : 저장할 이미지 경로
  • targetWidth : 리사이즈할 이미지 사이즈
  • targetHeight : 리사이즈할 이미지 사이즈
  • finalTargetSize : 패딩 처리 이후 최종 이미지 사이즈

SetBboxPadding

바운딩 박스 패딩처리

  • xmin : 바운딩 박스 좌표 xmin
  • ymin : 바운딩 박스 좌표 ymin
  • xmax : 바운딩 박스 좌표 xmax
  • ymax : 바운딩 박스 좌표 ymax
  • targetWidth : 패딩 전 이미지 크기
  • targetHeight : 패딩 전 이미지 크기
  • finalTargetSize : 패딩 후 최종 이미지 크기

SetBboxResizeNPadding

바운딩 박스 리사이즈 및 패딩 처리

  • imageToPredict: 원본 이미지
  • xmin : 바운딩 박스 좌표 xmin
  • ymin : 바운딩 박스 좌표 ymin
  • xmax : 바운딩 박스 좌표 xmax
  • ymax : 바운딩 박스 좌표 ymax
  • targetWidth : 리사이즈 후이면서 패딩 전 이미지 크기
  • targetHeight : 리사이즈 후이면서 패딩 전 이미지 크기
  • finalTargetSize : 패딩 후 최종 이미지 크기

SaveResizeNPadding(xml_path, current_img_path, new_image_path, new_txt_path, targetWidth, targetHeight, finalTargetSize, label2idx)

이미지와 바운딩 박스를 리사이즈/패딩 처리

  • xml_path : 원본 xml 경로
  • current_img_path : 원본 이미지 경로
  • new_image_path : 이미지를 리사이즈/패딩 처리하여 변경된 이미지를 저장할 경로
  • new_txt_path : xml 내 값을 리사이즈/패딩 처리하여 변경된 내용을 .txt로 저장할 경로
  • targetWidth : 리사이즈 후이면서 패딩 전 이미지 크기
  • targetHeight : 리사이즈 후이면서 패딩 전 이미지 크기
  • finalTargetSize : 패딩 후 최종 이미지 크기
  • label2idx: 라벨명을 숫자 라벨로 바꿔주기 위한 dictionary

소스

import os
import glob
import pandas as pd
import xml.etree.ElementTree as ET
from tqdm.notebook import tqdm
from google.colab.patches import cv2_imshow
import cv2
import numpy as np

def SetImagePadding(image_path, save_image_path, finalTargetSize):
    # 이미지 받을 경로, 저장할 경로, 중간 너비, 중간 높이, 최종 크기(정사각형이므로 길이하나만)
    # load image
    imageToPredict = cv2.imread(image_path, 3) # 단일 이미지 읽기
    img = np.array(imageToPredict); # 이미지 행렬화

    # padding
    y_zero, x_zero, target_height, target_width = (0, 0, img.shape[0], img.shape[1]) # 바뀐 이미지의 패딩계산을 위한 크기

    # 그림 주변에 검은색으로 칠하기
    padding_width = (finalTargetSize - (target_width - x_zero))/2  # w_x = (targetWidth - 그림)을 뺀 나머지 영역 크기 [ 그림나머지/2 [그림] 그림나머지/2 ]
    padding_height = (finalTargetSize - (target_height - y_zero))/2

    if(padding_width < 0):         # 크기가 -면 0으로 지정.
        padding_width = 0
    elif(padding_height < 0):
        padding_height = 0

    M = np.float32([[1, 0, padding_width], [0, 1, padding_height]])  #(2*3 이차원 행렬)
    img_re = cv2.warpAffine(img, M, (finalTargetSize, finalTargetSize)) # 패딩 실행

    # save image
    cv2.imwrite(save_image_path, img_re) # 저장


def SetImageResizeNPadding(image_path, save_image_path, targetWidth, targetHeight, finalTargetSize):
    # 이미지 받을 경로, 저장할 경로, 중간 너비, 중간 높이, 최종 크기(정사각형이므로 길이하나만)
    # load image
    imageToPredict = cv2.imread(image_path, 3) # 단일 이미지 읽기
    img = np.array(imageToPredict); # 이미지 행렬화

    # resize
    img = cv2.resize(imageToPredict, (targetWidth, targetHeight)); # 이미지 리사이즈
    img = np.array(img); # resize 된 이미지 행렬화

    # padding
    y_zero, x_zero, target_height, target_width = (0, 0, img.shape[0], img.shape[1]) # 바뀐 이미지의 패딩계산을 위한 크기

    # 그림 주변에 검은색으로 칠하기
    padding_width = (finalTargetSize - (target_width - x_zero))/2  # w_x = (targetWidth - 그림)을 뺀 나머지 영역 크기 [ 그림나머지/2 [그림] 그림나머지/2 ]
    padding_height = (finalTargetSize - (target_height - y_zero))/2

    if(padding_width < 0):         # 크기가 -면 0으로 지정.
        padding_width = 0
    elif(padding_height < 0):
        padding_height = 0

    M = np.float32([[1, 0, padding_width], [0, 1, padding_height]])  #(2*3 이차원 행렬)
    img_re = cv2.warpAffine(img, M, (finalTargetSize, finalTargetSize)) # 패딩 실행

    # save image
    cv2.imwrite(save_image_path, img_re) # 저장

    # return img, img_re

def SetBboxPadding(xmin, ymin, xmax, ymax, targetWidth, targetHeight, finalTargetSize):
    
    # padding
    y_zero, x_zero, target_height, target_width = (0, 0, targetHeight, targetWidth) # 원점설정과 패딩 추가 전 이미지 크기

    # 그림 주변에 검은색으로 칠하기
    padding_width = (finalTargetSize - (target_width - x_zero))/2  # padding_width = (targetWidth - 그림)을 뺀 나머지 영역 크기 [ 그림나머지/2 [그림] 그림나머지/2 ]
    padding_height = (finalTargetSize - (target_height - y_zero))/2  # 패딩크기

    if(padding_width < 0):         # 크기가 -면 0으로 지정.
        padding_width = 0
    elif(padding_height < 0):
        padding_height = 0

    return (int(xmin + padding_width), int(ymin + padding_height), int(xmax + padding_width), int(ymax + padding_height))


def SetBboxResizeNPadding(imageToPredict, xmin, ymin, xmax, ymax, targetWidth, targetHeight, finalTargetSize):
    # imageToPredict <- img # 패딩 없이 받아와서 패딩 추가 후 bbox 정렬
    img = np.array(imageToPredict); # 행렬화

    original_img_height = imageToPredict.shape[0] # 이미지의 높이
    original_img_width = imageToPredict.shape[1] # 너비
    x_scale = targetWidth / original_img_width # 패딩을 제외한 크기로 줄이기 위한 x비율
    y_scale = targetHeight / original_img_height # 패딩을 제외한 크기로 줄이기 위한 y비율
    # print(original_img_height, original_img_width, targetWidth, targetHeight)
    # print(xmin, ymin, xmax, ymax)
    xmin = int(np.round(xmin * x_scale)) # 비율에 맞추어 위치 재정렬 / 소수점 반올림 
    ymin = int(np.round(ymin * y_scale))  
    xmax = int(np.round(xmax * x_scale))
    ymax = int(np.round(ymax * y_scale))
    # print(xmin, ymin, xmax, ymax)
    # padding
    y_zero, x_zero, target_height, target_width = (0, 0, targetHeight, targetWidth) # 원점설정과 패딩 추가 전 이미지 크기

    # 그림 주변에 검은색으로 칠하기
    padding_width = (finalTargetSize - (target_width - x_zero))/2  # padding_width = (targetWidth - 그림)을 뺀 나머지 영역 크기 [ 그림나머지/2 [그림] 그림나머지/2 ]
    padding_height = (finalTargetSize - (target_height - y_zero))/2  # 패딩크기
    # print(padding_width, padding_height)
    if(padding_width < 0):         # 크기가 -면 0으로 지정.
        padding_width = 0
    elif(padding_height < 0):
        padding_height = 0

    return (int(xmin + padding_width), int(ymin + padding_height), int(xmax + padding_width), int(ymax + padding_height))


# xml 폴더경로, img 폴더경로, 새로운 img 저장할 경로, 새로운 txt 저장할 경로
def SaveResizeNPadding(xml_path, current_img_path, new_image_path, new_txt_path, targetWidth, targetHeight, finalTargetSize, label2idx):
    # load xml
    for xml_file in tqdm(glob.glob(xml_path + '/*.xml')): # 경로상 xml 모두 경로 포함 불러오기

        img_file_name = xml_file.split('/')[-1][:-4]                      # 파일명
        ouput_txt_filename = f'{new_txt_path}' + img_file_name + '.txt'   # 원하는 경로 / f'{dest_path}' +
        current_img_filename = current_img_path + img_file_name  + '.jpg' # 현재 xml 파일과 짝인 image 파일
        new_img_filename = new_image_path + img_file_name + '_{0}_{0}.jpg'.format(finalTargetSize, finalTargetSize) # 만들어줄 image 파일
        
        tree = ET.parse(xml_file) # xml을 읽기 
        root = tree.getroot()     # 변환

        original_image_width = int(root.find('size').find('width').text)      # 원래 이미지 크기
        original_image_height = int(root.find('size').find('height').text)
        # 이미지 크기가 바꾸고자 하는 이미지와 같을 경우 리사이즈를 안하고 패딩 처리만 하기 위해
        is_original_size_same = targetWidth == original_image_width or targetHeight == original_image_height   
        # 이미지 크기가 가로-세로, 세로-가로가 하나라도 바꾸고자 하는 이미지와 같을 경우 같은 구간은 맞춰주기 위해 
        is_original_size_small = targetWidth == original_image_height or targetHeight == original_image_width

        # image resize and padding
        if is_original_size_same:
          img_only_padding = SetImagePadding(current_img_filename, new_img_filename, finalTargetSize)
        elif is_original_size_small:
          # 최종 이미지와 원본 이미지와의 scale 계산
          original_image_scale = finalTargetSize/original_image_width           
          # 현재는 640 x 480 -> 480 x 480 같이 height가 같을 경우만 처리함. 재사용시 width가 같은 경우도 처리 필요
          targetHeight = int(original_image_height * original_image_scale)    
          SetImageResizeNPadding(current_img_filename, new_img_filename, targetWidth, targetHeight, finalTargetSize)
        else:
          SetImageResizeNPadding(current_img_filename, new_img_filename, targetWidth, targetHeight, finalTargetSize)

        original_img = cv2.imread(current_img_filename)
        f = open(ouput_txt_filename, 'w') # 작성 시작

        # bbox resize and padding
        # get xmin, ymin, xmax, ymax, width, height
        for member in root.findall('object'):           # 변환된 xml에서 객체수만큼 반복
            bbx = member.find('bndbox')                 # bbox 태그
            label = member.find('name').text            # trash 종류
            xmin = float(bbx.find('xmin').text)         # bbox 초기 xmin
            ymin = float(bbx.find('ymin').text)         # bbox 초기 ymin
            xmax = float(bbx.find('xmax').text)         # bbox 초기 xmax
            ymax = float(bbx.find('ymax').text)         # bbox 초기 ymax

            # 패딩 후 bbox 값을 튜플로 각 변수에 할당
            if is_original_size_same:
                (bbox_xmin, bbox_ymin, bbox_xmax, bbox_ymax) = SetBboxPadding(xmin, ymin, xmax, ymax, original_image_width, original_image_height, finalTargetSize)
            else:
                (bbox_xmin, bbox_ymin, bbox_xmax, bbox_ymax) = SetBboxResizeNPadding(original_img, xmin, ymin, xmax, ymax, 
                                                                              targetWidth, targetHeight, finalTargetSize)
            
            label_idx = label2idx[label]                    # trash의 라벨을 붙여준다
            x, y = bbox_xmin / finalTargetSize, bbox_ymin / finalTargetSize # 최종 이미지 크기에서의 비율로 변환
            w, h = (bbox_xmax - bbox_xmin) / finalTargetSize, (bbox_ymax - bbox_ymin) / finalTargetSize        

            info = f'{label_idx} {x} {y} {w} {h}' # 라벨과 나머지 값들을 통합
            
            f.write(info+'\n') # 파일 쓰기

실행

label2idx = {'bundle of ropes' : 0,'bundle of rope' : 0, 'rope' : 1, 'circular fish trap' : 2,'eel fish trap' : 3,'fish net' : 4,
                 'rectangular fish trap' : 5,'spring fish trap' : 6, 'tire' : 7, 'wood' : 8, 'other objects' : 9, 'other objets':9,
             'othe objects':9}


SaveResizeNPadding('/content/drive/MyDrive/Alpaco Object Detection/final/sonar_label/'
                  , '/content/drive/MyDrive/Alpaco Object Detection/final/sonar_image/'
                  , '/content/drive/MyDrive/Alpaco Object Detection/final/sonar_image_480/'
                  , '/content/drive/MyDrive/Alpaco Object Detection/final/sonar_label_480/'
                  , 480, 480, 480, label2idx)
                  
SaveResizeNPadding('/content/drive/MyDrive/Colab_Notebooks/Object_Detection_Project/final/underwater_label', 
                   '/content/drive/MyDrive/Colab_Notebooks/Object_Detection_Project/final/underwater_image/', 
                   '/content/img/', '/content/txt/', 640, 480, 640, label2idx)

참고

XML 파일 구조 및 내용

<?xml version="1.0" encoding="utf-8"?>
<annotation>
  <folder>[sonar]</folder>
  <filename>rope_spring fish trap_20111026_003_20027_12.jpg</filename>
  <path>../../[image]/[sonar]/rope_spring fish trap_20111026_003_20027_12.jpg</path>
  <size>
    <width>640</width>
    <height>640</height>
    <depth>3</depth>
  </size>
  <commoninfo>
    <datasetname>sonar dataset</datasetname>
    <createdate>2021-04-22 09:48:39.0</createdate>
  </commoninfo>
  <metainfo>
    <device>SonarBeam S-150</device>
    <viewername>PostScan</viewername>
    <viewerversion>v7.39</viewerversion>
    <location>
      <name>the West sea</name>
      <latitude>
        <DMS/>
        <DMM/>
        <DD>35.979781</DD>
      </latitude>
      <longitude>
        <DMS/>
        <DMM/>
        <DD>126.583333</DD>
      </longitude>
    </location>
    <depth-of-water>0.0</depth-of-water>
    <temperature/>
    <NTU/>
  </metainfo>
  <object>
    <name>rope</name>
    <bndbox>
      <xmin>276.57142857142856</xmin>
      <ymin>399.7142857142857</ymin>
      <xmax>634.2857142857142</xmax>
      <ymax>640.0</ymax>
      <width>357.71428571428567</width>
      <height>240.28571428571428</height>
    </bndbox>
    <grade>A</grade>
  </object>
  <object>
    <name>spring fish trap</name>
    <bndbox>
      <xmin>532.6051805492795</xmin>
      <ymin>318.0456496385726</ymin>
      <xmax>576.4202215874342</xmax>
      <ymax>336.0663520010395</ymax>
      <width>43.81504103815473</width>
      <height>18.020702362466864</height>
    </bndbox>
    <grade>A</grade>
  </object>
</annotation>

 

도움이 되셨길 바랍니다.

728x90
반응형

'개발' 카테고리의 다른 글

Couldn't instantiate the backend tokenizer from one of:  (0) 2022.12.29
yolov4 darknet weight to tensorflow model  (0) 2022.12.17
Model Ensamble in Keras  (0) 2022.12.03
BentoML 사용기  (0) 2022.12.03
learning rate for more improving  (0) 2022.11.29
  1. 이미지와 바운딩박스를 리사이즈, 패딩해주는 소스
  2. 함수 설명
  3. 소스
  4. 실행
  5. 참고
'개발' 카테고리의 다른 글
  • Couldn't instantiate the backend tokenizer from one of:
  • yolov4 darknet weight to tensorflow model
  • Model Ensamble in Keras
  • BentoML 사용기
내공얌냠
내공얌냠
내공냠냠
내공얌냠
내공냠냠
내공얌냠
전체
오늘
어제
  • 분류 전체보기 (254)
    • 개발 (113)
      • mediapipe (16)
      • insightface (5)
      • JongjuAR (3)
    • 자료구조 알고리즘 (79)
      • 코딩테스트 (64)
      • 이론 (15)
    • 공부 (7)
      • 단행본 (7)
      • 튜토리얼 (19)
      • 논문 (15)
      • 복기 (5)
    • 참여 (5)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

인기 글

태그

  • 컴퓨터 비전 책 추천
  • python telegrambot
  • flutter 행사
  • flutter
  • ios google places api
  • speaker adaptation tts
  • mediapipe translate
  • 컴퓨터 비전
  • 딥러닝 기반 음성인식 기초
  • git tutorial
  • kubeflow설치안됨
  • flutter tutorial
  • 음성인식 튜토리얼
  • 컴퓨터 비전 기초
  • postgresql install in mac
  • 머신러닝이란
  • kubeflow설치가이드
  • flutter conference
  • postgresql 재설치
  • vscode 스프링 설치
  • flutter 행사 후기
  • 깃 튜토리얼
  • google mediapipe
  • 구글 미디어파이프
  • 플러터 튜토리얼
  • 음성인식 기초
  • 미디어파이프
  • mediapipe
  • 플러터
  • torchscript vs onnx vs tensorrt

최근 댓글

최근 글

hELLO · Designed By 정상우.v4.2.2
내공얌냠
Image Resize and Padding
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.