Deep Learning through deep learning

# 3 데이터 전처리 - 이미지 파일 특성으로 폴더 분류 (Python 편) 본문

ML&DL/데이터 전처리

# 3 데이터 전처리 - 이미지 파일 특성으로 폴더 분류 (Python 편)

NeuroN 2023. 8. 9. 17:24

이번에도 어김없이 대회를 진행하면서 라벨링의 필요성을 느꼈고 그 과정에 대해 공유하고자 한다.

이번 대회는 k-ium 에서 주최하는 의료경진대회이다. 이미지를 보고 뇌동맥류 여부를 판별하는 모델을 구축하는 것을 목표로 한다.

모델은 image classification 혹은 object detection 이 필요해보였다.

대회에서 주어진 데이터셋은 그저 9000여장의 이미지 파일이었고, 바로 모델에 학습시키려고 보니 클래스 분류가 되어있지 않았다.

참 난감했고, 추가적으로 주어진 csv파일에서는 해당 이미지 이름을 인덱스로하여 뇌동맥류 여부 및 위치에 대한 정보가 들어있었다.

대회에서 요구하는 사항이 csv 파일을 통해 이미지를 알아서 분류하여 학습시켜라 라는 것임을 알 수 있었다.

이 글에서는 csv 파일을 이용해 이미지 파일 이름을 가지고 클래스를 분류해보겠다.

 


Introduction

주어진 데이터셋 구성이다.

  • 이미지를 살펴보면, 이름에 규칙이 있고, 모양에도 어느정도의 규칙이 있음을 알 수 있다.
  • 이 두가지 이미지 파일 특성을 가지고 클래스를 분류해보겠다.

주어진 csv 파일이다.

  • csv 파일도 살펴보면, 이미지 파일의 이름에서 인덱스를 가져왔다는 것을 확인할 수 있다.
  • Aneurysm 은 뇌동맥 여부를 의미한다.
  • 나머지 특성은 뇌동맥 여부가 참일 경우, 그 위치에 대한 정보를 의미한다.

주어진 이미지 파일과 csv 파일만을 가지고 어떻게 분류를 해나갈지 감이 잡히는가?

계속된 고민끝에 다다른 결론으로 다음과 같이 이미지 클래스를 분류하였다.

  1. 첫 번째로 먼저 이미지 생김새에 따라 클래스를 분류한다. LI-A, LI-B, ... RV-A,RV-B 까지 총 8종류의 생김새를 가진 이미지들을 확인할 수 있다. 이미지분류의 첫번째 과제는 먼저 비슷한 특성을 가진 이미지끼리 분류하는 것이다. (총 8class)
  2. 두 번째로는 뇌동맥류 여부에 따라 클래스를 분류한다. 이는 csv파일을 참고하여 해당 인덱스에 해당하는 이미지파일이 뇌동맥류 여부 참이면 1, 거짓이면 0으로 분류하였다. (총 2class)
  3. 세 번째로는 뇌동맥류 여부 참인 경우 위치에 대한 클래스로 분류한다. 이 과정에서 csv의 21가지의 위치 정보에 대해 1차원 배열로 만들어 중복을 제거한 결과 총 95가지의 위치 종류가 나왔다. (총 95class)

이렇게 분류한다면 모델을 어떻게 적용하는가?

3번의 학습을 진행하기로 했다.

첫 번째 8개의 클래스를 분류하기 위한 모델.

두 번째 2개의 클래스를 분류하기 위한 모델 8개 (LI-A,LI-B... 총 8가지)

세 번째 95개의 클래스를 분류하기 위한 모델 8개 (위와 같은 이유)

총 17개의 모델이 만들어져야 했고, 데이터 전처리 목적상 이에 대해서는 다루지 않겠다.

 

이렇게 분류한다면 모델은 classification 이지 않나? object detection 은 사용하지 않는가?

사용하지 않았다. yolo를 통해 학습도 시켜보았지만, 뇌동맥류 여부에 대한 라벨링은 인간의 손으로 직접해야했고, 눈으로 판별하여 라벨링하기엔 전문지식이 턱없이 부족했다.


Pyhton Coding

1. 먼저 이미지 생김새에 따라 8개의 폴더로 분류하는 코드이다.

import pandas as pd
import os 
import shutil
from PIL import Image	

