Semantic Segmentation 학습 용 Masking colored 이미지 만들기

이번에 할 거는 Semi-Supervised Segmentation Task를 진행을 할거티비. 2D Object Detection 이나, 3D Object Detection은 학습을 시켜본적이 있는데, 2D Semantic Segmentation은 아직 없당. 새로운 매커니즘이나 프레임워크를 제안하는 논문들 대부분이 DeepLab v3 에서 backbone을 ResNet50, 101 을 놓고 학습해서 성능 평가를 하기 때문에, 최근에 paperswithcode 를 보면 새롭게 SOTA 를 달성한 모델들이 많지만 일단은 DeepLab v3 를 학습 시키고자 한다.

일단은 Open되어있는 데이터들은 일단 해외에서 수집된 데이터라서 국내 환경에 맞는 자율 주행 개발하는데는 어려움이 있기 때문에 기본적으로 국내 데이터셋으로 모델을 학습 시키는 것이 맞다.

데이터가 너무 아쉬우니 일단 이거를 나중에 Semi-Supervised 로 Pseudo Label 생성해서 개선해 보기로 하자.

그래서 일단은 먼저 학습용 데이터를 생성을 해야한다. 밑에 그림 처럼 만들거임. Image

데이터 설명

과제에서 제공 받은 데이터는 이미지 파일 (.jpg), 라벨 파일 (.json) 이다. 원래 SS 학습시키는 데이터의 경우 이미지는 png확장자로 대부분 쓰는데.. 이 부분도 맘에 안듦 .. 하지만 주어진 환경에서 진행해야하기 때문에 일단은 그냥 한다. json 포맷으로 저장되는 라벨 파일내용은 아래와 같다

{
"Source_Image_Info": {
    "Img_name": "DA_00C_AC_211006_05_000191.jpg",
    "Img_path": "DA_00C_AC_211006_05/JPEGImages",
    "Resolution": [
        1920,
        1080
    ],
    "Copyrighter": "(주)--",
    "Time_zone": "주간",
    "Location": "사고상황",
    "Gps": "",
    "Weather": "흐림"
},
"Annotation": [
    {
        "Label": "sky",
        "Type": "polygon",
        "bbox": [
            0.0,
            0.0,
            1920.0,
            565.1
        ],
        "area": 970932.0,
        "Coordinate": [
            [
                0.0,
                446.2
            ],
            [
                1920.0,
                565.1
            ],
            [
                1920.0,
                0.0
            ],
            [
                0.0,
                0.0
            ]
        ],
        "Data_type": "worker"
    },
  ]
}

보통 Semantic Segmentation 테스크에서 많이 쓰이는 Cityscapes 라벨 포맷과 비슷해보이지만 다르다. 하지만 json은 파싱하기 편하기 때문에 ( 특히 python에서 !) 괜찮다. 언뜻 보면 굉장히 복잡해보이지만 사실 위의 라벨 파일에서 쓰이는 부분은 얘네들이다.
“Annotation” 안에 각 regime 에 대한 객체로 구성되어있고, 여기서 각 regime에 대한 “Label” 이랑 “Coordinates” array 객체들만 사용한다. Cityscape에서 “label” 과 “polygon” 이라고 생각하면 된다.

이제 이 폴리곤값을 가지고 검은 바탕 위에 해당 regime들이 클래스별 색깔로 칠해져있는 colored mask이미지가 필요하다.

클래스 별 ID, 색깔 값 가진 객체 만들기

먼저 코드 Note : Cityscapes Scripts보고 참고.

#classes.py 

from collections import namedtuple

Label  =  namedtuple('Label', ['name', 'id', 'color', 'hex'])
labels  = [
	Label('undefined object/area', 0, (0, 0, 0), '#000000'),
	Label('road', 1, (130, 130, 130), '#828282'),
	Label('lane', 2, (255, 255, 255), '#ffffff'),
	Label('road mark', 3, (184, 134, 11), '#b8860b'),
	Label('crosswalk', 4, (82, 226, 82), '#52e252'),
	Label('speed bump', 5, (255, 20, 145), '#ff1493'),
	Label('curb', 6, (124, 252, 0), '#7cfc00'),
	Label('static', 7, (255, 145, 220), '#ff91dc'),
	Label('sidewalk', 8, (255, 20, 145), '#ff1493'),
	Label('parking place', 9, (240, 255, 255), '#f0ffff'),
	Label('vehicle', 10, (0, 0, 255), '#0000ff'),
	Label('motorcycle', 11, (255, 240, 100), '#fff064'),
	Label('bicycle', 12, (244, 149, 81), '#f49551'),
	Label('pedestrian', 13, (255, 0, 0), '#ff0000'),
	Label('rider', 14, (255, 170, 40), '#ffaa28'),
	Label('dynamic', 15, (255, 130, 0), '#ff8200'),
	Label('traffic sign', 16, (50, 150, 215), '#3296d7'),
	Label('traffic light', 17, (191, 130, 191), '#bf82bf'),
	Label('pole', 18, (128, 128, 0), '#808000'),
	Label('building', 19, (0, 60, 100), '#9f814f'),
	Label('guardrail', 20, (210, 110, 110), '#d26e6e'),
	Label('sky', 21, (30, 144, 255), '#1e90ff'),
	Label('water', 22, (0, 206, 209), '#00ced1'),
	Label('mountain', 23, (34, 214, 178), '#22d6b2'),
	Label('vegetation', 24, (107, 142, 35), '#6b8e23'),
	Label('bridge', 25, (47, 79, 79), '#2f4f4f'),
]
name2label  = {label.name: label  for  label  in  labels}

