본문 바로가기
카테고리 없음

Opensearch와 Azure vision으로 비슷한 사진 찾기

by bieber085 2022. 8. 17.

유사사진 추천 기능을 구현해야해서 몇 주 전에 여러가지 방법을 찾았었다.

결국 생각해낸 방법 중 채택 된 것이 사진을 자동으로 태깅해서 Opensearch에 인덱싱 해놓고 태그를 기준으로 검색해서 비슷한 사진을 찾는 것이었다. 얼마전 PoC 진행했던 것을 간단히 기록으로 남기는 게 좋겠다 생각했다.

 

Azure vision으로 사진 태깅하기

azure computer vision은 사진과 동영상으로부터 여러가지 정보를 추출해주는 AI 서비스이다.

이 서비스를 이용하여 사진에 자동으로 태그를 달고, 설명을 달아줄 수 있다.

 

azure 클라우드 콘솔에서 Computer Vision 인스턴스를 아래와 같이 생성한다.

 

생성된 인스턴스의 상세 정보에서 '키 및 엔드포인트'를 선택하면 api access 키를 확인할 수 있다.

 

 

Azure computer vision 인스턴스에 접근할 수 있게끔 sdk를 제공한다. 나는 python으로 코드를 작성했다.

 

이미지는 s3에 저장하기 때문에 url 형태로 이미지를 제공하여 응답받는 형태로 만들었다.

pip을 이용해서 azure-cognitiveservices-vision-computervision 라이브러리를 설치 후 원하는 기능만 쓸 수 있도록 간단히 패키지를 작성했다.

 

from azure.cognitiveservices.vision.computervision import ComputerVisionClient
from azure.cognitiveservices.vision.computervision.models import OperationStatusCodes
from azure.cognitiveservices.vision.computervision.models import VisualFeatureTypes
from msrest.authentication import CognitiveServicesCredentials

class client:
    def __init__(self,subscription_key,endpoint):
        self.computervision_client = ComputerVisionClient(endpoint, CognitiveServicesCredentials(subscription_key))

    def get_tags(self,remote_image_url):
        tags_result_remote = self.computervision_client.tag_image(remote_image_url )
        return [tag.name for tag in tags_result_remote.tags]

    def get_desc(self,remote_image_url):
        capations_result_remote = self.computervision_client.describe_image(remote_image_url)
        return [caption.text for caption in capations_result_remote.captions]

 

아래처럼 임포트해서 사용해 볼 수 있다.

from VisionHandler import handler

subscription_key = "***********************************"
endpoint = "https://*****.cognitiveservices.azure.com/"

client = handler.client(subscription_key,endpoint)

image_url='https://picsum.photos/400/400'

desc = client.get_desc(image_url)
tags = client.get_tags(image_url)

print(desc,tags)

 

이제 서버에서 s3에 사진을 업로드하면, aws lambda에서 db에 태그와 설명을 업데이트 할 수 있게해야한다.

하지만 빠른 결과를 보기위해 태그와 설명을 직접 DB에 인서트 하였다.

 

 

Opensearch에 태그와 설명 정보 인덱싱하기

그 후 아래 글에서처럼 AWS Opensearch 인스턴스에 인덱싱하였다.

https://bieber.tistory.com/2

 

AWS lambda로 Opensearch index 생성하기

오늘은 Opensearch 데이터 인덱싱을 하는 람다 패키지를 만들어 배포했다. 기존에는 gcp kubernetes 환경에서 logstash를 이용하여 인덱싱을 했었다. 초기 개발비용 감소와 관리 포인트 감소의 장점이 있

blog.aug18th.com

 

Opensearch More Like This 쿼리로 비슷한 사진 찾기

 

Opensearch에 인덱싱은 아래처럼 되어있다.

{
"id" : 1,
...
"lat" : 38.06797947502193,
"lng" : 127.64364900163397,
"tags" : [
    "human face",
    "person",
    "clothing",
    "tree",
    ...
    ]
}

 

 

tags의 내용에 기반한 유사 사진 검색을 위해 more like this 쿼리를 사용했다.

입력값 중 일부로 query를 만들어서 검색하여 비슷한 document를 찾는다고 한다.

자세한 사용법은 가이드에서 참고했다.

 

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-mlt-query.html

 

More like this query | Elasticsearch Guide [8.3] | Elastic

The fields on which to perform MLT must be indexed and of type text or keyword`. Additionally, when using like with documents, either _source must be enabled or the fields must be stored or store term_vector. In order to speed up analysis, it could help to

www.elastic.co

 

도큐먼트 내용대로 쿼리를 작성했다.

텍스트 배열 필드인 tags와 텍스트 필드인 generatedDesc를 기준으로 _id 1인 document와 비슷한 사진을 photos 인덱스에서 검색하는 내용이다. 

min_term_freq는 최소 매칭되어야하는 query term의 갯수이고 이보다 적게 매칭된 document는 무시된다.

max_query_terms는  쿼리에 사용되는 최대 term의 갯수이고 쿼리는 정확해지지만 성능과 트레이드오프가 있다고 한다.

GET /photos/_search
{
  "query": {
    "more_like_this": {
      "fields": ["tags", "generatedDesc"],
      "like": [
        {
          "_index": "photos",
          "_id": 1
        }
      ],
      "min_term_freq": 1,
      "max_query_terms": 12
    }
  }
}

 

Opensearch 콘솔에서 정상적으로 응답이 온다

 

테스트용 화면에 나타난 응답

 

개발 중인 서비스에서 사진은 Stack이라는 묶음에 포함이 되어있는데, 한 묶음의 사진의 유형이 비슷하기 때문에

너무 서로 비슷한 사진들이 조회되는 문제가 있었다. 저 테스트 데이터 이미지 옆의 숫자는 같은 묶음의 사진이다.

 

따라서 한 묶음 당 한 장씩만 조회되도록 해야했는데 Collapse 키워드를 사용하여 처리했다.

stackId 필드가 사진 묶음의 아이디를 나타낸다. stackId 필드로 collaspe 한 결과 해당 문제가 해결되었다.

 

GET /photos/_search
{
  "query": {
    "more_like_this": {
      "fields": ["tags", "generatedDesc"],
      "like": [
        {
          "_index": "photos",
          "_id": 1
        }
      ],
      "min_term_freq": 1,
      "max_query_terms": 12
    }
  },
  "collapse": {
    "field": "stackId"
  }
}

 

collaspe를 적용한 후 테스트 응답화면

 

 

성공~