일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 뉴럴링크
- AI
- 생성형 AI
- 확산 모델
- 멀티모달
- 딥마인드
- 가상환경
- AI 기술
- tts
- 티스토리챌린지
- 메타
- ubuntu
- LORA
- 이미지 편집
- ControlNet
- ChatGPT
- OpenAI
- 오블완
- 우분투
- 인공지능
- TRANSFORMER
- 트랜스포머
- 아두이노
- javascript
- PYTHON
- 시간적 일관성
- 일론 머스크
- LLM
- 오픈AI
- 서보모터
- Today
- Total
AI 탐구노트
교통 CCTV 영상 기반의 자동차 속도 측정 본문
1.서론 : 효율성 높은 교통정보 수집 방법 (기존 CCTV 활용, 영상분석)
날씨가 제법 쌀쌀해졌습니다. 며칠 전만 해도 아... 내가 좋아하는 가을이 조금만 더 머물러 줬으면 하는 생각이었는데, 어제부터 갑자기 쌀쌀해지더니 오늘 아침 기온이 드디어 영하로 떨어졌습니다. 겨울이 다가와 도로 사정도 달라지는 이 시기면, 늘상 뉴스에 많이 오르내리는 단어가 있죠. '블랙아이스'... 도로 표면에 서리 등이 얼어붙어 만들어진 얼음을 말하는데, 이 때문에 차가 운행 중에 미끄러져 사고가 나는 일이 빈번하기 때문입니다. 완벽하진 못하지만, 그나마 할 수 있는 대응은 평소에 타이어 관리를 잘 하고 운전할 때 속도를 과하게 내지 않고 앞 차와는 충분한 거리를 두는 것 등의 기초적인 교통 준칙을 따르는 것이겠습니다.
교통 흐름을 효율적으로 관리하기 위해 많은 도시가 막대한 비용을 투자하고 있습니다. 하지만 모든 지역이 이만큼의 자원을 투입하기는 어렵습니다. 그렇다면 저비용, 고효율의 방법은 없을까? 여기서 등장하는 것이 바로 CCTV 영상 분석과 AI 기술입니다. CCTV는 이미 대부분의 도시에 엄청나게 많이 설치되어 있고, 이 기존 인프라를 활용해 교통 흐름을 감지하고 차량 속도를 자동으로 측정하면, 별도의 센서나 장비를 추가로 설치할 필요가 없기 때문입니다. 이는 큰 비용 절감으로 이어지며, 교통 관리의 문턱을 낮춰줍니다.
2.본론
2.1.CCTV와 AI: 교통 흐름의 실시간 분석
교통량을 분석하기 위해 CCTV 영상 분석은 훌륭한 도구입니다. 기존에는 단순히 기록 용도로 사용되던 CCTV가 이제는 실시간 데이터를 제공하는 중요한 자원이 되고 있습니다. 특히, AI 모델이 이 과정에서 핵심적인 역할을 합니다. 예를 들어, Yolo와 같은 물체 인식 모델은 도로 위 차량을 실시간으로 감지하고, Tracking 기술을 통해 각 차량의 이동 경로를 추적하고 속도를 예측합니다. 이를 통해 특정 구간의 평균 주행 속도, 교통 체증 여부, 사고 발생 지점 등을 자동으로 파악할 수 있습니다.
2.2.구현 시나리오
이번 글에서 해 보려는 것은 CCTV 카메라의 영상 분석을 통해 저비용으로 교통 정보 가운데, 차량의 속도를 대략적으로 측정하는 것을 해 보는 코드를 만들고 테스트 해 보는 것입니다. 제대로 된 속도 측정은 레이더나 도로 하부의 루프를 이용하는 방식 등 전문도구를 사용해야 함으로 그렇게 진행하지는 않습니다. 대신 영상에서 간격의 거리를 아는 특정 시작 지점과 끌지점을 두고 이를 통과하는데 걸리는 시간 정보를 이용해 대략적인 속도를 측정하는거죠.
2.3.필요 소프트웨어 또는 기술
이를 위해 필요한 사항은 다음과 같습니다.
- 테스트 영상 : 도로를 비추는 CCTV 영상
- 객체 감지 모델 : Yolov11
- 객체 트래킹 모델 : BoT SORT 또는 ByteTrack
- 속도 측정 기능 : OpenCV 이용
이 가운데 테스트 영상은 아래 사이트에 있는 것을 활용키로 했습니다. 국내 어느 지역의 사거리를 비추는 CCTV 영상 파일입니다.
이 파일 대신 국내 도로 곳곳의 실시간 도로현황을 볼 수 있는 도로공사 CCTV를 이용해도 될 것 같습니다.
참고) 도로공사 고속도로 교통정보 (ROADPLUS)
참고) 서울시 교통정보 시스템 (TOPIS)
객체 감지 모델과 객체 트래킹 모델은 Ultralytics 패키지를 이용하면 손쉽게 설정 가능하므로 제공되는 기본값을 이용합니다. 각각 Yolov11l과 BoT SORT 가 사용됩니다. Yolov11은 Ultralytics에서 공개한 모델로 빠른 속도와 높은 정확도로 물체를 인식할 수 있어, 교통 분석에 매우 적합합니다. 테스트 영상을 이용할 경우, Yolov11 모델 가운데 n(nano), s(small), m(medium)까지는 약간 멀리서 오고 있는 트럭을 제대로 감지하지 못했습니다. 그래서 일단은 l(large) 모델로 진행합니다.
2.4.시스템 환경 구성
가상 환경 및 필요 패키지 설치합니다.
# 가상 환경 생성
$ conda create -n yolov11 python=3.11
$ conda activate yolov11
# 필수 패키지 설치
$ pip install ultralytics, numpy, opencv-python
2.5.코드 생성
객체 감지와 트래킹을 위해 참고한 코드는 Ultralytics 사이트의 다음 링크에서 'Plotting Tracks Over Time' 부분입니다.
이후 ChatGPT에게 전달한 코드 생성 요구사항은 다음과 같습니다.
- 차량 클래스만 처리하도록 필터링 해야 합니다.
(참고) 제공되는 기본코드는 COCO 데이터셋 기반 80개 객체 감지하도록 되어 있기 때문. 다만, 이렇게 해도 motorbike는 제대로 감지하지 못했음 (x 모델로 해야하나?) - 차량에 bbox도 함께 표기하도록 수정해 주세요.
차량의 속도 측정을 위해 다음의 요구사항도 반영했습니다.
- 영상 속의 차량을 감지하고 동일한 차량이 이동한 거리와 소요된 시간을 이용해 속도를 측정합니다.
- 속도 측정을 위해 차량이 통과하는 나란한 2개의 선을 그립니다.
- 첫 영상 프레임을 이미지 저장한 다음, 이를 보여주고 각각 p1, p2, p3, p4 의 위치를 마우스로 클릭해서 수집합니다. 평행한 두개의 선은 p1과 p2, p3와 p4가 연결되는 선이 됩니다.
- 이 점들의 좌표는 파일로 저장해두고 프로그램이 다시 로딩될 때 불러와서 사용하며 4개 점의 위치를 입력받는 것은 저장된 파일이 없을 경우에만 진행합니다.
- 두 평행선 간의 거리는 15m 로 가정합니다. (코드 상에서서 dist라는 변수명으로 설정될 것입니다)
- 자동차 객체가 두 평행선을 지나가는데 걸린 시간을 측정해서 속도를 계산하고, 그 값을 화면 상에 표시해 주세요.
몇 번의 시행착오를 거쳐 생성된 코드는 다음과 같습니다.
실행코드
import os
import json
import cv2
import numpy as np
import time
from collections import defaultdict
from ultralytics import YOLO
# YOLO 모델 로드
model = YOLO("yolo11s.pt")
# 비디오 파일 로드
video_path = "video.mp4"
cap = cv2.VideoCapture(video_path)
if not cap.isOpened():
raise Exception("Error: Could not open video.")
# 선 좌표 수집 및 로드
def load_or_collect_points(frame):
coordinates_file = "points.json"
if os.path.exists(coordinates_file):
with open(coordinates_file, "r") as f:
return json.load(f)
else:
points = []
def click_event(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
points.append((x, y))
cv2.circle(param, (x, y), 5, (0, 255, 0), -1)
cv2.imshow('Point Collection', param)
cv2.imshow('Point Collection', frame)
cv2.setMouseCallback('Point Collection', click_event, frame.copy())
while len(points) < 4:
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyWindow('Point Collection')
with open(coordinates_file, "w") as f:
json.dump(points, f)
return points
# 속도 계산 함수
def calculate_speed(time_taken, dist=15):
return round((dist / time_taken) * 3.6, 1) if time_taken > 0 else 0
# 첫 프레임을 사용하여 좌표 수집
success, first_frame = cap.read()
if not success:
raise Exception("Failed to read video")
points = load_or_collect_points(first_frame)
p1, p2, p3, p4 = points
# 트랙 히스토리 및 시간 저장
track_history = defaultdict(list)
vehicle_times = defaultdict(lambda: {'start': None, 'end': None})
vehicle_speeds = {}
# 비디오 처리
cv2.namedWindow('tracking', flags=cv2.WINDOW_AUTOSIZE)
while cap.isOpened():
success, frame = cap.read()
if not success:
break
# 두 평행선 그리기
cv2.line(frame, tuple(map(int, p1)), tuple(map(int, p2)), (0, 255, 0), 2)
cv2.line(frame, tuple(map(int, p3)), tuple(map(int, p4)), (0, 255, 0), 2)
# YOLO 트래킹 수행
results = model.track(frame, persist=True)
# 검출된 차량 객체 처리
for box, cls, track_id in zip(results[0].boxes.xywh.cpu(), results[0].boxes.cls.cpu().tolist(), results[0].boxes.id.int().cpu().tolist()):
if cls not in [2, 3, 5, 7]: # 차량 클래스 필터링
continue
x, y, w, h = box
x1, y1, x2, y2 = int(x - w / 2), int(y - h / 2), int(x + w / 2), int(y + h / 2)
# 바운딩 박스 그리기
cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)
# 차량의 트랙 히스토리 업데이트
track = track_history[track_id]
track.append((float(x), float(y)))
if len(track) > 30:
track.pop(0)
# 트래킹 라인 그리기
if len(track) > 1:
points = np.array(track, np.int32).reshape((-1, 1, 2))
cv2.polylines(frame, [points], False, (230, 230, 230), 2)
# 차량이 선을 지나는지 확인 및 시간 기록
y_pos = float(y)
y_line1, y_line2 = (p1[1] + p2[1]) / 2, (p3[1] + p4[1]) / 2
if track_id not in vehicle_speeds:
if abs(y_pos - y_line1) < 5 and vehicle_times[track_id]['start'] is None:
vehicle_times[track_id]['start'] = time.time()
elif abs(y_pos - y_line2) < 5 and vehicle_times[track_id]['start'] is not None:
vehicle_times[track_id]['end'] = time.time()
time_taken = vehicle_times[track_id]['end'] - vehicle_times[track_id]['start']
vehicle_speeds[track_id] = calculate_speed(time_taken)
# 속도 표시
if track_id in vehicle_speeds:
cv2.putText(frame, f"ID: {track_id}, Speed: {vehicle_speeds[track_id]} km/h", (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
# 프레임 출력
cv2.imshow("tracking", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
2.6.결과 확인
아래 영상에서 보듯 처음에 입력한 4개의 점을 이용해 평행한 가로선이 2개 그어져 있습니다. 정확하진 않지만 대략 둘 간의 간격을 15m 정도로 가정하였고 차량이 감지된 차량들이 두 선을 통과하는 시간을 기준으로 속도 측정을 하도록 했습니다. 영상으로 확인해 보시면 됩니다.
2.7.시행착오
Ultralytics가 감지와 트래킹 모델을 손쉽게 사용할 수 있도록 제공하고 있어 작업 자체는 어렵지 않았습니다. 코드도 직접 개발하는게 아니라 ChatGPT에게 시키면 되었으니까 더 그렇겠죠. 하지만, 실제로는 중간에 많은 시행착오가 있었습니다. -_-;
간단한 cv2.imshow에서부터 (그래서 namedWindow로 다 변경), 세부 속도 구현 등까지... 일부 기능은 ChatGPT가 아무리 해도 자꾸 반복되는 실수만 하길래 무료 버전 Claude로도 같은 일을 시켜봤었는데 코드가 길어지긴 했지만 훨씬 정규화되고 구조적으로 짜여진 것을 만들어줘서 놀랐습니다. 결국 ChatGPT가 실패한 것을 Claude가 한번에 해 내고, 그 코드를 다시 ChatGPT한테 컴팩트하게 만들어 달라고 하는 방식으로 진행했습니다. 위의 코드는 그 결과물입니다. ^^;
3.후기
아이디어가 있으면 뭐든 이렇게 구현해 볼 수 있다는 것은 참 좋은 것 같습니다. 교통정보 수집에 CCTV와 AI를 결합한 솔루션은 국내외에 이미 많이 나와 있습니다. 적용 대상이 대부분 공공기관이나 지자체이다보니 한정적으로 납품할 수 밖에 없죠. 게다가 임의로 아무 곳에나 설치하는 것도 규제가 있어 힘들 것 같구요.
하지만, 효율적인 교통정보의 수집과 이를 통합하고 다시 분석해서 활용하는 것은 어느모로나 사회적으로 도움이 됩니다. 아... 한 10년 됐나요? 국내에 미세먼지가 워낙 심해졌을 때 정부, 기관에서 공개하는 수치가 턱없이 좁은 범위만 커버하고 수치 또한 못 미둬워 개개인들이 미세먼지 측정기를 직접 사서 그 정보를 공유했던 기억이 새록새록 합니다. 저는 집단 지성의 힘을 지지하는 편이라 교통정보 또한 그런 틀 안에서 제공할 수 있는 방안이 나오면 좋겠습니다.
4.참고정보
- 차량 영상
- Ultralytics YOLOv8 을 이용한 속도 예측
'DIY 테스트' 카테고리의 다른 글
머리(Head) 자세 기반의 화면 주시 여부 감지 (0) | 2024.11.22 |
---|---|
Yolov11-pose를 이용한 폭력 행위 감지 (Violence Detection) (29) | 2024.11.20 |
한글 십자말풀이 게임 도구 만들기 (2) | 2024.11.16 |
십자말풀이 게임 생성 테스트 (16) | 2024.11.15 |
카메라 영상을 이용한 침입감지 테스트 (8) | 2024.11.12 |