Android에서 Vision API 제품 검색 백엔드 호출

1. 시작하기 전에

bd8c01b2f8013c6d.png

휴대전화 카메라를 물체에 갖다 대면 온라인에서 구매할 수 있는 곳을 찾을 수 있는 Google 렌즈 데모를 보셨나요? 앱에 동일한 기능을 추가하는 방법을 알아보려면 이 Codelab을 참고하세요. 이 과정은 모바일 앱에 제품 이미지 검색 기능을 빌드하는 방법을 알려주는 학습 과정의 일부입니다.

이 Codelab에서는 모바일 앱에서 Vision API 제품 검색으로 빌드된 백엔드를 호출하는 방법을 알아봅니다. 이 백엔드는 쿼리 이미지를 가져와 제품 카탈로그에서 시각적으로 유사한 제품을 검색할 수 있습니다.

학습 과정에서 ML Kit 객체 감지 및 추적을 사용하여 쿼리 이미지에서 객체를 감지하고 사용자가 검색할 제품을 선택하도록 하는 방법을 비롯해 시각적 제품 검색 기능을 빌드하는 나머지 단계를 알아볼 수 있습니다.

빌드할 항목

  • 이 Codelab에서는 입력 이미지에서 객체를 감지할 수 있는 Android 앱으로 시작합니다. 사용자가 선택한 객체를 가져와 제품 검색 백엔드로 전송하고 검색 결과를 화면에 표시하는 코드를 작성합니다.
  • 결과적으로 오른쪽 이미지와 비슷한 화면이 표시됩니다.

학습할 내용

  • Android 앱에서 Vision API 제품 검색 API의 응답을 호출하고 파싱하는 방법

필요한 항목

  • Android 스튜디오 최신 버전 (v4.1.2 이상)
  • Android 스튜디오 에뮬레이터 또는 실제 Android 기기
  • 샘플 코드
  • Kotlin을 사용한 Android 개발에 관한 기본 지식

이 Codelab에서는 Vision API 제품 검색에 중점을 둡니다. 관련 없는 개념과 코드 블록은 다루지 않으며 간단히 복사하여 붙여넣을 수 있도록 제공됩니다.

2. Vision API 제품 검색 정보

Vision API 제품 검색은 사용자가 제품 카탈로그에서 시각적으로 유사한 제품을 검색할 수 있는 Google Cloud의 기능입니다. 소매업체는 여러 각도에서 제품을 시각적으로 보여주는 참조 이미지를 각각 포함한 제품을 만들 수 있습니다. 그런 다음 이러한 제품을 제품 세트 (예: 제품 카탈로그)에 추가할 수 있습니다. 현재 Vision API 제품 검색은 가정 용품, 의류, 완구, 포장 제품, 일반 등의 제품 카테고리를 지원합니다.

사용자가 자신의 이미지로 제품 세트를 쿼리하면 Vision API 제품 검색이 머신러닝을 적용하여 사용자의 쿼리 이미지에 있는 제품을 소매 업체의 제품 세트에 있는 이미지와 비교한 다음 시각적 및 의미론적으로 유사한 결과의 순위 목록을 반환합니다.

3. 시작 앱 다운로드 및 실행

코드 다운로드

다음 링크를 클릭하면 이 Codelab의 모든 코드를 다운로드할 수 있습니다.

다운로드한 ZIP 파일의 압축을 해제합니다. 그러면 필요한 모든 리소스가 포함된 루트 폴더 (odml-pathways-main)가 압축 해제됩니다. 이 Codelab에서는 product-search/codelab2/android 하위 디렉터리의 소스만 필요합니다.

odml-pathways 저장소의 codelab2 하위 디렉터리에는 다음 두 디렉터리가 포함됩니다.

  • android_studio_folder.pngstarter: 이 Codelab에서 빌드할 시작 코드입니다.
  • android_studio_folder.pngfinal: 완성된 샘플 앱의 완료된 코드입니다.

