드론 프로그래밍+LabelImg를 이용한 Yolo 라벨링+파이썬을 이용한 히트맵 구현

·2023년 5월 16일
0

졸업 프로젝트

목록 보기
2/2

얼레벌레 시작한 졸업 프로젝트도 이제 막바지이다. 스타트 기술 블로그를 쓰고 많은 일이 있었다.

1. 공을 인식하고 추적하는 드론 프로그래밍하기

엎어졌다.
드론이 공을 쫓아다니다보니 화면이 너무 흔들려서 경기 분석에 지장이 갔다. 드론 비행 높이가 생각보다 높아서 한 화면에 경기장을 다 담을 수 있었기 때문이기도 하다. 꽤 열심히 만들었어서 아쉽기는 하지만 정확도를 높이는 편이 서비스 측면에선 훨씬 도움이 되기 때문에 드론은 공중 촬영용으로만 사용하기로 했다.

그래도 아쉬우니까 엎어지기 전까지 진행된 부분을 올려본다.

import cv2
from djitellopy import tello
import cvzone
import numpy as np
from utlis import *

thres = 0.55
nmsThres = 0.2
# cap = cv2.VideoCapture(0)
# cap.set(3, 640)
# cap.set(4, 480)
w = 640
h = 480
pid = [0.4, 0.4, 0]
pError = 0
startCounter = 0  # for no Flight 1   - for flight 0



classNames = []
classFile = 'coco.names'
with open(classFile, 'rt') as f:
    classNames = f.read().split('\n')
print(classNames)
configPath = 'ssd_mobilenet_v3_large_coco_2020_01_14.pbtxt'
weightsPath = "frozen_inference_graph.pb"

net = cv2.dnn_DetectionModel(weightsPath, configPath)
net.setInputSize(320, 320)
net.setInputScale(1.0 / 127.5)
net.setInputMean((127.5, 127.5, 127.5))
net.setInputSwapRB(True)

myDrone = initializeTello()


# me.move_up(400)

while True:
    # success, img = cap.read()
    if startCounter == 0:
        myDrone.takeoff()
        startCounter = 37
    img = telloGetFrame(myDrone, w, h)
    classIds, confs, bbox = net.detect(img, confThreshold=thres, nmsThreshold=nmsThres)
    try:
        for classId, conf, box in zip(classIds.flatten(), confs.flatten(), bbox):

            myBallListC = []
            myBallListArea = []
            if classId == 1:
                cvzone.cornerRect(img, box)
                cv2.putText(img, f'{classNames[classId - 1].upper()} {round(conf * 100, 2)}',
                            (box[0] + 10, box[1] + 30), cv2.FONT_HERSHEY_COMPLEX_SMALL,
                            1, (0, 255, 0), 2)
                x = box[0]
                y = box[1]
                bw = box[2]
                bh = box[3]
                cx = x + bw//2
                cy = y + bh//2
                area = bw*bh
                myBallListArea.append(area)
                myBallListC.append([cx, cy])
                if len(myBallListArea) != 0:
                    i = myBallListArea.index(max(myBallListArea))
                    info = [myBallListC[i], myBallListArea[i]]
                else:
                    info = [[0, 0], 0]
                error = info[0][0] - bw // 2
                speed = pid[0] * error + pid[1] * (error - pError)
                speed = int(np.clip(speed, -100, 100))


            else:
                myDrone.send_rc_control(0, 0, 0, 0)
                error = 0

            if info[0][0] != 0:
                myDrone.yaw_velocity = speed
            if myDrone.send_rc_control:
                myDrone.send_rc_control(myDrone.left_right_velocity,
                                        myDrone.for_back_velocity,
                                        myDrone.up_down_velocity,
                                        myDrone.yaw_velocity)
            else:
                myDrone.send_rc_control(0, 0, 0, 0)


    except:
        pass

    #me.send_rc_control(0, 0, 0, 0)

    cv2.imshow("Image", img)
    cv2.waitKey(1)

짜잔
코드를 짤 무렵에 축구공이 수중에 없었기 때문에 일단 사람을 인식하여 추적하는 코드이다. 특정 물체가 인식되면 그 물체가 화면 중앙에 올 수 있도록 드론이 스스로 회전하는 아주 멋진 코드이다. 이후 축구공이 생기면 공을 추적하고, 회전 뿐만 아니라 물체와의 거리에 따라 전진 및 후진 하도록 코드를 살짝 수정할 예정이었다. 아무튼 이 코드로 테스트를 해 보니 눈에 띄는 문제가 발생했는데, 사람이 조금만 움직여도 드론이 미친듯이 회전한다는 것이었다. 추측컨대 내 코드는 물체를 인식한 이후 OpenCV를 통해 그려지는 box의 크기를 통해 드론이 회전할 각도를 계산하였는데, 사람 크기가 크다보니 box의 크기도 커져서 그런 결과가 나왔지 않나 싶다. 이미 학습 완료된 데이터셋을 이용한 코드여서 공 말고 다른 많은 물체들을 인식한다는 단점 아닌 단점도 있었다.

아무튼 그래서.. 드론은 그냥 조종해서 날리기로 하고 경기 분석에 쓰일 히트맵 코드를 짜는 임무를 맡았다.

2. 히트 맵 구현하기

히트 맵이란? 히트 맵은 열을 뜻하는 히트와 지도를 뜻하는 맵을 결합시킨 단어로, 색상으로 표현할 수 있는 다양한 정보를 일정한 이미지 위에 열분포 형태의 비주얼한 그래픽으로 출력하는 것이 특징이다. 축구같은 스포츠에서는 '선수들이 볼을 터치한 위치를 시각적으로 나타낸 지도' 정도의 의미를 가지고 있다. 각 팀 플레이 위치나 방향, 빈도 등을 유추할 수 있는 유의미한 분석이다.

히트맵 참고 블로그
위 블로그의 heatmappy(heatmappy 깃허브) 설명을 참고하여 코드를 만들었다. 다만 패키지를 다운받는데 계속 오류가 생겨서 며칠 애먹었다.

해결 방법은 위 사진 참고. 근데 기억상 이 방법도 안통해서 아나콘다 통해서 받았던 것 같기도 하다.
경기 분석 프로그램을 사용하여 선수들이 볼을 터치한 좌표를 받아온 다음 코드에 적용시키면, 지정한 사진 위에 좌표대로 히트맵을 찍어준다.

이런식이다. 전체 풋살 경기 중 30초 정도의 분량에서 구한 좌표를 사진 위에 찍어보았다. 영상과 비교해보면 정확도가 높은 걸 확인할 수 있다..

3. YOLOv5 학습용 이미지 라벨링

라벨링도 했다. 똑똑한 인공지능을 만들려면 인간의 수고로움이 필요하다. 많이.
https://github.com/heartexlabs/labelImg
라벨링 프로그램은 labelImg를 사용하였다. 위의 깃허브 참고. 다운로드만 하면 간단하게 사용이 가능해서 편리했다. yolo 버전과 다른 이름이 기억나지 않는 딥러닝 모델 버전을 선택할 수 있는데 처음 시작하면 yolo가 아니라 다른 딥러닝 모델 버전으로 설정되어 있으므로 처음에 바꾸고 시작해야 한다.

0개의 댓글