포스트

COCO API로 이미지, 인스턴스, 키포인트, 캡션 찾는 순서

COCO API를 읽는 핵심은 annotation JSON으로 COCO 객체를 만든 뒤, 카테고리 ID → 이미지 ID → annotation ID 순서로 대상을 좁히는 것입니다. Instances, keypoints, captions는 같은 이미지를 설명해도 서로 다른 JSON과 필드를 사용하므로 객체를 구분해야 합니다. 결과가 비었을 때는 설치를 반복하기보다 어느 ID 조회에서 목록이 사라졌는지 출력하는 것이 빠릅니다.

설치보다 먼저 맞춰야 할 파일

원문에서 사용한 저장소와 데이터 다운로드 위치는 다음 두 곳입니다.

리눅스에서는 아래 순서로 Python API를 빌드했습니다.

1
2
3
git clone https://github.com/cocodataset/cocoapi.git
cd PythonAPI
make

make 과정에서 C 관련 오류가 났을 때는 cython 설치를 먼저 시도했습니다. 이 명령 묶음은 원문 그대로라 clone된 저장소 루트로 이동하는 단계가 빠져 있으며, 완결된 설치 스크립트가 아닙니다.

1
pip install cython

Windows 기록은 Visual Studio 2015와 setup.py 수정을 전제로 하며, 수정 예시 자체도 완전한 파일이 아닙니다. 따라서 현재 환경의 보편적인 설치법으로 복사하기보다 당시 빌드 문제를 추적하기 위한 단서로만 보는 편이 안전합니다.

데이터 경로는 이미지 폴더와 annotation 폴더가 서로 맞아야 합니다. 아래 예시는 train2017을 사용하지만, 데이터 규모가 부담된다면 구조 확인에는 train2017보다 작은 val2017을 먼저 고르는 편이 낫습니다.

1
2
3
4
dataDir = 'G:\\dataset\\COCO'
dataType = 'train2017'
annFile = '{}/annotations/instances_{}.json'.format(dataDir, dataType)
coco = COCO(annFile)

이 코드는 import와 데이터 다운로드를 포함하지 않은 노트북 핵심 조각입니다. COCO, NumPy, Matplotlib, 이미지 I/O가 이미 준비됐다는 전제입니다.

카테고리에서 한 장의 이미지까지 좁히기

먼저 카테고리 이름과 상위 카테고리를 확인하면, 뒤에서 사용할 이름이 실제 annotation에 있는지 검증할 수 있습니다.

1
2
3
4
5
6
cats = coco.loadCats(coco.getCatIds())
nms = [cat['name'] for cat in cats]
print('COCO categories: \n{}\n'.format(' '.join(nms)))

nms = set(cat['supercategory'] for cat in cats)
print('COCO supercategories: \n{}'.format(' '.join(nms)))

그다음 원하는 카테고리 ID를 구하고, 해당 카테고리가 포함된 이미지 ID를 찾습니다. 특정 이미지 ID를 다시 지정할 수도 있고, 후보 중 한 장을 무작위로 고를 수도 있습니다.

1
2
3
4
5
6
catIds = coco.getCatIds(catNms=['person', 'dog', 'skateboard'])
imgIds = coco.getImgIds(catIds=catIds)

imgIds = coco.getImgIds(imgIds=[379520])
img = coco.loadImgs(imgIds[np.random.randint(0, len(imgIds))])[0]
print('img:', img)

선택한 이미지 메타데이터의 coco_url을 읽어 화면에 표시하는 흐름은 다음과 같습니다.

1
2
3
4
I = io.imread(img['coco_url'])
plt.axis('off')
plt.imshow(I)
plt.show()

여기까지가 공통 준비입니다. 이후에는 무엇을 보고 싶은지에 따라 annotation 파일만 바뀝니다.

Instances, Keypoints, Captions의 차이

인스턴스 annotation은 선택한 이미지와 카테고리에 해당하는 객체 annotation을 불러와 그립니다.

1
2
3
4
5
6
7
8
9
annFile = '{}/annotations/instances_{}.json'.format(dataDir, dataType)
coco = COCO(annFile)

annIds = coco.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
anns = coco.loadAnns(annIds)

plt.imshow(I)
plt.axis('off')
coco.showAnns(anns)

사람의 키포인트를 보려면 person_keypoints 파일로 별도의 객체를 만듭니다. 인스턴스용 객체와 섞지 않는 것이 중요한 확인점입니다.

1
2
3
4
5
6
7
8
9
annFile = '{}/annotations/person_keypoints_{}.json'.format(dataDir, dataType)
coco_kps = COCO(annFile)

annIds = coco_kps.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
anns = coco_kps.loadAnns(annIds)

plt.imshow(I)
plt.axis('off')
coco_kps.showAnns(anns)

캡션은 captions 파일에서 이미지 ID로 조회합니다.

1
2
3
4
5
6
7
8
9
10
annFile = '{}/annotations/captions_{}.json'.format(dataDir, dataType)
coco_caps = COCO(annFile)

annIds = coco_caps.getAnnIds(imgIds=img['id'])
anns = coco_caps.loadAnns(annIds)
coco_caps.showAnns(anns)

plt.imshow(I)
plt.axis('off')
plt.show()

정리하면 세 작업은 같은 이미지를 보더라도 서로 다른 JSON을 읽습니다. 결과가 비어 있다면 시각화 코드보다 먼저 dataType, 이미지 ID, 카테고리 ID, annotation 파일명이 같은 split을 가리키는지 확인해야 합니다.

실행 전 알아둘 한계

이 글은 COCO API 전체 사용법이 아니라 기존 pycocoDemo에서 자주 쓰는 조회 흐름을 추린 기록입니다. 대용량 이미지 다운로드 명령, Windows 빌드 수정, import 목록은 완결된 재현 절차가 아니므로 그대로 실행되는 튜토리얼로 포장하지 않았습니다.

