영상인식

Semantic Segmentation( feat. E-net )

728x90

Semantic Segmentation이란 물체들을 분할해서 표시하되 같은 물체들을 묶어서 분할하는 방법이다.( 같은 색으로 처리해 표현.)
 
즉, 한 이미지 데이터에 여러개의 객체를 구분하기 위해 Semantic Segmentation기법을 이용한다. 그 중 OpenCV DNN module인 E-net을 사용해 프로그래밍해보겠다.

E-net은 Blob이미지 데이터를 입력받는다. Blob은 동일한 방식으로 전처리된 동일한 너비, 높이 및 채널수를 가진 이미지를 말한다.

 

순서
    1. 이미지를 Blob 이미지 데이터로 변환.
    2. E-net모델을 사용해 Semantic Segmentation한다.
    3. softmax방식을 이용하기 때문에 클래스의 개수만큼의 데이터가 나오는데 하나의 이미지 데이터로 처리.      (argmax() 함수를 이용)
    4. 객체별 색깔의 데이터를 불러와 3에서 나온 값들에 매칭해 색깔을 입혀준다.
    5. 원래의 이미지와 Semantic Segmentation해서 Object별로 색깔이 입혀진 이미지를 가중치를 주어 합쳐준다.

import numpy as np
import imutils
import cv2

SET_WIDTH = int(600)
normalize_image = 1/255.0
resize_image_shape = (1024,512)

sample_img = cv2.imread('data4/images/example_02.jpg')
sample_img = imutils.resize(sample_img, width=SET_WIDTH)

blob_img = cv2.dnn.blobFromImage(sample_img, normalize_image, resize_image_shape, 0, swapRB=True, crop=False)

# ENET model 가져오기.
cv_enet_model = cv2.dnn.readNet('data4/enet-cityscapes/enet-model.net')

# 모델에 blob 데이터 입력.
cv_enet_model.setInput(blob_img)
print(cv_enet_model)

# 출력값 받아오기.
cv_enet_model_output = cv_enet_model.forward()
print( cv_enet_model_output.shape )

# 개채의 이름과 넘버를 가져져오기.
label_values = open('data4/enet-cityscapes/enet-classes.txt').read().split('\n')
label_values = label_values[ :-1]
# 끝에 '' 만 들어있는 데이터가 있어 버린다.
print(label_values)

IMG_OUTPUT_SHAPE_START = 1
IMG_OUTPUT_SHAPE_END = 4
classes_num, h, w = cv_enet_model_output.shape[IMG_OUTPUT_SHAPE_START : IMG_OUTPUT_SHAPE_END]

# 모델의 아웃풋 20개 핼렬을, 하나의 행렬로 만듦.
class_map = np.argmax(cv_enet_model_output[0], axis=0)

# 클래스별 색깔을 가져오기.
CV_ENET_IMG_COLORS = open('data4/enet-cityscapes/enet-colors.txt').read().split('\n')
CV_ENET_IMG_COLORS = CV_ENET_IMG_COLORS[ : -1]
CV_ENET_IMG_COLORS = np.array([ np.array( color.split(',') ).astype('int') for color in CV_ENET_IMG_COLORS ] )


# E-net으로 추출한 Semantic Segmentation된 데이터를 클래스에 맞는 색을 넣어주기.
mask_class_map = CV_ENET_IMG_COLORS[class_map]

# 크기를 바꿔주기.
mask_class_map = cv2.resize(mask_class_map, (sample_img.shape[1],sample_img.shape[0]), interpolation=cv2.INTER_NEAREST )

# 그냥 더하면 255 를 넘어가므로 가중치를 두어 합하기.
cv_enet_model_output = ( ( 0.4 * sample_img ) + ( 0.6 * mask_class_map ) ).astype('uint8')


# 어떤 색깔이 어떤 클래스인지 표시하기위한 코드.
my_legend = np.zeros( ( len(label_values) * 25 , 300, 3 ) , dtype='uint8' )
for( i,(class_name, img_color) ) in enumerate( zip( label_values, CV_ENET_IMG_COLORS) ):
    color_info = [ int(color) for color in img_color ]
    cv2.putText(my_legend, class_name, (5, (i*25)+17), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 2 )
    cv2.rectangle(my_legend, ( 100, (i*25) ), (300, (i*25)+25 ), tuple(color_info), -1  )

cv2.imshow('output', cv_enet_model_output)
cv2.imshow('origin', sample_img)
cv2.imshow('legend', my_legend)

cv2.waitKey()
cv2.destroyAllWindows()

 

 

코드를 살짝 수정해 주행하는 차량이 촬영한 동영상을 변환해보았다. 동영상의 프레임당 변환하는 속도가 0.3 ~ 0.4초정도 걸리기에 영상이 끊기는 모습이 보였는데, 물체 인식하려는 영역을 줄여준다면 속도향상에 도움이 될 것 같다.( 전체 영역을 수많은 작은 영역으로 나누어 검출하는게 아닌 IoU - Intersection of Union - 방법으로 더 적은 영역에 물체가 있을 확률이 높은 곳을 찾아 인식하는 아이디어를 적용하면 될 것 같음)

import numpy as np
import imutils
import cv2

SET_WIDTH = int(600)
normalize_image = 1/255.0
resize_image_shape = (1024,512)

normalize_image = 1 / 255.0
resize_image_shape = (1024, 512)

sv = cv2.VideoCapture('data4/video/video.mp4')
cv_enet_model = cv2.dnn.readNet('data4/enet-cityscapes/enet-model.net')

CV_ENET_IMG_COLORS = open('data4/enet-cityscapes/enet-colors.txt').read().split('\n')
CV_ENET_IMG_COLORS = CV_ENET_IMG_COLORS[ : -1]
CV_ENET_IMG_COLORS = np.array([ np.array( color.split(',') ).astype('int') for color in CV_ENET_IMG_COLORS ] )

try:
    prop = cv2.cv.CV_CAP_PROP_FRAME_COUNT if imutils.is_cv2() else cv2.CAP_PROP_FRAME_COUNT
    total = sv.get(prop)
    print("[INFO] {} total frames in video.".format(total))
except:
    print('[INFO] could not determine number of frames in video')
    total = -1

while sv.isOpened():
    grapbbed, frame = sv.read()
    if grapbbed == False:
        break 

    video_frame = imutils.resize(frame, width=SET_WIDTH)

    blob_img = cv2.dnn.blobFromImage( frame, normalize_image, resize_image_shape, 0, swapRB=True, crop=False )
    
    cv_enet_model.setInput(blob_img)
    cv_enet_model_output = cv_enet_model.forward()

    (classes_num, height, width) = cv_enet_model_output.shape[1:4]
    class_map = np.argmax(cv_enet_model_output[0], axis=0 )

    mask_class_map = CV_ENET_IMG_COLORS[class_map]

    mask_class_map = cv2.resize( mask_class_map, (video_frame.shape[1], video_frame.shape[0]), interpolation=cv2.INTER_NEAREST )
    cv_enet_model_output = ( (0.3 * video_frame) + (0.7 * mask_class_map ) ).astype('uint8')
    
    cv2.imshow('Frame', cv_enet_model_output)

    if cv2.waitKey(25) & 0xFF ==27:
        break

sv.release()
cv2.destroyAllWindows()
728x90