ImageMagick tutorial step 2 - Blur image

Demonstrates how to use the Google Cloud Vision API and ImageMagick to detect and blur offensive images that get uploaded to a Cloud Storage bucket.

Explore further

For detailed documentation that includes this code sample, see the following:

Code sample


/// <summary>
/// Downloads the Storage object specified by <paramref name="data"/>,
/// blurs it using ImageMagick, and uploads it to the "blurred" bucket.
/// </summary>
private async Task BlurImageAsync(StorageObjectData data, CancellationToken cancellationToken)
    // Download image
    string originalImageFile = Path.GetTempFileName();
    using (Stream output = File.Create(originalImageFile))
        await _storageClient.DownloadObjectAsync(data.Bucket, data.Name, output, cancellationToken: cancellationToken);

    // Construct the ImageMagick command
    string blurredImageFile = Path.GetTempFileName();
    // Command-line arguments for ImageMagick.
    // Paths are wrapped in quotes in case they contain spaces.
    string arguments = $"\"{originalImageFile}\" -blur 0x8, \"{blurredImageFile}\"";

    // Run the ImageMagick command line tool ("convert").
    Process process = Process.Start("convert", arguments);
    // Process doesn't expose a way of asynchronously waiting for completion.
    // See for examples of how
    // this can be achieved using events, but for the sake of brevity,
    // this sample just waits synchronously.

    // If ImageMagick failed, log the error but complete normally to avoid retrying.
    if (process.ExitCode != 0)
        _logger.LogError("ImageMagick exited with code {exitCode}", process.ExitCode);

    // Upload image to blurred bucket.
    using (Stream input = File.OpenRead(blurredImageFile))
        await _storageClient.UploadObjectAsync(
            s_blurredBucketName, data.Name, data.ContentType, input, cancellationToken: cancellationToken);

    string uri = $"gs://{s_blurredBucketName}/{data.Name}";
    _logger.LogInformation("Blurred image uploaded to: {uri}", uri);

    // Remove images from the file system.


// 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("inputBlob.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.
private 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.
  Path upload = Paths.get("/tmp/", "blurred-" + fileName);
  List<String> args = List.of("convert", download.toString(), "-blur", "0x8", upload.toString());
  try {
    ProcessBuilder pb = new ProcessBuilder(args);
    Process process = pb.start();
  } catch (Exception e) {"Error: %s", e.getMessage()));

  // Upload image to blurred bucket.
  BlobId blurredBlobId = BlobId.of(BLURRED_BUCKET_NAME, fileName);
  BlobInfo blurredBlobInfo =

  byte[] blurredFile = Files.readAllBytes(upload);
  storage.create(blurredBlobInfo, blurredFile);
      String.format("Blurred image uploaded to: gs://%s/%s", BLURRED_BUCKET_NAME, fileName));

  // 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.
  return fs.unlink(tempLocalPath);


// Blurs the given file using ImageMagick, and uploads it to another bucket.
function blurImage(
    Object $file,
    string $blurredBucketName
): void {
    $tempLocalPath = sys_get_temp_dir() . '/' . $file->name();

    // Download file from bucket.
    $image = new Imagick();
    try {
    } catch (Exception $e) {
        throw new Exception('Streaming download failed: ' . $e);

    // Blur file using ImageMagick
    // (The Imagick class is from the PECL 'imagick' package)
    $image->blurImage(0, 16);

    // Stream blurred image result to a different bucket. // (This avoids re-triggering this function.)
    $storage = new StorageClient();
    $blurredBucket = $storage->bucket($blurredBucketName);

    // Upload the Blurred image back into the bucket.
    $gcsPath = 'gs://' . $blurredBucketName . '/' . $file->name();
    try {
        $blurredBucket->upload($image->getImageBlob(), [
            'name' => $file->name()
        fwrite($log, 'Streamed blurred image to: ' . $gcsPath . PHP_EOL);
    } catch (Exception $e) {
        throw new Exception(
                'Unable to stream blurred image to %s: %s',


# 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.


require "tempfile"
require "mini_magick"

# Blurs the given file using ImageMagick.
def blur_image bucket_name, file_name
  tempfile =
    # Download the image file
    bucket = global(:storage_client).bucket bucket_name
    file = bucket.file file_name tempfile

    # Blur the image using ImageMagick tempfile.path do |image|
      image.blur "0x16"
    end "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 and tell your function
    # to ignore files marked as blurred (e.g. those with a "blurred" prefix.)
    blur_bucket_name = ENV["BLURRED_BUCKET_NAME"]
    blur_bucket = global(:storage_client).bucket blur_bucket_name
    blur_bucket.create_file tempfile.path, file_name "Blurred image uploaded to gs://#{blur_bucket_name}/#{file_name}"
    # Ruby will remove the temp file when garbage collecting the object,
    # but it is good practice to remove it explicitly.

What's next

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