전체 개요
- 단일 데이터로 복수의 데이터를 표현한다.
- 어레이 형태로 변환하여, Decoding 하면 특정 인덱스에서의 데이터 해석이 가능해진다.
- Computer Vision 캐글 Competition에서 컨셉을 접하게 되었으며, Image Segmentation 문제의 제출 포맷으로도 활용된다.
- 구체적으로는 특정 픽셀 인덱스 위치 값과, 픽셀 개수의 조합으로 Labeling 데이터를 구성하는데 사용되었으며, 실습으로 RLE 포맷을 이해할 수 있다.
사용해 보기
캐글의 "Severstal: Steel Defect Detection"을 활용하여 실습한다.
1. 데이터 준비 및 EncodedPixels Column 개요
- 데이터 프레임까지 준비 후, head() 메서드로 각 Column의 값들을 출력해 본다.
- EncodedPixels 값은 String 형태이다
- 각 숫자 데이터는 segmentation label 이 있는 픽셀 데이터를 나타낸다
- 즉, 해당 위치에 각 클래스에 해당되는 물체가 있다고 픽셀 데이터로 알려 주고 있는 것
- 데이터는 순서는 픽셀 위치, 픽셀 개수 순이며, 띄어쓰기로 각 데이터를 구분한다.(규칙성이 보인다.)
2. Code로 적용해 보기
전체적인 흐름은, 다음 과정으로 나누어서 생각할 수 있었다.
- 데이터 분리하기
- 데이터 타입 변경 하기(String ▶ int)
- 픽셀 위치, 픽셀 개수 어레이로 분리하기
- 마스크 된 이미지 만들기(1D ▶ 2D )
2-1. 데이터 분리하기
enco1_arr = train_df.EncodedPixels[0].split(' ')
enco1_arr[:10]
# ['29102', '12', '29346', '24', '29602', '24', '29858', '24', '30114', '24']
각 어레이 요소의 데이터 타입이 string이다.
하지만, 픽셀 인덱스와 길이 값으로 사용하기 위해서는 데이터 타입 변경이 수반되어야 한다.
2-2. 데이터 타입 변경하기(String ▶ int) + 2-3. 픽셀 위치, 개수 어레이로 분리하기
# position : 첫번째 요소 부터 두개씩 인터벌로 가져옴
# length : 두번째 요소 부터 두개씩 인터벌로 가져옴
enco1_pos = map(int, enco1_arr[0::2])
enco1_len = map(int, enco1_arr[1::2])
원본 어레이의 요소 값이 2개씩 규칙적으로 반복되기 때문에 위와 같이 어레이를 분류해 줄 수 있다.
2-4. 마스크 된 이미지 만들기(1D ▶ 2D)
이 작업에서는 원본 이미지가 있으며, 해당 이미지의 사이즈(width, height)를 알고 있다고 가정한다.
해당 이미지의 변수는 'img1'으로 정의하였다.
# 기존에 정의한 이미지 데이터 정보를 이용해서, 같은 수의 픽셀을 갖는 1차원 이미지를 정의 한다.
# np.zeros 는 numpy의 이미지 매트릭스를 생성하는 메소드 이며, 모든 픽셀의 값은 0 이다.
mask_1d = np.zeros(img1.shape[0] * img1.shape[1], dtype=np.uint8)
# ndarray.flatten 을 활용하는 방법도 있을 것이다.
과정 2-3에서 타겟으로 하고 있는 픽셀 위치와 픽셀 갯수를 분류했다. 해당 데이터를 이용해서, 타겟 위치의 픽셀값은 255 로 대입해준다. 255 는 가장 밝은 값의 픽셀을 나타낸다.( 픽셀 범위 : 0~255 )
for po, le in zip(enco1_pos, enco1_len):
mask_1d[po:po+le-1]=255
zeros 이미지에 마스킹(모든 픽셀 0의 상태, 타겟 부분만 가장 밝은 픽셀로 표시하는 것을 의미) 이 제대로 수행 됐는지 확인하기 위해, 1차원 매트릭스값의 모든 요소의 합을 구해본다.
mask_1d.sum()
# 1093440
위에서 보는 바와 같이, 원하는 위치에, 길이만큼 채워졌을 것으로 기대할 수 있다.
그럼, 이 1차원 데이터를, 원본 이미지에 맞게 reshape 해서 이미지를 완성해 본다.
- reshape을 할 때 order에 주의를 기울여야 한다.
- 공식 문서에 따르면, default는 C-like index 방식이지만,
- 이 문제(캐글 rule)에서는 1.top ▶ down, 2.left ▶ right 방식의 인덱스 룰(RLE포맷?)을 가진다는 부분이 있다.
- Fortran-like index order 방법을 따라야 한다.
참고한 내용은 다음과 같다.
해당 Kaggle Competition 'Evaluation' 중 해당 부분을 인용하였다.
The pixels are numbered from top to bottom, then left to right: 1 is pixel (1,1), 2 is pixel (2,1), etc.
고맙게도, reshape 메서드의 옵션 중에, reshape order를 지정할 수 있으며, "Fortran-like"에 해당하는 'F'를 부여함으로써 아주 쉽게 해결할 수 있다.
mask2d = mask_1d.reshape(img1.shape[0], img1.shape[1], order='F')
여기까지 마스크 이미지 만드는 과정은 끝이며, 결과를 확인해 보았다.
원본 이미지와 마스크 데이터를 동시에 출력해 보면 다음과 같다.
더욱, 분명하게 알아보기 위해서는, original 이미지에 겹쳐서 표현하는 게 좋을 것 같다.
해당 작업을 위해서, mask 이미지에 findContours 메서드를 사용하여 마스크 경계부 픽셀만 추출했다.
추출한 픽셀 데이터를 polylines 메소드를 이용하여 원본 이미지에 그려주면 다음과 같은 이미지를 출력할 수 있다.
'[ Python ]' 카테고리의 다른 글
[Python] numpy.ndarray.T 사용 해보기. (0) | 2023.05.02 |
---|---|
코랩(colab) 에서 캐글(kaggle) 데이터셋 이용 하는 방법 (0) | 2023.04.09 |
[Python] 뉴스 릴리스를 스크레이핑(Scraping) 하는 방법. (0) | 2020.01.06 |