자율주행/컴퓨터 비젼 프로젝트(python)

yolo 자동차 인식 프로그램

nosungmin 2023. 2. 11. 15:41
import cv2
import numpy as np
from google.colab.patches import cv2_imshow
import IPython
모듈 임포트
min_confidence = 0.5 #인식률 50퍼센트 이상인 것만 인식하기
net = cv2.dnn.readNet(weight_file, cfg_file)
 

이 코드는 OpenCV의 딥 러닝 모듈(cv2.dnn)을 사용하여, YOLO(You Only Look Once) 딥 러닝 객체 감지 모델을 불러오는 과정입니다.

  • cv2.dnn.readNet(weight_file, cfg_file) 함수는 가중치 파일(weight_file)과 설정 파일(cfg_file)을 읽어와서 딥 러닝 모델(net)을 생성합니다.
  • weight_file은 미리 학습된 모델의 가중치(weight) 파일의 경로를 나타냅니다. 이 파일은 모델이 학습된 가중치 값을 포함하고 있습니다.
  • cfg_file은 모델의 구성(configuration) 파일의 경로를 나타냅니다. 이 파일은 모델의 구조와 학습에 사용된 하이퍼파라미터(hyperparameters) 등의 정보를 포함하고 있습니다.
  • net은 딥 러닝 모델 객체를 나타내는 변수입니다. net 객체에는 학습된 모델의 구조와 가중치 값이 포함되어 있습니다. 이 모델은 이미지나 비디오에서 객체 감지를 수행하는 데 사용됩니다.
