Llama al backend de Product Search de la API de Vision en Android

1. Antes de comenzar

bd8c01b2f8013c6d.png

¿Viste la demostración de Google Lens en la que puedes apuntar la cámara del teléfono a un objeto y encontrar dónde comprarlo en línea? Si quieres aprender a agregar la misma función a tu app, este codelab es para ti. Forma parte de una ruta de aprendizaje que te enseña a compilar una función de búsqueda de imágenes de productos en una app para dispositivos móviles.

En este codelab, aprenderás a llamar a un backend compilado con Product Search de la API de Vision desde una app para dispositivos móviles. Este backend puede tomar una imagen de consulta y buscar productos visualmente similares en un catálogo de productos.

Puedes obtener información sobre los pasos restantes para compilar una función de búsqueda visual de productos, incluido cómo usar la detección y el seguimiento de objetos de ML Kit para detectar objetos en la imagen de búsqueda y permitir que los usuarios elijan qué producto quieren buscar, en la ruta de aprendizaje.

Qué compilarás

  • En este codelab, comenzarás con la app para Android que puede detectar objetos a partir de una imagen de entrada. Escribirás código para tomar el objeto que elige el usuario, enviarlo al backend de búsqueda de productos y mostrar el resultado de la búsqueda en la pantalla.
  • Al final, deberías ver algo similar a la imagen de la derecha.

Qué aprenderás

  • Cómo llamar a las APIs de Product Search de la API de Vision y analizar su respuesta desde una app para Android

Requisitos

  • Una versión reciente de Android Studio (v4.1.2 o posterior)
  • Emulador de Android Studio o un dispositivo Android físico
  • El código de muestra
  • Conocimientos básicos sobre el desarrollo de Android en Kotlin

Este codelab se enfoca en Product Search de la API de Vision. Los conceptos y los bloques de código no relevantes no se exploran, pero se proporcionan para que simplemente los copies y pegues.

2. Acerca de Product Search de la API de Vision

Product Search de la API de Vision es una función de Google Cloud que permite a los usuarios buscar productos visualmente similares en un catálogo de productos. Los minoristas pueden crear productos, cada uno con imágenes de referencia que describen de manera visual el producto desde un conjunto de puntos de vista. Luego, puedes agregar estos productos a los conjuntos de productos (es decir, al catálogo de productos). En este momento, Product Search de la API de Vision admite las siguientes categorías de productos: artículos para el hogar, indumentaria, juguetes, productos envasados y general.

Cuando los usuarios consultan el conjunto de productos con sus propias imágenes, Product Search de la API de Vision aplica el aprendizaje automático para comparar el producto en la imagen de consulta del usuario con las imágenes del conjunto de productos del minorista y, luego, devuelve una lista de clasificaciones con resultados visuales y semánticos similares.

3. Descarga y ejecuta la app de partida

Descarga el código

Haz clic en el siguiente vínculo a fin de descargar todo el código de este codelab:

Descomprime el archivo zip descargado. Esto descomprimirá una carpeta raíz (odml-pathways-main) con todos los recursos que necesitarás. Para este codelab, solo necesitarás las fuentes del subdirectorio product-search/codelab2/android.

El subdirectorio codelab2 del repositorio odml-pathways contiene dos directorios:

  • android_studio_folder.pngstarter: Código inicial que usarás como base para compilar en este codelab
  • android_studio_folder.pngfinal: Código completado para la app de ejemplo finalizada

La app de inicio que se muestra aquí es la que compilaste en el codelab Detect objects in images to build a visual product search: Android. Utiliza ML Kit Object Detection and Tracking para detectar objetos en una imagen y mostrarlos en la pantalla.

Importa la app a Android Studio

Comienza por importar la app starter en Android Studio.

Ve a Android Studio, selecciona Import Project (Gradle, Eclipse ADT, etc.) y elige la carpeta starter del código fuente que descargaste antes.

7c0f27882a2698ac.png

Cómo ejecutar la app de inicio

Ahora que importaste el proyecto a Android Studio, puedes ejecutar la app por primera vez. Conecta tu dispositivo Android a través de USB a tu host o inicia el emulador de Android Studio y haz clic en Run ( execute.png) en la barra de herramientas de Android Studio.

(Si este botón está inhabilitado, asegúrate de importar solo starter/app/build.gradle, no todo el repositorio)

Ahora, la app debería haberse iniciado en tu dispositivo Android. Ya tiene la capacidad de detectar objetos: detecta artículos de moda en la imagen y te muestra dónde están. Prueba con las fotos predeterminadas para confirmar.

