꿈꾸는 새벽하늘

[그로쓰] 이미지 및 텍스트 분석 기반 실종 반려견 찾기 서비스 본문

🌸 Capstone Design Project

[그로쓰] 이미지 및 텍스트 분석 기반 실종 반려견 찾기 서비스

rovemin 2023. 5. 16. 16:32

프로젝트 소개

우리 팀은 반려견을 잃어버렸을 때 견주들이 겪게 되는 시간적, 금전적 측면의 비효율 문제를 해결하기 위해 이미지 및 텍스트 분석 기반 실종 반려견 찾기 서비스인 POOM을 만들었다.

 

POOM은 실종 신고 등록 게시물과 목격/구조 제보 게시물을 단순하게 확인할 수 있었던 기존 서비스와는 다르게 아래의 2가지 핵심 기능과 1가지 부가 기능을 추가로 제공한다.

  • 이미지 분석 기반 검색 기능
  • 텍스트 분석 기반 검색 기능
  • 알림 기능

 

이 글에서는 이 프로젝트의 두 가지 핵심 기능을 어떻게 구현하였는지 상세히 설명하고자 한다.

 

1. 이미지 분석 기반 검색 기능

이미지 검색은 얼굴 이미지를 임베딩한 뒤 분류하여 이미지 간의 유사도가 높은 이미지들끼리 클러스터링하는 FaceNet 모델을 강아지에게 적용한 기능이다.

이미지 검색 기능은 서비스의 이미지 데이터셋 내에서 유사도를 계산하여 유사도가 높은 게시물들을 사용자에게 보여준다.

 

1) FaceNet 모델 학습

이미지 검색 기능 구현을 위해서는 먼저 모델 학습이 우선되어야 했다.

모델 학습은 1,393마리의 강아지 사진을 각각 폴더화하여 총 8,363장의 데이터로 진행하였다.

 

2) 클러스터링

데이터셋 내의 이미지들 동일한 크기로 조정

from PIL import Image
import cv2
import os
import glob
from PIL import Image
from keras.utils import img_to_array

for i in range (0, 297):                                                    
  imglist = glob.glob(f'/content/drive/MyDrive/data/dogfacenet/{i}/*.jpg')
  
  for img_path in imglist:
    img = Image.open(img_path)
    img.resize((224, 224)).save(img_path)
    np_img = img_to_array(img)
    # print(np_img.shape)
  
  for img_path in imglist:
    img = Image.open(img_path)
    img.resize((224, 224)).save(img_path)
    np_img = img_to_array(img)
    # print(np_img.shape)

데이터 전처리 과정

import os

filenames = []
labels = []
idx = 0

for path, dirs, files in os.walk(PATH):
  # print(path)
  if len(files) > 0:
    for file in files:
      file_path = os.path.join(path, file)
      filenames.append(file_path)
    labels = np.append(labels, np.ones(len(files))*idx)
    idx += 1

filenames.sort()

for i in range (len(filenames)):
  print(filenames[i])
h,w,c = SIZE
images = np.empty((len(filenames),h,w,c))
for i,f in enumerate(filenames):
    images[i] = sk.io.imread(f)
images /= 255.0		# Normalization
nbof_classes = len(np.unique(labels))
print(nbof_classes)

Loss Definition

alpha = 0.3
def triplet(y_true,y_pred):
    
    a = y_pred[0::3]
    p = y_pred[1::3]
    n = y_pred[2::3]
    
    ap = K.sum(K.square(a-p),-1)
    an = K.sum(K.square(a-n),-1)

    return K.sum(tf.nn.relu(ap - an + alpha))

Metric Definition

def triplet_acc(y_true,y_pred):
    a = y_pred[0::3]
    p = y_pred[1::3]
    n = y_pred[2::3]
    
    ap = K.sum(K.square(a-p),-1)
    an = K.sum(K.square(a-n),-1)
    
    return K.less(ap+alpha,an)

모델을 로드하여 클러스터링

from sklearn.cluster import KMeans

model = tf.keras.models.load_model('../model/2023.05.13.dogfacenet.h5', custom_objects={'triplet':triplet,'triplet_acc':triplet_acc})

mod = tf.keras.Model(model.layers[0].input, model.layers[-1].output)

predict=mod.predict(images)		# 은닉층 출력값 확인
predict=model.predict(images)

kmeans = KMeans(n_clusters=len(np.unique(labels)),max_iter=2000, random_state=0,tol=0.2).fit(predict)

