Retrieve image from Cloud Storage to blur and then upload to a storage bucket

This tutorial demonstrates using Cloud Run, Cloud Vision API, and ImageMagick to detect and blur offensive images uploaded to a Cloud Storage bucket.

Documentation pages that include this code sample

To view the code sample used in context, see the following documentation:

Code sample


// blur blurs the image stored at gs://inputBucket/name and stores the result in
// gs://outputBucket/name.
func blur(ctx context.Context, inputBucket, outputBucket, name string) error {
	inputBlob := storageClient.Bucket(inputBucket).Object(name)
	r, err := inputBlob.NewReader(ctx)
	if err != nil {
		return fmt.Errorf("NewReader: %v", err)

	outputBlob := storageClient.Bucket(outputBucket).Object(name)
	w := outputBlob.NewWriter(ctx)
	defer w.Close()

	// Use - as input and output to use stdin and stdout.
	cmd := exec.Command("convert", "-", "-blur", "0x8", "-")
	cmd.Stdin = r
	cmd.Stdout = w

	if err := cmd.Run(); err != nil {
		return fmt.Errorf("cmd.Run: %v", err)

	log.Printf("Blurred image uploaded to gs://%s/%s", outputBlob.BucketName(), outputBlob.ObjectName())

	return nil


  // Blurs the file described by blobInfo using ImageMagick,
  // and uploads it to the blurred bucket.
  public static void blur(BlobInfo blobInfo) throws IOException {
    String bucketName = blobInfo.getBucket();
    String fileName = blobInfo.getName();
    // Download image
    Blob blob = storage.get(BlobId.of(bucketName, fileName));
    Path download = Paths.get("/tmp/", fileName);

    // Construct the command.
    List<String> args = new ArrayList<String>();
    Path upload = Paths.get("/tmp/", "blurred-" + fileName);
    try {
      ProcessBuilder pb = new ProcessBuilder(args);
      Process process = pb.start();
    } catch (Exception e) {
      System.out.println(String.format("Error: %s", e.getMessage()));

    // Upload image to blurred bucket.
    BlobId blurredBlobId = BlobId.of(BLURRED_BUCKET_NAME, fileName);
    BlobInfo blurredBlobInfo =
    try {
      byte[] blurredFile = Files.readAllBytes(upload);
      Blob blurredBlob = storage.create(blurredBlobInfo, blurredFile);
          String.format("Blurred image uploaded to: gs://%s/%s", BLURRED_BUCKET_NAME, fileName));
    } catch (Exception e) {
      System.out.println(String.format("Error in upload: %s", e.getMessage()));

    // Remove images from fileSystem


// Blurs the given file using ImageMagick, and uploads it to another bucket.
const blurImage = async (file, blurredBucketName) => {
  const tempLocalPath = `/tmp/${path.parse(}`;

  // Download file from bucket.
  try {
    await{destination: tempLocalPath});

    console.log(`Downloaded ${} to ${tempLocalPath}.`);
  } catch (err) {
    throw new Error(`File download failed: ${err}`);

  await new Promise((resolve, reject) => {
      .blur(0, 16)
      .write(tempLocalPath, (err, stdout) => {
        if (err) {
          console.error('Failed to blur image.', err);
        } else {
          console.log(`Blurred image: ${}`);

  // Upload result to a different bucket, to avoid re-triggering this function.
  const blurredBucket = storage.bucket(blurredBucketName);

  // Upload the Blurred image back into the bucket.
  const gcsPath = `gs://${blurredBucketName}/${}`;
  try {
    await blurredBucket.upload(tempLocalPath, {destination:});
    console.log(`Uploaded blurred image to: ${gcsPath}`);
  } catch (err) {
    throw new Error(`Unable to upload blurred image to ${gcsPath}: ${err}`);

  // Delete the temporary file.
  const unlink = promisify(fs.unlink);
  return unlink(tempLocalPath);


# Blurs the given file using ImageMagick.
def __blur_image(current_blob):
    file_name =
    _, temp_local_filename = tempfile.mkstemp()

    # Download file from bucket.
    print(f"Image {file_name} was downloaded to {temp_local_filename}.")

    # Blur the image using ImageMagick.
    with Image(filename=temp_local_filename) as image:
        image.resize(*image.size, blur=16, filter="hamming")

    print(f"Image {file_name} was blurred.")

    # Upload result to a second bucket, to avoid re-triggering the function.
    # You could instead re-upload it to the same bucket + tell your function
    # to ignore files marked as blurred (e.g. those with a "blurred" prefix)
    blur_bucket_name = os.getenv("BLURRED_BUCKET_NAME")
    blur_bucket = storage_client.bucket(blur_bucket_name)
    new_blob = blur_bucket.blob(file_name)
    print(f"Blurred image uploaded to: gs://{blur_bucket_name}/{file_name}")

    # Delete the temporary file.

What's next

To search and filter code samples for other Google Cloud products, see the Google Cloud sample browser