관리 메뉴

클라이언트/ 서버/ 엔지니어 "게임 개발자"를 향한 매일의 공부일지

비전 에이전트 3 - [비전 에이전트 2] 교통약자 보호구역 알림 본문

인공지능/컴퓨터 비전

비전 에이전트 3 - [비전 에이전트 2] 교통약자 보호구역 알림

huenuri 2024. 11. 12. 04:53

이번에는 교통약자 보호구역 알림에 대한 비전 에이전트 실습을 진행해 보겠다.

 

어린이, 노인, 장애인과 같은 교통약자에 대한 보호 정책이 강화되고 있다. 이들 보호구역에 설치된 교통 표지판을 컴퓨터 비전 기술로 인식하여 운전자에게 알리면 사고를 줄이는데 크게 도움이 될 것이다. 이전 장에서 다룬 SIFT 특징과 SIFT를 사용한 프로그램 5-4를 잘 활용하면 교통 표지판을 인식할 수 있다.

블랙박스 또는 스마트폰을 통해 들어오는 동영상을 처리하면 좋겠지만 여기서는 사용자가 선택한 도로 영상에서 표지판을 찾는 일로 한정한다.


 

 

 

도로 영상에서 표지판 식별

다음 프로그램에서 실행 결과에 있는 GUI 윈도우를 먼저 살펴보자. <표지판 등록> 버튼은 세 종류의 표지판 모델 영상을 읽어 등록하고, <도로 영상 불러옴> 버튼은 사용자가 도로 영상을 선택하게 하며, <인식> 버튼은 표지판 영상을 인식하고 결과를 보여준다.

이 프로그램을 실행하려면 png 파일 3개를 소스 프로그램이 있는 폴더에 저장해두어야 한다.

 

교통약자 보호구역 알림 구현하기

이 코드는 교통 약자 보호 구역 표지판을 인식하는 GUI 애플리케이션이다. 사용자가 특정 표지판 모델 이미지를 등록하고 도로 영상을 입력하면, SIFT와 FLANN 매칭을 사용하여 표지판을 인식하고 경고 메시지를 제공한다.

 

 

이 코드도 정말 길어서 세부적으로 하나씩 나누어서 설명해 보겠다. 책에 있는 설명을 읽으면 이해가 잘 되지 않아 chatgpt에게 물어보는 것이 훨씬 더 나았다.

 

1. 라이브러리 임포트

import cv2 as cv
import numpy as np
from PyQt5.QtWidgets import QMainWindow, QPushButton, QApplication, QFileDialog, QLabel
import sys
import winsound
  • cv2와 numpy: OpenCV와 Numpy를 사용하여 이미지와 비디오를 처리.
  • PyQt5.QtWidgets: PyQt5를 사용하여 GUI 요소(QMainWindow, QPushButton 등)를 구성.
  • sys: 시스템 관련 기능을 사용하기 위해 필요.
  • winsound: 경고음을 내기 위해 사용.

 

2. TrafficWeak 클래스 정의

TrafficWeak 클래스는 GUI 애플리케이션의 메인 클래스이며, QMainWindow를 상속받는다.

초기화 함수 __init__

def __init__(self):
    super().__init__()
    self.setWindowTitle('교통약자 보호')
    self.setGeometry(200, 200, 700, 200)
  • GUI 창의 제목과 크기를 설정한다.

 

버튼과 라벨 설정

signButton = QPushButton('표지판 등록', self)
roadButton = QPushButton('도로 영상 불러옴', self)
recognitionButton = QPushButton('인식', self)
quitButton = QPushButton('나가기', self)
self.label = QLabel('환영합니다!', self)
  • 여러 버튼과 라벨을 생성하여 사용자 인터페이스 요소를 배치.

 

버튼 위치 및 이벤트 연결

  • 각 버튼의 위치와 크기를 설정하고, 클릭 시 연결할 콜백 함수를 정의한다.
signButton.clicked.connect(self.signFunction)
roadButton.clicked.connect(self.roadFunction)
recognitionButton.clicked.connect(self.recognitionFunction)
quitButton.clicked.connect(self.quitFunction)

 

 

3. 주요 함수 설명

signFunction: 표지판 이미지 등록

def signFunction(self):
    """표지판 모델 이미지를 등록하는 함수"""
    self.label.clear()
    self.label.setText('교통약자 번호판을 등록합니다.')
    
    for fname, _ in self.signFiles:
        self.signImgs.append(cv.imread(fname))  # 이미지 읽어오기
        cv.imshow(fname, self.signImgs[-1])  # 이미지 창에 표시

 

  • self.signFiles에 등록된 파일 이름 리스트를 반복하면서 이미지를 읽고, 각 이미지를 창에 표시
  • self.signImgs에 표지판 이미지 데이터를 저장하여 인식 단계에서 활용

 

roadFunction: 도로 영상 불러오기

def roadFunction(self):
    """도로 영상을 불러오는 함수"""
    if not self.signImgs: 
        self.label.setText('먼저 번호판을 등록하세요.')
    else:
        fname = QFileDialog.getOpenFileName(self, '파일 읽기', './')
        self.roadImg = cv.imread(fname[0])
        if self.roadImg is None:
            sys.exit('파일을 찾을 수 없습니다.')
        cv.imshow('Road scene', self.roadImg)

 

  • 도로 영상 파일을 열어 self.roadImg에 저장하고, cv.imshow로 화면에 표시.
  • 표지판 이미지가 먼저 등록되지 않았다면 경고 메시지를 표시한다.

 

recognitionFunction: 표지판 인식

