Machine Learning & DeepLearning

Convert multiclass mask image to coco json 작업 + TypeError: Argument 'bb' has incorrect type (expected numpy.ndarray, got list) 에러 해결

gellygelly 2024. 4. 25. 22:52

해당 글은 MMDetection의 Swin Mask R-CNN Model을 사용하여 유방암 세그멘테이션 모델 학습 중 발생한 문제의 해결 방안을 정리해둔 글입니다.

 

 

(참고) MMDetection

OpenMMLab의 computer vision 프로젝트 중 하나로 Pytorch 기반의 open source object detection/segmentation toolbox이다.

이미지 인식 작업에 사용되는 다양한 모델들을 간편하게 갈아 끼우며 train/test 할 수 있는 것이 주요 특징이다.

 

(참고) Swin Mask R-CNN

MMDetection에서 지원하는 모델 아키텍처 중 하나. 나중에 Image Segmentation 관련해서 주제 잡고 글 쓸 때 Swin Mask R-CNN 논문 관련해서 자료 작성해뒀던 것도 올릴 예정.

 


  • 문제 상황 요약👻
    • 현재 진행하고 있는 실험 주제: 캐글 BCSS(Breast Cancer Semantic Segmentation) Dataset을 이용하여 유방암 Segementation 모델 개발 (실험중)
    • 발생 오류: BCSS Dataset을 coco format으로 변환한 뒤, model train을 진행하려고 했는데 아래와 같은 에러 발생 (TypeError: Argument 'bb' has incorrect type (expected numpy.ndarray, got list)
File "/opt/conda/lib/python3.7/site-packages/pycocotools/coco.py", line 427, in annToRLE
rles = maskUtils.frPyObjects(segm, h, w)
File "pycocotools/_mask.pyx", line 295, in pycocotools._mask.frPyObjects
TypeError: Argument 'bb' has incorrect type (expected numpy.ndarray, got list)

 

(참고) BCSS Dataset을 Coco format으로 변환한 이유?

→ Kaggle에 업로드 되어 있는 BCSS Dataset은 위와 같은 Image 파일과 클래스 정보가 매핑된 mask 파일로 이루어져 있는데, MMDetection에서는 json 형태의 annotation format을 요구하므로 요구하는 여러 json 포맷 중 하나를 골라 mask image를 특정 format의 json 파일로 변경해줘야 함.

 

<coco json format 예시>

 

  • 문제 해결을 위해 시도해본 것 (실패 💥)

1. swin mask rcnn config file에서 metric=['bbox', 'segm', 'proposal', 'proposal_fast’] proposal쪽 삭제

→ 이전에 학습시킬 때는 'proposal', 'proposal_fast’ 항목을 포함시키지 않았기 때문에, 이번에 해당 점수를 계산하는 과정에서 문제가 생긴 것일지도 모른다고 생각했다.

 

 

2. find contour 코드 수정

_, thresh = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # 이진화
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

→ contour가 제대로 구해지지 않아 문제가 생긴 거 같아 이진화 과정을 추가

 

 

3. area, bbox 영역 코드 수정

annotation_info['area'] = cv2.contourArea(contour)
annotation_info['bbox'] = cv2.boundingRect(contour)

 

→ bbox를 incorrect type이라고 하는 것을 보아, BCSS dataset의 mask image를 coco json으로 변환하는 과정에서, area/bbox 값을 잘못 넘겨줬던 것이 기억나 수정(bbox = [ x,y,w,h ] 인데 bbox = [ x, y, x+w, y+h]으로 넘겨줬었음)

 

 

4. make2json.py의 NpEncoder np.ndarray를 list로 변경하는 코드 주석처리

→ 위의 에러 메시지에서 list형으로 강제 변환한 것 때문에 오류가 나는 거 같아 해당 부분 주석 처리 진행

 

참고로, NpEncoder는 이전에 json 파일을 쓸 때 아래와 같은 오류가 발생했어서 넣은 클래스(JSON은 표준 data type만 처리하기 때문에 numpy의 data type으로 json 파일 write 하려고 하면 오류 발생함)

TypeError: Object of type ~ is not JSON serializable

 

우선은 마음에 걸리는 것을 위주로 가설을 세우고 전부터 마음에 걸렸던 것들 위주로 이것저것 시도해봤는데 전부 다 실패해서 얌전히 집단 지성의 힘을 빌리기로 했다…

 

 

  • 해결 방법 및 해결 과정

서치 결과, 파악한 문제의 원인은 아래와 같음.

 

※ 문제 원인

coco json file의 annotations[’segmentation’] 부분의 list 길이가 4일 때 segmentation points가 아닌 bbox points로 잘못 인식해서 생기는 문제

 

※ 해결 방법

간단하게, annotations[’segmentation’] 부분의 list 길이가 4 이하인 부분은 아예 json 파일 생성 시 포함하지 않으면 된다.

혹시나 하는 마음에 시각화해서 확인해 봤는데 아무래도 annotations[’segmentation’] 부분의 list 길이가 4 이하라는 것은 좌표 2개(x1, y1, x2, y2)라는 것이라 일직선 혹은 그냥 점이어서 제외하고 해도 문제가 없음을 확실하게 확인했다.

 

그러던 와중, 기존의 객체 mask에서 contour를 추출하는 과정에서 contour 추출 로직이 미흡해서 아래와 같이 contour 추출이 잘 안 되고 있는 것을 알게 되었다.

 

왼쪽이 mask image 시각화 결과이고 오른쪽이 mask image를 기반으로 contour 추출을 한 결과이다. mask image 내의 모든 객체에 대해서 contour 추출이 되어야 하는데, 일부만 된 것을 볼 수 있다.

기존 contour 추출 로직 결과 시각화

 

 

그래서 기존에는 단일 mask image → contour 추출을 바로 진행했는데, 각 클래스별 sub mask를 추출한 뒤 각각의 sub mask에서 contour를 추출하는 방식으로 변경했다.

로직 수정 후 contour 추출 결과 시각화

 

(참고) 512 이미지 마스크 및 컨투어 시각화 결과 상세

 

 

※ 개선된 최종 Mask2CocoJson 변환 방법

 

GitHub - gellygelly/mask2coco-converter: convert multiclass mask image to coco json file

convert multiclass mask image to coco json file. Contribute to gellygelly/mask2coco-converter development by creating an account on GitHub.

github.com

  • mask image를 json 파일로 변경하기 전 주의점: 파일명이 숫자인지 확인!

숫자로 변경하지 않고 했더니 오류가 발생해서 살펴보니, mmdetection 내 coco json file(annotation file)을 처리하는 내부 코드에서 파일명 처리할 때 숫자로 받게 되어있음.

 

  1. mask image 준비(파일명 숫자)
  2. GrayScale 변환
  3. Sub mask 추출
  4. Contour 추출
  5. segm, bbox 좌표 추출