여기서 시작 앱은 이미지에서 객체를 감지하여 시각적 제품 검색 빌드: Android Codelab에서 빌드한 앱입니다. ML Kit 객체 감지 및 추적을 사용하여 이미지에서 객체를 감지하고 화면에 표시합니다.

Android 스튜디오로 앱 가져오기

먼저 starter 앱을 Android 스튜디오로 가져옵니다.

Android 스튜디오로 이동하여 Import Project (Gradle, Eclipse ADT, etc.)를 선택하고 이전에 다운로드한 소스 코드에서 starter 폴더를 선택합니다.

7c0f27882a2698ac.png

시작 앱 실행

이제 Android 스튜디오로 프로젝트를 가져왔으므로 앱을 처음으로 실행할 수 있습니다. USB를 통해 Android 기기를 호스트에 연결하거나 Android 스튜디오 에뮬레이터를 시작하고 Android 스튜디오 툴바에서 실행 ( execute.png)을 클릭합니다.

(이 버튼이 사용 중지된 경우 전체 저장소가 아닌 starter/app/build.gradle만 가져왔는지 확인하세요.)

이제 Android 기기에서 앱이 실행됩니다. 이미지에서 패션 상품을 감지하고 위치를 표시하는 객체 감지 기능이 이미 있습니다. 사전 설정된 사진으로 확인해 보세요.

c6102a808fdfcb11.png

이미지에서 객체를 감지할 수 있는 시작 앱의 스크린샷

다음으로 감지된 객체를 Vision API 제품 검색 백엔드로 전송하고 검색 결과를 화면에 표시하도록 앱을 확장합니다.

4. 객체 선택 처리

감지된 객체를 탭하여 선택하도록 허용

이제 사용자가 이미지에서 객체를 선택하고 제품 검색을 시작할 수 있도록 코드를 추가합니다. 시작 앱에는 이미 이미지에서 객체를 감지하는 기능이 있습니다. 이미지에 여러 객체가 있거나 감지된 객체가 이미지의 작은 부분만 차지할 수 있습니다. 따라서 사용자가 감지된 객체 중 하나를 탭하여 제품 검색에 사용할 객체를 표시해야 합니다.

9cdfcead6d95a87.png

이미지에서 감지된 패션 상품의 스크린샷

Codelab을 간단하게 유지하고 머신러닝에 집중하기 위해 사용자가 탭한 객체를 감지하는 데 도움이 되는 일부 상용구 Android 코드가 시작 앱에 구현되었습니다. 기본 활동 (ObjectDetectorActivity)에 이미지를 표시하는 뷰는 실제로 Android OS의 기본 ImageView를 확장하는 맞춤 뷰 (ImageClickableView)입니다. 여기에는 다음을 비롯한 편리한 유틸리티 메서드가 구현되어 있습니다.

  • fun setOnObjectClickListener(listener: ((objectImage: Bitmap) -> Unit)) 사용자가 탭한 객체만 포함된 잘린 이미지를 수신하는 콜백입니다. 이 잘린 이미지를 제품 검색 백엔드로 전송합니다.

감지된 객체를 탭하는 사용자를 처리하는 코드를 추가합니다.

ObjectDetectorActivity 클래스의 initViews 메서드로 이동하여 메서드 끝에 다음 줄을 추가합니다. (Android 스튜디오에 startProductImageSearch 메서드를 찾을 수 없다는 메시지가 표시됩니다. 걱정하지 마세요. 조금 후에 구현할 예정입니다.)

// Callback received when the user taps on any of the detected objects.
ivPreview.setOnObjectClickListener { objectImage ->
    startProductImageSearch(objectImage)
}

사용자가 화면에서 감지된 객체를 탭할 때마다 onObjectClickListener가 호출됩니다. 선택한 객체만 포함된 잘린 이미지를 수신합니다. 예를 들어 사용자가 오른쪽의 드레스를 입은 사람을 탭하면 아래와 같이 objectImage로 리스너가 트리거됩니다.