위의 코드는 각 label (class) 에 대한 정보를 객체화한 파이썬 코드이다. 약간 코드 더러운거 싫어하는 편이라 모두다 쪼개버리는 스타일. 요거를 import 해서 . 으로 참조해서 사용하면 된다. 위의 클래스들이 보통의 Cityscape 클래스랑 다른 것을 볼 수 있는데, 요게또 이제 “커스텀” 데이터 셋이라서 자기들이 맘대로 이름짓고 컬러 설정 해버렸다. 뭐 암튼.

Json 라벨 파일 읽고, Masking 이미지 만들기

먼저 헤더

#masking.py

import cv2
import json, glob
import numpy as np
from pathlib imrpot Path
from tqdm import tqdm
import classes #위에서 만든 classes 객체

base = '/상위 경로'
label= base+'label/'
data = base+'data/'

w = 1920
h = 1080

base, label, data, w, h 는 편의를 위한 것으로 그냥 절대경로 때려 박아버려도 됨. tqdm 은 프로세스바를 표시하기 위한 라이브러리 classes 는 위에서 생성한 class 라벨 객체

라벨 파일 읽기 이미지 파일을 읽는게 아니고, 라벨 파일만 읽는다. 폴더내의 모든 라벨파일을 검토하면서, JSON으로 로드한다. 마스킹 이미지는 검정 바탕에 regime에 대해서 클래스별 색깔에 따라서 마스크가 씌워지는 것이기 때문에 기본적으로 np.zeros 를 이용해서 3채널 검정 이미지를 만들어준다. 그 다음에 위에서 데이터 설명한 것 처럼, Annotation안에있는 Label을 읽어서 cls 변수에 저장한다. 그리고, 해당 cls값을 가지고 classes. py 에서 생성한 객체 name2label 에서 cls로 접근해서 해당 클래스에 대한 컬러 정보를 가져온다. OpenCV 는 기본적으로 컬러 값을 BGR로 읽기 때문에 컬러를 거꾸로 읽어온다.

#masking.py 이어서 

for  label  in  tqdm(sorted(glob.glob(label+"*.json"))):
	img  = np.zeros((h, w, 3), np.uint8)
	with  open(label) as  f:
		line  = json.load(f)
		for  polygons  in  line["Annotation"]:
			cls  =  polygons["Label"]
			color  = (classes.name2label[cls].color[2],
			classes.name2label[cls].color[1], classes.name2label[cls].color[0])

zeros이미지에 masking 씌우기 polygon은 일단 점이 3개 이상 있어야지, 그려지는게 맞기 때문에, Coordinate안에 들어간 배열의 길이를 검사하여, 3개 이상일 때만 fillPoly함수를 이용해서 다각형을 그리도록 한다. 데이터가 json에서 array 부분에 오류가 있는게 많아서, 원래 잘 가공된 데이터면 이런 검사과정도 필요없는데, ,학습 데이터 만드는 데 부터 오류 가있으므로 일단 넣어준다. 위에서 구한 color 까지 함수의 인자로 넣어줘서 img 객체에 polygon을 그려준다. for 문이 모두 돌아가면서 생성된 이미지는 마지막으로 imwrite 함수 이용해서 저장해준다.

    #masking.py 이어서 
    
    if(len(polygons["Coordinate"]) >=  3):
		polygon  = np.array(
			np.around(np.array(polygons["Coordinate"], np.float32)), np.int32)
		img  = cv2.fillPoly(img, [polygon], color)
	else:
		continue
new_file  =  str(str(data)+str(Path(label).stem)+"_color.png")
cv2.imwrite(new_file, img)

실행 결과

실행은 그냥

python masking.py 

Image

결과 Fine Image Image Coarse image Image

이상 입니닷.