images_cluster = [images[np.equal(kmeans.labels_,i)] for i in range(len(labels))]
labels_cluster = [labels[np.equal(kmeans.labels_,i)] for i in range(len(labels))]
for i in range(len(images_cluster)):
    length = len(images_cluster[i])
    if length > 0:
        print(labels_cluster[i])
        fig=plt.figure(figsize=(length*2,2))
        for j in range(length):
            plt.subplot(1,length,j+1)
            plt.imshow(images_cluster[i][j])
            plt.xticks([])
            plt.yticks([])
        plt.show()

위 클러스터링 코드를 실행하면 다음과 같은 결과를 확인할 수 있다. (아래 이미지는 클러스터링 결과의 일부분이다.)

 

3) 이미지 검색 기능 구현

메인 화면에서 이미지 검색 버튼 누르기
파일 업로드하기
이미지 검색 시작!
이미지 검색 결과 확인
이미지 검색 결과 화면에서 자신의 반려견으로 추정되는 사진을 클릭하면 해당 사진의 상세 페이지 확인 가능

 

2. 텍스트 분석 기반 검색 기능

텍스트 분석은 SBERT를 적용하여 실종 반려견에 대해 설명한 글을 의미론적으로 검색할 수 있는 검색 기능이다.

이 기능에서는 실종견의 기타 특징을 설명하는 문장들 간의 코사인 유사도를 계산하여 상위 12개의 게시물을 보여준다.

사진이 없는 게시물에서는 이러한 텍스트 검색 기능이 유용하게 활용될 것이라고 생각한다.

 

1) SBERT 적용

model = SentenceTransformer("Huffon/sentence-klue-roberta-base")

def textresult(request):
    docs = list(Dog_post.objects.values('description'))
    document_embeddings = model.encode(docs)

    query = request.POST['text_search']
    query_embedding = model.encode(query)

    top_k = min(5, len(docs))

    # 입력 문장 - 문장 후보군 간 코사인 유사도 계산
    cos_scores = util.pytorch_cos_sim(query_embedding, document_embeddings)[0]
    # 코사인 유사도 순으로 'top_k' 개 문장 추출
    top_results = torch.topk(cos_scores, k=top_k)
    results = []
    results_idx = []

    numbers = []

    for i, (score, idx) in enumerate(zip(top_results[0], top_results[1])):
        result = docs[idx]

        results_idx.append(idx.item()+1)
        results.append(result['description'])

    print_result = list(zip(results_idx, results))

    for i in range(top_k):
        numbers.append(i)

    return render(
        request,
        'register_dog/textresult.html',
        {
            'print_result':print_result,
        }
    )

 

2) 텍스트 검색 기능 구현

메인 화면에서 텍스트 검색 창에 검색할 내용 입력
검색 버튼 누르고 텍스트 검색 결과 확인
텍스트 검색 결과 화면에서 자신의 반려견으로 추정되는 설명을 클릭하면 해당 사진의 상세 페이지 확인 가능

 

반응형 웹 서비스

POOM은 앱이 아닌 웹이다. 이 서비스를 웹으로 개발한 이유는 원활한 서비스 운영을 위해 제보자의 목격/제보 게시물 등록이 중요하기 때문이다. 따라서 제보자의 입장에서 서비스의 편의성을 고려하여 실종견을 발견한 상황에서 빠르게 제보할 수 있는 웹으로 구현했다. 제보자에게는 앱스토어에서 앱을 다운 받고 제보를 하는 것보다 인터넷에 검색하여 상위에 나오는 웹서비스에 제보하는 것이 더 편리할 것이다.

더불어 사용자들이 모바일 환경에서도 서비스를 사용하는 데 불편함이 없도록 POOM은 반응형 웹으로 개발하였다.

 

POOM의 기대효과

이 서비스는 세부적으로 3가지 목표를 갖고 있다. 

  • 첫째, 보호자의 노력과 목격자의 선의에 기대어 돌아가던 기존 시스템을 알림을 통해 자동화한다. 
  • 둘째, 반려견 실종 및 실종·유기견 발견 시 사람들의 다양한 대처 방법을 하나의 플랫폼으로 통일한다. 
  • 셋째, 이미지 및 텍스트 분석 기술을 통해 등록된 신고를 높은 정확도와 빠른 속도로 검색하도록 한다. 

이에 따라 궁극적으로는 실종견들이 최대한 빨리 보호자의 품으로 돌아가게 되기를 기대한다.