9cac8458d0f326e6.png

onObjectClickListener에 전달된 잘린 이미지의 예

잘린 이미지를 제품 검색 활동에 전송

이제 분리된 활동 (ProductSearchActivity)에서 쿼리 이미지를 Vision API 제품 검색 백엔드로 전송하는 로직을 구현합니다.

모든 UI 구성요소가 미리 구현되어 있으므로 제품 검색 백엔드와 통신하는 코드를 작성하는 데 집중할 수 있습니다.

25939f5a13eeb3c3.png

ProductSearchActivity의 UI 구성요소 스크린샷

사용자가 선택한 객체 이미지를 ProductSearchActivity에 전송하는 코드를 추가합니다.

Android 스튜디오로 돌아가서 ObjectDetectorActivity 클래스에 다음 startProductImageSearch 메서드를 추가합니다.

private fun startProductImageSearch(objectImage: Bitmap) {
    try {
        // Create file based Bitmap. We use PNG to preserve the image quality
        val savedFile = createImageFile(ProductSearchActivity.CROPPED_IMAGE_FILE_NAME)
        objectImage.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(savedFile))

        // Start the product search activity (using Vision Product Search API.).
        startActivity(
            Intent(
                    this,
                    ProductSearchActivity::class.java
            ).apply {
                // As the size limit of a bundle is 1MB, we need to save the bitmap to a file
                // and reload it in the other activity to support large query images.
                putExtra(
                    ProductSearchActivity.REQUEST_TARGET_IMAGE_PATH,
                    savedFile.absolutePath
                )
            })
    } catch (e: Exception) {
        // IO Exception, Out Of memory ....
        Toast.makeText(this, e.message, Toast.LENGTH_SHORT).show()
        Log.e(TAG, "Error starting the product image search activity.", e)
    }
}

이 코드 스니펫은 다음 3가지 작업을 실행합니다.

  • 잘린 이미지를 가져와 PNG 파일로 직렬화합니다.
  • 제품 검색 시퀀스를 실행하기 위해 ProductSearchActivity를 시작합니다.
  • ProductSearchActivity가 나중에 검색 이미지로 사용할 수 있도록 잘린 이미지 URI를 start-activity 인텐트에 포함합니다.

명심해야 할 몇 가지 사항이 있습니다.

  • 객체를 감지하고 백엔드를 쿼리하는 로직은 Codelab을 더 쉽게 이해할 수 있도록 2개의 활동으로만 분할되었습니다. 앱에서 이를 구현하는 방법은 개발자가 결정합니다.
  • 쿼리 이미지는 Android 인텐트의 1MB 크기 제한보다 클 수 있으므로 쿼리 이미지를 파일에 작성하고 활동 간에 이미지 URI를 전달해야 합니다.
  • 무손실 형식인 PNG로 쿼리 이미지를 저장할 수 있습니다.

제품 검색 활동에서 쿼리 이미지를 가져옵니다.

ProductSearchActivity에서 쿼리 이미지를 가져와 화면에 표시하는 코드는 이미 시작 앱에 구현되어 있습니다.

onCreate 메서드로 이동하여 이 코드가 이미 있는지 확인합니다.

// Receive the query image and show it on the screen
intent.getStringExtra(REQUEST_TARGET_IMAGE_PATH)?.let { absolutePath ->
    viewBinding.ivQueryImage.setImageBitmap(BitmapFactory.decodeFile(absolutePath))
}

앱 실행

이제 Android 스튜디오 툴바에서 Run ( execute.png)을 클릭합니다.

앱이 로드되면 사전 설정된 이미지를 탭하고 감지된 객체 중 하나를 선택합니다.

탭한 이미지와 함께 ProductSearchActivity가 표시되는지 확인합니다. 검색 버튼은 아직 아무 작업도 하지 않지만 다음에 구현할 예정입니다.

fed40f81b8b43801.png

감지된 객체 중 하나를 탭하면 비슷한 화면이 표시됩니다.

