Offline asynchronous batch image annotation

The Vision API can run offline detection services and annotation of a batch of image files using any Vision feature type. For example, you can specify one or multiple Vision API features (such as TEXT_DETECTION, LABEL_DETECTION, and OBJECT_LOCALIZATION) for a single batch of images.

Output from an offline batch request is written to a JSON file created in the specified Google Cloud Storage bucket.

Limitations

The Vision API accepts up to 2,000 image files. A larger batch of image files will return an error.

Currently supported feature types

All feature types
TYPE_UNSPECIFIED Unspecified feature type.
FACE_DETECTION Run face detection.
LANDMARK_DETECTION Run landmark detection.
LOGO_DETECTION Run logo detection.
LABEL_DETECTION Run label detection.
TEXT_DETECTION Run text detection / optical character recognition (OCR). Text detection is optimized for areas of text within a larger image; if the image is a document, use DOCUMENT_TEXT_DETECTION instead.
DOCUMENT_TEXT_DETECTION Run dense text document OCR. Takes precedence when both DOCUMENT_TEXT_DETECTION and TEXT_DETECTION are present.
SAFE_SEARCH_DETECTION Run Safe Search to detect potentially unsafe or undesirable content.
IMAGE_PROPERTIES Compute a set of image properties, such as the image's dominant colors.
CROP_HINTS Run crop hints.
WEB_DETECTION Run web detection.
OBJECT_LOCALIZATION Run localizer for object detection.

Sample code

Use the following code samples to run offline annotation services on a batch of image files in Google Cloud Storage.

Java

Before trying this sample, follow the Java setup instructions in the Vision Quickstart Using Client Libraries . For more information, see the Vision Java API reference documentation .

import com.google.api.core.ApiFuture;
import com.google.api.gax.paging.Page;
import com.google.cloud.storage.Blob;
import com.google.cloud.storage.Bucket;
import com.google.cloud.storage.Storage;
import com.google.cloud.storage.Storage.BlobListOption;
import com.google.cloud.storage.StorageOptions;
import com.google.cloud.vision.v1p4beta1.AnnotateImageRequest;
import com.google.cloud.vision.v1p4beta1.AsyncBatchAnnotateImagesRequest;
import com.google.cloud.vision.v1p4beta1.BatchAnnotateImagesResponse.Builder;
import com.google.cloud.vision.v1p4beta1.BatchAnnotateImagesResponse;
import com.google.cloud.vision.v1p4beta1.Feature;
import com.google.cloud.vision.v1p4beta1.Feature.Type;
import com.google.cloud.vision.v1p4beta1.GcsDestination;
import com.google.cloud.vision.v1p4beta1.Image;
import com.google.cloud.vision.v1p4beta1.ImageAnnotatorClient;
import com.google.cloud.vision.v1p4beta1.ImageSource;
import com.google.cloud.vision.v1p4beta1.OutputConfig;