가장 안전한 확인 순서는 다음과 같습니다.

  1. annotation JSON 하나를 COCO로 여는지 확인합니다.
  2. 카테고리 이름과 ID를 출력합니다.
  3. 이미지 한 장을 고정 ID로 불러옵니다.
  4. instances, keypoints, captions 중 필요한 객체를 별도로 만듭니다.

문제가 생기면 설치부터 반복하기보다 어느 단계에서 ID 목록이 비는지 출력해 보는 것이 이 코드의 의도를 가장 잘 살리는 사용법입니다.

데이터 질의가 맞는지 어떤 순서로 검증하나

먼저 열고 있는 annotation 파일의 split과 작업 종류를 파일명으로 확인합니다. 파일이 정상적으로 열리면 category 이름과 ID 목록을 출력하고, 알고 있는 category 하나를 고정합니다. 그다음 해당 category가 포함된 image ID가 실제로 반환되는지 확인합니다. 무작위 선택은 이 흐름이 성공한 뒤에 넣어야 같은 오류를 재현할 수 있습니다.

이미지 한 장을 고른 뒤에는 metadata의 파일명과 실제 이미지 경로를 대조합니다. Annotation 객체가 이미지를 내려받아 주는 것이 아니므로 JSON은 열리는데 화면 표시만 실패할 수 있습니다. 이 경우 category 질의와 디스크 경로를 한 문제로 섞지 않습니다.

마지막으로 instances, keypoints, captions를 같은 image ID로 각각 조회합니다. 어떤 이미지에는 특정 annotation 종류가 없을 수 있으므로 빈 목록 자체를 곧바로 API 오류로 단정하지 않습니다. 필요한 field를 먼저 출력하고 시각화 함수가 기대하는 형태와 맞는지 확인해야 합니다.

함께 읽으면 이해가 이어지는 글

자주 묻는 질문

COCO API에서는 왜 category ID부터 찾나요?

카테고리 이름을 내부 ID로 바꾼 뒤 그 ID가 붙은 이미지와 annotation을 좁혀야 하기 때문입니다. 이름, 이미지, annotation을 한 번에 찾으려 하면 어느 단계에서 빈 결과가 생겼는지 알기 어렵습니다.

instances와 keypoints와 captions JSON을 하나의 COCO 객체로 같이 읽나요?

각 annotation 파일은 목적과 필드가 다르므로 필요한 JSON마다 COCO 객체를 따로 만드는 편이 명확합니다. 같은 image ID를 기준으로 결과를 연결할 수 있습니다.

getAnnIds 결과가 비어 있으면 설치 문제인가요?

반드시 그렇지는 않습니다. 잘못된 category ID나 image ID, 다른 annotation split을 열었을 가능성을 먼저 봐야 합니다. 각 단계의 ID 목록을 출력하면 설치와 질의 문제를 분리할 수 있습니다.

Instances, Keypoints, Captions를 언제 선택해야 하나

물체의 class와 bounding box 또는 segmentation이 필요하면 instances annotation을 봅니다. 사람 관절 위치가 목적이면 keypoints 객체가 필요하고, 이미지에 붙은 자연어 설명을 다루면 captions 객체를 엽니다. 세 파일은 같은 image ID를 공유해 연결할 수 있지만 annotation 하나의 필드가 모두 같은 것은 아닙니다.

예를 들어 특정 category의 box를 확인하려면 category ID로 image 후보를 구하고, 한 image를 고정한 뒤 instances의 annotation ID를 찾습니다. 사람 keypoint도 함께 보고 싶다면 같은 image ID를 keypoints용 COCO 객체에 전달합니다. Caption은 class filter가 아니라 이미지에 달린 문장을 조회한다는 차이를 코드 흐름에 반영해야 합니다.

시각화 전에 raw annotation 한 개를 출력해 필드와 배열 shape를 봅니다. Segmentation이 polygon인지 다른 표현인지, keypoints 값이 어떤 순서로 묶였는지, caption이 문자열인지 확인합니다. showAnns가 화면을 그린다는 사실만으로 후속 학습 코드가 원하는 tensor가 자동 생성되는 것은 아닙니다.

데이터 subset을 만들 때는 ID 목록과 실제 파일 복사를 분리합니다. JSON에서 선택한 image ID를 저장하고 metadata의 file name으로 원본 이미지를 찾습니다. 중간에 이미지가 누락되면 annotation을 다시 내려받기보다 다운로드 폴더와 split이 맞는지 확인합니다.

재현 가능한 예제에는 무작위 한 장 대신 고정 image ID와 선택 기준을 남깁니다. Category 이름, annotation 파일명, split, 반환된 ID 개수를 함께 출력하면 데이터 버전이나 경로가 달라졌을 때 어느 조건이 달라졌는지 찾기 쉽습니다.

학습용 변환 코드를 만들 때는 원본 annotation을 직접 덮어쓰지 않습니다. 선택한 ID와 변환 결과를 새 파일에 쓰고, 일부 샘플을 COCO API로 다시 열어 box, mask, keypoint가 이미지 경계와 맞는지 확인합니다. Category ID를 연속 index로 바꿨다면 원래 ID와의 mapping도 저장해야 평가 결과를 올바른 이름으로 되돌릴 수 있습니다.

THE END / OPSOAI

여기까지 읽었습니다

핵심 장면을 한 번 더 떠올려 보세요. 이해가 남았다면 이 책은 제 역할을 다했습니다.

다른 책 고르기
표지 1

키와 좌우 스와이프를 지원합니다. 읽던 페이지는 이 기기에 저장됩니다.

CONTENTS

이 책의 목차

    8개 장 15 분읽는 시간