Instructivo de ImageMagick

En este instructivo, se muestra cómo usar Cloud Functions, la API de Google Cloud Vision y, también, ImageMagick para detectar y difuminar imágenes ofensivas que se suben al depósito de Cloud Storage.

Objetivos

  • Implementar una función de Cloud Functions en segundo plano activada por Storage
  • Usar la API de Cloud Vision para detectar contenido violento o destinado para adultos
  • Usar ImageMagick para difuminar imágenes ofensivas
  • Probar la función con solo subir una imagen de un zombi que come carne humana

Costos

Este instructivo usa componentes facturables de Cloud Platform, incluidos los siguientes:

  • Google Cloud Functions
  • Google Cloud Storage
  • API de Google Cloud Vision

Usa la calculadora de precios para generar una estimación de los costos según el uso previsto.

Los usuarios nuevos de Cloud Platform pueden ser aptos para una prueba gratuita.

Antes de comenzar

  1. Sign in to your Google Account.

    If you don't already have one, sign up for a new account.

  2. Select or create a Google Cloud Platform project.

    Go to the Manage resources page

  3. Comprueba que la facturación esté habilitada en tu proyecto.

    Descubre cómo puedes habilitar la facturación

  4. Habilita las Cloud Functions, Cloud Storage y Cloud Vision API necesarias.

    Habilita las API

  5. Install and initialize the Cloud SDK.
  6. Actualiza y, luego, instala los componentes de gcloud:

    Node.js 6

    gcloud components update

    Node.js 8 (Beta)

    gcloud components update &&
    gcloud components install beta

    Python (Beta)

    gcloud components update &&
    gcloud components install beta

    Go (Beta)

    gcloud components update &&
    gcloud components install beta
  7. Prepara tu entorno de programación.

Visualiza el flujo de datos

El flujo de datos en la aplicación de instructivo de ImageMagick incluye varios pasos como se muestra a continuación:

  1. Se sube una imagen a un depósito de Cloud Storage.
  2. La función de Cloud Functions analiza la imagen con la API de Cloud Vision.
  3. Si se detecta contenido violento o destinado para adultos, la función de Cloud Functions usa ImageMagick para difuminar la imagen.
  4. La imagen difuminada se sube sobre la original en el depósito de Cloud Storage.

Prepara la aplicación

  1. Crea un depósito de Cloud Storage para subir imágenes, en el que YOUR_IMAGE_BUCKET_NAME es un nombre de depósito único a nivel global:

    gsutil mb gs://YOUR_IMAGE_BUCKET_NAME
    
  2. Clona el repositorio de la app de muestra en tu máquina local como se muestra a continuación:

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    De manera opcional, puedes descargar la muestra como un archivo zip y extraerla.

    Python (Beta)

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    De manera opcional, puedes descargar la muestra como un archivo zip y extraerla.

    Go (Beta)

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    De manera opcional, puedes descargar la muestra como un archivo zip y extraerla.

  3. Ve al directorio que contiene el código de muestra de Cloud Functions, como sigue:

    Node.js

    cd nodejs-docs-samples/functions/imagemagick/

    Python (Beta)

    cd python-docs-samples/functions/imagemagick/

    Go (Beta)

    cd golang-samples/functions/imagemagick/

Comprende el código

Importa dependencias

La aplicación debe importar varias dependencias con el fin de interactuar con los servicios de Google Cloud Platform y con ImageMagick y el sistema de archivos:

Node.js

const gm = require('gm').subClass({imageMagick: true});
const fs = require('fs');
const path = require('path');
const {Storage} = require('@google-cloud/storage');
const storage = new Storage();
const vision = require('@google-cloud/vision').v1p1beta1;

const client = new vision.ImageAnnotatorClient();

Python (Beta)

import os
import tempfile

from google.cloud import storage, vision
from wand.image import Image

storage_client = storage.Client()
vision_client = vision.ImageAnnotatorClient()

Go (Beta)

// Package imagemagick contains an example of using ImageMagick from a Cloud
// Function.
package imagemagick

import (
	"context"
	"fmt"
	"log"
	"os/exec"
	"strings"

	"cloud.google.com/go/storage"
	vision "cloud.google.com/go/vision/apiv1"
	visionpb "google.golang.org/genproto/googleapis/cloud/vision/v1"
)

// Global API clients used across function invocations.
var (
	storageClient *storage.Client
	visionClient  *vision.ImageAnnotatorClient
)