5. 제품 검색 백엔드 살펴보기

제품 이미지 검색 백엔드 빌드

이 Codelab에는 Vision API 제품 검색으로 빌드된 제품 검색 백엔드가 필요합니다. 이를 달성하는 방법에는 두 가지가 있습니다.

옵션 1: 배포된 데모 백엔드 사용

Google에서 이미 배포한 제품 검색 백엔드를 사용하여 이 Codelab을 진행할 수 있습니다. 데모 백엔드는 Vision API 제품 검색 빠른 시작을 따라 복제할 수 있습니다.

옵션 2: Vision API 제품 검색 빠른 시작에 따라 자체 백엔드 만들기

이 옵션은 나중에 자체 제품 카탈로그용 제품 검색 백엔드를 빌드할 수 있도록 제품 검색 백엔드를 빌드하는 방법을 자세히 알아보려는 사용자에게 권장됩니다. 다음이 필요합니다.

  • 결제가 사용 설정된 Google Cloud 계정 (무료 체험판 계정이어도 됩니다.)
  • 프로젝트, 서비스 계정 등 Google Cloud 개념에 대한 지식

이 작업의 방법은 학습 과정의 후반부에서 알아볼 수 있습니다.

중요한 개념 알아보기

제품 검색 백엔드와 상호작용할 때 다음과 같은 개념을 접하게 됩니다.

  • 제품 세트: 제품 세트는 제품 그룹의 단순한 컨테이너입니다. 제품 카탈로그는 제품 세트와 제품으로 표현할 수 있습니다.
  • 제품: 제품 세트를 만든 후 제품을 만들어 제품 세트에 추가할 수 있습니다.
  • 제품의 참조 이미지: 제품의 다양한 보기를 포함하는 이미지입니다. 참조 이미지는 시각적으로 유사한 제품을 검색하는 데 사용됩니다.
  • 제품 검색: 제품 세트를 만들고 제품 세트의 색인을 생성한 후에는 Cloud Vision API를 사용하여 제품 세트를 쿼리할 수 있습니다.

사전 설정된 제품 카탈로그 이해하기

이 Codelab에서 사용된 제품 검색 데모 백엔드는 Vision API 제품 검색과 약 100개의 신발 및 드레스 이미지 제품 카탈로그를 사용하여 생성되었습니다. 카탈로그의 이미지는 다음과 같습니다.

4f1a8507b74ab178.png 79a5fc6c829eca77.png 3528c872f813826e.png

사전 설정된 제품 카탈로그의 예

제품 검색 데모 백엔드 호출

Google Cloud API 키를 설정하고 API 키에 대한 액세스를 앱으로만 제한하여 모바일 앱에서 직접 Vision API 상품 검색을 호출할 수 있습니다.

이 Codelab을 간단하게 유지하기 위해 API 키와 인증에 대해 걱정하지 않고 데모 백엔드에 액세스할 수 있는 프록시 엔드포인트가 설정되었습니다. 모바일 앱에서 HTTP 요청을 수신하고 API 키를 추가한 후 Vision API 제품 검색 백엔드로 요청을 전달합니다. 그런 다음 프록시가 백엔드로부터 응답을 수신하여 모바일 앱에 반환합니다.

이 Codelab에서는 Vision API 제품 검색의 두 가지 API를 사용합니다.

6. API 클라이언트 구현

제품 검색 워크플로 이해하기

다음 워크플로에 따라 백엔드로 제품 검색을 실행하세요.

API 클라이언트 클래스 구현

