베란다 새 관찰 프로젝트 개발일지
🐦 베란다 새 관찰 프로젝트 개발기
고양이를 키우고 있다.
그런데 해가 뜨기도 전에 베란다에서 울어댄다.
이유는 밥이 아니라, 새 밥을 달라는 것
어머니가 베란다에서 말리는 것들을 새들이 쪼아 먹는데,
그 모습을 구경하는 재미가 붙은 모양이다
처음엔 참새만 오더니, 비둘기, 직박구리, 심지어 황조롱이까지 나타났다
문득 “어떤 새들이, 얼마나 자주 오는지” 궁금해져 관찰·분석 프로젝트를 시작했다
🎖️프로젝트 목표와 설계
목표
- 언제, 어떤 새가 왔는지 기록하고
- 이 기록을 웹에서 쉽게 확인한다
설계
- 하드웨어 : 라즈베리파이 4B + USB 웹캠
- 소프트웨어 : Raspbian + Python
- OpenCV로 움직임 감지 (Background Subtraction)
- YOLO로 객체 탐지
- Firebase를 이용한 결과 저장 및 웹 제공
즉 아침에 새들이 많이 왔다갈 때, 이를 촬영하고
이를 어떤 새인지를 분석하는 프로젝트
🦅완성
완성된 프로젝트는 하단 링크에서 볼 수 있다
📗개발 일지
4주 안에 프로젝트를 완성하는 것으로 목표를 잡았다
애자일 방식을 이용하여 각 주마다 목표를 설정하고 이를 완성하는 것이 목표다
- 1주차 : 환경 세팅 & 카메라 변경
- 2주차 : YOLO로 새 분류
- 3주차 : 웹 페이지 구현
- 4주차 : 성능 개선 & 마무리
1주차 : 📷카메라 구동 및 동작하는 최소 버전 만들기
초기 세팅 진행 및 카메라 선택
먼저 라즈베리파이의 OS 설치 후 VNC 세팅 등과 같은 초기 세팅을 진행했다
이후 라즈베리 파이를 구매할 때 껴준 Pi Camera 연결 및 테스트를 해봤다
정식 명칭은 라즈베리파이 Rev 1.3 -5 mega pixel
인데,
최대 1080p까지밖에 지원하지 않아 화질이 너무 안좋았다
다른 카메라를 사자니 배송까지 생각하면 오래 걸릴것 같아
집에 있던 USB FHD 웹캠을 이용하는 것으로 계획을 변경했다
웹캠으로 변경 후 fswebcam
을 이용하여 구동 테스트 및 화질을 체크해봤는데
괜찮아서 이걸로 결정
크로스플랫폼 도입
라즈베리 파이에서 직접 개발하려니 VNC 쓰기도 귀찮고,
타이핑이 지연이 있어서 애로사항이 있었다
일단 윈도우에서 개발한 뒤, 라즈베리파이에 포팅하는
크로스플랫폼 개발 방식을 이용하는걸로 변경
움직임 감지는 어떻게?
먼저 새를 촬영하기 위해서는 새가 왔는가?
라는 로직을 판별을 할 수 있어야 했다
이를 위해 CCTV같은 시스템에서 객체 탐지, 인식을 수행하는 방식을 찾아보니
- 프레임 차이 (Frame Differencing)
- 바로 이전 프레임과 현재 프레임의 픽셀 값 차이를 계산하여, 변화가 있는 부분을 감지
- 가장 간단하고 연산이 빠르나, 움직이는 객체가 멈추면 감지하지 못하고, 조명 변화에 취약함
- 광학 흐름(Optical Flow)
- 연속된 프레임 간에 픽셀들이나 특징점들이 어떤 방향과 속도로 움직였는지, ‘움직임 벡터’를 계산함
- 객체의 이동 방향과 속도까지 알 수 있어 정교한 분석이 가능하지만, 계산량이 많음
- 템플릿 매칭(Template Matching)
- 찾고자 하는 객체의 작은 이미지(템플릿)을 가지고, 전체 이미지 위를 이동시키면서 가장 유사한 영역을 찾음
- 찾고자 하는 대상의 모양이 명확하고 변하지 않을 때 효과적이지만, 객체의 크기, 회전과 같은 형태 변화에 취약함
등 다양한 방법들이 있지만 대표적인 방법으로
Background Substraction, 배경 차분
이라는 방식을 사용한다고 한다
배경 모델과 현재 입력된 프레임의 차이를 분석하여,
다른 부분을 찾아서 객체를 탐지하는 방식인데
OpenCV에서 해당 함수를 지원하여 쉽게 사용할 수 있었다.
MOG2라는 모델을 이용하여 현재 프레임과 과거 여러 프레임들을 통해,
통계적으로 모델링 된 배경 모델을 만들고, 이후 이를 현재 프레임과 비교하는 것이다.
얼핏 보면 프레임 간 차이 방식과 유사하지만
모델을 사용하여 학습한 History와 비교한다는 점이 다르다
이 방식을 이용하여 고정된 배경 이미지를 촬영하고, 이를 비교하는 방식으로 동작시켰다
2주차 : 🐦 YOLO 새 분석
성능 개선을 위한 몸부림
움직임이 발생했을때만, 해당 프레임을 캡쳐하여 어떤 새인지 판별하는 방식을 이용하기로 했다
계속 영상 촬영을 하면서 YOLO를 이용해 객체 판별을 해도 고성능 PC에서는 상관 없지만,
해당 프로그램은 라즈베리파이에서 돌려야하므로, 성능이 매우 중요하다고 생각했다
그렇기 때문에, 영상전체를 계속해서 YOLO를 돌리는것 보다는
- 저해상도 영상으로 움직임만 체크하고
- 움직임이 발생하면, 해당 프레임을 촬영하여 이를 객체 판별을 돌린다
이렇게 돌리는게 리소스를 절약할 수 있을거라고 생각했다
감지 이후 저장을 영상으로 할지, 이미지로 할지도 고민해봤으나
영상은 용량이 너무 많이 나갈것 같아서 포기했다
이 방법을 적용하기 위해서, 1주차에 개발한 배경 차분 방식을 이용하여
객체의 크기가 일정 이상이 감지되면 움직임으로 판단하고, 이를 프레임 촬영을 수행하도록 했다
🔬새 분류 모델 만들기
이제 촬영한 사진을 새인지 판단해야한다
혹시나 국내 새를 분류하는 모델이 있을지 않을까 싶어 여기저기 뒤져봤으나
그런 모델은 없었다
결국 직접 모델을 학습시켜야했는데, 데이터셋 조차 없어서 먼저 이를 만들어야했다
구글은 이미지 크롤링 불가능하여 다른 포털에서 이미지를 크롤링하는 파이썬 파일을 만들었다
각 새 별로 이미지를 100장씩 학습시키고,
모델이 잘 판단하지 못하는 새는 데이터를 더 추가하는 방식으로 모델을 학습시켰다
클래스는 집 근처에 자주 출몰하는
까마귀, 까치, 박새, 비둘기, 직박구리, 참새, 황조롱이
총 7가지로
약 1천장의 이미지를 학습시켰다
데이터셋 구축 작업은 Label Studio
를 이용해서 진행했다
각 새 이미지를 해당 프로그램에 불러와서, 박스를 씌우고 저장
이 작업을 수작업으로 하는데 오랜 시간이 걸렸다
🤔어떤 모델을 쓸지?
객체 탐지에 사용하는 모델은 다양하다.
크게, R-CNN 계열과 YOLO 계열로 나뉘는데
R-CNN쪽은 정확도가 높으나, 속도가 느리고,
YOLO는 속도가 빠르나, 정확도가 살짝 떨어진다고 한다
이전 프로젝트에서 R-CNN을 다뤄본적이 있어서, 이번에는 YOLO를 사용하기로 결정했다
그 중에서 어떤 YOLO 모델을 사용할 것인지를 정해야 했는데,
2025년 기준 YOLO V8(2023)까지 출시됬다
그 중에서, 라즈베리파이 4B가 성능이 그리 좋지 않으므로,
저사양에서 구동 가능한 YoloV5를 선택하고, 그 중 소형 모델인 YoloV5s
를 사용하기로 결정했다
이후 모델을 학습시켜서 해당 클래스의 새를 잘 탐지하는 것을 확인했다
클래스 이외의 새가 나와버리면 뻗는 문제가 발생했으나,
등록되지 않은 새가 감지되면 예외처리하는것으로 해결했다
Pytorch를 설치하고, 학습한 모델을 돌려보는에 계속 오류가 발생했다
Torch를 CPU 버전으로 사용하고 있었으나, Pytorch를 설치하는 과정에서 CUDA를 설치했고
여기서 GPU 버전과 CPU버전의 충돌이 발생해서 오류가 발생했다
torch 버전 삭제 후, CPU 버전으로 재설치를 해서 여차저차 해결했다
3주차 : 📃웹 페이지 만들기
Firebase 도입
새를 감지하고 캡처한 이미지와 언제 어떤 새가 왔는지에 대한 사진과 메타데이터를 얻었다
이를 어디에 저장하고 웹에서 불러올지를 고민하다가
이미지 분석을 라즈베리파이에서 수행하므로, 여기에 서버까지 돌리면 뻗을것 같아서
Firebase를 이용하기로 결정했다
구글에서 제공하는 Firebase는 간단한 웹 페이지는 서버 호스팅도 지원하므로 적합하다고 판단했다
Gemini를 이용한 프론트엔드 개발
Vue를 다뤄본적이 있어 Vue를 이용해 페이지를 만들기로 결정
그러나 너무 오래전에 다뤄봐서 다 까먹었다
이를 위해 Gemini를 긴급 투입하여 바이브 코딩으로 진행해봤다
Gemini CLI를 이용했는데, VSCODE에서도 실행 가능해서
터미널창에 ‘vue로 웹페이지 만들어줘’ 한마디 하니
자기혼자 뚝딱거리더니 실행 가능한 웹페이지를 만들어줬다
직접 개발할때는 State니 props니 엄청 복잡했던것 같은데,
이제는 AI 가 다 해주고 나는 문제가 없는지만 판단하면 되는 세상이 왔구나 싶었다
그러나 아직은 사람이 개입해야하는 부분이 많았다.
이 내용은 추후에 바이브 코딩의 문제점으로 다뤄볼 예정
간단한 디자인을 Figma로 제작한 뒤, 이를 토대로 제작하였다
어떤 새가 얼마나 왔는지 정도의 대시보드의 기능만 수행하면 되므로,
FE 코드는 크게 복잡한 내용이 없었으나,
이를 Firebase와 연동하는데 꽤 오랜시간이 걸렸다.
Firebase만 간단히 쓰면 될 줄 알았으나
API 키 세팅이라던지, 권한 설정이라던지 생각보다 할 일이 많았다.
또한 API 키 노출이 되지 않도록 설정하는것이 매우 중요!!
4주차 : ✨완성도 높히기
모델 성능 개선
모델이 동작은 하지만 새 판별의 정확도나, 속도의 개선이 필요하다고 생각했다
움직임을 감지하고, 새를 판별하는 도중에 다른 새가 오면 감지를 못하는 문제가 발생했다
이를 위해 스레드를 도입해서, 첫번째 스레드는 움직임 감지 및 캡쳐만 수행하고
두번째 스레드는 큐에 등록된 이미지들을 새 분석을 하는 방식으로 해결했다
그러나 움직임이 계속해서 감지되면 계속 큐에 넣어서 큐가 터지는 문제가 발생했고,
약 1분~5분의 쿨타임을 도입해서, 한번 캡쳐하면 몇분 동안은 캡쳐하지 않도록 막았다
또한 모델의 성능 개선이 필요하다고 판단하여 모델을 다양한 버전으로 학습시켰다
Epoch와 이미지 사이즈를 각각 조절하면서, 어떤 모델이 가장 성능이 좋은지를 테스트해봤다
이미지 사이즈를 320, 640, 1280로 나눠서,
Epoch를 몇번씩 수행할 것인지, patience를 얼마나 설정할 것인지 등을 설정해서 여러번 학습을 진행했다
1
2
3
4
5
6
7
8
9
- imgsz 640, patience 20으로 가져갔을 때, mAP50: 0.864, mAP50-95: 0.661 가 나옴
- 비둘기, 직박구리등은 성능이 낮음. 이미지 더 추가해서 학습해야할듯 함
- imgsz 1280, p 20으로 학습
- recall은 0.736에서 0.876으로 크게 상승함. 새를 감지하는 능력 자체는 상승함
- mAP50: 0.862, mAP50-95: 0.61로 성능이 하락함
- imgsz 320, pt30. 이미지 처리량을 줄여 연산 속도를 높인 버전
- mAP50 = 0.864, mAP50-95 = 0.669로 가장 뛰어남. 또한 추론 속도도 1.7 ms/img로 매우 빠름
- imgsz 418 : 정밀도가 0.943으로 가장 높음
- 320과 418 중 선택해서 실제 구동 속도와 탐지율을 비교하면서 모델을 선택하면 될듯 함
최종적으로 이미지 사이즈가 320, 418 인 모델들이 성능이 가장 잘 나왔다
배경 차분 방식 개선
CCTV 등에서 사용하는 전형적인 배경 차분(Background Subtraction) 방식은,
고정된 이미지를 기준으로 하거나, 여러 프레임을 이용해 배경 모델을 학습하여 변화를 감지한다
야외 환경에 노출되면 날씨나 조도, 그림자나 바람에 의한 환경 변화들이 감지되어
불필요한 오탐지가 발생할 수 있다
그렇다고 이러한 변화들을 모두 탐지하고 제거하기 위해서 모델을 학습시키면
모델 업데이트 과정에서 연산량이 커지는 문제가 발생한다
이번 프로젝트는 라즈베리파이라는 제한된 성능에서 돌아가야 하므로,
이러한 복잡한 연산을 수행하기는 어렵다고 생각했다
그래서 배경 모델 학습 없이 단순히 몇 프레임 전과 현재 프레임을 비교하는
‘프레임 간 차분’ 방식을 일부 적용했다
새가 날아와 움직이는 순간을 포착하는데 충분히 효과적이고,
구현도 간단하며, 연산 부담도 적기 때문이다
정리하자면
- 고정된 기준 이미지와 비교 방식 : 연산량은 적지만, 환경 변화에 취약하다
- 배경 모델 기반 차분 : 정확도는 높지만, 배경 모델을 학습해야 하고, 연산량이 훨씬 많다
- 프레임 간 차분 : 배경 모델을 유지할 필요 없이, 구현도 간단하고, 연산량이 가장 적음
물론 해당 방법도 움직임이 멈춘 새는 감지하지 못한다는 단점이 존재하지만,
이번 프로젝트에서는 새가 날아오는 과정이 무조건 포함되므로, 문제가 되지 않는다
기타 개선
이후 자잘한 수정을 거쳤다
Firebase 세팅 및 더미 데이터 추가, 규칙을 추가해서 보안 설정
FE 디자인 수정하고, 이미지 삭제나, 필터 등 최소 기능을 구현
프로젝트가 기능대로 동작하는지 체크 및 실성능 체크 진행
마무리
여름이 되어 먹을게 풍부해졌는지,
아쉽게도 프로젝트의 주인공들인 새들이 베란다에 찾아오지 않아
실제 데이터를 쌓는 마지막 단계는 잠시 멈추게 되었다
다음 겨울, 새들이 다시 찾아올 때를 기약하며 프로젝트를 잠시 마무리하고,
그동안의 경험을 회고해보고자 한다
첫째, YOLO 모델을 직접 다루며 딥러닝 모델의 전체 사이클을 경험할 수 있었다
단순히 모델을 가져다 쓰는 것이 아니라, 국내 새 이미지 데이터셋을 직접 구축하고,
다양한 하이퍼파라미터를 조정해 YOLOv5s 모델을 최적화하는 과정은 꽤 복잡했지만,
그만큼 객체 탐지 기술에 대한 이해도를 한층 높일 수 있었다
특히 라즈베리파이라는 저사양 환경에서 최적의 성능을 위해
다양한 아이디어를 내는 부분이 매우 재미있었다
둘째, Firebase를 활용한 서버리스 아키텍처를 구축하며 백엔드 인프라를 다뤄볼 수 있어 좋았다
물론 진짜 백엔드에 비하면 턱없이 간소화 되어있지만,
데이터 저장(Firestore)부터 이미지 호스팅(Storage), 웹 호스팅까지,
복잡한 서버 설정 없이 아이디어를 빠르게 현실화할 수 있다는 점이 매우 편하고 재미있었다
물론 API 키 관리나 보안 규칙 설정 등 신경 써야 할 부분이 많다는 것도 뼈저리게 느꼈다
셋째, GEMINI를 이용한 바이브 코딩을 직접 경험해 볼 수 있었다
Vue.js 코드를 순식간에 만들어내는 AI의 능력은 분명 생산성을 엄청나게 높혀주었다.
하지만 AI가 생성한 코드를 이해하고, 문제를 해결하며, 원하는 대로 수정하는 것은
결국 개발자의 몫이라고 생각한다
AI는 강력한 도구이지만, 맹목적으로 의존하기보다 그 원리를 이해하고 활용해야 한다는 점,
그리고 아직은 사람의 개입이 필수적이라는 점을 배웠다
비록 실제 새들의 데이터를 쌓지는 못했지만,
기술적인 도전과 새로운 개발 방식의 탐구를 통해 한 뼘 더 성장할 수 있었던 프로젝트였다