728x90
반응형

FCN (Fully Convolutional Network for Semantic Segmentation)

평이한 느낌이지만 당시, classification에 flatten한 fully connected가 필수였지만 이 모델은 convolution만으로 이루어진 모델임.

 

Semantic Segmentation Encoder-Decoder Model

- 원본 이미지를 convolution으로 차원 축소하여 응축된 정보를 가지고, 이를 다시 복원하면서 필요한 정보를 학습

- 이렇게 학습된 정보를 기반으로 segmentation 수행

축소하면서 사이즈는 줄어들지만 채널 깊이는 깊어짐

응축된 상태에서 재창조, 재해석 추구

feature map 정보를 upsampling하면서 masking 정보를 하면서 prediction 모델의 weight를 채움

 

Fully connected layer vs Fully convolutional layer

imageNet은 classification할 object가 1000개 생김

그런데 가변적인 image size가 들어오기 때문에 좀더 융통성있는 convolution으로 하면 좋다.

grid by grid별로 cat의 정보가 있는 것

각 pixel에 확률값이 있고, 확률값이 우세하면 cat으로 classification

 

 

FCN (Fully Convolutional Network for Semantic Segmentation)

 

마지막의 fully connected를 fully convolution으로 설정

3번째로 32x32로 쭉 늘리면서 복원을 함

masking 정보와 gtbox 정보를 매칭하면서 학습시킴

 

개별 pixel을 학습 시키면서 weight도 적용, upsampling도 weight 적용

 

32x upsampling을 통한 pixel wise prediction

32배 upsampling하면서 뭉개진 형태가 됨

그래서 de-convolution을 통해서 최대한 비슷하게 복구하는 방법 찾음

Feature map 혼합 pixel wise prediction

- con7을 2배시켜서 2x2로 만들어서 pool4이랑 합치고, 합친 concat pool을 16x upsampling 해서 prediction한게 FCN 16s

- con7을 4배시켜서 4x4로 만들어서 2배 pool4이랑 pool3이랑 합치고, 합친 concat pool을 8x upsampling 해서 prediction한게 FCN 8s

 

  FCN-32s FCN-16s FCN-8s
IOU 59.4% 62.4% 62.7%

 

반응형

'Computer_Science > Mask RCNN' 카테고리의 다른 글

13-3~4. MASK RCNN  (0) 2021.10.31
13-1. Segmentation  (0) 2021.10.27
728x90
반응형

Compound Scaling

- 너무 거대한 backbone, 여러겹의 fpn, 큰 입력 이미지의 크기 등의 개별적인 부분들에 집중하는 것은 비효율적

- EfficientNet에서 개별 요소들을 함께 Scaling 하면서 최적 결합을 통한 성능 향상을 보여줌

- EfficientDet에서도 backbone, BiFPN, Prediction Layer, 입력 이미지 크기를 Scaling 기반으로 최적 결합 하여 D0~D7 모델 구성

Backbone Netwrok

- EfficientNet B0~B6 로 Scaling 그대로 적용

BiFPN Network

- depth는 BiFPN 기본 반복 block을 3개로 설정하고 scaling 적용

Dbifpn = 3 + o

- width 채널 수는 {1.2, 1.25, 1.3, 1.25, 1.4, 1.45} 중 grid Search를 통해 1.35로 Scaling계수를 선택하고 이를 기반으로 Scaling 적용

Wbifpn = 64x(1.35^o)

Prediction Network

- width 채널수는 BiFPN채널 수 와 동일

- Detth는 아래식을 적용

Dbox = Dclass = 3 + [o/3]

입력 이미지 크기

Rinput = 512 + ox128

 

 

EfficientDet 기타 적용 요소 및 성능 평가

기타 적용 요소

- activation : siLU(swish)

- Loss : Focal Loss

- augmentation : horizontal flip 과 scale jittering

- NMS : soft nms

 

반응형
728x90
반응형

 Compound Scaling

- 네트웍의 깊이(Depth), 필터 수(width), 이미지 Resolution크기를 함께 최적으로 조합하여 모델 성능 극대화

   => 깊이로 resNet이 이득을 봄

- 그러나 단순한 depth의 양으로는 한계가 있어 엔지니어링 테크닉으로 한계 극복 노력

 

EfficientNet architect

- 필터 수 변경 : channels를 wider

- 네트웍 깊이 변경 : deepper

- 이미지 resolution : 224x224 => 512x512가 더 성능이 좋음

=> compound scaling은 이 3요소를 조합하여 모델 개선

 

개별 scaling 요소에 따른 성능 향상 테스트

                        필터수 변경                                깊이 변경                                이미지 resolution 변경

- 필터수, 네트웍 깊이를 일정 수준 이상 늘려도 성능향상 미비, resource는 더욱 많이 듬

- 단, Resolution이 경우 어느정도 약간씩 성능 향상이 지속.

- ImageNet 데이터 세트 기준 80% 정확도에서 개별 scaling 요소를 증가시키더라도 성능향상이 어려움