c6102a808fdfcb11.png

Captura de pantalla de la app de partida que puede detectar objetos en una imagen

A continuación, extenderás la app para enviar los objetos detectados al backend de Product Search de la API de Vision y mostrar los resultados de la búsqueda en la pantalla.

4. Cómo controlar la selección de objetos

Permite que los usuarios presionen un objeto detectado para seleccionarlo

Ahora agregarás código para permitir que los usuarios seleccionen un objeto de la imagen y comiencen la búsqueda de productos. La app de inicio ya tiene la capacidad de detectar objetos en la imagen. Es posible que haya varios objetos en la imagen o que el objeto detectado solo ocupe una pequeña parte de la imagen. Por lo tanto, el usuario debe presionar uno de los objetos detectados para indicar cuál quiere usar para la búsqueda de productos.

9cdfcead6d95a87.png

Una captura de pantalla de los artículos de moda detectados en la imagen

Para que el codelab sea simple y se centre en el aprendizaje automático, se implementó código Android estándar en la app inicial para ayudarte a detectar en qué objeto presionó el usuario. La vista que muestra la imagen en la actividad principal (ObjectDetectorActivity) es, en realidad, una vista personalizada (ImageClickableView) que extiende el ImageView predeterminado del SO Android. Implementa algunos métodos de utilidad convenientes, incluidos los siguientes:

  • fun setOnObjectClickListener(listener: ((objectImage: Bitmap) -> Unit)) Esta es una devolución de llamada para recibir la imagen recortada que contiene solo el objeto en el que el usuario presionó. Enviarás esta imagen recortada al backend de búsqueda de productos.

Agrega código para controlar el toque de un usuario en los objetos detectados.

Ve al método initViews en la clase ObjectDetectorActivity y agrega estas líneas al final del método: (Android Studio te indicará que no puede encontrar el método startProductImageSearch . No te preocupes, lo implementarás un poco más adelante.

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

Se llama a onObjectClickListener cada vez que el usuario presiona cualquiera de los objetos detectados en la pantalla. Recibe la imagen recortada que contiene solo el objeto seleccionado. Por ejemplo, si el usuario presiona la persona que usa el vestido a la derecha, se activará el objeto de escucha con objectImage, como se muestra a continuación.

9cac8458d0f326e6.png

Un ejemplo de la imagen recortada que se pasa a onObjectClickListener

Envía la imagen recortada a la actividad de búsqueda de productos

Ahora implementarás la lógica para enviar la imagen de consulta al backend de Product Search de la API de Vision en una actividad separada (ProductSearchActivity).

Todos los componentes de la IU se implementaron con anticipación para que puedas concentrarte en escribir el código para comunicarte con el backend de la búsqueda de productos.

25939f5a13eeb3c3.png

Captura de pantalla de los componentes de la IU en ProductSearchActivity

Agrega código para enviar la imagen del objeto que seleccionó el usuario a ProductSearchActivity.

Vuelve a Android Studio y agrega este método startProductImageSearch a la clase 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)
    }
}

El fragmento de código hace 3 cosas:

  • Toma la imagen recortada y la serializa en un archivo PNG.
  • Inicia el ProductSearchActivity para ejecutar la secuencia de búsqueda de productos.
  • Incluye el URI de la imagen recortada en el intent de inicio de actividad para que ProductSearchActivity pueda recuperarlo más tarde y usarlo como imagen de búsqueda.

Hay algunos aspectos que debes tener en cuenta:

  • La lógica para detectar objetos y consultar el backend se dividió en 2 actividades solo para que el codelab sea más fácil de comprender. Tú decides cómo implementarlos en tu app.
  • Debes escribir la imagen de la búsqueda en un archivo y pasar el URI de la imagen entre actividades, ya que la imagen de la búsqueda puede superar el límite de tamaño de 1 MB de una intent de Android.
  • Puedes almacenar la imagen de la búsqueda en formato PNG porque es un formato sin pérdida.

Recupera la imagen de la búsqueda en la actividad de búsqueda de productos

En ProductSearchActivity, el código para recuperar la imagen de la búsqueda y mostrarla en la pantalla ya se implementó en la app de inicio.

Ve al método onCreate y confirma que este código ya esté allí:

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

Ejecuta la app

Ahora haz clic en Run ( execute.png) en la barra de herramientas de Android Studio.

Una vez que se cargue la app, presiona cualquier imagen predeterminada y selecciona uno de los objetos detectados.

