Java tutorial
/* * Copyright 2017 IBM Corp. 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 com.ibm.watson.developer_cloud.visual_recognition.v3; import java.lang.reflect.Type; import java.util.List; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.reflect.TypeToken; import com.ibm.watson.developer_cloud.http.HttpMediaType; import com.ibm.watson.developer_cloud.http.RequestBuilder; import com.ibm.watson.developer_cloud.http.ResponseConverter; import com.ibm.watson.developer_cloud.http.ServiceCall; import com.ibm.watson.developer_cloud.service.WatsonService; import com.ibm.watson.developer_cloud.util.GsonSingleton; import com.ibm.watson.developer_cloud.util.ResponseConverterUtils; import com.ibm.watson.developer_cloud.util.Validator; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.AddImageToCollectionOptions; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.ClassifierOptions; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.ClassifyImagesOptions; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.Collection; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.CollectionImage; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.DetectedFaces; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.FindSimilarImagesOptions; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.RecognizedText; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.VisualClassification; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.VisualClassifier; import com.ibm.watson.developer_cloud.visual_recognition.v3.model.VisualRecognitionOptions; import okhttp3.HttpUrl; import okhttp3.MultipartBody; import okhttp3.MultipartBody.Builder; import okhttp3.RequestBody; /** * Visual Recognition allows you to derive insights from an image based on its visual content. You can organize image * libraries, understand an individual image, and create custom classifiers for specific results that are tailored to * your needs. * * @version v3 * @see <a href= "http://www.ibm.com/watson/developercloud/visual-recognition.html"> Visual Recognition</a> */ public class VisualRecognition extends WatsonService { private static final String PARAM_API_KEY = "api_key"; private static final String PARAM_CLASSIFIERS = "classifiers"; private static final String PARAM_IMAGES_FILE = "images_file"; private static final String PARAM_NAME = "name"; private static final String PARAM_NEGATIVE_EXAMPLES = "negative_examples"; private static final String PARAM_PARAMETERS = "parameters"; private static final String PARAM_POSITIVE_EXAMPLES = "positive_examples"; private static final String PARAM_URL = "url"; private static final String PARAM_CLASSIFIER_IDS = "classifier_ids"; private static final String PARAM_THRESHOLD = "threshold"; private static final String PARAM_COLLECTIONS = "collections"; private static final String PARAM_IMAGE_FILE = "image_file"; private static final String PARAM_IMAGES = "images"; private static final String PARAM_SIMILAR_IMAGES = "similar_images"; private static final String PARAM_LIMIT = "limit"; private static final String PARAM_METADATA = "metadata"; private static final String PATH_CLASSIFIER = "/v3/classifiers/%s"; private static final String PATH_CLASSIFIERS = "/v3/classifiers"; private static final String PATH_CLASSIFY = "/v3/classify"; private static final String PATH_DETECT_FACES = "/v3/detect_faces"; private static final String PATH_RECOGNIZE_TEXT = "/v3/recognize_text"; private static final String PATH_COLLECTION = "/v3/collections/%s"; private static final String PATH_COLLECTIONS = "/v3/collections"; private static final String PATH_COLLECTION_IMAGES = "/v3/collections/%s/images"; private static final String PATH_COLLECTION_IMAGE = "/v3/collections/%s/images/%s"; private static final String PATH_FIND_SIMILAR_IMAGES = "/v3/collections/%s/find_similar"; private static final Type TYPE_LIST_IMAGES = new TypeToken<List<CollectionImage>>() { }.getType(); private static final Type TYPE_LIST_COLLECTIONS = new TypeToken<List<Collection>>() { }.getType(); private static final Type TYPE_LIST_CLASSIFIERS = new TypeToken<List<VisualClassifier>>() { }.getType(); private static final String SERVICE_NAME = "watson_vision_combined"; private static final String URL = "https://gateway-a.watsonplatform.net/visual-recognition/api"; private static final String VERBOSE = "verbose"; /** Version date. */ public static final String VERSION_DATE_2016_05_20 = "2016-05-20"; private String versionDate; /** * Instantiates a new Visual Recognition V3 service. * * @param versionDate The version date (yyyy-MM-dd) of the REST API to use. Specifying this value will keep your API * calls from failing when the service introduces breaking changes. */ public VisualRecognition(String versionDate) { super(SERVICE_NAME); if ((getEndPoint() == null) || getEndPoint().isEmpty()) { setEndPoint(URL); } Validator.notNull(versionDate, "versionDate cannot be null. Use '2016-05-19'"); this.versionDate = versionDate; } /** * Instantiates a new Visual Recognition V3 service with a given API key. * * @param versionDate The version date (yyyy-MM-dd) of the REST API to use. Specifying this value will keep your API * calls from failing when the service introduces breaking changes. * @param apiKey the api key */ public VisualRecognition(String versionDate, String apiKey) { this(versionDate); setApiKey(apiKey); } /** * Adds the Alchemy API key to HTTP request. * * @param builder the builder * @param apiKey the API key token */ private void addApiKeyToRequest(okhttp3.Request.Builder builder, String apiKey) { final HttpUrl url = HttpUrl.parse(builder.build().url().toString()); if ((url.query() == null) || url.query().isEmpty()) { builder.url(builder.build().url() + "?" + apiKey); } else { builder.url(builder.build().url() + "&" + apiKey); } } private JsonObject getParametersAsJson(ClassifyImagesOptions options) { JsonObject ret = new JsonObject(); if ((options.url() != null) && (options.images() == null)) { ret.addProperty(PARAM_URL, options.url().toString()); } if ((options.classifierIds() != null) && !options.classifierIds().isEmpty()) { JsonArray array = new JsonArray(); for (String cId : options.classifierIds()) { array.add(new JsonPrimitive(cId)); } ret.add(PARAM_CLASSIFIER_IDS, array); } if (options.threshold() != null) { ret.addProperty(PARAM_THRESHOLD, options.threshold()); } return ret; } /** * Gets the request parameters as {@link JsonObject}. * * @param options the options * @return the parameters as {@link JsonObject} */ private JsonObject getParametersAsJson(VisualRecognitionOptions options) { JsonObject ret = new JsonObject(); ret.addProperty(PARAM_URL, options.url().toString()); return ret; } /* * (non-Javadoc) * * @see com.ibm.watson.developer_cloud.service.WatsonService#setAuthentication(okhttp3.Request. Builder) */ @Override protected void setAuthentication(okhttp3.Request.Builder builder) { if (getApiKey() == null) { throw new IllegalArgumentException("api_key needs to be specified. Use setApiKey()"); } addApiKeyToRequest(builder, PARAM_API_KEY + "=" + getApiKey()); } /** * Classify. * * @param options the classify options * @return the {@link VisualClassification} */ public ServiceCall<VisualClassification> classify(ClassifyImagesOptions options) { Validator.notNull(options, "'options' cannot be null"); // build body Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); if (options.images() != null) { RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.images()); bodyBuilder.addFormDataPart(PARAM_IMAGES_FILE, options.images().getName(), requestBody); } if (options.imagesBinary() != null) { RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.imagesBinary()); bodyBuilder.addFormDataPart(PARAM_IMAGES_FILE, options.imageName(), requestBody); } bodyBuilder.addFormDataPart(PARAM_PARAMETERS, getParametersAsJson(options).toString()); RequestBuilder requestBuilder = RequestBuilder.post(PATH_CLASSIFY); requestBuilder.query(VERSION, versionDate).body(bodyBuilder.build()); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(VisualClassification.class)); } /** * Train a new classifier on the uploaded image data. Upload a compressed (.zip) file of images (.jpg, .png, or .gif) * with positive examples that show your classifier and another compressed file with negative examples that are * similar to but do NOT show your classifier. <br> * * @param options The parameters to create a classifier * @return The created {@link VisualClassifier} * @see ClassifierOptions * @see VisualClassifier */ public ServiceCall<VisualClassifier> createClassifier(ClassifierOptions options) { Validator.notNull(options, " options cannot be null"); validateClassifierOptions(options); Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); bodyBuilder.addFormDataPart(PARAM_NAME, options.classifierName()); // Classes for (String className : options.classNames()) { String dataName = className + "_" + PARAM_POSITIVE_EXAMPLES; RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.positiveExamplesByClassName(className)); bodyBuilder.addFormDataPart(dataName, options.positiveExamplesByClassName(className).getName(), requestBody); } if (options.negativeExamples() != null) { RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.negativeExamples()); bodyBuilder.addFormDataPart(PARAM_NEGATIVE_EXAMPLES, options.negativeExamples().getName(), requestBody); } RequestBuilder requestBuilder = RequestBuilder.post(PATH_CLASSIFIERS); requestBuilder.query(VERSION, versionDate).body(bodyBuilder.build()); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(VisualClassifier.class)); } /** * Validates the {@link ClassifierOptions}. * * @param options the options */ private void validateClassifierOptions(ClassifierOptions options) { String errorMessage = "To create a classifier, you must supply at least 2 zip files - " + "either 2 positive example sets, or 1 positive and 1 negative set"; Validator.notNull(options.classifierName(), "'classifierName' cannot be null"); Validator.isTrue(!options.classNames().isEmpty(), "There are no classes. " + errorMessage); boolean hasExamples = (options.classNames().size() > 1) || ((options.negativeExamples() != null) && (options.classNames().size() == 1)); Validator.isTrue(hasExamples, errorMessage); } /** * Update an existing classifier by adding new classes, or by adding new images to existing classes. To update the * existing classifier, use several compressed (.zip) files, including files containing positive or negative images * (.jpg, or .png). You must supply at least one compressed file, with additional positive or negative examples. <br> * * @param classifierId the classifier id * @param options The parameters to create a classifier * @return The created {@link VisualClassifier} * @see ClassifierOptions * @see VisualClassifier */ public ServiceCall<VisualClassifier> updateClassifier(String classifierId, ClassifierOptions options) { Validator.notNull(classifierId, "classifierId cannot be null"); Validator.notNull(options, " options cannot be null"); String errorMessage = "To update a classifier, you must supply at least 1 zip file - " + "either a positive or negative zip file."; Validator.isTrue(!options.classNames().isEmpty() || (options.negativeExamples() != null), errorMessage); Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); // Classes for (String className : options.classNames()) { String dataName = className + "_" + PARAM_POSITIVE_EXAMPLES; RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.positiveExamplesByClassName(className)); bodyBuilder.addFormDataPart(dataName, options.positiveExamplesByClassName(className).getName(), requestBody); } if (options.negativeExamples() != null) { RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.negativeExamples()); bodyBuilder.addFormDataPart(PARAM_NEGATIVE_EXAMPLES, options.negativeExamples().getName(), requestBody); } RequestBuilder requestBuilder = RequestBuilder.post(String.format(PATH_CLASSIFIER, classifierId)); requestBuilder.query(VERSION, versionDate).body(bodyBuilder.build()); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(VisualClassifier.class)); } /** * Deletes a classifier. * * @param classifierId the classifier ID to delete * @return the service call * @see VisualClassifier */ public ServiceCall<Void> deleteClassifier(String classifierId) { Validator.isTrue((classifierId != null) && !classifierId.isEmpty(), "classifierId cannot be null or empty"); RequestBuilder requestBuilder = RequestBuilder.delete(String.format(PATH_CLASSIFIER, classifierId)); requestBuilder.query(VERSION, versionDate); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getVoid()); } /** * Detect faces. * * @param options the recognize text options * @return the {@link VisualClassification} */ public ServiceCall<DetectedFaces> detectFaces(VisualRecognitionOptions options) { // build body Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); if (options.url() != null) { bodyBuilder.addFormDataPart(PARAM_PARAMETERS, getParametersAsJson(options).toString()); } else { RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.images()); bodyBuilder.addFormDataPart(PARAM_IMAGES_FILE, options.images().getName(), requestBody); } RequestBuilder requestBuilder = RequestBuilder.post(PATH_DETECT_FACES); requestBuilder.query(VERSION, versionDate).body(bodyBuilder.build()); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(DetectedFaces.class)); } /** * Retrieve information about a specific classifier. Only user-trained classifiers may be addressed. * * @param classifierId the classifier ID * @return the classifier * @see VisualClassifier */ public ServiceCall<VisualClassifier> getClassifier(String classifierId) { Validator.isTrue((classifierId != null) && !classifierId.isEmpty(), "classifierId cannot be null or empty"); RequestBuilder requestBuilder = RequestBuilder.get(String.format(PATH_CLASSIFIER, classifierId)); requestBuilder.query(VERSION, versionDate); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(VisualClassifier.class)); } /** * Retrieve the user-trained classifiers. * * @return the classifier list * @see VisualClassifier */ public ServiceCall<List<VisualClassifier>> getClassifiers() { RequestBuilder requestBuilder = RequestBuilder.get(PATH_CLASSIFIERS).query(VERSION, versionDate) .query(VERBOSE, true); ResponseConverter<List<VisualClassifier>> converter = ResponseConverterUtils .getGenericObject(TYPE_LIST_CLASSIFIERS, PARAM_CLASSIFIERS); return createServiceCall(requestBuilder.build(), converter); } /** * Recognize text. * * @param options the recognize text options * @return the {@link RecognizedText} */ public ServiceCall<RecognizedText> recognizeText(VisualRecognitionOptions options) { // build body Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); if (options.url() != null) { bodyBuilder.addFormDataPart(PARAM_PARAMETERS, getParametersAsJson(options).toString()); } else { RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.images()); bodyBuilder.addFormDataPart(PARAM_IMAGES_FILE, options.images().getName(), requestBody); } RequestBuilder requestBuilder = RequestBuilder.post(PATH_RECOGNIZE_TEXT); requestBuilder.query(VERSION, versionDate).body(bodyBuilder.build()); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(RecognizedText.class)); } /** * The Visual Recognition V3 service use an <code>api_key</code> instead of username and password. <br> * Use: {@link VisualRecognition#setApiKey(String)} * * @param username the username * @param password the password */ @Override @Deprecated public void setUsernameAndPassword(String username, String password) { throw new IllegalArgumentException("This service requires an api_key. Use the setApiKey() method instead"); } /** * Gets the collections. * * @return the collections */ public ServiceCall<List<Collection>> getCollections() { RequestBuilder requestBuilder = RequestBuilder.get(PATH_COLLECTIONS).query(VERSION, versionDate); ResponseConverter<List<Collection>> converter = ResponseConverterUtils .getGenericObject(TYPE_LIST_COLLECTIONS, PARAM_COLLECTIONS); return createServiceCall(requestBuilder.build(), converter); } /** * Gets the collection information. * * @param collectionId the collection id * @return the collection */ public ServiceCall<Collection> getCollection(String collectionId) { Validator.isTrue((collectionId != null) && !collectionId.isEmpty(), "collectionId cannot be null or empty"); RequestBuilder requestBuilder = RequestBuilder.get(String.format(PATH_COLLECTION, collectionId)); requestBuilder.query(VERSION, versionDate); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(Collection.class)); } /** * Deletes a collection. * * @param collectionId the collection id * @return the service call */ public ServiceCall<Void> deleteCollection(String collectionId) { Validator.isTrue((collectionId != null) && !collectionId.isEmpty(), "collectionId cannot be null or empty"); RequestBuilder requestBuilder = RequestBuilder.delete(String.format(PATH_COLLECTION, collectionId)); requestBuilder.query(VERSION, versionDate); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getVoid()); } /** * Deletes a collection. * * @param collectionId the collection id * @param imageId the image id * @return the service call */ public ServiceCall<Void> deleteCollectionImage(String collectionId, String imageId) { Validator.isTrue((collectionId != null) && !collectionId.isEmpty(), "collectionId cannot be null or empty"); Validator.isTrue((imageId != null) && !imageId.isEmpty(), "imageId cannot be null or empty"); RequestBuilder requestBuilder = RequestBuilder .delete(String.format(PATH_COLLECTION_IMAGE, collectionId, imageId)); requestBuilder.query(VERSION, versionDate); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getVoid()); } /** * Adds the image to collection. * * @param options the options * @return the service call */ public ServiceCall<Void> addImageToCollection(AddImageToCollectionOptions options) { Validator.notNull(options, " options cannot be null"); Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.image()); bodyBuilder.addFormDataPart(PARAM_IMAGE_FILE, options.image().getName(), requestBody); if (options.metadata() != null && !options.metadata().isEmpty()) { String metadata = GsonSingleton.getGsonWithoutPrettyPrinting().toJson(options.metadata()); bodyBuilder.addFormDataPart(PARAM_METADATA, metadata); } RequestBuilder requestBuilder = RequestBuilder .post(String.format(PATH_COLLECTION_IMAGES, options.collectionId())); requestBuilder.query(VERSION, versionDate).body(bodyBuilder.build()); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getVoid()); } /** * Gets the collection images. * * @param collectionId the collection id * @return the collection images */ public ServiceCall<List<CollectionImage>> getCollectionImages(String collectionId) { Validator.isTrue((collectionId != null) && !collectionId.isEmpty(), "collectionId cannot be null or empty"); RequestBuilder requestBuilder = RequestBuilder.get(String.format(PATH_COLLECTION_IMAGES, collectionId)) .query(VERSION, versionDate); ResponseConverter<List<CollectionImage>> converter = ResponseConverterUtils .getGenericObject(TYPE_LIST_IMAGES, PARAM_IMAGES); return createServiceCall(requestBuilder.build(), converter); } /** * Gets the collection image. * * @param collectionId the collection id * @param imageId the imageId * @return the collection image */ public ServiceCall<CollectionImage> getCollectionImage(String collectionId, String imageId) { Validator.isTrue((collectionId != null) && !collectionId.isEmpty(), "collectionId cannot be null or empty"); Validator.isTrue((imageId != null) && !imageId.isEmpty(), "imageId cannot be null or empty"); RequestBuilder requestBuilder = RequestBuilder .get(String.format(PATH_COLLECTION_IMAGE, collectionId, imageId)).query(VERSION, versionDate); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(CollectionImage.class)); } /** * Find similar images. * * @param options the options * @return the service call */ public ServiceCall<List<CollectionImage>> findSimilarImages(FindSimilarImagesOptions options) { Validator.notNull(options, "'options' cannot be null"); Builder bodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM); RequestBody requestBody = RequestBody.create(HttpMediaType.BINARY_FILE, options.image()); bodyBuilder.addFormDataPart(PARAM_IMAGE_FILE, options.image().getName(), requestBody); RequestBuilder requestBuilder = RequestBuilder .post(String.format(PATH_FIND_SIMILAR_IMAGES, options.collectionId())); requestBuilder.query(VERSION, versionDate).body(bodyBuilder.build()); if (options.limit() != null) { requestBuilder.query(PARAM_LIMIT, options.limit()); } ResponseConverter<List<CollectionImage>> converter = ResponseConverterUtils .getGenericObject(TYPE_LIST_IMAGES, PARAM_SIMILAR_IMAGES); return createServiceCall(requestBuilder.build(), converter); } /** * Creates a collection. * * @param name the collection name * @return the service call */ public ServiceCall<Collection> createCollection(String name) { Validator.isTrue((name != null) && !name.isEmpty(), "collectionId cannot be null or empty"); RequestBody body = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart(PARAM_NAME, name) .build(); RequestBuilder requestBuilder = RequestBuilder.post(PATH_COLLECTIONS); requestBuilder.body(body); requestBuilder.query(VERSION, versionDate); requestBuilder.query(PARAM_NAME, name); return createServiceCall(requestBuilder.build(), ResponseConverterUtils.getObject(Collection.class)); } }