=> 체감 한계가 있어서 최적화로 성능향상 필요

 

최적 scaling 도출 기반 식

- depth, width, resolution에 따른 FLOPS 변화를 기반으로 최적 시 도출

- width, resolution은 2배가 되면 flops는 4배가 됨(그래서 제곱을 곱함)

 

- 3가지 scaling Factor를 동시 고려하는 compound scaling 적용

- 최초에는 승수를 1로 고정하고 grid search기반으로 a, b, r의 최적 값을 찾아냄.

   EfficientNetB0의 경우 a = 1.2, b = 1.1, r = 1.15

- 다음으로 a, b, r을 고정하고 승수를 증가시켜가면서 efficient B1 ~ B7까지 Scale up 구성

최적화된 architect로 최적화된 resource로 최적화된 속도, 성능을 냄

 

 

반응형
728x90
반응형

Scaleble and Efficient Object Detection

Backbone : EfficientNet

Neck : FPN을 BiFPN으로 바꿈

 => FPN이 성과를 거두자 발전시키려는 노력

Compound Scailing : 한가지 요소에 가중치가 아닌 합쳐서

 

 

BiFPN

 

Compound Scailing 

 

 

 

EfficientDet 성능

적은 연산수, 적은 파라미터 수에 비해 상대적으로 타모델보다 높은 모델 예측성능을 나타냄

D0는 yolo와도 맞먹고, flops도 압도적으로 적다.

 

 

 

 