Confirma que ProductSearchActivity aparezca con la imagen en la que presionaste. El botón Search aún no hace nada, pero lo implementaremos a continuación.

fed40f81b8b43801.png

Deberías ver una pantalla similar después de presionar uno de los objetos detectados.

5. Explora el backend de la búsqueda de productos

Compila el backend de búsqueda de imágenes de productos

Este codelab requiere un backend de búsqueda de productos compilado con Product Search de la API de Vision. Existen dos opciones para hacerlo:

Opción 1: Usa el backend de demostración que se implementó para ti

Puedes continuar con este codelab usando el backend de búsqueda de productos que Google ya implementó para ti. El backend de demostración se puede replicar siguiendo la guía de inicio rápido de Product Search de la API de Vision.

Opción 2: Crea tu propio backend siguiendo la guía de inicio rápido de Product Search de la API de Vision

Esta opción se recomienda para quienes desean aprender en profundidad cómo crear un backend de búsqueda de productos para que puedan crear uno para su propio catálogo de productos más adelante. Debe tener lo siguiente:

  • Una cuenta de Google Cloud con la facturación habilitada (Puede ser una cuenta de prueba gratuita).
  • Conocimientos básicos sobre los conceptos de Google Cloud, incluidos los proyectos, las cuentas de servicio, etcétera

Puedes obtener información para hacerlo más adelante en la ruta de aprendizaje.

Aprende los conceptos importantes

Te encontrarás con estos conceptos cuando interactúes con el backend de la búsqueda de productos:

  • Conjunto de productos: Un conjunto de productos es un contenedor simple para un grupo de productos. Un catálogo de productos se puede representar como un conjunto de productos y sus productos.
  • Producto: Después de crear un conjunto de productos, puedes crear productos y agregarlos al conjunto.
  • Imágenes de referencia del producto: Son imágenes que contienen varias vistas de tus productos. Las imágenes de referencia se usan para buscar productos visualmente similares.
  • Busca productos: Una vez que hayas creado tu conjunto de productos y que este se haya indexado, puedes consultarlo con la API de Cloud Vision.

Información sobre el catálogo de productos predeterminado

El backend de demostración de búsqueda de productos que se usó en este codelab se creó con Product Search de la API de Vision y un catálogo de productos de aproximadamente cien imágenes de zapatos y vestidos. Estas son algunas imágenes del catálogo:

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

Ejemplos del catálogo de productos predeterminado

Llama al backend de la demostración de búsqueda de productos

Puedes llamar a la búsqueda de productos de la API de Vision directamente desde una app para dispositivos móviles configurando una clave de API de Google Cloud y restringiendo el acceso a la clave de API solo a tu app.

Para que este codelab sea sencillo, se configuró un extremo de proxy que te permite acceder al backend de demostración sin preocuparte por la clave de API ni la autenticación. Recibe la solicitud HTTP de la app para dispositivos móviles, agrega la clave de API y reenvía la solicitud al backend de Product Search de la API de Vision. Luego, el proxy recibe la respuesta del backend y se la devuelve a la app para dispositivos móviles.

En este codelab, usarás dos APIs de Product Search de la API de Vision:

6. Implementa el cliente de la API

Comprende el flujo de trabajo de la búsqueda de productos

Sigue este flujo de trabajo para realizar búsquedas de productos con el backend:

Implementa la clase del cliente de la API

Ahora implementarás código para llamar al backend de búsqueda de productos en una clase dedicada llamada ProductSearchAPIClient. Se implementó código de ejemplo en la app de inicio:

  • class ProductSearchAPIClient: Esta clase está casi vacía ahora, pero tiene algunos métodos que implementarás más adelante en este codelab.
  • fun convertBitmapToBase64(bitmap: Bitmap): Convierte una instancia de Bitmap en su representación en base64 para enviarla al backend de la búsqueda de productos.
  • fun annotateImage(image: Bitmap): Task<List<ProductSearchResult>>: Llama a la API de projects.locations.images.annotate y analiza la respuesta.
  • fun fetchReferenceImage(searchResult: ProductSearchResult): Task<ProductSearchResult>: Llama a la API de projects.locations.products.referenceImages.get y analiza la respuesta.
  • SearchResult.kt: Este archivo contiene varias clases de datos para representar los tipos que devuelve el backend de Product Search de la API de Vision.

Especifica la configuración de la API

Ve a la clase ProductSearchAPIClient y verás que ya se definieron algunos parámetros de configuración del backend de la búsqueda de productos:

