hashlib으로 id 값 식별되지 않은 객체 추적하기

정예슬·2024년 7월 23일
0

침착한 일상

목록 보기
17/17
post-thumbnail

여러가지 객체 인식 모델과 추적기를 사용해서 새로운 객체가 인식되고, 일정 age 동안 추적되었을 때 서버에 저장하는 모듈을 개발 중이였다.

먼저 추적기를 썼던 이유는 모델이 인식되는 대로 저장하게 되면 중복 객체가 너무 많이 저장되는 문제가 있었고, 이를 막기 위해서 추적기를 통해 같은 id는 중복 저장되지 않도록 처리하기 위함이였다.

그런데 개발 과정에서 이 추적기가 만능이 아니라는 것을 알았다. 사람, 차와 같은 동적이고 비교적 특징추출이 쉬운(?) 녀석들은 id tracking이 잘 되는데, 내가 추적해야 하는 일부 객체는 id tracking이 되지 않는 것이였다.

그래서 이 부분을 해결하기 위해 찾아보다가, hashing을 통해 이 문제를 보완할 수 있다는 것을 알게되었다.

전체 코드

import hashlib
import time

class noIDTracker:
    def __init__(self):
        self.saved_hashes = {}
        self.save_interval = 3  # save time interval
        self.coord_tolerance = 100  # image pixel coord to tolerate 

    def update(self, box, class_idx):
        box_hash = self.generate_box_hash(box, class_idx) # 해시 값 생성
        current_time = time.time()

        if box_hash in self.saved_hashes:
            last_saved_time = self.saved_hashes[box_hash]['time']
            if current_time - last_saved_time < self.save_interval:
                # print('Not saving duplicated data')
                return False  # prevent duplication
        self.saved_hashes[box_hash] = {'time': current_time, 'box': box}
        return True # 박스가 새로 저장된 경우 

    def generate_box_hash(self, box, class_idx):
        x1, y1, x2, y2 = self.round_coordinates(box.xyxy[0].tolist()) # 근사 좌표값 생성
        # 박스 좌표와 클래스 인덱스를 결합하여 고유한 해시값을 생성 
        hash_input = f"{class_idx}-{x1}-{y1}-{x2}-{y2}"
        return hashlib.md5(hash_input.encode()).hexdigest()

    def round_coordinates(self, coords): 
        return [int(round(coord / self.coord_tolerance) * self.coord_tolerance) for coord in coords]
    # 좌표 값을 설정한 tolerance 값으로 나누고 round 후 다시 곱하여 좌표 값 근사하기

id 없는 객체들만 따로 처리하기 위한 로직이므로, noidTracker라고 이름 지어줬다.

설명

    def __init__(self):
        self.saved_hashes = {}
        self.save_interval = 3  # save time interval
        self.coord_tolerance = 100  # image pixel coord to tolerate 
  • saved_hashes : 해시 값 저장공간
  • save_interval : 해시 값이 들어왔을때 중복으로 처리할 시간 간격
  • coord_tolerance : 바운딩 박스 좌표값이 들어왔을 때 동일 객체로 처리할 픽셀 간격

먼저 위 값들을 초기화해 준다.

    def update(self, box, class_idx):
        box_hash = self.generate_box_hash(box, class_idx) # 해시 값 생성
        current_time = time.time()

        if box_hash in self.saved_hashes:
            last_saved_time = self.saved_hashes[box_hash]['time']
            if current_time - last_saved_time < self.save_interval:
                # print('Not saving duplicated data')
                return False  # prevent duplication
        self.saved_hashes[box_hash] = {'time': current_time, 'box': box}
        return True # 박스가 새로 저장된 경우 
  • generate_box_hash 함수에 box, class index 값을 넘겨서 box hash 값을 생성한다.
  • 해시 값 저장공간에서 동일한 해시값이 존재하는지 확인한다.
  • 동일한 해시 값이 없다면, 박스를 새로 저장하고 True를 반환
  • 동일한 해시 값이 있다면, 현재 시간과 마지막으로 저장된 시간의 차이가 기준을 넘지 않는 경우 중복 객체로 판별하고 False를 반환
  • 기준을 넘는 경우 박스를 새로 저장하고 True를 반환
    def generate_box_hash(self, box, class_idx):
        # 근사 좌표값 생성
        x1, y1, x2, y2 = self.round_coordinates(box.xyxy[0].tolist()) #
        # 박스 좌표와 클래스 인덱스를 결합하여 고유한 해시값을 생성 
        hash_input = f"{class_idx}-{x1}-{y1}-{x2}-{y2}"
        return hashlib.md5(hash_input.encode()).hexdigest()
        
## hash_input.encode() # -> 문자열을 utf-8 byte로 인코딩
## hashlib.md5().hexdigest() # -> MD5 해시 값 생성 후 16진수 문자열로 반환 
  • box, class_index 값을 받아서 고유 해시값을 생성한다.
  • 이 때, round_coordinates 함수를 통해 좌표값을 근사하여 tolerance 값 미만인 경우 같은 좌표로 인식되게 한다.
    def round_coordinates(self, coords): 
        return [int(round(coord / self.coord_tolerance) * self.coord_tolerance) for coord in coords]
    # 좌표 값을 설정한 tolerance 값으로 나누고 round 후 다시 곱하여 좌표 값 근사하기
  • box 좌표 값을 받아서 설정한 tolerance 값으로 나누고, round 함수 적용 후 다시 곱하여 좌표값을 근사한다.

olerance 값을 두고 근사 좌표를 생성하는 이유는, 좌표 값이 조금씩 달라질 때 근사 좌표로 통일하여 더 효율적인 중복 검출을 하기 위함.

예를 들어, tolerance 값이 100인 경우, 박스 좌표가 [120.4, 210.2, 320.9, 410.5], [125.6, 215.7, 318.3, 405.2]일 때를 비교해 보자.

  • 기존 좌표
    [120.4, 210.2, 320.9, 410.5] → [1.204, 2.102, 3.209, 4.105] → [100, 200, 300, 400]
  • 새 좌표
    [125.6, 215.7, 318.3, 405.2] → [1.256, 2.157, 3.183, 4.052] → [100, 200, 300, 400]

이렇게 근접 좌표에 있는 객체를 인식하면 동일한 객체로 처리하고 중복 저장을 방지할 수 있다.

활용

noidtrack = noIDTracker()

for class_idx, box in zip(classes, boxes):
	noidtrack.update(box, class_idx)
	# -> update 함수에서 T, F값을 반환하므로 이 밑에 if True 조건문 작성하면 됨 

기존 object tracking 코드에서 id가 없는 경우, 위와 같이 활용할 수 있다.

profile
춘식이랑 함께하는 개발일지.. 그런데 이제 먼작귀를 곁들인

0개의 댓글