이제 ProductSearchAPIClient이라는 전용 클래스에서 제품 검색 백엔드를 호출하는 코드를 구현합니다. 시작 앱에 다음과 같은 상용구 코드가 구현되어 있습니다.

  • class ProductSearchAPIClient: 이 클래스는 현재 대부분 비어 있지만 이 Codelab의 뒷부분에서 구현할 메서드가 몇 개 있습니다.
  • fun convertBitmapToBase64(bitmap: Bitmap): 제품 검색 백엔드로 전송하기 위해 Bitmap 인스턴스를 base64 표현으로 변환
  • fun annotateImage(image: Bitmap): Task<List<ProductSearchResult>>: projects.locations.images.annotate API를 호출하고 응답을 파싱합니다.
  • fun fetchReferenceImage(searchResult: ProductSearchResult): Task<ProductSearchResult>: projects.locations.products.referenceImages.get API를 호출하고 응답을 파싱합니다.
  • SearchResult.kt: 이 파일에는 Vision API 제품 검색 백엔드에서 반환하는 유형을 나타내는 여러 데이터 클래스가 포함되어 있습니다.

API 구성 지정

ProductSearchAPIClient 클래스로 이동하면 제품 검색 백엔드의 일부 구성이 이미 정의되어 있습니다.

// Define the product search backend
// Option 1: Use the demo project that we have already deployed for you
const val VISION_API_URL =
    "https://us-central1-odml-codelabs.cloudfunctions.net/productSearch"
const val VISION_API_KEY = ""
const val VISION_API_PROJECT_ID = "odml-codelabs"
const val VISION_API_LOCATION_ID = "us-east1"
const val VISION_API_PRODUCT_SET_ID = "product_set0"
  • VISION_API_URL은 Cloud Vision API의 API 엔드포인트입니다. 데모 백엔드를 진행할 때 프록시 엔드포인트로 설정합니다. 하지만 자체 백엔드를 배포하는 경우 Cloud Vision API 엔드포인트로 변경해야 합니다. https://vision.googleapis.com/v1.
  • VISION_API_KEY는 Cloud 프로젝트의 API 키입니다. 프록시에서 이미 인증을 처리하므로 이 입력란을 비워 두어도 됩니다.
  • VISION_API_PROJECT_ID는 Cloud 프로젝트 ID입니다. odml-codelabs은 데모 백엔드가 배포된 Cloud 프로젝트입니다.
  • VISION_API_LOCATION_ID는 제품 검색 백엔드가 배포된 Cloud 위치입니다. us-east1은 데모 백엔드를 배포한 위치입니다.
  • VISION_API_PRODUCT_SET_ID는 시각적으로 유사한 제품을 검색할 제품 카탈로그 (Vision API 용어로는 '제품 세트')의 ID입니다. 하나의 Cloud 프로젝트에 여러 카탈로그를 사용할 수 있습니다. product_set0은 데모 백엔드의 사전 설정된 제품 카탈로그입니다.

7. 제품 검색 API 호출

API 요청 및 응답 형식 살펴보기

이미지의 Google Cloud Storage URI, 웹 URL 또는 base64 인코딩 문자열을 Vision API 제품 검색에 전달하여 특정 이미지와 유사한 제품을 찾을 수 있습니다. 이 Codelab에서는 base64 인코딩된 문자열 옵션을 사용합니다. 쿼리 이미지가 사용자 기기에만 있기 때문입니다.

다음 요청 JSON 본문으로 projects.locations.images.annotate 엔드포인트에 POST 요청을 보내야 합니다.

{
  "requests": [
    {
      "image": {
        "content": {base64-encoded-image}
      },
      "features": [
        {
          "type": "PRODUCT_SEARCH",
          "maxResults": 5
        }
      ],
      "imageContext": {
        "productSearchParams": {
          "productSet": "projects/{project-id}/locations/{location-id}/productSets/{product-set-id}",
          "productCategories": [
               "apparel-v2"
          ],
        }
      }
    }
  ]
}

지정해야 하는 매개변수가 있습니다.

  • base64-encoded-image: 질문 이미지의 바이너리 데이터의 base64 표현 (ASCII 문자열)입니다.
  • project-id: GCP 프로젝트 ID입니다.
  • location-id: 유효한 위치 식별자입니다.
  • product-set-id: 작업을 실행할 제품 세트의 ID입니다.