func init() {
	// Declare a separate err variable to avoid shadowing the client variables.
	var err error

	storageClient, err = storage.NewClient(context.Background())
	if err != nil {
		log.Fatalf("storage.NewClient: %v", err)
	}

	visionClient, err = vision.NewImageAnnotatorClient(context.Background())
	if err != nil {
		log.Fatalf("vision.NewAnnotatorClient: %v", err)
	}
}

Analiza imágenes

Se invoca la siguiente función cuando una imagen se sube al depósito de Cloud Storage que creaste para almacenar imágenes. La función usa la API de Cloud Vision para detectar contenido violento o destinado para adultos en imágenes que se suben.

Node.js

// Blurs uploaded images that are flagged as Adult or Violence.
exports.blurOffensiveImages = event => {
  const object = event.data || event; // Node 6: event.data === Node 8+: event

  // Exit if this is a deletion or a deploy event.
  if (object.resourceState === 'not_exists') {
    console.log('This is a deletion event.');
    return;
  } else if (!object.name) {
    console.log('This is a deploy event.');
    return;
  }

  const file = storage.bucket(object.bucket).file(object.name);
  const filePath = `gs://${object.bucket}/${object.name}`;

  // Ignore already-blurred files (to prevent re-invoking this function)
  if (file.name.startsWith('blurred-')) {
    console.log(`The image ${file.name} is already blurred.`);
    return;
  }

  console.log(`Analyzing ${file.name}.`);

  return client
    .safeSearchDetection(filePath)
    .catch(err => {
      console.error(`Failed to analyze ${file.name}.`, err);
      return Promise.reject(err);
    })
    .then(([result]) => {
      const detections = result.safeSearchAnnotation;

      if (
        detections.adult === 'VERY_LIKELY' ||
        detections.violence === 'VERY_LIKELY'
      ) {
        console.log(
          `The image ${file.name} has been detected as inappropriate.`
        );
        return blurImage(file);
      } else {
        console.log(`The image ${file.name} has been detected as OK.`);
      }
    });
};

Python (Beta)

# Blurs uploaded images that are flagged as Adult or Violence.
def blur_offensive_images(data, context):
    file_data = data

    file_name = file_data['name']
    bucket_name = file_data['bucket']

    blob = storage_client.bucket(bucket_name).get_blob(file_name)
    blob_uri = f'gs://{bucket_name}/{file_name}'
    blob_source = {'source': {'image_uri': blob_uri}}

    # Ignore already-blurred files
    if file_name.startswith('blurred-'):
        print(f'The image {file_name} is already blurred.')
        return

    print(f'Analyzing {file_name}.')

    result = vision_client.safe_search_detection(blob_source)
    detected = result.safe_search_annotation

    # Process image
    if detected.adult == 5 or detected.violence == 5:
        print(f'The image {file_name} was detected as inappropriate.')
        return __blur_image(blob)
    else:
        print(f'The image {file_name} was detected as OK.')

Go (Beta)

// GCSEvent is the payload of a GCS event. Please refer to the docs for
// additional information regarding GCS events.
type GCSEvent struct {
	Bucket string `json:"bucket"`
	Name   string `json:"name"`
}

// BlurOffensiveImages blurs offensive images uploaded to GCS.
func BlurOffensiveImages(ctx context.Context, e GCSEvent) error {
	if strings.HasPrefix(e.Name, "blurred-") {
		log.Printf("The image %q is already blurred", e.Name)
		return nil
	}

	img := vision.NewImageFromURI(fmt.Sprintf("gs://%s/%s", e.Bucket, e.Name))

	resp, err := visionClient.DetectSafeSearch(ctx, img, nil)
	if err != nil {
		return fmt.Errorf("AnnotateImage: %v", err)
	}

	if resp.GetAdult() == visionpb.Likelihood_VERY_LIKELY ||
		resp.GetViolence() == visionpb.Likelihood_VERY_LIKELY {
		return blur(ctx, e.Bucket, e.Name)
	}
	log.Printf("The image %q was detected as OK.", e.Name)
	return nil
}

Difumina imágenes

La siguiente función recibe una llamada cuando se detecta contenido violento o destinado para adultos en una imagen que se sube. La función descarga la imagen ofensiva, usa ImageMagick para difuminarla y, luego, sube la imagen difuminada sobre la imagen original.

Node.js

