커리큘럼 : 이유한님 캐글 필사 스터디
https://kaggle-kr.tistory.com/32
캐글 대회 : 2018 Data Science Bowl
https://www.kaggle.com/competitions/data-science-bowl-2018
필사한 커널 : Teaching notebook for total imaging newbies (STEPHEN BAILEY)
https://www.kaggle.com/code/stkbailey/teaching-notebook-for-total-imaging-newbies/notebook
분석이 처음인 사람을 위하여
본 캐글은 scipy와 numpy, skickit-image를 활용해 간단한 파이프 라인을 구성하는데 초점을 둡니다.
매개 변수를 최적화 하는 용도 외에 훈련 이미지 조차 사용하지 않습니다.
데이터 가져오기
imageio.imread( path )
Grey 스케일
- scikit-image : 기본적으로 이미지를 자른다던가 단순한 필터링 등의 이미지 조작이 가능하고 Numpy 배열로 동작하기때문에 Numpy를 활용한 연산이 쉽습니다. 일부 Pillow보다 고급 기능을 제공하기도 하고 픽셀 값이 0과 1사이에 있는 float 이미지를 다룰 수도 있습니다.
- rgb2gray : rgb 3 채널을 gray 1채널로 바꿉니다.
PNG 파일은 위에서 언급했든 4채널이기 때문에 다음과 같이 오류 메세지가 떴습니다.
error : the input array must have size 3 along channel_axis, got (520, 696, 4)
채널 수 확인 했습니다.
Removing background
- 아마도 이 문제에 대한 가장 간단한 접근법은 이미지에 관심 있는 개체와 배경이라는 두 가지 클래스가 있다고 가정하는 것이다.
- 이 가정 하에서, 우리는 데이터가 강도의 바이모달 분포에 속할 것으로 예상한다.
- 만약 우리가 최고의 분리값을 찾는다면, 우리는 배경 데이터를 "mask"할 수 있을 것이고, 그리고 우리에게 남겨진 물체들을 간단히 셀 수 있을 것이다.
- 임계값을 찾을 수 있는 "가장 멍청한" 방법은 평균 또는 중위수와 같은 간단한 설명 통계를 사용하는 것입니다.
- "Otsu" 방법은 이미지를 바이모달 분포로 모델링하고 최적의 분리 값을 찾기 때문에 유용합니다.
각 개체에 대한 개별 마스크 도출
- 이번 대회에서는 각 핵마다 별도의 마스크를 씌워야 합니다.
- 이 작업을 수행하는 한 가지 방법은 연결된 마스크의 모든 개체(핵)를 찾고 ndimage.label을 사용하여 각 개체에 번호를 할당하는 것입니다.
- 그런 다음 각 label_id를 반복하여 목록과 같은 반복 가능한 파일에 추가할 수 있습니다.
- ndimage.rotate 를 하면 이미지를 다각도로 회전시켜볼 수 있습니다.
- ndimage.label 를 사용 하면 특정 값(가령 thresh_val) 보다 큰 부위(구역)를 각각 라벨링 할 수 있습니다.
가령 두 구역이 잡힐 경우 nlabels 는 2가 됩니다. - skimage.measure.regionprops 를 사용하면 구역의 픽셀 갯수, bounding box와 같은 사이즈의 이진 region 이미지, bounding box의 데이터 등을 확인할 수 있겠끔 해준다고 합니다.
참고 : https://cumulu-s.tistory.com/38
흝어본 결과..
- 독립적으로 서 있는 몇 개의 개별 픽셀이 있습니다(예: 오른쪽 상단).
- 일부 셀은 단일 마스크(예: 상단-중간)로 결합됩니다.
- ndimage 사용.find_filename, 우리는 추가적인 처리 단계를 적용하기 위해 발견된 개별 핵을 확대하면서 마스크를 통해 반복할 수 있다.
- find_objects는 이미지의 각 레이블링된 객체에 대한 좌표 범위 목록을 반환합니다.
너무 작은 잡음은 제거
침식과 팽창
레이블 #2에는 "인접 셀" 문제가 있습니다. 두 셀은 동일한 객체의 일부로 간주됩니다. 여기서 우리가 할 수 있는 한 가지는 마스크를 축소하여 세포 간의 차이를 "open up" 할 수 있는지 확인하는 것입니다. 이것은 mask erosion이라고 불립니다. 그런 다음 원래 비율을 회복하기 위해 다시 확장시킬 수 있습니다.
binary_opening은 mask erosion 작업과 동시에 opening 작업을 한다고 보면 될 것 같습니다.
erosion이 침식이라는 의미 그대로 레이어를 한번 씌울 때마다 noise가 벗겨지는걸 생각하면 될 것 같습니다.
위 함수에서는 8번 noise가 벗겨졌습니다.
그리고 일반적으로 erosion만 하는게 아니라 그만큼 팽창을 또 한다고 하네요.
물론 팽창을 하고 나면, erosion으로 깎인 부분이 다시 돌아오겠지만 noise는 다시 돌아오지 않는다고 합니다.
그런 의미에서 erosion 작업과 opening 작업이 같이 이루어진다라고 말하는 것 같습니다.
위 링크에서 처럼 관련 개념은 erosion과 팽창을 검색하니 많이 나오는 것 같습니다.
후에 관련 개념을 다시 정리할 일이 올 것입니다.
Convert each labeled object to Run Line Encoding
마지막으로 각 label_mask를 "run line encoded" 문자열로 인코딩해야 합니다.
Combine it into a single function
- Now that we've seen the basic steps to processing an image in a "dumb" way, we can combine it all into a single function.
- 이제 이미지를 "덤" 방식으로 처리하는 기본 단계를 보았으므로, 이 모든 것을 single function으로 결합할 수 있습니다.
- 이 함수는 이미지 경로를 취하여 위에 설명된 프로세스를 수행하고 발견된 각 마스크에 대해 RLE strings가 포함된 데이터 프레임을 출력합니다.
- 우리는 또한 데이터 세트의 모든 이미지에 대해 단일 데이터 프레임을 뱉어내는 wrapper function를 만든다.
import pandas as pd
def analyze_image(im_path):
'''
Take an image_path (pathlib.Path object), preprocess and label it, extract the RLE strings
and dump it into a Pandas DataFrame.
'''
# Read in data and convert to grayscale
im_id = im_path.parts[-3]
im = imageio.imread(str(im_path))
im_gray = rgb2gray(im)
# Mask out background and extract connected objects
thresh_val = threshold_otsu(im_gray)
mask = np.where(im_gray > thresh_val, 1, 0)
if np.sum(mask==0) < np.sum(mask==1):
mask = np.where(mask, 0, 1)
labels, nlabels = ndimage.label(mask)
labels, nlabels = ndimage.label(mask)
# Loop through labels and add each to a DataFrame
im_df = pd.DataFrame()
for label_num in range(1, nlabels+1):
label_mask = np.where(labels == label_num, 1, 0)
if label_mask.flatten().sum() > 10:
rle = rle_encoding(label_mask)
s = pd.Series({'ImageId': im_id, 'EncodedPixels': rle})
im_df = im_df.append(s, ignore_index=True)
return im_df
def analyze_list_of_images(im_path_list):
'''
Takes a list of image paths (pathlib.Path objects), analyzes each,
and returns a submission-ready DataFrame.'''
all_df = pd.DataFrame()
for im_path in im_path_list:
im_df = analyze_image(im_path)
all_df = all_df.append(im_df, ignore_index=True)
return all_df
testing = pathlib.Path('../input/stage1_test/').glob('*/images/*.png')
df = analyze_list_of_images(list(testing))
df.to_csv('submission.csv', index=None)
소스코드 주소
https://github.com/Jungtaxi/MyFirstKaggleStudy/blob/main/Segmentation/For_Newbie.ipynb
'EDA' 카테고리의 다른 글
[pandas] timedelta 구하기 (2) | 2022.10.11 |
---|---|
[pandas] sort가 stable 하다는걸 활용한 다중 정렬 (0) | 2022.10.11 |
[Pandas] dictionery를 사용한 맞춤 정렬 (0) | 2022.09.11 |
[numpy] kmeans.cluster_centers_[kmeans.labels_] (0) | 2022.08.27 |
[SNS] Seaborn 의 factorplot을 이용해보자. (0) | 2022.07.19 |