제품 카탈로그에 신발과 드레스 이미지만 포함되어 있으므로 productCategoriesapparel-v2로 지정합니다. 여기서 v2는 의류 제품 검색 머신러닝 모델의 버전 2를 사용한다는 의미입니다.

요청이 성공하면 서버가 200 OK HTTP 상태 코드와 응답을 JSON 형식으로 반환합니다. 응답 JSON에는 다음과 같은 두 가지 결과 유형이 있습니다.

  • productSearchResults - 전체 이미지에 일치하는 제품의 목록을 포함합니다.
  • productGroupedResults - 이미지에서 식별된 각 제품의 경계 상자 좌표와 일치 항목을 포함합니다.

제품이 이미 원본 이미지에서 잘렸으므로 productSearchResults 목록에서 결과를 파싱합니다.

다음은 제품 검색 결과 객체의 몇 가지 중요한 필드입니다.

  • product.name: projects/{project-id}/locations/{location-id}/products/{product_id} 형식의 제품 고유 식별자
  • product.score: 검색 결과가 쿼리 이미지와 얼마나 유사한지를 나타내는 값입니다. 값이 높을수록 유사성이 높습니다.
  • product.image: projects/{project-id}/locations/{location-id}/products/{product_id}/referenceImages/{image_id} 형식의 제품 참조 이미지의 고유 식별자입니다. 화면에 표시되도록 이 참조 이미지의 URL을 가져오려면 projects.locations.products.referenceImages.get에 다른 API 요청을 보내야 합니다.
  • product.labels: 제품의 사전 정의된 태그 목록입니다. 검색 결과를 필터링하여 드레스와 같은 하나의 의류 카테고리만 표시하려는 경우에 유용합니다.

질의 이미지를 base64로 변환

질의 이미지를 base64 문자열 표현으로 변환하고 요청 본문의 JSON 객체에 문자열을 첨부해야 합니다.

ProductSearchAPIClient 클래스로 이동하여 빈 convertBitmapToBase64 메서드를 찾아 다음 구현으로 바꿉니다.

private fun convertBitmapToBase64(bitmap: Bitmap): String {
    val byteArrayOutputStream = ByteArrayOutputStream()
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
    val byteArray: ByteArray = byteArrayOutputStream.toByteArray()
    return Base64.encodeToString(byteArray, Base64.DEFAULT)
}

API 호출 구현

그런 다음 제품 검색 API 요청을 작성하여 백엔드로 전송합니다. Volley를 사용하여 API 요청을 하고 Task API를 사용하여 결과를 반환합니다.

ProductSearchAPIClient 클래스로 돌아가서 빈 annotateImage 메서드를 찾아 다음 구현으로 바꿉니다.

fun annotateImage(image: Bitmap): Task<List<ProductSearchResult>> {
    // Initialization to use the Task API
    val apiSource = TaskCompletionSource<List<ProductSearchResult>>()
    val apiTask = apiSource.task

    // Convert the query image to its Base64 representation to call the Product Search API.
    val base64: String = convertBitmapToBase64(image)

    // Craft the request body JSON.
    val requestJson = """
        {
          "requests": [
            {
              "image": {
                "content": """".trimIndent() + base64 + """"
              },
              "features": [
                {
                  "type": "PRODUCT_SEARCH",
                  "maxResults": $VISION_API_PRODUCT_MAX_RESULT
                }
              ],
              "imageContext": {
                "productSearchParams": {
                  "productSet": "projects/${VISION_API_PROJECT_ID}/locations/${VISION_API_LOCATION_ID}/productSets/${VISION_API_PRODUCT_SET_ID}",
                  "productCategories": [
                       "apparel-v2"
                     ]
                }
              }
            }
          ]
        }
    """.trimIndent()

    // Add a new request to the queue
    requestQueue.add(object :
        JsonObjectRequest(
            Method.POST,
            "$VISION_API_URL/images:annotate?key=$VISION_API_KEY",
            JSONObject(requestJson),
            { response ->
                // Parse the API JSON response to a list of ProductSearchResult object/
                val productList = apiResponseToObject(response)

                // Return the list.
                apiSource.setResult(productList)
            },
            // Return the error
            { error -> apiSource.setException(error) }
        ) {
        override fun getBodyContentType() = "application/json"
    }.apply {
        setShouldCache(false)
    })

    return apiTask
}