import com.google.longrunning.Operation;
import com.google.protobuf.util.JsonFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AsyncBatchAnnotateImagesGcs {

  // Performs asynchronous batch annotation of images on Google Cloud Storage
  public static void asyncBatchAnnotateImagesGcs(String gcsSourcePath, String gcsDestinationPath)
      throws Exception {
    // String gcsSourcePath = "gs://YOUR_BUCKET_ID/path_to_your_data";
    // String gcsDestinationPath = "gs://YOUR_BUCKET_ID/path_to_store_annotation";

    try (ImageAnnotatorClient client = ImageAnnotatorClient.create()) {
      List<AnnotateImageRequest> requests = new ArrayList<>();

      ImageSource imgSource = ImageSource.newBuilder().setImageUri(gcsSourcePath).build();

      Image image = Image.newBuilder().setSource(imgSource).build();

      // Set the GCS destination path for where to save the results.
      GcsDestination gcsDestination =
          GcsDestination.newBuilder().setUri(gcsDestinationPath).build();

      // Create the configuration for the output with the batch size.
      // The batch size sets how many pages should be grouped into each json output file.
      OutputConfig outputConfig =
          OutputConfig.newBuilder().setGcsDestination(gcsDestination).setBatchSize(2).build();

      // Select the Features required by the vision API
      Feature features =
          Feature.newBuilder()
              .setType(Type.LABEL_DETECTION)
              .setType(Type.TEXT_DETECTION)
              .setType(Type.IMAGE_PROPERTIES)
              .build();

      // Build the request
      AnnotateImageRequest annotateImageRequest =
          AnnotateImageRequest.newBuilder().setImage(image).addFeatures(features).build();

      requests.add(annotateImageRequest);
      AsyncBatchAnnotateImagesRequest request =
          AsyncBatchAnnotateImagesRequest.newBuilder()
              .addAllRequests(requests)
              .setOutputConfig(outputConfig)
              .build();

      ApiFuture<Operation> future = client.asyncBatchAnnotateImagesCallable().futureCall(request);
      // Wait for the request to finish. (The result is not used, since the API saves the result to
      // the specified location on GCS.)
      Operation response = future.get(180, TimeUnit.SECONDS);

      System.out.println("Waiting for the operation to finish.");

      // Once the request has completed and the output has been
      // written to GCS, we can list all the output files.
      Storage storage = StorageOptions.getDefaultInstance().getService();

      // Get the destination location from the gcsDestinationPath
      Pattern pattern = Pattern.compile("gs://([^/]+)/(.+)");
      Matcher matcher = pattern.matcher(gcsDestinationPath);

      if (matcher.find()) {
        String bucketName = matcher.group(1);
        String prefix = matcher.group(2);

        // Get the list of objects with the given prefix from the GCS bucket
        Bucket bucket = storage.get(bucketName);
        Page<Blob> pageList = bucket.list(BlobListOption.prefix(prefix));

        Blob firstOutputFile = null;

        // List objects with the given prefix.
        System.out.println("Output files:");
        for (Blob blob : pageList.iterateAll()) {
          System.out.println(blob.getName());

          // Process the first output file from GCS.
          // Since we specified batch size = 2, the first response contains
          // the first two image requests
          if (firstOutputFile == null) {
            firstOutputFile = blob;
          }
        }

        // Get the contents of the file and convert the JSON contents to an
        // BatchAnnotateImagesResponse
        // object. If the Blob is small read all its content in one request
        // (Note: the file is a .json file)
        // Storage guide: https://cloud.google.com/storage/docs/downloading-objects
        String jsonContents = new String(firstOutputFile.getContent());
        Builder builder = BatchAnnotateImagesResponse.newBuilder();
        JsonFormat.parser().merge(jsonContents, builder);

        // Build the AnnotateFileResponse object
        BatchAnnotateImagesResponse batchAnnotateImagesResponse = builder.build();

        // Here we print the response for the first image
        // The response contains more information:
        // annotation/pages/blocks/paragraphs/words/symbols/colors
        // including confidence score and bounding boxes
        System.out.format("\nResponse: %s\n", batchAnnotateImagesResponse.getResponses(0));

      } else {
        System.out.println("No MATCH");
      }
    } catch (Exception e) {
      System.out.println("Error during asyncBatchAnnotateImagesGcs: \n" + e.toString());
    }
  }
}

Node.js

Before trying this sample, follow the Node.js setup instructions in the Vision Quickstart Using Client Libraries . For more information, see the Vision Node.js API reference documentation .

// Imports the Google Cloud client libraries
const {ImageAnnotatorClient} = require('@google-cloud/vision').v1p4beta1;

// Creates a client
const client = new ImageAnnotatorClient();

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// GCS path where the image resides
// const inputImageUri = 'gs://my-bucket/my_image.jpg';
// GCS path where to store the output json
// const outputUri = 'gs://mybucket/out/'

const features = [
  {type: 'DOCUMENT_LABEL_DETECTION'},
  {type: 'DOCUMENT_TEXT_DETECTION'},
  {type: 'DOCUMENT_IMAGE_DETECTION'},
];

const outputConfig = {
  gcsDestination: {
    uri: outputUri,
  },
};

const request = {
  requests: [
    {
      image: {
        source: {
          imageUri: inputImageUri,
        },
      },
      features: features,
    },
  ],
  outputConfig,
};

const [operation] = await client.asyncBatchAnnotateImages(request);
const [filesResponse] = await operation.promise();

const destinationUri = filesResponse.outputConfig.gcsDestination.uri;
console.log(`Json saved to: ${destinationUri}`);

Python

Before trying this sample, follow the Python setup instructions in the Vision Quickstart Using Client Libraries . For more information, see the Vision Python API reference documentation .

def async_batch_annotate_images_uri(input_image_uri, output_uri):
    """Batch annotation of images on Google Cloud Storage asynchronously.

    Args:
    input_image_uri: The path to the image in Google Cloud Storage (gs://...)
    output_uri: The path to the output path in Google Cloud Storage (gs://...)
    """
    import re

    from google.cloud import storage
    from google.protobuf import json_format
    from google.cloud import vision_v1p4beta1 as vision
    client = vision.ImageAnnotatorClient()

    # Construct the request for the image(s) to be annotated:
    image_source = vision.types.ImageSource(image_uri=input_image_uri)
    image = vision.types.Image(source=image_source)
    features = [
        vision.types.Feature(type=vision.enums.Feature.Type.LABEL_DETECTION),
        vision.types.Feature(type=vision.enums.Feature.Type.TEXT_DETECTION),
        vision.types.Feature(type=vision.enums.Feature.Type.IMAGE_PROPERTIES),
    ]
    requests = [
        vision.types.AnnotateImageRequest(image=image, features=features),
    ]

    gcs_destination = vision.types.GcsDestination(uri=output_uri)
    output_config = vision.types.OutputConfig(
        gcs_destination=gcs_destination, batch_size=2)

    operation = client.async_batch_annotate_images(
        requests=requests, output_config=output_config)

    print('Waiting for the operation to finish.')
    operation.result(timeout=10000)

    # Once the request has completed and the output has been
    # written to Google Cloud Storage, we can list all the output files.
    storage_client = storage.Client()

    match = re.match(r'gs://([^/]+)/(.+)', output_uri)
    bucket_name = match.group(1)
    prefix = match.group(2)

    bucket = storage_client.get_bucket(bucket_name=bucket_name)

    # Lists objects with the given prefix.
    blob_list = list(bucket.list_blobs(prefix=prefix))
    print('Output files:')
    for blob in blob_list:
        print(blob.name)

    # Processes the first output file from Google Cloud Storage.
    # Since we specified batch_size=2, the first response contains
    # annotations for the first two annotate image requests.
    output = blob_list[0]

    json_string = output.download_as_string()
    response = json_format.Parse(json_string,
                                 vision.types.BatchAnnotateImagesResponse())

    # Prints the actual response for the first annotate image request.
    print(u'The annotation response for the first request: {}'.format(
        response.responses[0]))