// Blurs the given file using ImageMagick.
function blurImage(file) {
  const tempLocalPath = `/tmp/${path.parse(file.name).base}`;

  // Download file from bucket.
  return file
    .download({destination: tempLocalPath})
    .catch(err => {
      console.error('Failed to download file.', err);
      return Promise.reject(err);
    })
    .then(() => {
      console.log(
        `Image ${file.name} has been downloaded to ${tempLocalPath}.`
      );

      // Blur the image using ImageMagick.
      return new Promise((resolve, reject) => {
        gm(tempLocalPath)
          .blur(0, 16)
          .write(tempLocalPath, (err, stdout) => {
            if (err) {
              console.error('Failed to blur image.', err);
              reject(err);
            } else {
              resolve(stdout);
            }
          });
      });
    })
    .then(() => {
      console.log(`Image ${file.name} has been blurred.`);

      // Mark result as blurred, to avoid re-triggering this function.
      const newName = `blurred-${file.name}`;

      // Upload the Blurred image back into the bucket.
      return file.bucket
        .upload(tempLocalPath, {destination: newName})
        .catch(err => {
          console.error('Failed to upload blurred image.', err);
          return Promise.reject(err);
        });
    })
    .then(() => {
      console.log(`Blurred image has been uploaded to ${file.name}.`);

      // Delete the temporary file.
      return new Promise((resolve, reject) => {
        fs.unlink(tempLocalPath, err => {
          if (err) {
            reject(err);
          } else {
            resolve();
          }
        });
      });
    });
}

Python (Beta)

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

    # Download file from bucket.
    current_blob.download_to_filename(temp_local_filename)
    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')
        image.save(filename=temp_local_filename)

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

    # Send Blurred image back to the bucket (with a 'blurred-' prefix).
    # The prefix is necessary to avoid re-invoking the function upon upload.
    new_file_name = f'blurred-{file_name}'
    new_blob = current_blob.bucket.blob(new_file_name)
    new_blob.upload_from_filename(temp_local_filename)
    print(f'Blurred image was uploaded to {new_file_name}.')

    # Delete the temporary file.
    os.remove(temp_local_filename)

Go (Beta)

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

	outputBlob := storageClient.Bucket(bucket).Object("blurred-" + 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 has been uploaded to %s", outputBlob.ObjectName())

	return nil
}

Implementa la función

  1. Para implementar tu función de Cloud con un activador de Storage, ejecuta el siguiente comando en el directorio imagemagick:

    Node.js 6

    gcloud functions deploy blurOffensiveImages --runtime nodejs6 --trigger-bucket YOUR_IMAGE_BUCKET_NAME

    Node.js 8 (Beta)

    gcloud functions deploy blurOffensiveImages --runtime nodejs8 --trigger-bucket YOUR_IMAGE_BUCKET_NAME

    Python (Beta)

    gcloud functions deploy blur_offensive_images --runtime python37 --trigger-bucket YOUR_IMAGE_BUCKET_NAME

    Go (Beta)

    gcloud functions deploy BlurOffensiveImages --runtime go111 --trigger-bucket YOUR_IMAGE_BUCKET_NAME

    en el que YOUR_IMAGE_BUCKET_NAME es el nombre del depósito de Cloud Storage para subir imágenes.

Sube una imagen

  1. Sube una imagen ofensiva, como esta imagen de un zombi que come carne humana:

    gsutil cp zombie.jpg gs://YOUR_IMAGE_BUCKET_NAME
    

    en la que YOUR_IMAGE_BUCKET_NAME es el depósito de Cloud Storage que creaste para subir imágenes.

  2. Revisa los registros para asegurarte de que las ejecuciones se completaron:

    gcloud functions logs read --limit 100
    
  3. Puedes ver las imágenes difuminadas en el depósito de Cloud Storage que creaste anteriormente para subir imágenes.

Limpieza

Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud Platform por los recursos que usaste en este instructivo:

Cómo borrar el proyecto

La manera más fácil de eliminar la facturación es borrar el proyecto que creaste para el instructivo.

Para borrar el proyecto, haz lo siguiente:

  1. In the GCP Console, go to the Projects page.

    Go to the Projects page

  2. In the project list, select the project you want to delete and click Delete .
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Borra la función de Cloud Functions

Borrar las funciones de Cloud Functions no quita ningún recurso almacenado en Cloud Storage.

Para borrar la función de Cloud Functions que implementaste en este instructivo, ejecuta el siguiente comando:

Node.js

gcloud functions delete blurOffensiveImages 

Python (Beta)

gcloud functions delete blur_offensive_images 

Go (Beta)

gcloud functions delete BlurOffensiveImages 

También puedes borrar las funciones de Cloud Functions desde Google Cloud Platform Console.

¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Documentación de Cloud Functions