def recognitionFunction(self):
    """표지판 인식을 수행하는 함수"""
    if not hasattr(self, 'roadImg'):
        self.label.setText('먼저 도로 영상을 입력하세요.')
    else:
        sift = cv.SIFT_create()  # SIFT 객체 생성
  • SIFT(Scale-Invariant Feature Transform) 객체를 생성하여 도로 영상과 표지판 모델 이미지에서 특징점과 서술자를 추출.

 

표지판 모델 이미지에서 SIFT 특징점 추출

KD = []
for img in self.signImgs: 
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    KD.append(sift.detectAndCompute(gray, None))
  • 표지판 모델 이미지를 그레이스케일로 변환하고, 각 이미지에 대해 SIFT를 사용하여 특징점과 서술자를 추출

 

도로 영상에서 SIFT 특징점 추출

grayRoad = cv.cvtColor(self.roadImg, cv.COLOR_BGR2GRAY)
road_kp, road_des = sift.detectAndCompute(grayRoad, None)
  • 도로 영상 역시 그레이스케일로 변환한 후, 특징점과 서술자를 추출한다.

 

FLANN을 사용하여 매칭 수행

matcher = cv.DescriptorMatcher_create(cv.DescriptorMatcher_FLANNBASED)
GM = []  # 각 표지판 모델에 대한 good match 저장
for sign_kp, sign_des in KD:
    knn_match = matcher.knnMatch(sign_des, road_des, 2)
    T = 0.7
    good_match = []
    for nearest1, nearest2 in knn_match:
        if (nearest1.distance / nearest2.distance) < T:
            good_match.append(nearest1)
    GM.append(good_match)
  • FLANN(Fast Library for Approximate Nearest Neighbors)을 사용하여 도로 영상과 각 표지판 모델 간의 매칭을 수행.
  • KNN을 이용해 두 최근접 이웃을 찾고, 거리 비율 임계값(T = 0.7)을 만족하는 매칭을 good_match에 저장

 

최적의 매칭 쌍 선택

best = GM.index(max(GM, key=len))
if len(GM[best]) < 4:
    self.label.setText('표지판이 없습니다.')
else:
    ...

 

  • 매칭 쌍의 개수가 최대인 표지판을 선택한다.
  • 만약 최적의 매칭 쌍 개수가 4개 미만이면 인식 실패로 간주하고 경고 메시지를 출력한다.

 

호모그래피 계산 및 시각화

sign_kp = KD[best][0]
good_match = GM[best]
points1 = np.float32([sign_kp[gm.queryIdx].pt for gm in good_match])
points2 = np.float32([road_kp[gm.trainIdx].pt for gm in good_match])
H, _ = cv.findHomography(points1, points2, cv.RANSAC)

 

  • 매칭된 특징점 좌표를 사용하여 호모그래피 변환 행렬을 계산하고, 인식된 표지판의 위치를 도로 영상에서 시각화한다.

 

결과 표시 및 경고음 출력

self.roadImg = cv.polylines(self.roadImg, [np.int32(box2)], True, (0, 255, 0), 4)
self.label.setText(f'{self.signFiles[best][1]} 보호구역입니다. 30km로 서행하세요.')
winsound.Beep(3000, 500)
  • 인식된 표지판을 초록색으로 강조 표시하고, 경고 메시지를 출력한 후, 경고음을 재생한다.

 

quitFunction: 애플리케이션 종료

app = QApplication(sys.argv)
win = TrafficWeak()
win.show()
app.exec_()
  • QApplication을 생성하여 GUI 애플리케이션을 실행한다.

 

요약

이 프로그램은 사용자가 등록한 표지판 모델 이미지를 바탕으로 도로 영상에서 특정 표지판을 인식하고, 성공적으로 인식되면 해당 보호 구역 안내 메시지를 표시하고 경고음을 재생하는 기능을 제공한다.


 

 

 

코드 실행해 보기

 

 

 

이렇게 3개의 표지판을 등록했다.


 

 

 

 

1. 노인 보호 구역 표지판

 

 

그런 다음 도로 영상을 불러온다.

 

 

 

 

그러면 삐 소리가 나면서 다음과 같이 표지판이 인식되는데 노인 보호 표지판으로 인식되었음을 확인했다. 다른 표지판 영상도 불러오기로 하자.

 

 

 

 

 

 

 

이 영상 역시 노인 보호라고 인식했다.


 

 

 

2. 어린이 보호 구역 표지판

 

 

 

 

 

이 표지판은 어린이 보호라고 인식했다.


 

 

 

 

3. 장애인 보호 구역 표지판

 

 

 

 

 

 

 

 

 

 

이 표지판은 이미지가 흐릿해서 표지판이 인식을 못하고 있다.


 

 

 

4. 그 외의 표지판

 

 

 

 

 

 

 

 

 

 

이 사진은 표지판이 없고 바닥에 장애인 보호라고 글씨만 써있다. 그래도 인식이 잘 되는지 보겠다.

 

 

 

 

 

 

 

이것도 인식이 매우 잘 되었다.

 


 

 

 

학습을 마치고

컴퓨터 비전이 사용되는 프로그램을 살펴보니 정말 놀라웠다. 이런 식으로 상용화가 된다면 세상은 정말 편리해지고 윤택해질 것 같다. 어렵기는 하지만 내가 꼭 배워야 하는 과목임이 분명해졌다. 

교통 표지판은 제시된 이미지가 정말 많았는데 2개만 빼고 모두 캡처하여 올려보았다. 이렇게 코드의 실행 과정을 학습일지로 써야 나중에도 보기 쉽고 어떤 공부를 어떻게 진행했는지 파악할 수 있다.

 

비전에이전트 실습이 두 개 정도 남아있는데 이어서 학습해 보겠다.