이미지와 바운딩박스를 리사이즈, 패딩해주는 소스
* 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 |