예전부터 imgaug를 통해 이미지를 augmentation하고 싶은 마음이 있었으나, classification 과제를 할 당시에는 torchvision.transforms 를 통해 다양한 augmentation이 손쉽게 가능하여 필요성을 느끼지 못했다.
그런데! Semantic Segmentation을 활용하여 프로젝트를 하려고 하니, 원본 이미지 하나도 annotation하기 귀찮은데, augmentation한 이미지에 대해서는 annotation을 다시 어떻게 해주지?? 라는 생각이 들었다.
이렇게 augmentation은 포기해야 하나 싶었던 찰나에 imgaug는 이미지 뿐만 아니라, Heatmaps, Segmentation Maps, Masks, Keypoints/Landmarks, Bounding Boxes, Polygons, Line Strings을 모두 augmentation할 수 있다는 것을 알게 되었다.
Classification할 때는 읽히지 않았던 imgaug 깃헙 내 Example jupyter notebooks가 필요한 입장이 되니 술술 읽혔다. 사람은 참 간사한 동물이다ㅎㅅㅎ
그 중, 블로그 포스팅을 위해 참고한 예제는 Segmentation Maps를 augmentation하는 예제이다. (원문은 여기를 참고 바랍니다)
자 그럼 시작!
1. SegmentationMapsOnImage에 대한 설명
1) imgaug에서 segmentation map을 저장하는 방법
Segmentation maps는 2D array로 모든 픽셀은 정확히 하나의 클래스에 할당되어 있다.
imgaug에서 이는 imgaug.augmentables.segmaps.SegmentationMapsOnImage 인스턴스로 저장된다.
코드 상으로는 아래와 같다.
SegmentationMapsOnImage(arr, shape)
이 코드를 통해 2D array 형태의 Segmentation maps를 SegmentationMapsOnImage 인스턴스로 변환해준다.
* arr : 2D segmentation map
* shape : annotation 대상 image와 같은 shape
2) SegmentationMapsOnImage의 attributes
① .shape : 대상 이미지의 shape을 저장하고 있음
② .arr : segmentation map을 가지고 있음
3) SegmentationMapsOnImage의 주요 함수
① get_arr() : shape과 dtype이 SegmentationMapsOnImage 생성자에 처음 제공된 것과 동일한 segmentation map으로 변환시켜줌
② draw([size], [colors]) : segmentation map을 RGB 이미지로 변환시켜줌
③ draw_on_image(image, [alpha], [resize], [colors], [draw_background]) : segmentation map을 RGB 이미지로 바꾸준 후, 이를 image와 합성시켜줌
④ pad([top], [right], [bottom], [left], [mode], [cval]) : segmentation map을 지정한 면에 채워줌
⑤ pad_to_aspect_ratio(aspect_ration, [mode], [cval], [return_pad_amounts]) : segmentation map을 지정한 비율로 채워줌
⑥ resize(sizes, [interpolation]) : segmentation map을 지정한 사이즈로 변환해줌
⑦ augment_segmentation_maps() : segmentation을 augment해줌. 하나의 SegmentationMapsOnImage 인스턴스 혹은 SegmentationMapsOnImage 인스턴스의 리스트가 필요
⑧ augment(image=..., segmentation_map=...)
2. 예제
0) imgaug 설치
pip install six numpy scipy Pillow matplotlib scikit-image opencv-python imageio Shapely
pip install imgaug
1) Points로 부터 Segmentation Map 만들기
첫 예제의 목표는 이미지를 로드하고, segmentation map을 생성하고, 이미지와 segmap 모두를 augment하는 것이다. 가장 먼저 이미지를 로드하고 시각화하여 보자.
imageio 라이브러리를 통해 귀여운 다람쥐 사진을 불러왔다. 원본 이미지 크기는 (2128, 3193, 3)이다.
imgaug 라이브러리로 이미지의 가로 세로 길이를 각각 0.15배로 줄였다.
이제부터 (319, 479, 3) shape의 이미지를 활용하여 예제를 진행해 보겠다.
이제 다람쥐 이미지의 segmentation map이 필요하다.
"나무" 클래스와 "다람쥐" 클래스를 표기하기 위해 총 2개의 클래스가 필요하다. 그 외 모든 픽셀은 배경부가 된다.
나무와 다람쥐 부분을 다각형(polygons)로 표기하고 이를 segmentation map array로 만들어보자.
가장 먼저 나무 부분의 네 개 코너 점을 정의해주자.
이제 같은 방법으로 다람쥐 다각형의 꼭짓점도 따보자.
다음으로, 꼭짓점 리스트를 imgaug.augmentables.polys.Polygon 인스턴스(객체)로 만들어 준다.
이제, 두개의 polygons를 하나의 segmentation map으로 변환시켜줄 것이다.
가장 먼저 빈 (H,W,3) 크기의 배열을 만든다. 채널의 개수가 3인 이유는 배경, 나무, 다람쥐 총 세개의 classes로 분류할 것이기 때문이다.
그 다음, 마치 하나의 이미지인 것 처럼 빈 array 위에 tree polygons와 chipmunk polygons를 그려준다.
배열의 두번째 채널에 나무 polygons을 그려서 초록색으로 표기하고, 세번째 채널에는 다람쥐 polygons를 그려서 파란색으로 나타내자.
마지막으로 이 세개의 채널을 merge하여 하나의 segmentation map을 만드는 순으로 진행한다.
이제 본격적으로 segmentation map을 augmentation하기 위해 앞에서 만들어준 segmap을 imgaug.augmentables.segmaps.SegmentationMapsOnImage 클래스의 객체로 만들어 줘야 한다.
2) Segmentation Map을 Augment하기
segmentation map을 만들었으니 이제 해당 이미지와 함께 증폭(augment)시켜보자.
가장 먼저, augmentation할 sequence를 만들어준다.
이 예제에서는 coarse dropout, affine transformation, elastic transformation을 시퀀스에 추가해줄 것이다.
(여기서 segmentation map은 기하학적인 변형에 대해서만 변경이 되니 affine transformation, elastic transformation만 적용된다)
seq.augment(image=..., segmentation_maps=...)
# or
seq(image=..., segmentation_maps=...)
이제 위 코드를 사용하여 augmentation 파이브라인(seq)을 segmentation map과 image에 적용한다.
segmentation map이 affine 변환과 elastic 변환에 의해 어떻게 영향을 받는지 확인해보자. coarse dropout에 의해서는 영향을 받지 않았음을 확인할 수 있다.
또한 coarse dropout는 사각형 모양으로 dropout을 하게 되는데, 이후에 적용된 elastic 변환에 의해 일그러진 모습까지도 확인할 수 있다.
이때 image와 segmentation을 함께 augment해주지 않고 따로 augment해준다면 아래 오른쪽 그림과 같이 이미지와 segmap이 매칭되지 않게 된다.
3) Scaling Segmentation Maps
Segmentation maps는 resize하여 저해상도의 segmap으로 네트워크에 가볍게 주입될 수 있다.
resize()
위 함수를 활용하여 segmap을 1/4 크기로 줄이는 예제를 한번 살펴보자.
비율로 조정하는 것 외에 고정된 크기로도 조절 할 수 있다. (e.g. (100,200) 로 설정)
크기를 줄이기 전과 후의 segmap을 한번 시각화해보자.
오른쪽 사진은 segmap의 크기를 줄였다가 다시 키워 이미지 위에 그려졌다.
4) 크기를 줄인 Segmentation Maps를 Augment하기
network의 ground truth output segmentation map은 대개 input image보다 크기가 작다.
간단히 크기가 작은 segmentation map을 이미지와 같은 사이즈인 것 처럼 augmentation pipeline에 feed하면 된다.
다만 SegmentationMapsOnImage의 .shape attribute만 input image size와 동일하게 해주어야 한다.
이제부터 example image와 3)에서 크기를 줄인 segmap을 augment하는 코드를 살펴보자.
시작하기 앞서 이미지와 줄어든 segmap의 shape을 출력해보자.
이미지는 (319, 479, 3)
segmap의 실제 크기는 (80, 120, 1)
이미지 위에서의 segmap 크기는 (319, 479, 3) 이다 --> imgaug에서 자동으로 관리해줌
이제 augment를 하고, 이미지와 small scale segmap을 시각화해보자
smaller sized segmentation maps의 augmentation 역시 제대로 작동하는 것을 볼 수 있다.
다음은 image가 아래로부터 50 픽셀, 왼쪽으로부터 200 픽셀씩 crop된 경우에 대한 시나리오를 살펴보자.
이 때, 픽셀 수는 자동으로 smaller sized segmap에 대응되는 픽셀 수로 변환된다.
(비교를 위해 이미지와 크기가 같은 segmap 또한 augment하였다)
왼쪽이 normal scale segmap을 augment한 결과이고, 오른쪽이 small scale segmap을 augment한 결과이다. 두 segmap 모두 image 사이즈로 upscale되었으며 동일한 해상도를 가진다. (오른쪽 segmap은 저해상도를 upscale하여 깨져보이긴 하다)
글이 길어지는 관계로 남은 내용은 다음 포스팅에서 다뤄야겠다.
* 남은 내용들
5) Convert Augmented Segmentation Maps to Numpy Arrays
6) Pad Segmentation Maps
7) Draw Segmentation Maps
8) Change Segmentation Maps with Non-Geometric Augmentations
'Python' 카테고리의 다른 글
[PyQt5] Python으로 UI 만들기 (0) | 2020.04.08 |
---|---|
[Python] 아나콘다 설치 및 환경변수 설정 (0) | 2020.02.24 |
Pyinstaller 설치, 사용법, 옵션, 에러해결 (2) | 2020.02.04 |
[Python] # -*- coding: utf-8 -*- (0) | 2020.02.04 |
#!/usr/bin/env 정리글 (0) | 2020.02.04 |