문제점. AP가 0.9점 대로 굉장히 높음에도 불구하고 테스트 셋에 대한 성능이 극악했다. 이유가 뭘까 고심해보았고, 학습 세트 레이블링이 잘못된 건 아닐까 하고 뜯어보았다.
1. PIL image open을 이용하여 이미지와 레이블을 읽어 보니 레이블링이 엉망이었다.
PASCAL VOC 레이블링을 읽고 다음과 같이 DataFrame 형태로 저장해둔다.
df = pd.DataFrame()
for i, file in tqdm(enumerate(selcet_files)):
tree = elemTree.parse(file)
bbox = tree.find('object').find('bndbox')
filename = tree.find('filename').text
xmin = bbox.find('xmin').text
ymin = bbox.find('ymin').text
xmax = bbox.find('xmax').text
ymax = bbox.find('ymax').text
objectname = tree.find('object').find('name').text
df.loc[i, 'image'] = filename
df.loc[i, 'name'] = objectname
df.loc[i, 'xmin'] = xmin
df.loc[i, 'ymin'] = ymin
df.loc[i, 'xmax'] = xmax
df.loc[i, 'ymax'] = ymax
그러면 열어보면 df는 아래와 같은 형태로 나타난다. 이제 이를 하나씩 읽고 bounding box를 이미지 내에 표현해보자.
import matplotlib.pyplot as plt
from matplotlib import patches
def get_rectangle_edges_from_pascal_bbox(bbox):
xmin_top_left, ymin_top_left, xmax_bottom_right, ymax_bottom_right = bbox
bottom_left = (xmin_top_left, ymax_bottom_right)
width = xmax_bottom_right - xmin_top_left
height = ymin_top_left - ymax_bottom_right
return bottom_left, width, height
def draw_pascal_voc_bboxes(
plot_ax,
bboxes,
get_rectangle_corners_fn=get_rectangle_edges_from_pascal_bbox,
):
for bbox in bboxes:
bottom_left, width, height = get_rectangle_corners_fn(bbox)
rect_1 = patches.Rectangle(
bottom_left,
width,
height,
linewidth=4,
edgecolor="black",
fill=False,
)
rect_2 = patches.Rectangle(
bottom_left,
width,
height,
linewidth=2,
edgecolor="white",
fill=False,
)
# Add the patch to the Axes
plot_ax.add_patch(rect_1)
plot_ax.add_patch(rect_2)
def show_image(
image, bboxes=None, draw_bboxes_fn=draw_pascal_voc_bboxes, figsize=(10, 10)
):
fig, ax = plt.subplots(1, figsize=figsize)
ax.imshow(image)
if bboxes is not None:
draw_bboxes_fn(ax, bboxes)
plt.show()
from pathlib import Path
import PIL
import numpy as np
class CarsDatasetAdaptor:
def __init__(self, images_dir_path, annotations_dataframe):
self.images_dir_path = Path(images_dir_path)
self.annotations_df = annotations_dataframe
self.images = self.annotations_df.image.unique().tolist()
def __len__(self) -> int:
return len(self.images)
def get_image_and_labels_by_idx(self, index):
image_name = self.images[index]
image = PIL.Image.open(self.images_dir_path / image_name)
# image = ImageOps.exif_transpose(image)
# image = image.transpose(PIL.Image.FLIP_TOP_BOTTOM)
# image = image.transpose(PIL.Image.FLIP_LEFT_RIGHT)
pascal_bboxes = self.annotations_df[self.annotations_df.image == image_name][
["xmin", "ymin", "xmax", "ymax"]
].values
class_labels = np.ones(len(pascal_bboxes))
return image, pascal_bboxes, class_labels, index
def show_image(self, index):
image, bboxes, class_labels, image_id = self.get_image_and_labels_by_idx(index)
print(f"image_id: {image_id}")
show_image(image, bboxes.tolist())
print(class_labels)
cars_train_ds = CarsDatasetAdaptor(train_data_path/'../JPEGImages/', df)
이미지 레이블링이 뭔가 규칙적으로 개떡같다...
처음에는 레이블링이 잘못된줄 알았다. 그랬지만....
2. 레이블링 툴을 이용하여 학습 데이터 셋을 읽을 경우 레이블링이 잘 되어있음을 확인하였다. (레이블링 문제는 아님)
내 코드에 뭐가 잘못된 걸까? 확인에 확인을 거듭해봤는데 이상한 점은 없었다.
3. 학습 모델이 학습 세트는 제대로 디텍팅하는지 확인해봤다.
그러다가 가로 세로가 뒤 바뀐게 아닌가?하는 생각이 들었다.
PIL image rotated로 검색을 해보니... 비슷한 현상을 경험한 누군가가 있었다.
https://stackoverflow.com/questions/4228530/pil-thumbnail-is-rotating-my-image
When a picture is taller than it is wide, it means the camera was rotated. Some cameras can detect this and write that info in the picture's EXIF metadata. Some viewers take note of this metadata and display the image appropriately.
PIL can read the picture's metadata, but it does not write/copy metadata when you save an Image. Consequently, your smart image viewer will not rotate the image as it did before.
이미지가 길쭉하면 카메라가 회전되어 있다는 걸 의미한단다. 어떤 카메라는 이런 메타정보까지 읽어서 저장한다. PIL은 이 사진의 메타정보를 읽어서 길쭉한걸 회전시켜서 읽어버린다...
그렇다 문제의 원인은 PIL.Image.open()이 쓸 데없이 고성능인 데 있다.
해결 방안은 코드 한 줄이다.
from PIL import Image, ImageOps
original_image = Image.open(filename)
fixed_image = ImageOps.exif_transpose(original_image)
그렇다면 학습도 이상하게 된 것일까? 학습 시 데이터 제너레이터에 위 코드를 적용시켜보고 ImageOps.exif_transpose전후를 비교해보았다. 전에는 넓찍하게 나온다. 원래 길쭉한 이미지인데... 저리하면 bounding box값도 이상하게 되지... 변환하니 길쭉하게 제대로 나오네
원인 파악 및 해결 완료
완전 삽질했네. 누군가가 삽질하지 않길 바라며 이 글을 쓴다.
학습이 얼마나 잘 되었는지는 결과를 공유하겠다.
'Data-science > deep learning' 카테고리의 다른 글
배경 차이가 뚜렷한 이미지에서, cv2.connectedComponetsWithStats 활용하여 객체 분리하고 자르기 + efficientDet (0) | 2021.08.30 |
---|---|
efficientDet을 이용한 detection, deeplearning (0) | 2021.08.28 |
[딥러닝] Graph2Vec 4가지 특징 (0) | 2021.05.04 |
[pytorch] torch.utils.data.DataLoader 이용시 파일 경로 출력 (0) | 2021.03.13 |
[imaginaire] coco-funit, mode collapse 문제 해결 삽질 (1) | 2021.03.10 |