2017-08-05 18:17:15 +00:00
|
|
|
// Copyright 2017, Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
// http://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.
|
|
|
|
|
|
|
|
package vision
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
pb "google.golang.org/genproto/googleapis/cloud/vision/v1"
|
|
|
|
"google.golang.org/genproto/googleapis/rpc/status"
|
|
|
|
"google.golang.org/grpc"
|
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
)
|
|
|
|
|
|
|
|
var batchResponse = &pb.BatchAnnotateImagesResponse{
|
|
|
|
Responses: []*pb.AnnotateImageResponse{{
|
|
|
|
FaceAnnotations: []*pb.FaceAnnotation{
|
|
|
|
{RollAngle: 1}, {RollAngle: 2}},
|
|
|
|
LandmarkAnnotations: []*pb.EntityAnnotation{{Mid: "landmark"}},
|
|
|
|
LogoAnnotations: []*pb.EntityAnnotation{{Mid: "logo"}},
|
|
|
|
LabelAnnotations: []*pb.EntityAnnotation{{Mid: "label"}},
|
|
|
|
TextAnnotations: []*pb.EntityAnnotation{{Mid: "text"}},
|
|
|
|
FullTextAnnotation: &pb.TextAnnotation{Text: "full"},
|
|
|
|
SafeSearchAnnotation: &pb.SafeSearchAnnotation{Spoof: pb.Likelihood_POSSIBLE},
|
|
|
|
ImagePropertiesAnnotation: &pb.ImageProperties{DominantColors: &pb.DominantColorsAnnotation{}},
|
|
|
|
CropHintsAnnotation: &pb.CropHintsAnnotation{CropHints: []*pb.CropHint{{Confidence: 0.5}}},
|
|
|
|
WebDetection: &pb.WebDetection{WebEntities: []*pb.WebDetection_WebEntity{{EntityId: "web"}}},
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that all the "shortcut" methods use the underlying
|
|
|
|
// BatchAnnotateImages RPC correctly.
|
|
|
|
func TestClientMethods(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
c, err := NewImageAnnotatorClient(ctx, clientOpt)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mockImageAnnotator.resps = []proto.Message{batchResponse}
|
|
|
|
img := &pb.Image{Source: &pb.ImageSource{ImageUri: "http://foo.jpg"}}
|
|
|
|
ictx := &pb.ImageContext{LanguageHints: []string{"en", "fr"}}
|
|
|
|
req := &pb.AnnotateImageRequest{
|
|
|
|
Image: img,
|
|
|
|
ImageContext: ictx,
|
|
|
|
Features: []*pb.Feature{
|
|
|
|
{Type: pb.Feature_LABEL_DETECTION, MaxResults: 3},
|
|
|
|
{Type: pb.Feature_FACE_DETECTION, MaxResults: 4},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range []struct {
|
|
|
|
call func() (interface{}, error)
|
|
|
|
wantFeatures []*pb.Feature
|
|
|
|
wantRes interface{}
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.AnnotateImage(ctx, req) },
|
|
|
|
req.Features, batchResponse.Responses[0],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectFaces(ctx, img, ictx, 2) },
|
|
|
|
[]*pb.Feature{{pb.Feature_FACE_DETECTION, 2}},
|
|
|
|
batchResponse.Responses[0].FaceAnnotations,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectLandmarks(ctx, img, ictx, 2) },
|
|
|
|
[]*pb.Feature{{pb.Feature_LANDMARK_DETECTION, 2}},
|
|
|
|
batchResponse.Responses[0].LandmarkAnnotations,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectLogos(ctx, img, ictx, 2) },
|
|
|
|
[]*pb.Feature{{pb.Feature_LOGO_DETECTION, 2}},
|
|
|
|
batchResponse.Responses[0].LogoAnnotations,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectLabels(ctx, img, ictx, 2) },
|
|
|
|
[]*pb.Feature{{pb.Feature_LABEL_DETECTION, 2}},
|
|
|
|
batchResponse.Responses[0].LabelAnnotations,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectTexts(ctx, img, ictx, 2) },
|
|
|
|
[]*pb.Feature{{pb.Feature_TEXT_DETECTION, 2}},
|
|
|
|
batchResponse.Responses[0].TextAnnotations,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectDocumentText(ctx, img, ictx) },
|
|
|
|
[]*pb.Feature{{pb.Feature_DOCUMENT_TEXT_DETECTION, 0}},
|
|
|
|
batchResponse.Responses[0].FullTextAnnotation,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectSafeSearch(ctx, img, ictx) },
|
|
|
|
[]*pb.Feature{{pb.Feature_SAFE_SEARCH_DETECTION, 0}},
|
|
|
|
batchResponse.Responses[0].SafeSearchAnnotation,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectImageProperties(ctx, img, ictx) },
|
|
|
|
[]*pb.Feature{{pb.Feature_IMAGE_PROPERTIES, 0}},
|
|
|
|
batchResponse.Responses[0].ImagePropertiesAnnotation,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.DetectWeb(ctx, img, ictx) },
|
|
|
|
[]*pb.Feature{{pb.Feature_WEB_DETECTION, 0}},
|
|
|
|
batchResponse.Responses[0].WebDetection,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
func() (interface{}, error) { return c.CropHints(ctx, img, ictx) },
|
|
|
|
[]*pb.Feature{{pb.Feature_CROP_HINTS, 0}},
|
|
|
|
batchResponse.Responses[0].CropHintsAnnotation,
|
|
|
|
},
|
|
|
|
} {
|
|
|
|
mockImageAnnotator.reqs = nil
|
|
|
|
res, err := test.call()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
got := mockImageAnnotator.reqs[0]
|
|
|
|
want := &pb.BatchAnnotateImagesRequest{
|
|
|
|
Requests: []*pb.AnnotateImageRequest{{
|
|
|
|
Image: img,
|
|
|
|
ImageContext: ictx,
|
|
|
|
Features: test.wantFeatures,
|
|
|
|
}},
|
|
|
|
}
|
|
|
|
if !testEqual(got, want) {
|
|
|
|
t.Errorf("#%d:\ngot %v\nwant %v", i, got, want)
|
|
|
|
}
|
|
|
|
if got, want := res, test.wantRes; !testEqual(got, want) {
|
|
|
|
t.Errorf("#%d:\ngot %v\nwant %v", i, got, want)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func testEqual(a, b interface{}) bool {
|
|
|
|
if a == nil && b == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if a == nil || b == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
t := reflect.TypeOf(a)
|
|
|
|
if t != reflect.TypeOf(b) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if am, ok := a.(proto.Message); ok {
|
|
|
|
return proto.Equal(am, b.(proto.Message))
|
|
|
|
}
|
|
|
|
if t.Kind() != reflect.Slice {
|
|
|
|
panic(fmt.Sprintf("testEqual can only handle proto.Message and slices, got %s", t))
|
|
|
|
}
|
|
|
|
va := reflect.ValueOf(a)
|
|
|
|
vb := reflect.ValueOf(b)
|
|
|
|
if va.Len() != vb.Len() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := 0; i < va.Len(); i++ {
|
|
|
|
if !testEqual(va.Index(i).Interface(), vb.Index(i).Interface()) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestAnnotateOneError(t *testing.T) {
|
|
|
|
ctx := context.Background()
|
|
|
|
c, err := NewImageAnnotatorClient(ctx, clientOpt)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
mockImageAnnotator.resps = []proto.Message{
|
|
|
|
&pb.BatchAnnotateImagesResponse{
|
|
|
|
Responses: []*pb.AnnotateImageResponse{{
|
|
|
|
Error: &status.Status{Code: int32(codes.NotFound), Message: "not found"},
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = c.annotateOne(ctx,
|
|
|
|
&pb.Image{Source: &pb.ImageSource{ImageUri: "http://foo.jpg"}},
|
|
|
|
nil, pb.Feature_LOGO_DETECTION, 1, nil)
|
2018-01-23 18:40:42 +00:00
|
|
|
if c := grpc.Code(err); c != codes.NotFound {
|
|
|
|
t.Errorf("got %v, want NotFound", c)
|
2017-08-05 18:17:15 +00:00
|
|
|
}
|
|
|
|
}
|