// 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 es el extremo de la API de Cloud Vision. A medida que avanzas con el backend de demostración, configura esto en el extremo del proxy. Sin embargo, si implementas tu propio backend, deberás cambiarlo al endpoint de la API de Cloud Vision. https://vision.googleapis.com/v1.
  • VISION_API_KEY es la clave de API de tu proyecto de Cloud. Como el proxy ya controla la autenticación, puedes dejar este campo en blanco.
  • VISION_API_PROJECT_ID es el ID del proyecto de Cloud. odml-codelabs es el proyecto de Cloud en el que se implementa el backend de demostración.
  • VISION_API_LOCATION_ID es la ubicación de Cloud en la que se implementa el backend de Product Search. us-east1 es donde implementamos el backend de demostración.
  • VISION_API_PRODUCT_SET_ID es el ID del catálogo de productos (también conocido como "conjunto de productos" en la terminología de la API de Vision) en el que deseas buscar productos visualmente similares. Puedes tener varios catálogos en un proyecto de Cloud. product_set0 es el catálogo de productos predeterminado del backend de demostración.

7. Llama a la API de Búsqueda de productos

Explora el formato de solicitud y respuesta de la API

Puedes encontrar productos similares a una imagen determinada si pasas el URI de Google Cloud Storage, la URL web o la cadena codificada en base64 de la imagen a Product Search de la API de Vision. En este codelab, usarás la opción de cadena codificada en base64, ya que la imagen de la búsqueda solo existe en el dispositivo del usuario.

Debes enviar una solicitud POST al extremo projects.locations.images.annotate con este cuerpo JSON de la solicitud:

{
  "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"
          ],
        }
      }
    }
  ]
}

Hay algunos parámetros que se deben especificar:

  • base64-encoded-image: Es la representación en base64 (cadena ASCII) de los datos binarios de la imagen de búsqueda.
  • project-id: Es el ID de tu proyecto de GCP.
  • location-id: Es un identificador de ubicación válido.
  • product-set-id: Es el ID del conjunto de productos en el que deseas ejecutar la operación.

Como tu catálogo de productos solo contiene imágenes de zapatos y vestidos, especifica que productCategories sea apparel-v2. Aquí, v2 significa que usamos la versión 2 del modelo de aprendizaje automático de búsqueda de productos de indumentaria.

Si la solicitud se completa de forma correcta, el servidor muestra un código de estado HTTP 200 OK y la respuesta en formato JSON. El JSON de respuesta incluye los siguientes dos tipos de resultados:

  • productSearchResults: Contiene una lista de productos coincidentes para toda la imagen.
  • productGroupedResults: Contiene coordenadas de cuadro de límite y elementos coincidentes para cada producto identificado en la imagen.

Como el producto ya se recortó de la imagen original, analizarás los resultados en la lista productSearchResults.

Estos son algunos campos importantes del objeto de resultado de la búsqueda de productos:

  • product.name: Es el identificador único de un producto en el formato projects/{project-id}/locations/{location-id}/products/{product_id}.
  • product.score: Es un valor que indica qué tan similar es el resultado de la búsqueda a la imagen de la búsqueda. Los valores más altos significan más similitud.
  • product.image: Es el identificador único de la imagen de referencia de un producto en el formato projects/{project-id}/locations/{location-id}/products/{product_id}/referenceImages/{image_id}. Deberás enviar otra solicitud a la API de projects.locations.products.referenceImages.get para obtener la URL de esta imagen de referencia y que se muestre en la pantalla.
  • product.labels: Es una lista de etiquetas predefinidas del producto. Esto es útil si deseas filtrar los resultados de la búsqueda para mostrar solo una categoría de ropa, como vestidos.

Convierte la imagen de la búsqueda a base64

Debes convertir la imagen de búsqueda en su representación de cadena base64 y adjuntar la cadena al objeto JSON en el cuerpo de la solicitud.

Ve a la clase ProductSearchAPIClient, busca el método convertBitmapToBase64 vacío y reemplázalo por esta implementación:

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)
}

Implementa la llamada a la API

A continuación, crea una solicitud a la API de Product Search y envíala al backend. Usarás Volley para realizar la solicitud a la API y devolver el resultado con la API de Task.

Regresa a la clase ProductSearchAPIClient, busca el método annotateImage vacío y reemplázalo por esta implementación:

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
}

Mostrar el resultado de la búsqueda en la IU

Ahora, el código de la API en ProductSearchAPIClient está listo. Vuelve a la actividad ProductSearchActivity para implementar el código de la IU.