classes = []
with open(name_file, 'r'as f:
    classes = [line.strip() for line in f.readlines()]
print(classes)
layer_names = net.getLayerNames()
output_layers = [layer_names[i[0] - 1for i in net.getUnconnectedOutLayers()]
colors = np.random.uniform(0255, size=(len(classes), 3))
 

이 코드는 YOLO 객체 감지 알고리즘에서 사용되는 네트워크에서 감지할 클래스 이름을 포함하고 있는 파일을 읽어와서, 해당 클래스들과 연관된 랜덤한 색상을 생성합니다.

  • name_file은 클래스 이름을 포함하고 있는 파일의 경로입니다.
  • with open(name_file, 'r') as f:은 name_file을 읽기 모드로 열고, 이를 f 객체에 할당합니다. with 구문을 사용하면 파일을 열고, 작업이 끝나면 자동으로 닫히게 됩니다.
  • classes = [line.strip() for line in f.readlines()]은 f에서 읽어온 각 줄을 strip()하여 클래스 이름을 구성하는 리스트 classes에 저장합니다.
  • layer_names = net.getLayerNames()는 딥러닝 모델의 레이어 이름들을 리스트 형태로 반환합니다.
  • output_layers = [layer_names[i[0] - 1] for i in net.getUnconnectedOutLayers()]는 객체 감지 결과를 출력하기 위한 레이어 이름들을 리스트 형태로 반환합니다.
  • colors = np.random.uniform(0, 255, size=(len(classes), 3))는 classes 리스트의 길이와 같은 크기의 0에서 255 범위의 랜덤한 색상값을 생성하고, 이를 colors 배열에 저장합니다. 각 클래스마다 다른 랜덤한 색상을 지정하여, 객체 감지 결과를 시각적으로 구분할 수 있도록 합니다.
def detectAndDisplay(frame):
    IPython.display.clear_output(wait=True)
    height, width, channedls = frame.shape
    blob = cv2.dnn.blobFromImage(frame, 0.00392, (416416), (000), True, crop=False)
    net.setInput(blob)
    outs = net.forward(output_layers)
 

이 코드는 OpenCV의 dnn 모듈을 사용하여 이미지에서 객체 탐지를 수행하는 함수인 detectAndDisplay를 정의하고 있습니다.

해당 함수는 입력으로 이미지 frame을 받아들이고, net 객체(사전에 YOLOv3 모델을 로드하여 생성된 cv2.dnn_Net 객체)를 사용하여 frame에서 객체를 탐지합니다.

코드에서 사용된 cv2.dnn.blobFromImage 함수는 입력 이미지 frame을 전처리하는데 사용됩니다. 전처리 작업은 입력 이미지의 크기와 형식을 모델에 맞게 조정하고, 평균값을 빼거나 스케일링 등의 작업을 수행하여 모델의 입력 형식에 맞게 변경하는 작업입니다.

blobFromImage 함수에서는 다음과 같은 매개변수를 사용합니다.

  • frame: 전처리할 이미지
  • scalefactor: 입력 이미지의 크기 조정 비율
  • size: 모델에 맞게 조정할 이미지의 크기
  • mean: 입력 이미지에서 빼야 할 평균값
  • swapRB: R, B 채널을 교환할 것인지 여부
  • crop: 크롭(crop) 여부

그 다음, net.setInput 함수를 사용하여 blob을 모델의 입력으로 설정하고, net.forward 함수를 사용하여 YOLOv3 모델의 출력값을 계산합니다. output_layers는 모델의 출력 레이어 이름(들)을 저장하는 리스트입니다.

YOLOv3 모델은 3개의 출력 레이어를 가지며, 이 레이어들은 각기 다른 크기의 객체를 탐지합니다. 따라서, net.forward 함수는 모델의 3개 출력 레이어에서 나온 결과값들을 outs에 저장하고, outs를 사용하여 객체를 식별하고 경계 상자를 그리는 등의 작업을 수행합니다.

 

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

 

    for out in outs:
        for detection in out:
            scores = detection[5:]
            class_id = np.argmax(scores)
            confidence = scores[class_id]
            if confidence > min_confidence:
                # Object detected
                center_x = int(detection[0] * width)
                center_y = int(detection[1] * height)
                w = int(detection[2] * width)
                h = int(detection[3] * height)

 

                # Rectangle coordinates
                x = int(center_x - w / 2)
                y = int(center_y - h / 2)

 

                boxes.append([x, y, w, h])
                confidences.append(float(confidence))
                class_ids.append(class_id)

이 코드는 객체 감지 결과(outs)에서 각 객체의 정보를 추출하여 각 객체를 포함하는 bounding box의 좌표와 확률 등을 리스트(boxes, confidences, class_ids)에 저장하는 과정입니다.

  • for out in outs:은 객체 감지 결과(outs)에서 각 객체의 정보를 추출하기 위해 outs를 반복(iterate)합니다.
  • for detection in out:은 현재 객체(out)에서 각 detection(감지 결과)을 반복(iterate)합니다.
  • scores = detection[5:]는 detection에서 5번 인덱스 이후의 값(각 클래스에 대한 확률)을 scores 변수에 저장합니다.
  • class_id = np.argmax(scores)는 가장 높은 확률을 가진 클래스의 인덱스(class_id)를 찾습니다.
  • confidence = scores[class_id]는 가장 높은 확률을 가진 클래스의 확률(confidence)을 찾습니다.
  • if confidence > min_confidence:은 추출한 객체의 확률이 min_confidence보다 큰 경우에만 객체를 포함하는 bounding box 정보를 추출합니다.
  • center_x, center_y, w, h는 detection에서 추출한 객체의 중심 좌표와 폭/높이 정보를 계산합니다.
  • x, y는 bounding box의 좌측 상단 좌표를 계산합니다.
  • boxes, confidences, class_ids 리스트에 bounding box의 좌표와 확률 등을 각각 추가합니다. 이렇게 함으로써 각 객체의 정보를 리스트에 저장할 수 있습니다.
    indexes = cv2.dnn.NMSBoxes(boxes, confidences, min_confidence, 0.4)
 
#indexes = cv2.dnn.NMSBoxes(boxes, confidences, min_confidence, 0.4)이 코드는 "cv2.dnn.NMSBoxes" 함수를 사용해서 Non-maximum Suppression (NMS)을 수행하는 코드입니다.
#NMS는 여러 개의 바운딩 박스 중에서 최종적으로 검출될 바운딩 박스를 선택하는 과정입니다. 특정 객체에 대해서 여러 개의 바운딩 박스가 생성될 수 있지만, 이를 한 개로 결합해야 합니다. 
#NMSBoxes 함수는 이 과정에서, 검출된 바운딩 박스 중에서 "min_confidence" 보다 큰 confidence score를 갖는 박스만을 유지하며, 나머지 박스는 제거합니다.
#NMSBoxes 함수는 최종적으로 유지되는 바운딩 박스의 인덱스를 반환합니다.
#0.4는 NMS를 적용하는 과정에서 두 바운딩 박스가 겹치는 정도(IOU, Intersection over Union)를 나타낸다. 
#즉, 두 바운딩 박스가 0.4보다 높은 IOU를 갖으면, 높은 점수를 갖는 바운딩 박스만을 유지하고 나머지 바운딩 박스는 제거된다.
 
    font = cv2.FONT_HERSHEY_COMPLEX
    for i in range(len(boxes)):
        if i in indexes:
            x, y, w, h = boxes[i]
            label = str(classes[class_ids[i]])
            print(i, label)
            color = colors[i]
            cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2)
            cv2.putText(frame, label, (x, y + 30), font, 0.5, (02550), 1)  
           
        #이 코드는 사각형을 그리고, 각 사각형 안에 클래스 이름을 표시하는 기능을 합니다.
        #우선, 'boxes' 리스트에 저장된 각 객체의 좌표를 순회하여 그 좌표에 대한 색상(color)을 결정합니다. 
        #이때, 'indexes' 리스트에 저장된 좌표만 그려집니다.
        #그리고, 각 사각형 안에 각 객체가 속한 클래스의 이름을 표시하기 위해 'cv2.putText' 함수를 사용합니다.  

 

#-- 2. Read the video stream
cap = cv2.VideoCapture(file_name)
if not cap.isOpened:
    print('--(!)Error opening video capture')
    exit(0)
while True:
    ret, frame = cap.read()
    if frame is None:
        print('--(!) No captured frame -- Break!')
        break
    detectAndDisplay(frame)
 

이 코드는 OpenCV 라이브러리를 사용하여 동영상 파일을 읽어와서, 각 프레임에 대해 객체 감지 알고리즘을 적용하고 결과를 출력하는 루프를 수행합니다.

  • cap = cv2.VideoCapture(file_name)은 file_name으로 지정한 동영상 파일을 열어 cap 변수에 저장합니다.
  • if not cap.isOpened:은 cap 객체가 정상적으로 열렸는지 확인합니다. 만약 열리지 않았을 경우, 오류 메시지를 출력하고 프로그램을 종료합니다.
  • while True:은 무한 루프를 시작합니다. 이 루프에서는 cap.read() 메소드를 사용하여 동영상 파일에서 프레임을 읽어옵니다.
  • ret, frame = cap.read()은 cap 객체에서 다음 프레임을 읽어와 frame 변수에 저장합니다. ret은 읽기가 성공적으로 이루어졌는지를 나타내는 Boolean 값입니다.
  • if frame is None:은 프레임이 정상적으로 읽혀지지 않은 경우에 대한 예외 처리를 수행합니다. 프레임이 없는 경우, 오류 메시지를 출력하고 루프를 종료합니다.
  • detectAndDisplay(frame)은 현재 프레임에 대해 객체 감지 알고리즘을 적용하고 결과를 출력하는 함수입니다. frame을 함수의 인자로 전달하여 실행합니다. 이 함수는 frame에 대해 객체 감지 알고리즘을 수행하고, 결과를 프레임에 표시합니다. 이후 다음 프레임에 대해 반복적으로 실행됩니다.

출력 결과물