UI에 검색 결과 표시

이제 ProductSearchAPIClient의 API 코드가 준비되었습니다. 활동 ProductSearchActivity로 돌아가 UI 코드를 구현합니다.

활동에 이미 searchByImage(queryImage: Bitmap) 메서드를 트리거하는 상용구 코드가 있습니다. 현재 비어 있는 이 메서드에 백엔드를 호출하고 UI에 결과를 표시하는 코드를 추가합니다.

apiClient.annotateImage(queryImage)
    .addOnSuccessListener { showSearchResult(it) }
    .addOnFailureListener { error ->
        Log.e(TAG, "Error calling Vision API Product Search.", error)
        showErrorResponse(error.localizedMessage)
    }

showSearchResult 메서드에는 API 응답을 파싱하고 화면에 표시하는 상용구 코드가 포함되어 있습니다.

실행하기

이제 Android 스튜디오 툴바에서 Run ( execute.png)을 클릭합니다. 앱이 로드되면 사전 설정된 이미지를 탭하고, 감지된 객체를 선택하고, 검색 버튼을 탭하여 백엔드에서 반환된 검색 결과를 확인합니다. 다음과 같은 화면을 볼 수 있습니다.

bb5e7c27c283a2fe.png

제품 검색 결과 화면의 스크린샷

백엔드에서 사전 설정된 제품 카탈로그의 시각적으로 유사한 제품 목록을 이미 반환하고 있습니다. 하지만 제품 이미지는 여전히 비어 있습니다. projects.locations.images.annotate 엔드포인트는 projects/odml-codelabs/locations/us-east1/products/product_id77/referenceImages/image77와 같은 제품 이미지 ID만 반환하기 때문입니다. projects.locations.products.referenceImages.get 엔드포인트에 다른 API 호출을 하고 이 참조 이미지의 URL을 가져와 화면에 표시해야 합니다.

8. 제품 참조 이미지 가져오기

API 요청 및 응답 형식 살펴보기

빈 요청 본문이 있는 GET HTTP 요청을 projects.locations.products.referenceImages.get 엔드포인트로 보내 제품 검색 엔드포인트에서 반환된 제품 이미지의 URI를 가져옵니다.

HTTP 요청은 다음과 같습니다.

GET $VISION_API_URL/projects/odml-codelabs/locations/us-east1/products/product_id77/referenceImages/image77?key=$VISION_API_KEY

요청이 성공하면 서버가 200 OK HTTP 상태 코드와 응답을 JSON 형식으로 반환합니다.

{
  "name":"projects/odml-codelabs/locations/us-east1/products/product_id77/referenceImages/image77",
  "uri":"gs://cloud-ai-vision-data/product-search-tutorial/images/46991e7370ba11e8a1bbd20059124800.jpg"
}
  • name: 참조 이미지 식별자
  • uri: Google Cloud Storage (GCS)에 있는 이미지의 URI입니다.

데모 제품 검색 백엔드의 참조 이미지는 public-read 권한을 갖도록 설정되었습니다. 따라서 GCS URI를 HTTP URL로 쉽게 변환하여 앱 UI에 표시할 수 있습니다. gs:// 접두사를 https://storage.googleapis.com/로 바꾸기만 하면 됩니다.

API 호출 구현

그런 다음 제품 검색 API 요청을 작성하여 백엔드로 전송합니다. 제품 검색 API 호출과 마찬가지로 Volley 및 Task API를 사용합니다.

ProductSearchAPIClient 클래스로 돌아가서 빈 fetchReferenceImage 메서드를 찾아 다음 구현으로 바꿉니다.

