1// Copyright 2017, Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package vision
16
17import (
18	"context"
19
20	gax "github.com/googleapis/gax-go/v2"
21	pb "google.golang.org/genproto/googleapis/cloud/vision/v1"
22	"google.golang.org/grpc/codes"
23	"google.golang.org/grpc/status"
24)
25
26// AnnotateImage runs image detection and annotation for a single image.
27func (c *ImageAnnotatorClient) AnnotateImage(ctx context.Context, req *pb.AnnotateImageRequest, opts ...gax.CallOption) (*pb.AnnotateImageResponse, error) {
28	res, err := c.BatchAnnotateImages(ctx, &pb.BatchAnnotateImagesRequest{
29		Requests: []*pb.AnnotateImageRequest{req},
30	}, opts...)
31	if err != nil {
32		return nil, err
33	}
34	return res.Responses[0], nil
35}
36
37// Called for a single image and a single feature.
38func (c *ImageAnnotatorClient) annotateOne(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, ftype pb.Feature_Type, maxResults int, opts []gax.CallOption) (*pb.AnnotateImageResponse, error) {
39	res, err := c.AnnotateImage(ctx, &pb.AnnotateImageRequest{
40		Image:        img,
41		ImageContext: ictx,
42		Features:     []*pb.Feature{{Type: ftype, MaxResults: int32(maxResults)}},
43	}, opts...)
44	if err != nil {
45		return nil, err
46	}
47	// When there is only one image and one feature, the response's Error field is
48	// unambiguously about that one detection, so we "promote" it to the error return
49	// value.
50	// res.Error is a google.rpc.Status. Convert to a Go error. Use a gRPC
51	// error because it preserves the code as a separate field.
52	// TODO(jba): preserve the details field.
53	if res.Error != nil {
54		return nil, status.Errorf(codes.Code(res.Error.Code), "%s", res.Error.Message)
55	}
56	return res, nil
57}
58
59// DetectFaces performs face detection on the image.
60// At most maxResults results are returned.
61func (c *ImageAnnotatorClient) DetectFaces(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.FaceAnnotation, error) {
62	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_FACE_DETECTION, maxResults, opts)
63	if err != nil {
64		return nil, err
65	}
66	return res.FaceAnnotations, nil
67}
68
69// DetectLandmarks performs landmark detection on the image.
70// At most maxResults results are returned.
71func (c *ImageAnnotatorClient) DetectLandmarks(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.EntityAnnotation, error) {
72	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_LANDMARK_DETECTION, maxResults, opts)
73	if err != nil {
74		return nil, err
75	}
76	return res.LandmarkAnnotations, nil
77}
78
79// DetectLogos performs logo detection on the image.
80// At most maxResults results are returned.
81func (c *ImageAnnotatorClient) DetectLogos(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.EntityAnnotation, error) {
82	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_LOGO_DETECTION, maxResults, opts)
83	if err != nil {
84		return nil, err
85	}
86	return res.LogoAnnotations, nil
87}
88
89// DetectLabels performs label detection on the image.
90// At most maxResults results are returned.
91func (c *ImageAnnotatorClient) DetectLabels(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.EntityAnnotation, error) {
92	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_LABEL_DETECTION, maxResults, opts)
93	if err != nil {
94		return nil, err
95	}
96	return res.LabelAnnotations, nil
97}
98
99// DetectTexts performs text detection on the image.
100// At most maxResults results are returned.
101func (c *ImageAnnotatorClient) DetectTexts(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, maxResults int, opts ...gax.CallOption) ([]*pb.EntityAnnotation, error) {
102	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_TEXT_DETECTION, maxResults, opts)
103	if err != nil {
104		return nil, err
105	}
106	return res.TextAnnotations, nil
107}
108
109// DetectDocumentText performs full text (OCR) detection on the image.
110func (c *ImageAnnotatorClient) DetectDocumentText(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.TextAnnotation, error) {
111	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_DOCUMENT_TEXT_DETECTION, 0, opts)
112	if err != nil {
113		return nil, err
114	}
115	return res.FullTextAnnotation, nil
116}
117
118// DetectSafeSearch performs safe-search detection on the image.
119func (c *ImageAnnotatorClient) DetectSafeSearch(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.SafeSearchAnnotation, error) {
120	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_SAFE_SEARCH_DETECTION, 0, opts)
121	if err != nil {
122		return nil, err
123	}
124	return res.SafeSearchAnnotation, nil
125}
126
127// DetectImageProperties computes properties of the image.
128func (c *ImageAnnotatorClient) DetectImageProperties(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.ImageProperties, error) {
129	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_IMAGE_PROPERTIES, 0, opts)
130	if err != nil {
131		return nil, err
132	}
133	return res.ImagePropertiesAnnotation, nil
134}
135
136// DetectWeb computes a web annotation on the image.
137func (c *ImageAnnotatorClient) DetectWeb(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.WebDetection, error) {
138	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_WEB_DETECTION, 0, opts)
139	if err != nil {
140		return nil, err
141	}
142	return res.WebDetection, nil
143}
144
145// CropHints computes crop hints for the image.
146func (c *ImageAnnotatorClient) CropHints(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.CropHintsAnnotation, error) {
147	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_CROP_HINTS, 0, opts)
148	if err != nil {
149		return nil, err
150	}
151	return res.CropHintsAnnotation, nil
152}
153
154// LocalizeObject runs the localizer for object detection.
155func (c *ImageAnnotatorClient) LocalizeObjects(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) ([]*pb.LocalizedObjectAnnotation, error) {
156	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_OBJECT_LOCALIZATION, 0, opts)
157	if err != nil {
158		return nil, err
159	}
160	return res.LocalizedObjectAnnotations, nil
161}
162
163// ProductSearch searches the image for products.
164func (c *ImageAnnotatorClient) ProductSearch(ctx context.Context, img *pb.Image, ictx *pb.ImageContext, opts ...gax.CallOption) (*pb.ProductSearchResults, error) {
165	res, err := c.annotateOne(ctx, img, ictx, pb.Feature_PRODUCT_SEARCH, 0, opts)
166	if err != nil {
167		return nil, err
168	}
169	return res.ProductSearchResults, nil
170}
171