La actividad ya tiene código estándar que activa el método searchByImage(queryImage: Bitmap). Agrega código para llamar al backend y mostrar los resultados en la IU en este método actualmente vacío.

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

El método showSearchResult contiene código estándar que analiza la respuesta de la API y la muestra en la pantalla.

Ejecución

Ahora haz clic en Run ( execute.png) en la barra de herramientas de Android Studio. Una vez que se cargue la app, presiona cualquier imagen predeterminada, selecciona un objeto detectado, presiona el botón Buscar y consulta los resultados de la búsqueda que se devolvieron desde el backend. Verá un resultado similar al que se detalla a continuación:

bb5e7c27c283a2fe.png

Captura de pantalla de la pantalla de resultados de la búsqueda de productos

El backend ya devuelve una lista de productos visualmente similares del catálogo de productos predeterminado. Sin embargo, puedes ver que la imagen del producto sigue vacía. Esto se debe a que el extremo projects.locations.images.annotate solo devuelve IDs de imágenes de productos, como projects/odml-codelabs/locations/us-east1/products/product_id77/referenceImages/image77. Deberás realizar otra llamada a la API al extremo projects.locations.products.referenceImages.get y obtener la URL de esta imagen de referencia para mostrarla en la pantalla.

8. Obtén las imágenes de referencia del producto

Explora el formato de solicitud y respuesta de la API

Enviarás una solicitud GET HTTP con un cuerpo de solicitud vacío al extremo projects.locations.products.referenceImages.get para obtener los URIs de las imágenes de productos que devuelve el extremo de búsqueda de productos.

La solicitud HTTP se ve de la siguiente manera:

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

Si la solicitud es exitosa, el servidor muestra un código de estado HTTP 200 OK y la respuesta en formato JSON, como se muestra a continuación:

{
  "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: Es el identificador de la imagen de referencia.
  • uri: Es el URI de la imagen en Google Cloud Storage (GCS).

Las imágenes de referencia del backend de búsqueda de productos de demostración se configuraron para tener permiso de lectura pública. Por lo tanto, puedes convertir fácilmente el URI de GCS en una URL HTTP y mostrarlo en la IU de la app. Solo debes reemplazar el prefijo gs:// por https://storage.googleapis.com/.

Implementa la llamada a la API

A continuación, crea una solicitud a la API de Product Search y envíala al backend. Usarás Volley y la API de Tasks de manera similar a la llamada a la API de Product Search.

Regresa a la clase ProductSearchAPIClient, busca el método fetchReferenceImage vacío y reemplázalo por esta implementación:

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
}

Este método toma un objeto searchResult: ProductSearchResult que devolvió el extremo de búsqueda de productos y, luego, sigue estos pasos:

  1. Llama al extremo de la imagen de referencia para obtener el URI de GCS de la imagen de referencia.
  2. Convierte el URI de GCS en una URL HTTP.
  3. Actualiza la propiedad httpUri del objeto searchResult con esta URL HTTP.

Conecta las dos solicitudes a la API

Vuelve a annotateImage y modifícalo para obtener todas las URLs HTTP de las imágenes de referencia antes de devolver la lista ProductSearchResult a su llamador.

Busca esta línea:

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

Luego, reemplázala por esta implementación:

// 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) }

El código estándar para mostrar las imágenes de referencia en la pantalla ya está implementado en la clase ProductSearchAdapter, por lo que puedes volver a ejecutar la app.

Ejecución

Ahora haz clic en Run ( execute.png) en la barra de herramientas de Android Studio. Una vez que se cargue la app, presiona cualquier imagen predeterminada, selecciona un objeto detectado y presiona el botón Buscar para ver los resultados de la búsqueda, esta vez con las imágenes de los productos.

¿Los resultados de la búsqueda de productos tienen sentido para ti?

25939f5a13eeb3c3.png

9. ¡Felicitaciones!

Aprendiste a llamar a un backend de Product Search de la API de Vision para agregar la capacidad de búsqueda de imágenes de productos a tu app para Android. Eso es todo lo que necesitas para que funcione.

A medida que avances, es posible que desees compilar tu propio backend con tu catálogo de productos. Consulta el próximo codelab en la ruta de aprendizaje de Búsqueda de imágenes de productos para aprender a compilar tu propio backend y configurar la clave de API para llamarlo desde una app para dispositivos móviles.

Temas abordados

  • Cómo llamar al backend de Product Search de la API de Vision desde una app para Android

Próximos pasos

Más información