-
이미지 처리-8 이미지 라벨링이미지 처리 2023. 6. 19. 18:50
1. 데이터 라벨링
데이터 라벨링은 ai 학습을 위한 데이터에 대해서 이름을 붙이는 것이다. 인공지능을 위해서 많은 양의 학습 데이터와 학습 시간이 필요하며 학습 데이터는 사용 용도에 맞게 적절하게 분류되고 라벨링 되는 것이 중요하다.
2. 이미지 라벨링
이미지 라벨링은 데이터 중 이미지 데이터에 대해서 라벨링을 진행하는 것으로 대표적인 종류로 Classification, Detection, Segmentation이 있다.
이 중 분류는 이미지의 라벨 정보만 필요하지만, 감지와 분석은 이미지 내부의 정보를 필요로 하기 때문에 라벨을 제외하고 이미지 내부의 정보를 설명할 수 있는 별도의 정보가 필요하며 이것을 만드는 것을 annotate라고 한다.
3. 라벨링 방법
Bounding Box: 바운딩 박스는 이미지 내부에 박스를 쳐서 박스에 대한 좌표를 annotate 하는 방법이다. 보통 x1, x2, y1, y2 좌표 또는 x, y좌표, w, h 좌표로 표현하며 yolo 포맷은 x center, y center, w, h값을 상대적인 값으로 표현한다.
쉽고 빠른 데이터 가공이 가능하지만 바운딩 박스 내부에 해당 객체에 속하지 않는 픽셀이 다수 포함될 수 있다.
Polygon: 폴리곤은 객체의 외곽선을 점을 찍어서 x, y 좌표로 다각형을 표현한다. 폴리곤은 바운딩 박스보다 정확하게 객체를 어노테이션 할 수 있으며 그만큼 힘든 작업이다.
Polyline: 많은 점을 선으로 그어서 구성하는 작업이며 시작과 끝이 없는 선을 구분할 때 사용하기 용이하다.
Keypoint: 키 포인트는 데이터의 외곽선을 포인트로 연결하여 포인트의 좌표, 순서와 개수 정보를 어노테이션 하는 것이다.
Cuboid: 큐보이드는 데이터를 3차원으로 기록하여 더 많은 정보를 제공하여 정확도를 높이기 위해 정육면체로 이미지를 어노테이션 하는 방법이다.
4. json 어노테이션 시각화
import json json_data_path = './data/instances_default.json' with open(json_data_path,'r', encoding='utf-8') as j: json_data =json.load(j) print(json_data) {'licenses': [{'name': '', 'id': 0, 'url': ''}], 'info': {'contributor': '', 'date_created': '', 'description': '', 'url': '', 'version': '', 'year': ''}, 'categories': [{'id': 1, 'name': 'cat', 'supercategory': ''}, {'id': 2, 'name': 'dog', 'supercategory': ''}], 'images': [{'id': 1, 'width': 640, 'height': 480, 'file_name': '01.jpg', 'license': 0, 'flickr_url': '', 'coco_url': '', 'date_captured': 0}], 'annotations': [{'id': 1, 'image_id': 1, 'category_id': 2, 'segmentation': [], 'area': 42499.856999999996, 'bbox': [468.94, 92.01, 171.06, 248.45], 'iscrowd': 0, 'attributes': {'occluded': False}}, {'id': 2, 'image_id': 1, 'category_id': 1, 'segmentation': [], 'area': 42994.3464, 'bbox': [3.96, 183.38, 200.88, 214.03], 'iscrowd': 0, 'attributes': {'occluded': False}}]}
특정 사진에 대한 json 데이터를 가지고 있는 파일을 불러온다. licenses, info, categories 등의 항목을 가지고 있는 것을 알 수 있다.
print(json_data['categories']) print(json_data['images']) print(json_data['annotations']) [{'id': 1, 'name': 'cat', 'supercategory': ''}, {'id': 2, 'name': 'dog', 'supercategory': ''}] [{'id': 1, 'width': 640, 'height': 480, 'file_name': '01.jpg', 'license': 0, 'flickr_url': '', 'coco_url': '', 'date_captured': 0}] [{'id': 1, 'image_id': 1, 'category_id': 2, 'segmentation': [], 'area': 42499.856999999996, 'bbox': [468.94, 92.01, 171.06, 248.45], 'iscrowd': 0, 'attributes': {'occluded': False}}, {'id': 2, 'image_id': 1, 'category_id': 1, 'segmentation': [], 'area': 42994.3464, 'bbox': [3.96, 183.38, 200.88, 214.03], 'iscrowd': 0, 'attributes': {'occluded': False}}]
이 중 주요 정보를 담고 있는 categories, images, annotations를 확인한다. 개와 고양이 사진에 관한 json 파일이며 해당 영역이 어노테이션 되어 있는 것을 알 수 있다.
categories_info = json_data['categories'] images_info = json_data['images'] annotations_info = json_data['annotations'] label_dict = {1 : 'cat', 2: 'dog'}
해당 데이터를 활용하기 위해 각각 변수에 저장한다. 이후에 cat, dog의 순서와 이름을 쉽게 꺼내 사용하기 위해 label_dict를 구성한다.
for image_json in images_info : file_name = image_json['file_name'] image_width = image_json['width'] image_heigth = image_json['height'] image_id = image_json['id'] image_path = os.path.join("./data/", file_name) image = cv2.imread(image_path) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) for anno_json in annotations_info : if image_id == anno_json['image_id'] : bbox = anno_json['bbox'] x = int(bbox[0]) y = int(bbox[1]) w = int(bbox[2]) h = int(bbox[3]) print("bbox 좌표 확인 >> ", x, y , w, h) cv2.rectangle(image, (x,y), (x+w, y+h), (0,255,0), 2) categroy_id = anno_json['category_id'] print(categroy_id) label_name = label_dict[categroy_id] print(label_name) image = cv2.putText(image, label_name, (x, y-10), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 1, (0,255,255), 2, cv2.LINE_AA) plt.imshow(image) plt.show() bbox 좌표 확인 >> 468 92 171 248 2 dog bbox 좌표 확인 >> 3 183 200 214 1 cat
json_data ['images']에서 width, height, file_name, id를 꺼내서 변수에 저장하고 os.path.join 함수로 이미지의 경로를 지정한다. 이 경로를 가지고 파일을 읽어올 수 있다.
이제 해당 이미지에서 어노테이션이 어떻게 구성되어 있는지 확인하기 위해 시각화한다. json_data ['images']에서 꺼낸 image_id가 json_data ['annotations']에서 꺼낸 image_id와 일치할 때 json_data ['annotations']에서 bbox를 꺼내서 좌표를 저장하고 rectangle 함수로 사각형을 그린다.
categori_id를 꺼내서 변수에 저장하고 label_dict에서 categori_id에 따른 변수를 label_name에 저장한다. 이 label_name을 cv2.putText를 사용하여 이미지 위에 라벨을 표시하고 최종 이미지를 확인할 수 있다.
5. xml 어노테이션 시각화
from xml.etree.ElementTree import parse def xml_read(xml_path) : root = parse(xml_path).getroot() image_info = root.findall('image') for image in image_info : bbox = image.findall('box') image_width = image.attrib['width'] image_height = image.attrib['height'] print("이미지 크기 정보 >> " , image_width, image_height) image_name = image.attrib['name'] print("이미지 이름 : " , image_name) image_path = os.path.join("./data/", image_name) print("이미지 경로 >> ", image_path) image = cv2.imread(image_path) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) for box_info in bbox : label = box_info.attrib['label'] print(label) xtl = box_info.attrib['xtl'] ytl = box_info.attrib['ytl'] xbr = box_info.attrib['xbr'] ybr = box_info.attrib['ybr'] xtl_f = float(xtl) ytl_f = float(ytl) xbr_f = float(xbr) ybr_f = float(ybr) xtl_i = int(xtl_f) ytl_i = int(ytl_f) xbr_i = int(xbr_f) ybr_i = int(ybr_f) image = cv2.rectangle(image, (xtl_i, ytl_i) , (xbr_i, ybr_i), (0,255,0), 2) image = cv2.putText(image, label, (xtl_i, ytl_i-10), cv2.FONT_HERSHEY_SCRIPT_SIMPLEX, 1, (0,255,255), 2, cv2.LINE_AA) plt.imshow(image) plt.show() xml_read("./data/annotations.xml") 이미지 크기 정보 >> 640 480 이미지 이름 : 01.jpg 이미지 경로 >> ./data/01.jpg dog cat
이번에는 xml파일에 어노테이션 돼있는 정보를 시각화한다. xml 파일을 읽어오기 위해서 parse 모듈이 필요하며 elementpath를 설치한다.
xml_read 함수를 만들어 파일경로를 넣으면 어노테이션 정보를 그릴 수 있도록 한다. parse함수를 사용하여 주어진 xml파일을 파싱하고 getroot 메서드를 사용하여 root 엘리먼트를 root변수에 할당한다.
root.findall 함수를 사용하여 루트 엘리먼트에서 image 태그를 가진 모든 엘리먼트를 찾아서 image_info에 할당한다. image_info에서 findall을 사용하여 어노테이션 정보를 가진 box 태그를 모두 찾아서 bbox에 저장한다. image 태그를 가진 엘리먼트에서 image.attrib를 사용하여 width, height, name, path 속성 값을 변수에 저장한다.
이 경로를 파일 경로와 join 하여 파일을 열고 읽어온다. 이제 bbox에 있는 정보를 토대로 불러온 이미지 위에 라벨링을 할 수 있다. bbox에서 label 속성을 가져와 label에 저장하고 xtl, ytl, xbr, ybr의 좌표정보를 가져와 각 변수에 저장한다.
이 좌표가 소수점을 가지고 있기 때문에 실수로 변환하고 다시 정수로 변환하여 사각형을 그리고 라벨을 표시하면 어노테이션 정보가 이미지 위에 표시된 것을 볼 수 있다.
6. yolo 포맷으로 변경
json_path = "./data/instances_default.json" with open(json_path, 'r', encoding='utf-8') as f : json_data = json.load(f) categories_info = json_data['categories'] images_info = json_data['images'] annotations_info = json_data['annotations'] label_dict = {1 : 0 , 2 : 1} for image_json in images_info : print(image_json) image_id = image_json['id'] image_name = image_json['file_name'] image_width = image_json['width'] image_height = image_json['height'] image_path = os.path.join("./data/", image_name) print(image_path) image = cv2.imread(image_path) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) for anno_json in annotations_info : print("anno_json >> " , anno_json) if image_id == anno_json['image_id'] : bbox = anno_json['bbox'] print("bbox >> " , bbox) x = int(bbox[0]) y = int(bbox[1]) w = int(bbox[2]) h = int(bbox[3]) category_id = anno_json['category_id'] label_number = label_dict[category_id] center_x = ((2 * x + w)/(2 * image_width)) center_y = ((2 * y + h)/(2 * image_height)) yolo_w = w/image_width yolo_h = h/image_height print(label_number, center_x, center_y, yolo_w, yolo_h) file_nmae_temp = image_name.replace(".jpg" , "") print(file_nmae_temp) os.makedirs("./label_data/", exist_ok=True) with open(f'./label_data/{file_nmae_temp}.txt','a') as f : f.write(f"{label_number} {center_x} {center_y} {yolo_w} {yolo_h} \n") cv2.rectangle(image, (x, y), (x+w, y+h), (0,255,0), 2) plt.imshow(image) plt.show() {'id': 1, 'width': 640, 'height': 480, 'file_name': '01.jpg', 'license': 0, 'flickr_url': '', 'coco_url': '', 'date_captured': 0} ./data/01.jpg anno_json >> {'id': 1, 'image_id': 1, 'category_id': 2, 'segmentation': [], 'area': 42499.856999999996, 'bbox': [468.94, 92.01, 171.06, 248.45], 'iscrowd': 0, 'attributes': {'occluded': False}} bbox >> [468.94, 92.01, 171.06, 248.45] 1 0.86484375 0.45 0.2671875 0.5166666666666667 01 anno_json >> {'id': 2, 'image_id': 1, 'category_id': 1, 'segmentation': [], 'area': 42994.3464, 'bbox': [3.96, 183.38, 200.88, 214.03], 'iscrowd': 0, 'attributes': {'occluded': False}} bbox >> [3.96, 183.38, 200.88, 214.03] 0 0.1609375 0.6041666666666666 0.3125 0.44583333333333336 01
json 데이터를 불러오는 과정을 동일하게 진행한다. 이후에 이미지를 읽어온 후 json_data ['annotations']에서 image_id와 bbox를 가져온다. bbox의 좌표를 각각 저장하고 이미지 width, height 정보와 결합하여 x, y 중앙값을 구한다. 이것을 다시 width, height 값으로 나눠서 비율로 나타내면 yolo 방식으로 표현할 수 있다.
이 값을 label_data 폴더에 txt 확장자로 저장한다.
'이미지 처리' 카테고리의 다른 글
이미지 처리-10 json으로 영상 데이터 이미지 추출 (0) 2023.07.04 이미지 처리-9 이미지 증강 (0) 2023.06.26 이미지 처리-7 객체 추적 (0) 2023.06.19 이미지 처리-6 기본 동영상 처리 (0) 2023.06.19