path = '이미지데이터폴더경로/' # 이미지 데이터가 담긴 폴더 경로 넣기
os.chdir(path) 
files = os.listdir(path) 

NNN = 'LI-A'

for j in range(8):
	os.makedirs('저장할폴더경로/'+NNN) # '.'안에 폴더를 저장할 경로 넣기
	for k in range(len(files)):
		if NNN in files[k]:
			shutil.copy(files[k], '저장할폴더경로/'+NNN) # '.'안에 위의 저장할 경로와 같은 경로 넣기
	if j==0:
		NNN = 'LI-B'
	if j==1:
		NNN = 'LV-A'
	if j==2:
		NNN = 'LV-B'
	if j==3:
		NNN = 'RI-A'
	if j==4:
		NNN = 'RI-B'
	if j==5:
		NNN = 'RV-A'
	if j==6:
		NNN = 'RV-B'
  1. import 는 생략한다.
  2. path 를 통해 이미지데이터 속 이미지 경로를 리스트로 불러온다.
  3. 이미지 생김새 종류가 8개이므로, 최상단 for문은 8번 반복이된다.
  4. 먼저 저장할 경로폴더 안에 해당 이미지 생김새 종류에 대한 폴더를 생성한다 (제일 처음은 LI-A 폴더)
  5. 이후 for문에서는 이미지 생김새 종류인 NNN 문자를 이미지데이터 폴더 이름에서 찾아 생성한 폴더로 이미지를 복사해준다.
  6. 이후 j가 증가할때마다 NNN 문자가 갱신된다.

결과는 아래와 같다.

8개의 클래스 폴더가 생성되어 이미지 분류 모델에 넣고 학습시키면 된다.

 

2. 다음은 뇌동맥류 여부에 따른 1,0 클래스 폴더 분류 코드이다.

import pandas as pd
import csv
data = pd.read_csv('csv파일경로') # 이미지 데이터 정보가 담기 csv파일 경로

import os 
import shutil
from PIL import Image

path = '위에서생성한폴더경로/LI-A/' # 이전에 만들었던 폴더의 LI-A 부터 RV-A까지 총 8개의 경로를 차례대로 넣으시면 됩니다. 여기서는 LI-A에 대한 경로를 넣었습니다.
os.chdir(path) 
files = os.listdir(path) 
index_1 = []
index_0 = []
for i in range(len(data)):
	if data.iloc[i,1] == 1:
		index_1.append(data.iloc[i,0])
	else:
		index_0.append(data.iloc[i,0])

for i in range(len(index_1)):
	for j in range(len(files)):
		os.chdir(path) 
		if str(index_1[i]) in files[j]:
			shutil.move(files[j], '위에서생성한폴더경로/LI-A/대동맥여부_1') # 경로에 추가할 폴더 1폴더 경로
		
for i in range(len(index_0)):
	for j in range(len(files)):
		os.chdir(path) 
		if str(index_0[i]) in files[j]:
			shutil.move(files[j], '위에서생성한폴더경로/LI-A/대동맥여부_0') # 경로에 추가할 폴더 0폴더 경로
  1. 이제부터는 csv 파일이 필요하다. data에서 csv 파일을 불러와준다.
  2. path 에서는 위에서 생성한 폴더를 이용한다. LI-A 부터 RV-B 까지 총 8개이므로 8번 코드를 실행해주면서 경로를 바꿔주면 된다.
  3. 첫 번째 for문에서는 csv파일 속 뇌동맥류 여부가 1인 것과 0인것의 인덱스를 분류해 index_1, index_0 리스트에 담는다.
  4. 두 번째 for문에서는 뇌동맥류 참 여부인 1에 대한, 세 번째 for문에서는 뇌동맥류 거짓여부인 0에 대한 폴더 분류에 들어간다.
  5. 폴더 분류는 이미지 데이터 폴더 경로인 path에서 이미지 이름과 index_1, index_0 의 인덱스를 비교하여 대동맥여부_1 또는 대동맥여부_0 폴더로 분류한다.

결과는 다음과 같다.

2개의 클래스로 분류되어 모델에 넣고 학습을 돌리면 된다.

 

3. 마지막으로 뇌동맥류 여부가 1인 경우(참인 경우) 그 위치에 대한 클래스 폴더 분류 코드이다.

import pandas as pd
import csv
data = pd.read_csv('./train.csv') # 이미지 데이터에 대한 csv 파일 경로 넣기

