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.png최종: 완료된 샘플 앱의 완성된 코드입니다.

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

Android 스튜디오에 앱 가져오기

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

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

7c0f27882a2698ac.png

시작 앱 실행

프로젝트를 Android 스튜디오로 가져왔으므로 이제 앱을 처음으로 실행할 수 있습니다. USB를 통해 Android 기기를 호스트에 연결하거나 Android 스튜디오 에뮬레이터 시작을 클릭하고 Android 스튜디오 툴바에서 Run ( 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)) 이 콜백은 사용자가 탭한 객체만 포함하여 잘린 이미지를 수신하는 콜백입니다. 이렇게 잘린 이미지가 제품 검색 백엔드로 전송됩니다.

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

다음으로 이동: initViews 방법을ObjectDetectorActivity 클래스의 끝에 다음 행을 추가합니다.Android 스튜디오가 startProductImageSearch 메서드를 호출합니다. 걱정하지 마세요. 나중에 구현하겠습니다.)

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

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

99c8458d0f326e6.png

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

잘린 이미지를 제품 검색 활동으로 보내기

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

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

25939f5a13eeb3c3.png

ProductSearchActivity의 UI 구성요소 스크린샷

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

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

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에 표시되는지 확인합니다. Search 버튼은 아직 아무 작업도 하지 않지만 다음 단계에서 구현합니다.

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): 비트맵 인스턴스를 base64로 변환하여 제품 검색 백엔드로 보냅니다.
  • fun annotateImage(image: Bitmap): Task<List<ProductSearchResult>>: projects.locations.images.annotation 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는 클라우드 프로젝트 ID입니다. odml-codelabs는 데모 백엔드가 배포되는 Cloud 프로젝트입니다.
  • VISION_API_LOCATION_ID는 제품 검색 백엔드가 배포되는 클라우드 위치입니다. us-east1는 데모 백엔드를 배포했습니다.
  • VISION_API_PRODUCT_SET_ID는 시각적으로 유사한 제품을 검색하려는 제품 카탈로그 ID (Vision API 용어에서 '제품 세트'라고 함)의 ID입니다. Cloud 프로젝트 하나에 여러 개의 카탈로그를 포함할 수 있습니다. product_set0는 데모 백엔드의 미리 설정된 제품 카탈로그입니다.

7 제품 검색 API 호출

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

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

이 요청 JSON 본문과 함께 projects.locations.images.annotation 엔드포인트에 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입니다.

제품 카탈로그에는 신발과 드레스 이미지만 포함되므로 productCategoryapparel-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.annotation 엔드포인트가 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입니다.

데모 제품 검색 백엔드의 참조 이미지가 공개 읽기 권한을 갖도록 설정되었습니다. 따라서 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. searchResult 객체의 httpUri 속성을 이 HTTP URL로 업데이트합니다.

두 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. 축하합니다.

Vision API 제품 검색 백엔드를 호출하여 Android 앱에 제품 이미지 검색 기능을 추가하는 방법을 알아보았습니다. 이것으로 모든 작업을 시작할 수 있습니다.

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

학습한 내용

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

다음 단계

자세히 알아보기