functions/imagemagick/imagemagick.go (66 lines of code) (raw):
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// [START functions_imagemagick_setup]
// Package imagemagick contains an example of using ImageMagick to process a
// file uploaded to Cloud Storage.
package imagemagick
import (
"context"
"errors"
"fmt"
"log"
"os"
"os/exec"
"cloud.google.com/go/storage"
vision "cloud.google.com/go/vision/apiv1"
"cloud.google.com/go/vision/v2/apiv1/visionpb"
)
// 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)
}
}
// [END functions_imagemagick_setup]
// [START functions_imagemagick_analyze]
// GCSEvent is the payload of a GCS event.
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 {
outputBucket := os.Getenv("BLURRED_BUCKET_NAME")
if outputBucket == "" {
return errors.New("BLURRED_BUCKET_NAME must be set")
}
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: %w", err)
}
if resp.GetAdult() == visionpb.Likelihood_VERY_LIKELY ||
resp.GetViolence() == visionpb.Likelihood_VERY_LIKELY {
return blur(ctx, e.Bucket, outputBucket, e.Name)
}
log.Printf("The image %q was detected as OK.", e.Name)
return nil
}
// [END functions_imagemagick_analyze]
// [START functions_imagemagick_blur]
// 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: %w", 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: %w", err)
}
log.Printf("Blurred image uploaded to gs://%s/%s", outputBlob.BucketName(), outputBlob.ObjectName())
return nil
}
// [END functions_imagemagick_blur]