import os 
import shutil
from PIL import Image

path = './train_set' # 이미지 데이터가 담긴 폴더 경로 넣기
os.chdir(path) 
files = os.listdir(path) 
a = []
b = []

for i in range(21):
	a.append(0)
    
for i in range(len(data)):
	for j in range(2,23):
		if data.iloc[i,j]==1:
			a[j-2] += 1
	b.append(a)
	a = []
	for i in range(21):
		a.append(0)

result = []
for value in b:
    if value not in result:
        result.append(value)
        

nlist = []
reslist = []
cls_res_list = [[0 for j in range(1)] for i in range(len(result))]
count = 0
for i in range(len(result)):
	for j in range(len(data)):
		for k in range(2,23):
			nlist.append(data.iloc[j,k])
				
		if (nlist == result[i]):
			reslist.append(data.iloc[j,0])
		nlist.clear()
	print(reslist)
	for k in range(len(reslist)):
		cls_res_list[i].append(reslist[k])
	reslist.clear()

for i in range(len(result)):
	print(i,"번: ",result[i])
for i in range(len(cls_res_list)):
	del cls_res_list[i][0]
for i in range(len(cls_res_list)):
	print(i,"번: ",cls_res_list[i]) 

for j in range(1, len(cls_res_list)):
	os.makedirs('./'+str(j)) # '.'안에 폴더를 저장할 경로 넣기
	for k in range(len(cls_res_list[j])):
		for i in range(len(files)):
			if str(cls_res_list[j][k]) in files[i]:
				shutil.copy(files[i], './'+str(j)) # '.'안에 위의 저장할 경로와 같은 경로 넣기
  1. for문 이전까지는 csv와 이미지데이터셋을 불러오는 과정이니 생략한다.
  2. 첫 번째 for문에서 a에 위치 정보 21가지에 대한 값을 넣기위해 0으로 초기화해준다.
  3. 두 번째 for문에서 csv 파일 정보 data를 통해 위치가 1인 곳의 인덱스를 1로 나머지는 0 으로하여 b 배열에 1차원 배열 a를 담는 방식이다. a는 또다시 0이 담긴 21개짜리 1차원 배열로 초기화해주며 이 과정을 반복하면, b 2차원 배열에는 위치에 대한 1차원 배열들이 csv파일 index 수만큼 들어가게 된다.
  4. 이후 result에서는 b 배열에서 중복값을 없애준다. 결과 result에는 중복이 없는 위치에 대한 1차원 배열 95개가 들어가게 된다.
  5. 이후 나오는 3중 for문을 요약하면, cls_res_list 라는 배열에 result 위치 정보 배열를 토대로 같은 위치를 가진 인덱스끼리 그룹을 만들어 2차원 배열을 형성해준다.
  6. 이후 나오는 3개의 for문을 확인해보면,
    1. 첫 번째 for문에서는 result에는 위치 벡터에 대한 2차원 배열이
    2. 두 번째 for문에서는 위 3중 for문에서 들어간 쓰레기 값을 없애기 위해 0번째 인덱스 값을 다 없애주는 과정을
    3. 세 번째 for문에서는 cls_res_list 가 어떻게 구성되어있는지 확인이 가능하다. 같은 위치 인덱스끼리 묶어있는 것을 확인할 수 있다.
  7. 마지막으로 3중 for문이 한번 더 나오는데,
    1. 여기서는 본격적으로 첫 번째 for문에서 저장한 폴더를 만들어준다. 이때 폴더 이름은 0~94까지 만들어진다.
    2. 두, 세 번째 for문에서는 cls_res_list의 인덱스번호랑 이미지파일 이름이랑 비교하여 해당하는 이미지를 해당 폴더 이름에 넣어준다.

결과는 다음과 같다.

총 95개의 클래스로 분류되어 같은 위치의 이미지끼리 묶여 들어간다.


else

이미지의 특징과 이름, 주어진 csv파일 정보를 토대로 python 알고리즘을 활용해 데이터셋을 분류하는 방법에 대해 알아보았다.

물론 이미 분류된 데이터셋을 구하면 좋겠다면, 현실에서 우리의 마음대로 데이터셋을 활용하기 위해서는 대부분 날것을 가져오기 때문에 이런 가공의 과정이 필요하다고 생각한다.