2026. 7. 22. 16:46ㆍAI 기반 프론트엔드 웹개발자 양성과정

[YOLO 심화] 히트맵·보안·카운팅
+ SAHI·속도추정·FastSAM
2026.07.21 · 학습 범위: YOLO 심화 02~19 (solutions · SAHI · FastSAM · OpenVINO)
📋 오늘 다룬 것들
어제 추적(Tracking)에 이어, 오늘은 YOLO를 감싼 ultralytics solutions 모듈을 파고들었다. 코드 몇 줄로 히트맵·보안 알람·블러·거리 측정·영역/출입 카운팅·속도 추정 같은 실전 기능이 뚝딱 나온다는 게 놀라웠다. 후반엔 작은 물체를 놓치지 않는 SAHI, 텍스트로 물체를 잘라내는 FastSAM까지 다뤘다. 라이브러리가 얼마나 많은 걸 대신해 주는지 체감한 하루였다.
🧩 ultralytics.solutions 클래스 모음
from ultralytics import solutions 하나로 실전 기능들이 클래스로 들어 있다. 오늘은 이 중 여러 개를 직접 써봤다.
model())과 조금 다르다. 프레임을 직접 넣어 호출하고(results = 객체(frame)), 결과가 그려진 이미지는 results[0].plot()이 아니라 results.plot_im으로 꺼낸다. show=True면 창을 알아서 띄우고, show=False면 cv2.imshow(..., results.plot_im)으로 직접 띄운다.
🎥 실시간 분석 솔루션
02 — 히트맵 (Heatmap)
물체가 자주 지나간 곳을 빨강(자주)~파랑(드묾) 열지도로 누적 시각화한다. 매장 동선 분석 등에 쓴다.
heatmap = solutions.Heatmap(
model="yolo11n.pt",
colormap=cv2.COLORMAP_MAGMA # 색상 지도(MAGMA=보라~노랑)
)
results = heatmap(frame)
cv2.imshow("HEATMAP", results.plot_im) # 누적 결과
03 — 보안 알람 (SecurityAlarm)
특정 클래스(예: 자동차)가 감지되면 이메일을 자동 발송한다. 탐지 → 추적 → 누적 카운트 → SMTP 메일 발송이 내부에서 한 번에 처리된다.
alarm = solutions.SecurityAlarm(
model="yolo11n.pt", show=False,
records=2, # N회 감지 시 1회 메일(오탐 방지)
classes=[2] # 2 = car
)
alarm.authenticate(from_email, password, to_email) # SMTP 로그인
results = alarm(frame)
password에 넣어야 한다. 일반 로그인 비밀번호로는 SMTP 인증이 막힌다. 실무에선 비밀번호를 코드에 직접 쓰지 말고 환경변수로 분리하는 게 안전하다.04 — 블러 (ObjectBlurrer)
방송의 모자이크처럼, 탐지된 물체 영역만 골라 자동으로 흐리게 처리한다. 좌표를 직접 계산할 필요가 없다.
blurrer = solutions.ObjectBlurrer(
model="yolo11n.pt", show=False,
blur_ratio=0.5 # 0.1~1.0, 높을수록 더 흐림
)
results = blurrer(frame)
cv2.imshow("BLUR", results.plot_im)
📏 거리 · 영역 · 출입
05 — 거리 계산 (DistanceCalculation)
영상 위에서 마우스로 객체 2개를 클릭하면 두 중심점 사이의 픽셀 거리를 실시간으로 잰다.
distance = solutions.DistanceCalculation(model="yolo11n.pt", show=True) distance(frame) # 좌클릭 2회로 대상 선택 → 거리 표시
06 — 거리 상태 분류 (실습)
거리 값을 꺼내 신호등처럼 안전/경고/위험으로 분류하는 실습. 여기서 solutions 객체를 process()로 호출하면 결과 객체가 나오고, 거기서 pixels_distance를 꺼낼 수 있다는 걸 배웠다.
results = distance.process(frame) # 결과 객체 반환 d = results.pixels_distance # 두 점 사이 픽셀 거리 if d is None or d == 0: print("입력 안됨"); continue # 아직 선택 전 elif d >= 150: status = "Safe" elif d >= 100: status = "Warning" else: status = "Danger"
distance(frame) vs distance.process(frame) — 앞엣것은 화면에 그려 주기만 하고, 뒤엣것은 결과 객체를 돌려줘 pixels_distance 같은 값을 코드로 꺼내 조건 분기에 쓸 수 있다. 상태를 판단해야 하니 process()를 썼다. 임계값(150·100)은 카메라 위치·줌에 따라 달라져 직접 보정해야 한다.07 — 영역 카운팅 (RegionCounter)
다각형 영역(4점)을 정의하고, 그 안에 들어온 객체만 센다. 좌표는 화면 해상도에 맞춰야 해서 프레임을 640×480으로 통일했다.
region_points = { "region-01": [(179,114), (173,466), (636,359), (217,133)] }
region = solutions.RegionCounter(
model="yolo11n.pt", show=False,
region=region_points, conf=0.4 # CCTV 저화질 대응
)
re_frame = cv2.resize(frame, (640, 480)) # 좌표계 통일 필수
results = region(re_frame)
08 — 마우스로 영역 좌표 추출 (get_region)
영역 좌표를 코드에 하드코딩하지 않고, 화면을 클릭해서 좌표를 뽑는 방법. setMouseCallback에 콜백을 연결해 좌클릭한 (x, y)를 리스트에 모은다.
points = [] def mouse_callback(event, x, y, flags, param): if event == cv2.EVENT_LBUTTONDOWN: # 좌클릭 감지 points.append((x, y)) print(f"클릭 좌표: {x, y}") cv2.namedWindow("GET_X_Y", cv2.WINDOW_NORMAL) cv2.setMouseCallback("GET_X_Y", mouse_callback) # 좌상단→좌하단→우하단→우상단 순서로 4점 클릭 → region_points로 복사
event(무슨 일)·x, y(어디)·flags(같이 누른 키)·param(밖에서 넘긴 값). 안 써도 자리는 비워두면 안 된다(OpenCV 호출 규칙). 그리고 좌표 측정 창과 실제 카운팅 코드의 resize 크기가 같아야(640×480) 영역이 안 어긋난다.09 — 출입 카운팅 (ObjectCounter)
화면에 선(2점)을 긋고 그 선을 넘는 사람·차를 세어 IN/OUT을 집계한다. 4점을 주면 RegionCounter처럼, 2점을 주면 선 카운터로 동작한다.
count_points = [(231,465), (637,418)] # 점 2개 = 선 1개 counter = solutions.ObjectCounter( model="yolo11n.pt", show=False, region=count_points ) results = counter(re_frame) # 탐지+추적+선통과+IN/OUT 한 번에
🔍 SAHI · 속도 · FastSAM
conda activate py39로 Python 3.9 환경에 들어가 pip install sahi로 설치. (버전 호환 때문에 전용 환경을 쓰는 게 편했다.)10 — SAHI 데이터 준비
멀리 있는 작은 차·사람은 일반 YOLO가 잘 못 본다. SAHI로 해결하는데, 먼저 테스트용 이미지를 내려받았다.
from sahi.utils.file import download_from_url download_from_url( ".../demo_data/small-vehicles1.jpeg", # 작은 차량 "demo_data/small-vehicles1.jpeg", )
11 — SAHI 기본 추론 (비교용)
사진을 통째로 한 번 추론해 탐지 개수를 세어 둔다. 이 숫자를 다음 슬라이싱 결과와 비교하면 SAHI 효과가 한눈에 보인다.
model = YOLO("yolo11n.pt") results = model("demo_data/image_1.png") detected = len(results[0].boxes) # 탐지 수 기억! print(f"탐지수 : {detected}")
12 — SAHI 슬라이싱 추론
사진을 작은 타일(200×200)로 잘라 각 조각마다 추론한 뒤 결과를 합친다. 큰 그림을 돋보기로 한 칸씩 보는 셈이라 작은 물체 탐지가 확 늘어난다.
from sahi.predict import get_sliced_prediction from sahi import AutoDetectionModel det_model = AutoDetectionModel.from_pretrained( model_type="ultralytics", model_path="yolo11n.pt", confidence_threshold=0.4 ) results = get_sliced_prediction( "demo_data/image_1.png", det_model, slice_width=200, slice_height=200, # 타일 크기 overlap_width_ratio=0.1, overlap_height_ratio=0.1 # 경계 10% 겹침 ) results.export_visuals(export_dir="sahi/")
overlap을 10~20% 준다.13 — 속도 추정 (SpeedEstimator)
실시간 CCTV에서 차량 속도를 km/h로 추정한다. 핵심은 픽셀을 실제 거리로 바꾸는 meter_per_pixel 보정값.
speed = solutions.SpeedEstimator(
model="yolo11n.pt", show=False,
classes=[2], # car만
max_speed=120, # 상한 km/h
meter_per_pixel=0.5 # 1픽셀 = 0.5m (환경별 보정 필수)
)
results = speed(frame)
cv2.imshow("Speed", results.plot_im)
meter_per_pixel은 화면 1픽셀이 실제 몇 m인지의 환산 계수다. 도로 점선 한 칸(3m)이 화면에서 6픽셀이면 3 ÷ 6 = 0.5. 카메라 높이·각도·렌즈마다 달라서 직접 측정해야 하고, 원근 왜곡 때문에 도로 면 근처에서 재야 정확하다.15 — FastSAM (텍스트로 잘라내기)
SAM(Segment Anything)은 학습 없이 모든 물체를 분할하지만 무겁다. FastSAM은 그걸 YOLO 골격으로 갈아 끼워 훨씬 빠르게 만들고, "cat"처럼 텍스트 한 단어로 원하는 물체만 콕 집어 잘라낸다.
from ultralytics import FastSAM model = FastSAM("FastSAM-s.pt") # s=소형·빠름 results = model("img_cat.jpg", texts="cat") # "cat"만 분할 cv2.imwrite("result.jpg", results[0].plot())
ssl·CA_BUNDLE 관련 설정으로 다운로드를 우회했다.)⚡ 모델 최적화 · 웹 UI
16 — OpenVINO 변환
OpenVINO는 Intel이 만든 CPU 전용 부스터. PyTorch .pt 모델을 변환하면 GPU 없이 CPU만으로도 2~3배 빠른 추론이 가능하다.
model = YOLO("yolo11n.pt") model.export(format="openvino") # → yolo11n_openvino_model/ 폴더 생성
format은 "openvino" 외에 "onnx"·"tensorrt" 등도 된다. GPU가 없는 노트북에서 빠르게 돌리고 싶을 때 변환이 유용하다.17 — PT vs OpenVINO FPS 비교
FPS(초당 프레임 수)로 원본과 변환본의 속도를 겨뤄봤다. 한 프레임 추론에 걸린 시간을 재서 1초로 나누면 FPS가 나온다.
start = time.time() results = model(frame, verbose=False) fps = 1 / (time.time() - start) # 1초 ÷ 1장 걸린 시간 cv2.putText(annotated, f"{fps:.1f} FPS", (10,30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
yolo11n.pt ↔ yolo11n_openvino_model/로 바꿔 같은 코드로 비교한다. CPU 환경일수록 OpenVINO 효과가 커서 보통 2~3배 빨라진다. verbose=False는 콘솔 로그를 꺼 측정을 깔끔하게 하려는 용도.18 — 이미지 검색 (SearchApp)
"내 사진첩 전용 구글". 폴더를 주면 "cat" 같은 단어로 비슷한 이미지를 찾아주는 웹앱이다. 내부에 CLIP 임베딩 + FAISS 인덱스가 들어 있어 "비슷한 그림" 찾기가 된다.
app = solutions.SearchApp(
data="demo_data", # 검색 대상 이미지 폴더
device="cpu" # GPU면 "cuda"
)
app.run(debug=False) # 브라우저에서 자동 오픈
python 파일.py로 실행한다(streamlit run 쓰면 오류). 여기서도 HuggingFace 모델 다운로드 인증서 문제로 ssl·CA_BUNDLE을 우회 설정했다.19 — Streamlit 추론 UI
코드 두 줄로 뜨는 YOLO 대시보드. 모델 선택·웹캠 입력·결과 표시를 solutions.Inference가 알아서 그려준다.
inf = solutions.Inference(model="yolo11n.pt") inf.inference() # 실행: streamlit run 파일.py
python이 아니라 streamlit run이라는 점만 주의하면 된다.✍️ 마치며
어제까지는 "YOLO로 박스 그리기"였는데, 오늘 solutions를 보니 히트맵·알람·카운팅·속도까지 실제 서비스에 쓸 법한 기능이 이미 클래스로 다 만들어져 있었다. 결국 중요한 건 각 클래스가 어떤 입력(영역 좌표·이메일·보정값)을 필요로 하는지, 그리고 결과를 results.plot_im·process()로 어떻게 꺼내 쓰는지를 아는 것이었다. SAHI의 "잘라서 보기"나 FastSAM의 "말로 자르기"처럼, 문제를 바라보는 관점 자체가 새로워서 재미있었다. 마지막엔 OpenVINO로 속도를 끌어올리고 SearchApp·Streamlit으로 웹 UI까지 붙여봤다. 다음엔 SAM 본체와 직접 모은 데이터로 커스텀 학습을 이어서 해볼 예정이다.
🏷️ YOLO | solutions | SAHI | OpenCV | AI | 학습일지
'AI 기반 프론트엔드 웹개발자 양성과정' 카테고리의 다른 글
| [260723 18일차] Streamlit · Gradio · Claude Code (0) | 2026.07.23 |
|---|---|
| [260722 17일차] OpenAPI · HuggingFace · Transformers · OCR (1) | 2026.07.22 |
| [260720 15일차] Data, YOLO (0) | 2026.07.20 |
| [260716 14일차] Anaconda, Python (0) | 2026.07.16 |
| [260715 13일차] Vercel, Claude Web (0) | 2026.07.15 |