Response

A successful request returns response JSON files in the Google Cloud Storage bucket you indicated in the code sample. The number of responses per JSON file is dictated by batch_size in the code sample.

The returned response is similar to regular Cloud Vision API feature responses, depending on which features you request for an image.

The following responses show LABEL_DETECTION and TEXT_DETECTION annotations for image1.png, IMAGE_PROPERTIES annotations for image2.jpg, and OBJECT_LOCALIZATION annotations for image3.jpg.

The response also contain a context field showing the file's URI.

offline_batch_output/output-1-to-2.json

{
  "responses": [
    {
      "labelAnnotations": [
        {
          "mid": "/m/07s6nbt",
          "description": "Text",
          "score": 0.93413997,
          "topicality": 0.93413997
        },
        {
          "mid": "/m/0dwx7",
          "description": "Logo",
          "score": 0.8733531,
          "topicality": 0.8733531
        },
        ...
        {
          "mid": "/m/03bxgrp",
          "description": "Company",
          "score": 0.5682425,
          "topicality": 0.5682425
        }
      ],
      "textAnnotations": [
        {
          "locale": "en",
          "description": "Google\n",
          "boundingPoly": {
            "vertices": [
              {
                "x": 72,
                "y": 40
              },
              {
                "x": 613,
                "y": 40
              },
              {
                "x": 613,
                "y": 233
              },
              {
                "x": 72,
                "y": 233
              }
            ]
          }
        },
        ...
                ],
                "blockType": "TEXT"
              }
            ]
          }
        ],
        "text": "Google\n"
      },
      "context": {
        "uri": "gs://cloud-samples-data/vision/document_understanding/image1.png"
      }
    },
    {
      "imagePropertiesAnnotation": {
        "dominantColors": {
          "colors": [
            {
              "color": {
                "red": 229,
                "green": 230,
                "blue": 238
              },
              "score": 0.2744754,
              "pixelFraction": 0.075339235
            },
            ...
            {
              "color": {
                "red": 86,
                "green": 87,
                "blue": 95
              },
              "score": 0.025770646,
              "pixelFraction": 0.13109145
            }
          ]
        }
      },
      "cropHintsAnnotation": {
        "cropHints": [
          {
            "boundingPoly": {
              "vertices": [
                {},
                {
                  "x": 1599
                },
                {
                  "x": 1599,
                  "y": 1199
                },
                {
                  "y": 1199
                }
              ]
            },
            "confidence": 0.79999995,
            "importanceFraction": 1
          }
        ]
      },
      "context": {
        "uri": "gs://cloud-samples-data/vision/document_understanding/image2.jpg"
      }
    }
  ]
}

offline_batch_output/output-3-to-3.json

{
  "responses": [
    {
      "context": {
        "uri": "gs://cloud-samples-data/vision/document_understanding/image3.jpg"
      },
      "localizedObjectAnnotations": [
        {
          "mid": "/m/0bt9lr",
          "name": "Dog",
          "score": 0.9669734,
          "boundingPoly": {
            "normalizedVertices": [
              {
                "x": 0.6035543,
                "y": 0.1357359
              },
              {
                "x": 0.98546547,
                "y": 0.1357359
              },
              {
                "x": 0.98546547,
                "y": 0.98426414
              },
              {
                "x": 0.6035543,
                "y": 0.98426414
              }
            ]
          }
        },
        ...
        {
          "mid": "/m/0jbk",
          "name": "Animal",
          "score": 0.58003056,
          "boundingPoly": {
            "normalizedVertices": [
              {
                "x": 0.014534635,
                "y": 0.1357359
              },
              {
                "x": 0.37197515,
                "y": 0.1357359
              },
              {
                "x": 0.37197515,
                "y": 0.98426414
              },
              {
                "x": 0.014534635,
                "y": 0.98426414
              }
            ]
          }
        }
      ]
    }
  ]
}
Was this page helpful? Let us know how we did:

Send feedback about...

Cloud Vision API Documentation
Need help? Visit our support page.