BiFPN ( Bi Directional FPN

- Cross Scale Connections

기존 FPN : TOP DOWN

- Feature Fusion : feature가 아래로 내려가면서 합쳐짐

중국 PANET : BOTTON UP

=> FEATURE 가 풍부해짐

BiFPN : Cross Scale

input feature를 서로 더해서 반영

 

- Weighted Feature Fusion

- 가중치를 어떻게 부여할 것인가에 대한 문제

 

초기 FPN : RetinaNet에서의 가능성

- bottom-up pathway => top-Down Pathway 

- Lateral Connection + upsampling

 

FPN의 발전 : Path Aggregation Network 

- 내려가던 것에서 올라가던 것

Nas-FPN

- 수천시간의 강화학습을 통해서 최적의 네트웍을 찾아냄

 

BiFPN

- 최상위, 최하위 feature map을 생략

- 원본 feature에서도 추출

- 3개의 추출 : 원본 feature, 하위 feature, Lateral Connection

 

- repeated block

=> compound Scaling으로 block을 몇개 할지 정함

 

Weighted Feature Fusion

서로 다른 resolution (Feature map size)를 가지는 input feature map들은 output feature map을 생성하는 기여도가 다르기 때문에 서로 다른 가중치를 부여하여 합쳐질 수 있어야 한다.

 

- Fast Normalized fusion

 

Unbounded Fusion

- 입력 Feature map에 가중치를 곱해서 출력 feature map  생성

- 여기서 Wi는 정해진 값이 아니라 학습 시켜서 도출된 weight임.

- 연산량 감소를 위해 BiFPN NetWork 구현 시 separable Convolution 적용

 

 

 

 

반응형
728x90
반응형

Segmentation

- masking 정보로 의미별 문맥으로 분할

- semantic segmentation : 동일한 object는 붙임

- instance Segmentation : 개별 object로 분할

  * mask RCNN(instance rcnn) = FCN(semantic) + faster RCNN 

 

Semantic Segmentation

- pixel wise classification 

    이미지가 800x1000이라면 (0, 0)은 tree 영역 등으로 픽셀단위 classification

- object label 별 classification 후 pixel map으로 찍고 합친것

 

 

Semantic Segmentation Encoder-Decoder Model

- AutoEncoder와 유사한 구조

- size는 줄어들고, channel은 깊어지고 : 위치 정보가 깨짐, 주요 정보가 추상화됨

- 차원축소를 하는 것 => hidden factor를 찾기

- encoder-decoder는 원본과 똑같은 걸찾는 것이 아닌 원본에서는 없던 특성을 찾는 것임

 

 

 

반응형

'Computer_Science > Mask RCNN' 카테고리의 다른 글

13-3~4. MASK RCNN  (0) 2021.10.31
13-2. Semantic Segmentation FCN  (0) 2021.10.31
728x90
반응형

RetinaNet 특징

- One Stage Detector의 빠른 detection 시간의 장점을 가지면서 One stage detector의 detection 성능 저하 문제 개선

- 수행 시간은 YOLO나 SSD보다 느리지만 Faster RCNN보다 빠름

- 수행 성능은 타 detection 모델보다 뛰어남. 특히 One stage detector 보다 작은 object에 대한 detection능력이 뛰어남

 

# Focal Loss + Feature Pyramid Network

- Focal loss : Cross Entropy를 대체한 loss function 

- Feature Pyramid Network을 backbone에 적용

 

Focal Loss의 필요성

Cross Entropy

object의 classification pred_proba를 로그하고 실제값과 곱함

One-stage detector의 class imbalance 이슈

넓은 이미지 속에서 background엔 detect할 정보가 많은데, detect하고싶은 object는 아주 적은 문제

- Anchor box가 bg에 학습값이 치중되어 분명한 object여도 확률값이 작아짐

 

Easy Example : 찾기 쉬운 대상들. Background 나 크고 선명한 대상 오브젝트. 이미 높은 예측확률을 가지고 있음

Hard Example : 찾기 어려운 대상들. 작고 형태가 불분명하여 낮은 예측 확률을 가지고 있음

 

- Easy Example이 많고 Hard Example이 적은 적은 Class imbalance 이슈는 object detection이 안고 있는 고유문제

- Two Stage detector의 경우 Region Proposal Network에서 오브젝트가 있을만한 높은 확률 순으로 필터링을 먼저 수행할 수 있음

- 하지만 One-Stage는 Region Proposal과 Detection을 같이 수행하므로 매우 많은 오브젝트 후보들에 대해서 detection을 수행해야하므로 class imbalance로 인한 성능 저하 영향이 큼

=> 이미 잘 예측된 object의 loss를 줄이려고 학습이 진행되는데 보통 잘 예측된 object는 Easy Example이다.

     배경이라 anchorbox 할당도 많이 되고, 학습할 개수도 많고, 확률값도 높으니깐

- two-stage도 있는 문제지만 one-stage에서 큼

 

Class imbalance 해결방안

- 기존 : 학습 시 경험치에 기반한 샘플링이나 data augmentation

- RetinaNet : 동적으로 cross entropy 조절

 

Focal loss - Cross Entropy에 가중치 부여

가중치때문에 전반적인 loss가 떨어지는데 확률이 높은 쪽은 아주 많이 떨어지고, 확률이 작은 쪽은 적게 떨어짐

보통 감마 값을 0.25로 잡음

Focal loss를 통해 anchor box를 많이 만듬

 

FPN (Feature Pyramid Network)

- 서로 다른 크기를 가지는 object들을 효과적으로 Detection하기 위해 bottom up과 top down 방식으로 추출된 feature map들을 lateral connection으로 연결하는 방식

- 서로 다른 크기의 object를 detect하기 위해 image size를 줄여나가는 방식으로 하면 큰 object를 detect를 할 수 있지만 computing 시간이 오래걸림, 그래서 최근에는 잘 안씀

- 대신 feature map을 기반으로 predict하는 방식이 일반적임. 그러나 너무 최상위 feature map은 너무 추상화가 되어있고 핵심 feature를 가지고 있지만 위치 정보를 잃어버려서 정확도가 떨어짐

- 그래서 feature map을 사용하긴 하는데 각 map을 predict하게 됨

- layer 특성이 상위 feature map으로 갈수록 소실되어서 detection 정확도가 떨어지는 문제가 발생.

 

- 그래서 bottom up 방식을 사용, 위로 올라갈수록 시멘틱하지만 레졸루션은 떨어짐

- 레졸루션이 떨어져서 image size가 작아질수록 skip connection으로 보완

- upsampling + skip connection

 

* 기존 resnet의 경우 layer가 깊어지면 gradient vanishing 문제가 발생하고 오히려 성능이 떨어짐

  => skip connection을 통해서 jump해서 보내줘서 되살리는 원리를 차용함

 

RetinaNet FPN AnchorBox

- 9개의 anchor box가 p2~p5의 개별 Layer의 개별 grid에 할당

- 3개의 서로다른크기와 3개의 서로다른 스케일을 가짐

- 약 100k의 anchor box들

- 개별 anchor box 는 classification을 위한 k개의 클래스 확률값과 bounding box regression을 위한 4개 좌표값을 가짐

   * 20개 class 확률 + 4개 좌표

 

 

반응형
728x90
반응형

Ultralytics Yolo v3 는 yolo v5로 알려져 있다.

v3에서 v4까지 수많은 테크닉이 등장함.

예측 성능, 예측 시간도 크게 단출

 

Edgo AI integrated into custom iOS and Android apps for realtime 30FPS video inference

=> mobile에서도 괜찮은 fps 성능을 냄

=> 그동안 yolo의 약점 이 cpu에서 성능이 떨어진다는 점인데 deep spot 을 적용하여 극복

 

# 특징

견고한 구현 : Enterprise 솔루션 지향

다양한 편의 기능

- 학습 시 loss, weight 등에 대한 시각적인 util 기능 제공

- 편리한 evalutation 결과 도출 및 시각화

 

 

 

반응형

728x90
반응형

OpenCV Darknet Yolo를 이용하여 이미지Object Detection

  • yolo와 tiny-yolo 를 이용하여 Object Detection

입력 이미지로 사용될 이미지 다운로드/보기

!mkdir /content/data
!wget -O ./data/beatles01.jpg https://raw.githubusercontent.com/chulminkw/DLCV/master/data/image/beatles01.jpg

Darknet Yolo사이트에서 coco로 학습된 Inference모델와 환경파일을 다운로드 받은 후 이를 이용해 OpenCV에서 Inference 모델 생성

### coco 데이터 세트로 pretrained 된 yolo weight 파일과 config 파일 다운로드하여 /content/pretrained 디렉토리 아래에 저장. 
!mkdir ./pretrained
!echo "##### downloading pretrained yolo/tiny-yolo weight file and config file"
!wget -O /content/pretrained/yolov3.weights https://pjreddie.com/media/files/yolov3.weights
!wget -O /content/pretrained/yolov3.cfg https://github.com/pjreddie/darknet/blob/master/cfg/yolov3.cfg?raw=true 

!wget -O /content/pretrained/yolov3-tiny.weights https://pjreddie.com/media/files/yolov3-tiny.weights
!wget -O /content/pretrained/yolov3-tiny.cfg https://github.com/pjreddie/darknet/blob/master/cfg/yolov3-tiny.cfg?raw=true

!ls /content/pretrained

readNetFromDarknet(config파일, weight파일)을 이용하여 yolo inference network 모델을 로딩

import os
import cv2

weights_path = '/content/pretrained/yolov3.weights'
config_path =  '/content/pretrained/yolov3.cfg'
#config 파일 인자가 먼저 옴. 
cv_net_yolo = cv2.dnn.readNetFromDarknet(config_path, weights_path)

COCO class id와 class 명 매핑

labels_to_names_seq = {0:'person',1:'bicycle',2:'car',3:'motorbike',4:'aeroplane',5:'bus',6:'train',7:'truck',8:'boat',9:'traffic light',10:'fire hydrant',
                        11:'stop sign',12:'parking meter',13:'bench',14:'bird',15:'cat',16:'dog',17:'horse',18:'sheep',19:'cow',20:'elephant',
                        21:'bear',22:'zebra',23:'giraffe',24:'backpack',25:'umbrella',26:'handbag',27:'tie',28:'suitcase',29:'frisbee',30:'skis',
                        31:'snowboard',32:'sports ball',33:'kite',34:'baseball bat',35:'baseball glove',36:'skateboard',37:'surfboard',38:'tennis racket',39:'bottle',40:'wine glass',
                        41:'cup',42:'fork',43:'knife',44:'spoon',45:'bowl',46:'banana',47:'apple',48:'sandwich',49:'orange',50:'broccoli',
                        51:'carrot',52:'hot dog',53:'pizza',54:'donut',55:'cake',56:'chair',57:'sofa',58:'pottedplant',59:'bed',60:'diningtable',
                        61:'toilet',62:'tvmonitor',63:'laptop',64:'mouse',65:'remote',66:'keyboard',67:'cell phone',68:'microwave',69:'oven',70:'toaster',
                        71:'sink',72:'refrigerator',73:'book',74:'clock',75:'vase',76:'scissors',77:'teddy bear',78:'hair drier',79:'toothbrush' }

3개의 scale Output layer에서 결과 데이터 추출

layer_names = cv_net_yolo.getLayerNames()
print('### yolo v3 layer name:', layer_names)
print('final output layer id:', cv_net_yolo.getUnconnectedOutLayers())
print('final output layer name:', [layer_names[i[0] - 1] for i in cv_net_yolo.getUnconnectedOutLayers()])

### yolo v3 layer name: ['conv_0', 'bn_0', 'relu_0', 'conv_1', 'bn_1', 'relu_1', 'conv_2', 'bn_2', 'relu_2', 'conv_3', 'bn_3', 'relu_3', 'shortcut_4', 'conv_5', 'bn_5', 'relu_5', 'conv_6', 'bn_6', 'relu_6', 'conv_7', 'bn_7', 'relu_7', 'shortcut_8', 'conv_9', 'bn_9', 'relu_9', 'conv_10', 'bn_10', 'relu_10', 'shortcut_11', 'conv_12', 'bn_12', 'relu_12', 'conv_13', 'bn_13', 'relu_13', 'conv_14', 'bn_14', 'relu_14', 'shortcut_15', 'conv_16', 'bn_16', 'relu_16', 'conv_17', 'bn_17', 'relu_17', 'shortcut_18', 'conv_19', 'bn_19', 'relu_19', 'conv_20', 'bn_20', 'relu_20', 'shortcut_21', 'conv_22', 'bn_22', 'relu_22', 'conv_23', 'bn_23', 'relu_23', 'shortcut_24', 'conv_25', 'bn_25', 'relu_25', 'conv_26', 'bn_26', 'relu_26', 'shortcut_27', 'conv_28', 'bn_28', 'relu_28', 'conv_29', 'bn_29', 'relu_29', 'shortcut_30', 'conv_31', 'bn_31', 'relu_31', 'conv_32', 'bn_32', 'relu_32', 'shortcut_33', 'conv_34', 'bn_34', 'relu_34', 'conv_35', 'bn_35', 'relu_35', 'shortcut_36', 'conv_37', 'bn_37', 'relu_37', 'conv_38', 'bn_38', 'relu_38', 'conv_39', 'bn_39', 'relu_39', 'shortcut_40', 'conv_41', 'bn_41', 'relu_41', 'conv_42', 'bn_42', 'relu_42', 'shortcut_43', 'conv_44', 'bn_44', 'relu_44', 'conv_45', 'bn_45', 'relu_45', 'shortcut_46', 'conv_47', 'bn_47', 'relu_47', 'conv_48', 'bn_48', 'relu_48', 'shortcut_49', 'conv_50', 'bn_50', 'relu_50', 'conv_51', 'bn_51', 'relu_51', 'shortcut_52', 'conv_53', 'bn_53', 'relu_53', 'conv_54', 'bn_54', 'relu_54', 'shortcut_55', 'conv_56', 'bn_56', 'relu_56', 'conv_57', 'bn_57', 'relu_57', 'shortcut_58', 'conv_59', 'bn_59', 'relu_59', 'conv_60', 'bn_60', 'relu_60', 'shortcut_61', 'conv_62', 'bn_62', 'relu_62', 'conv_63', 'bn_63', 'relu_63', 'conv_64', 'bn_64', 'relu_64', 'shortcut_65', 'conv_66', 'bn_66', 'relu_66', 'conv_67', 'bn_67', 'relu_67', 'shortcut_68', 'conv_69', 'bn_69', 'relu_69', 'conv_70', 'bn_70', 'relu_70', 'shortcut_71', 'conv_72', 'bn_72', 'relu_72', 'conv_73', 'bn_73', 'relu_73', 'shortcut_74', 'conv_75', 'bn_75', 'relu_75', 'conv_76', 'bn_76', 'relu_76', 'conv_77', 'bn_77', 'relu_77', 'conv_78', 'bn_78', 'relu_78', 'conv_79', 'bn_79', 'relu_79', 'conv_80', 'bn_80', 'relu_80', 'conv_81', 'permute_82', 'yolo_82', 'identity_83', 'conv_84', 'bn_84', 'relu_84', 'upsample_85', 'concat_86', 'conv_87', 'bn_87', 'relu_87', 'conv_88', 'bn_88', 'relu_88', 'conv_89', 'bn_89', 'relu_89', 'conv_90', 'bn_90', 'relu_90', 'conv_91', 'bn_91', 'relu_91', 'conv_92', 'bn_92', 'relu_92', 'conv_93', 'permute_94', 'yolo_94', 'identity_95', 'conv_96', 'bn_96', 'relu_96', 'upsample_97', 'concat_98', 'conv_99', 'bn_99', 'relu_99', 'conv_100', 'bn_100', 'relu_100', 'conv_101', 'bn_101', 'relu_101', 'conv_102', 'bn_102', 'relu_102', 'conv_103', 'bn_103', 'relu_103', 'conv_104', 'bn_104', 'relu_104', 'conv_105', 'permute_106', 'yolo_106']
final output layer id: [[200]
 [227]
 [254]]
final output layer name: ['yolo_82', 'yolo_94', 'yolo_106']
#전체 Darknet layer에서 13x13 grid, 26x26, 52x52 grid에서 detect된 Output layer만 filtering
layer_names = cv_net_yolo.getLayerNames()
outlayer_names = [layer_names[i[0] - 1] for i in cv_net_yolo.getUnconnectedOutLayers()]
print('output_layer name:', outlayer_names)

img = cv2.imread('./data/beatles01.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# 로딩한 모델은 Yolov3 416 x 416 모델임. 원본 이미지 배열을 사이즈 (416, 416)으로, BGR을 RGB로 변환하여 배열 입력
cv_net_yolo.setInput(cv2.dnn.blobFromImage(img, scalefactor=1/255.0, size=(416, 416), swapRB=True, crop=False))

# Object Detection 수행하여 결과를 cvOut으로 반환 
cv_outs = cv_net_yolo.forward(outlayer_names)
print('cv_outs type:', type(cv_outs), 'cv_outs의 내부 원소개수:', len(cv_outs))
print(cv_outs[0].shape, cv_outs[1].shape, cv_outs[2].shape)
print(cv_outs)

output_layer name: ['yolo_82', 'yolo_94', 'yolo_106']
cv_outs type: <class 'list'> cv_outs의 내부 원소개수: 3
(507, 85) (2028, 85) (8112, 85)
[array([[0.03803749, 0.0470234 , 0.3876816 , ..., 0.        , 0.        ,
        0.        ],
       [0.04705836, 0.03385845, 0.2689603 , ..., 0.        , 0.        ,
        0.        ],
       [0.04941482, 0.03791986, 0.7151826 , ..., 0.        , 0.        ,
        0.        ],
       ...,
       [0.9585798 , 0.9460585 , 0.35046625, ..., 0.        , 0.        ,
        0.        ],
       [0.96015006, 0.9630715 , 0.29724196, ..., 0.        , 0.        ,
        0.        ],
       [0.9663636 , 0.9657401 , 0.79356086, ..., 0.        , 0.        ,
        0.        ]], dtype=float32), array([[0.01637367, 0.02457962, 0.04684627, ..., 0.        , 0.        ,
        0.        ],
       [0.01678773, 0.01458679, 0.46203217, ..., 0.        , 0.        ,
        0.        ],
       [0.02219823, 0.01376948, 0.0662718 , ..., 0.        , 0.        ,
        0.        ],
       ...,
       [0.97421783, 0.97686917, 0.04557502, ..., 0.        , 0.        ,
        0.        ],
       [0.98114103, 0.9762939 , 0.33147967, ..., 0.        , 0.        ,
        0.        ],
       [0.97884774, 0.98335934, 0.07896643, ..., 0.        , 0.        ,
        0.        ]], dtype=float32), array([[0.00859342, 0.00442324, 0.01781066, ..., 0.        , 0.        ,
        0.        ],
       [0.010101  , 0.01088366, 0.01980249, ..., 0.        , 0.        ,
        0.        ],
       [0.01071996, 0.00756924, 0.20484295, ..., 0.        , 0.        ,
        0.        ],
       ...,
       [0.9901033 , 0.9906244 , 0.01741469, ..., 0.        , 0.        ,
        0.        ],
       [0.9907341 , 0.9876037 , 0.01802968, ..., 0.        , 0.        ,
        0.        ],
       [0.98756605, 0.99131656, 0.17707303, ..., 0.        , 0.        ,
        0.        ]], dtype=float32)]

3개의 scale output layer에서 Object Detection 정보를 모두 수집.

  • center와 width,height좌표는 모두 좌상단, 우하단 좌표로 변경.
import numpy as np

# 원본 이미지를 네트웍에 입력시에는 (416, 416)로 resize 함. 
# 이후 결과가 출력되면 resize된 이미지 기반으로 bounding box 위치가 예측 되므로 이를 다시 원복하기 위해 원본 이미지 shape정보 필요
rows = img.shape[0]
cols = img.shape[1]

conf_threshold = 0.5
nms_threshold = 0.4

# bounding box의 테두리와 caption 글자색 지정
green_color=(0, 255, 0)
red_color=(0, 0, 255)

class_ids = []
confidences = []
boxes = []

# 3개의 개별 output layer별로 Detect된 Object들에 대해서 Detection 정보 추출 및 시각화 
for ix, output in enumerate(cv_outs):
    print('output shape:', output.shape)
    # feature map에 있는 anchor 갯수만큼 iteration하면서 Detected 된 Object 추출.(13x13x3, 26x26x3, 52x52x3)
    for jx, detection in enumerate(output):
        # class score는 detetection배열에서 5번째 이후 위치에 있는 값. 
        class_scores = detection[5:]
        # class_scores배열에서 가장 높은 값을 가지는 값이 class confidence, 그리고 그때의 위치 인덱스가 class id
        class_id = np.argmax(class_scores)
        confidence = class_scores[class_id]

        # confidence가 지정된 conf_threshold보다 작은 값은 제외 
        if confidence > conf_threshold:
            print('ix:', ix, 'jx:', jx, 'class_id', class_id, 'confidence:', confidence)
            # detection은 scale된 좌상단, 우하단 좌표를 반환하는 것이 아니라, detection object의 중심좌표와 너비/높이를 반환
            # 원본 이미지에 맞게 scale 적용 및 좌상단, 우하단 좌표 계산
            center_x = int(detection[0] * cols)
            center_y = int(detection[1] * rows)
            width = int(detection[2] * cols)
            height = int(detection[3] * rows)
            left = int(center_x - width / 2)
            top = int(center_y - height / 2)
            # 3개의 개별 output layer별로 Detect된 Object들에 대한 class id, confidence, 좌표정보를 모두 수집
            class_ids.append(class_id)
            confidences.append(float(confidence))
            boxes.append([left, top, width, height])
            


output shape: (507, 85)
ix: 0 jx: 319 class_id 0 confidence: 0.9317017
ix: 0 jx: 328 class_id 0 confidence: 0.96232384
ix: 0 jx: 334 class_id 0 confidence: 0.9984486
ix: 0 jx: 343 class_id 0 confidence: 0.9978433
output shape: (2028, 85)
ix: 1 jx: 831 class_id 2 confidence: 0.8169964
ix: 1 jx: 955 class_id 2 confidence: 0.8472691
ix: 1 jx: 1262 class_id 0 confidence: 0.9877816
ix: 1 jx: 1280 class_id 0 confidence: 0.99840033
ix: 1 jx: 1295 class_id 0 confidence: 0.6916561
ix: 1 jx: 1313 class_id 0 confidence: 0.9205806
output shape: (8112, 85)
ix: 2 jx: 2883 class_id 2 confidence: 0.9077368
ix: 2 jx: 2886 class_id 2 confidence: 0.63324535
ix: 2 jx: 3048 class_id 2 confidence: 0.9412014
ix: 2 jx: 3051 class_id 2 confidence: 0.615405
ix: 2 jx: 3184 class_id 2 confidence: 0.95041
ix: 2 jx: 3214 class_id 2 confidence: 0.9064125
ix: 2 jx: 3373 class_id 2 confidence: 0.68998003
ix: 2 jx: 3394 class_id 0 confidence: 0.76407045

NMS를 이용하여 각 Output layer에서 Detected된 Object의 겹치는 Bounding box를 제외.

conf_threshold = 0.5
nms_threshold = 0.4
idxs = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)

idxs
array([[ 2],
       [ 7],
       [ 3],
       [ 6],
       [14],
       [12],
       [10],
       [15],
       [ 5],
       [ 4],
       [17],
       [16],
       [11],
       [13]], dtype=int32)
idxs.flatten()

array([ 2,  7,  3,  6, 14, 12, 10, 15,  5,  4, 17, 16, 11, 13],
      dtype=int32)

NMS로 최종 filtering된 idxs를 이용하여 boxes, classes, confidences에서 해당하는 Object정보를 추출하고 시각화.

import matplotlib.pyplot as plt

# cv2의 rectangle()은 인자로 들어온 이미지 배열에 직접 사각형을 업데이트 하므로 그림 표현을 위한 별도의 이미지 배열 생성. 
draw_img = img.copy()

# NMS로 최종 filtering된 idxs를 이용하여 boxes, classes, confidences에서 해당하는 Object정보를 추출하고 시각화.
if len(idxs) > 0:
    for i in idxs.flatten():
        box = boxes[i]
        left = box[0]
        top = box[1]
        width = box[2]
        height = box[3]
        # labels_to_names 딕셔너리로 class_id값을 클래스명으로 변경. opencv에서는 class_id + 1로 매핑해야함.
        caption = "{}: {:.4f}".format(labels_to_names_seq[class_ids[i]], confidences[i])
        #cv2.rectangle()은 인자로 들어온 draw_img에 사각형을 그림. 위치 인자는 반드시 정수형.
        cv2.rectangle(draw_img, (int(left), int(top)), (int(left+width), int(top+height)), color=green_color, thickness=2)
        cv2.putText(draw_img, caption, (int(left), int(top - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, red_color, 1)
        print(caption)

img_rgb = cv2.cvtColor(draw_img, cv2.COLOR_BGR2RGB)
plt.figure(figsize=(12, 12))
plt.imshow(img_rgb)
person: 0.9984
person: 0.9984
person: 0.9978
person: 0.9878
car: 0.9504
car: 0.9412
car: 0.9077
car: 0.9064
car: 0.8473
car: 0.8170
person: 0.7641
car: 0.6900
car: 0.6332
car: 0.6154
<matplotlib.image.AxesImage at 0x7f4dd35bdc90>

단일 이미지를 Yolo로 detect하는 get_detected_img() 함수 생성.

def get_detected_img(cv_net, img_array, conf_threshold, nms_threshold, is_print=True):
    
    # 원본 이미지를 네트웍에 입력시에는 (416, 416)로 resize 함. 
    # 이후 결과가 출력되면 resize된 이미지 기반으로 bounding box 위치가 예측 되므로 이를 다시 원복하기 위해 원본 이미지 shape정보 필요
    rows = img_array.shape[0]
    cols = img_array.shape[1]
    
    draw_img = img_array.copy()
    
    #전체 Darknet layer에서 13x13 grid, 26x26, 52x52 grid에서 detect된 Output layer만 filtering
    layer_names = cv_net.getLayerNames()
    outlayer_names = [layer_names[i[0] - 1] for i in cv_net.getUnconnectedOutLayers()]
    
    # 로딩한 모델은 Yolov3 416 x 416 모델임. 원본 이미지 배열을 사이즈 (416, 416)으로, BGR을 RGB로 변환하여 배열 입력
    cv_net.setInput(cv2.dnn.blobFromImage(img_array, scalefactor=1/255.0, size=(416, 416), swapRB=True, crop=False))
    start = time.time()
    # Object Detection 수행하여 결과를 cvOut으로 반환 
    cv_outs = cv_net.forward(outlayer_names)
    layerOutputs = cv_net.forward(outlayer_names)
    # bounding box의 테두리와 caption 글자색 지정
    green_color=(0, 255, 0)
    red_color=(0, 0, 255)

    class_ids = []
    confidences = []
    boxes = []

    # 3개의 개별 output layer별로 Detect된 Object들에 대해서 Detection 정보 추출 및 시각화 
    for ix, output in enumerate(cv_outs):
        # Detected된 Object별 iteration
        for jx, detection in enumerate(output):
            scores = detection[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]
            # confidence가 지정된 conf_threshold보다 작은 값은 제외 
            if confidence > conf_threshold:
                #print('ix:', ix, 'jx:', jx, 'class_id', class_id, 'confidence:', confidence)
                # detection은 scale된 좌상단, 우하단 좌표를 반환하는 것이 아니라, detection object의 중심좌표와 너비/높이를 반환
                # 원본 이미지에 맞게 scale 적용 및 좌상단, 우하단 좌표 계산
                center_x = int(detection[0] * cols)
                center_y = int(detection[1] * rows)
                width = int(detection[2] * cols)
                height = int(detection[3] * rows)
                left = int(center_x - width / 2)
                top = int(center_y - height / 2)
                # 3개의 개별 output layer별로 Detect된 Object들에 대한 class id, confidence, 좌표정보를 모두 수집
                class_ids.append(class_id)
                confidences.append(float(confidence))
                boxes.append([left, top, width, height])
    
    # NMS로 최종 filtering된 idxs를 이용하여 boxes, classes, confidences에서 해당하는 Object정보를 추출하고 시각화.
    idxs = cv2.dnn.NMSBoxes(boxes, confidences, conf_threshold, nms_threshold)
    if len(idxs) > 0:
        for i in idxs.flatten():
            box = boxes[i]
            left = box[0]
            top = box[1]
            width = box[2]
            height = box[3]
            # labels_to_names 딕셔너리로 class_id값을 클래스명으로 변경. opencv에서는 class_id + 1로 매핑해야함.
            caption = "{}: {:.4f}".format(labels_to_names_seq[class_ids[i]], confidences[i])
            #cv2.rectangle()은 인자로 들어온 draw_img에 사각형을 그림. 위치 인자는 반드시 정수형.
            cv2.rectangle(draw_img, (int(left), int(top)), (int(left+width), int(top+height)), color=green_color, thickness=2)
            cv2.putText(draw_img, caption, (int(left), int(top - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, red_color, 1)

    if is_print:
        print('Detection 수행시간:',round(time.time() - start, 2),"초")
    return draw_img
import cv2
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import time
import os

# image 로드 
img = cv2.imread('/content/data/beatles01.jpg')

weights_path = '/content/pretrained/yolov3.weights'
config_path =  '/content/pretrained/yolov3.cfg'

# darknet yolo pretrained 모델 로딩
cv_net_yolo = cv2.dnn.readNetFromDarknet(config_path, weights_path)
conf_threshold = 0.5
nms_threshold = 0.4
# Object Detetion 수행 후 시각화 
draw_img = get_detected_img(cv_net_yolo, img, conf_threshold=conf_threshold, nms_threshold=nms_threshold, is_print=True)

img_rgb = cv2.cvtColor(draw_img, cv2.COLOR_BGR2RGB)

plt.figure(figsize=(12, 12))
plt.imshow(img_rgb)

Detection 수행시간: 4.08 초
<matplotlib.image.AxesImage at 0x7f4dc2adbd50>

 

 

 

 

tiny Yolo로 Object Detection 수행하기.

import cv2
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import time
import os

# image 로드 
img = cv2.imread('/content/data/beatles01.jpg')

weights_path = '/content/pretrained/yolov3-tiny.weights'
config_path =  '/content/pretrained/yolov3-tiny.cfg'

# darknet tiny yolo pretrained 모델 로딩
cv_net_yolo_tiny = cv2.dnn.readNetFromDarknet(config_path, weights_path)

 

conf_threshold = 0.2
nms_threshold = 0.4
# Object Detetion 수행 후 시각화 
draw_img = get_detected_img(cv_net_yolo_tiny, img, conf_threshold=conf_threshold, nms_threshold=nms_threshold, is_print=True)

img_rgb = cv2.cvtColor(draw_img, cv2.COLOR_BGR2RGB)

plt.figure(figsize=(12, 12))
plt.imshow(img_rgb)

Detection 수행시간: 0.48 초
<matplotlib.image.AxesImage at 0x7f4dc2275ad0>

 

 

 

 

반응형
728x90
반응형

OpenCVDNN 으로 YOLO inference 구현 시 유의 사항

- opencv yolo inference 코드는 기존 opencv inference 코드와 다름

- 3개의 output feature map 에서 직접 object detection 정보 추출

 

Pretrained inference model loading 방법

- weight model file 과 config file은 darknet 사이트에서 download 가능

- cv2.dnn.readNetFromDarknet(config file, weight model file)으로 pretrained inference model loading

- readNetFromDarket(config file, weight model file)에서 config file 인자가 weight model file 인자보다 먼저 위치함

 

82번 layer, 92번 layer, 106번 layer 등

사용자가 직접 3개의 다른 scale별 구성된 output layer에서 object detect 결과를 추출해야함

사용자가 직접, NMS로 최종 결과 필터링 해야 함

 

Bounding box 정보 추출 시 직접 85개의 구성에서 추출

- coco 데이터 세트로 pretrained model에서 bbox 정보추출하기

   * bounding box 정보를 4개 좌표, 1개 object score, 그리고 80개 class score로 구성된 85개의 정보구성

   * class id와 class score는 80개 vector에서 가장 높은 값을 가지는 위치 인덱스와 그값임

 

추출 좌표의 변환

bx = d(tx)+cx

by = d(ty)+cy

bw = pwe^tw

bh = phe^th

* OpenCV yolo 로 추출한 좌표는 detected object의 center와 width, height 값이므로, 좌상단, 우하단 좌표로 변경 필요.

 

 

OpenCV yolo inference 구현 절차

 

 

 

반응형

+ Recent posts