private fun fetchReferenceImage(searchResult: ProductSearchResult): Task<ProductSearchResult> {
    // Initialization to use the Task API
    val apiSource = TaskCompletionSource<ProductSearchResult>()
    val apiTask = apiSource.task

    // Craft the API request to get details about the reference image of the product
    val stringRequest = object : StringRequest(
        Method.GET,
        "$VISION_API_URL/${searchResult.imageId}?key=$VISION_API_KEY",
        { response ->
            val responseJson = JSONObject(response)
            val gcsUri = responseJson.getString("uri")

            // Convert the GCS URL to its HTTPS representation
            val httpUri = gcsUri.replace("gs://", "https://storage.googleapis.com/")

            // Save the HTTPS URL to the search result object
            searchResult.imageUri = httpUri

            // Invoke the listener to continue with processing the API response (eg. show on UI)
            apiSource.setResult(searchResult)
        },
        { error -> apiSource.setException(error) }
    ) {

        override fun getBodyContentType(): String {
            return "application/json; charset=utf-8"
        }
    }
    Log.d(ProductSearchActivity.TAG, "Sending API request.")

    // Add the request to the RequestQueue.
    requestQueue.add(stringRequest)

    return apiTask
}

이 메서드는 제품 검색 엔드포인트에서 반환된 searchResult: ProductSearchResult 객체를 사용하고 다음 단계를 따릅니다.

  1. 참조 이미지 엔드포인트를 호출하여 참조 이미지의 GCS URI를 가져옵니다.
  2. GCS URI를 HTTP URL로 변환합니다.
  3. 이 HTTP URL로 searchResult 객체의 httpUri 속성을 업데이트합니다.

두 API 요청 연결

annotateImage로 돌아가 호출자에게 ProductSearchResult 목록을 반환하기 전에 모든 참조 이미지의 HTTP URL을 가져오도록 수정합니다.

다음 줄을 찾습니다.

// Return the list.
apiSource.setResult(productList)

그런 다음 다음 구현으로 바꿉니다.

// Loop through the product list and create tasks to load reference images.
// We will call the projects.locations.products.referenceImages.get endpoint
// for each product.
val fetchReferenceImageTasks = productList.map { fetchReferenceImage(it) }

// When all reference image fetches have completed,
// return the ProductSearchResult list
Tasks.whenAllComplete(fetchReferenceImageTasks)
    // Return the list of ProductSearchResult with product images' HTTP URLs.
    .addOnSuccessListener { apiSource.setResult(productList) }
    // An error occurred so returns it to the caller.
    .addOnFailureListener { apiSource.setException(it) }

화면에 참조 이미지를 표시하는 상용구 코드는 ProductSearchAdapter 클래스에 이미 구현되어 있으므로 앱을 다시 실행하면 됩니다.

실행하기

이제 Android 스튜디오 툴바에서 Run ( execute.png)을 클릭합니다. 앱이 로드되면 사전 설정된 이미지를 탭하고, 감지된 객체를 선택하고, 검색 버튼을 탭하여 검색 결과를 확인합니다. 이번에는 제품 이미지가 표시됩니다.

제품 검색 결과가 이해되시나요?

25939f5a13eeb3c3.png

9. 축하합니다.

Android 앱에 제품 이미지 검색 기능을 추가하기 위해 Vision API 제품 검색 백엔드를 호출하는 방법을 배웠습니다. 이제 앱을 실행할 수 있습니다.

진행하면서 제품 카탈로그를 사용하여 자체 백엔드를 빌드할 수도 있습니다. 제품 이미지 검색 학습 과정의 다음 Codelab에서 자체 백엔드를 빌드하고 모바일 앱에서 호출할 API 키를 설정하는 방법을 알아보세요.

학습한 내용

  • Android 앱에서 Vision API 제품 검색 백엔드를 호출하는 방법

다음 단계

자세히 알아보기