Для вызова API Google Cloud из вашего приложения необходимо создать промежуточный REST API, который обрабатывает авторизацию и защищает секретные значения, такие как ключи API. Затем вам нужно написать код в вашем мобильном приложении для аутентификации и взаимодействия с этим промежуточным сервисом.
Один из способов создания такого REST API — использование Firebase Authentication and Functions, который предоставляет управляемый бессерверный шлюз к API Google Cloud, обрабатывающий аутентификацию и доступный для вызова из мобильного приложения с помощью предварительно настроенных SDK.
В этом руководстве показано, как использовать этот метод для вызова API Cloud Vision из вашего приложения. Этот метод позволит всем авторизованным пользователям получать доступ к платным услугам Cloud Vision через ваш облачный проект, поэтому, прежде чем продолжить, подумайте, достаточно ли этого механизма аутентификации для вашего случая.
Прежде чем начать
Настройте свой проект
- Если вы еще этого не сделали, добавьте Firebase в свой Android-проект .
Если вы еще не включили облачные API для своего проекта, сделайте это сейчас:
- Откройте страницу API Firebase ML в консоли Firebase .
Если вы еще не перевели свой проект на тарифный план Blaze с оплатой по факту использования , нажмите «Обновить» , чтобы сделать это. (Вам будет предложено обновить тарифный план только в том случае, если ваш проект не подключен к тарифному плану Blaze.)
Использовать облачные API могут только проекты, использующие тарифный план Blaze.
- Если облачные API еще не включены, нажмите «Включить облачные API» .
- Настройте существующие ключи API Firebase, чтобы запретить доступ к API Cloud Vision:
- Откройте страницу «Учетные данные» в консоли Cloud.
- Для каждого ключа API в списке откройте окно редактирования и в разделе «Ограничения по ключу» добавьте в список все доступные API, кроме Cloud Vision API.
Разверните вызываемую функцию
Далее разверните облачную функцию, которую вы будете использовать для связи вашего приложения с API Cloud Vision. В репозитории functions-samples содержится пример, который вы можете использовать.
По умолчанию доступ к API Cloud Vision через эту функцию будет разрешен только авторизованным пользователям вашего приложения. Вы можете изменить функцию в соответствии с другими требованиями.
Для развертывания функции:
- Клонируйте или скачайте репозиторий functions-samples и перейдите в каталог
Node-1st-gen/vision-annotate-image:git clone https://github.com/firebase/functions-samplescd Node-1st-gen/vision-annotate-image - Установите зависимости:
cd functionsnpm installcd .. - Если у вас нет Firebase CLI, установите его .
- Создайте проект Firebase в каталоге
vision-annotate-image. При появлении запроса выберите свой проект в списке.firebase init
- Разверните функцию:
firebase deploy --only functions:annotateImage
Добавьте Firebase Auth в ваше приложение.
Вызываемая выше функция будет отклонять любые запросы от неаутентифицированных пользователей вашего приложения. Если вы еще этого не сделали, вам необходимо добавить Firebase Auth в ваше приложение.
Добавьте необходимые зависимости в ваше приложение.
<project>/<app-module>/build.gradle.kts или <project>/<app-module>/build.gradle ): implementation("com.google.firebase:firebase-functions:22.1.0") implementation("com.google.code.gson:gson:2.8.6")
Теперь вы готовы подписывать изображения.
1. Подготовьте входное изображение.
Для вызова Cloud Vision изображение должно быть отформатировано как строка, закодированная в base64. Для обработки изображения из сохраненного файла по URI:- Получите изображение в виде объекта
Bitmap:Kotlin
var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
Java
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
- При желании можно уменьшить масштаб изображения для экономии трафика. См. рекомендуемые размеры изображений в Cloud Vision.
Kotlin
private fun scaleBitmapDown(bitmap: Bitmap, maxDimension: Int): Bitmap { val originalWidth = bitmap.width val originalHeight = bitmap.height var resizedWidth = maxDimension var resizedHeight = maxDimension if (originalHeight > originalWidth) { resizedHeight = maxDimension resizedWidth = (resizedHeight * originalWidth.toFloat() / originalHeight.toFloat()).toInt() } else if (originalWidth > originalHeight) { resizedWidth = maxDimension resizedHeight = (resizedWidth * originalHeight.toFloat() / originalWidth.toFloat()).toInt() } else if (originalHeight == originalWidth) { resizedHeight = maxDimension resizedWidth = maxDimension } return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false) }
Java
private Bitmap scaleBitmapDown(Bitmap bitmap, int maxDimension) { int originalWidth = bitmap.getWidth(); int originalHeight = bitmap.getHeight(); int resizedWidth = maxDimension; int resizedHeight = maxDimension; if (originalHeight > originalWidth) { resizedHeight = maxDimension; resizedWidth = (int) (resizedHeight * (float) originalWidth / (float) originalHeight); } else if (originalWidth > originalHeight) { resizedWidth = maxDimension; resizedHeight = (int) (resizedWidth * (float) originalHeight / (float) originalWidth); } else if (originalHeight == originalWidth) { resizedHeight = maxDimension; resizedWidth = maxDimension; } return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false); }
Kotlin
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640)
Java
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640);
- Преобразуйте объект растрового изображения в строку, закодированную в формате Base64:
Kotlin
// Convert bitmap to base64 encoded string val byteArrayOutputStream = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val imageBytes: ByteArray = byteArrayOutputStream.toByteArray() val base64encoded = Base64.encodeToString(imageBytes, Base64.NO_WRAP)
Java
// Convert bitmap to base64 encoded string ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream); byte[] imageBytes = byteArrayOutputStream.toByteArray(); String base64encoded = Base64.encodeToString(imageBytes, Base64.NO_WRAP);
Изображение, представленное объектом
Bitmap , должно быть прямым, без необходимости дополнительного поворота. 2. Вызовите вызываемую функцию для присвоения метки изображению.
Для маркировки объектов на изображении вызовите функцию, передав ей JSON-запрос Cloud Vision .Сначала инициализируйте экземпляр Cloud Functions:
Kotlin
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functionsJava
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();Определите метод для вызова функции:
Kotlin
private fun annotateImage(requestJson: String): Task<JsonElement> { return functions .getHttpsCallable("annotateImage") .call(requestJson) .continueWith { task -> // This continuation runs on either success or failure, but if the task // has failed then result will throw an Exception which will be // propagated down. val result = task.result?.data JsonParser.parseString(Gson().toJson(result)) } }Java
private Task<JsonElement> annotateImage(String requestJson) { return mFunctions .getHttpsCallable("annotateImage") .call(requestJson) .continueWith(new Continuation<HttpsCallableResult, JsonElement>() { @Override public JsonElement then(@NonNull Task<HttpsCallableResult> task) { // This continuation runs on either success or failure, but if the task // has failed then getResult() will throw an Exception which will be // propagated down. return JsonParser.parseString(new Gson().toJson(task.getResult().getData())); } }); }Создайте JSON-запрос, установив тип
LABEL_DETECTION:Kotlin
// Create json request to cloud vision val request = JsonObject() // Add image to request val image = JsonObject() image.add("content", JsonPrimitive(base64encoded)) request.add("image", image) // Add features to the request val feature = JsonObject() feature.add("maxResults", JsonPrimitive(5)) feature.add("type", JsonPrimitive("LABEL_DETECTION")) val features = JsonArray() features.add(feature) request.add("features", features)Java
// Create json request to cloud vision JsonObject request = new JsonObject(); // Add image to request JsonObject image = new JsonObject(); image.add("content", new JsonPrimitive(base64encoded)); request.add("image", image); //Add features to the request JsonObject feature = new JsonObject(); feature.add("maxResults", new JsonPrimitive(5)); feature.add("type", new JsonPrimitive("LABEL_DETECTION")); JsonArray features = new JsonArray(); features.add(feature); request.add("features", features);Наконец, вызовите функцию:
Kotlin
annotateImage(request.toString()) .addOnCompleteListener { task -> if (!task.isSuccessful) { // Task failed with an exception // ... } else { // Task completed successfully // ... } }Java
annotateImage(request.toString()) .addOnCompleteListener(new OnCompleteListener<JsonElement>() { @Override public void onComplete(@NonNull Task<JsonElement> task) { if (!task.isSuccessful()) { // Task failed with an exception // ... } else { // Task completed successfully // ... } } });
3. Получите информацию об объектах с соответствующими обозначениями.
Если операция разметки изображения прошла успешно, в результате выполнения задачи будет возвращен JSON-ответ типа BatchAnnotateImagesResponse . Каждый объект в массивеlabelAnnotations представляет собой то, что было размечено на изображении. Для каждой метки можно получить текстовое описание метки, ее идентификатор сущности в Knowledge Graph (если доступен) и оценку достоверности совпадения. Например: Kotlin
for (label in task.result!!.asJsonArray[0].asJsonObject["labelAnnotations"].asJsonArray) {
val labelObj = label.asJsonObject
val text = labelObj["description"]
val entityId = labelObj["mid"]
val confidence = labelObj["score"]
}
Java
for (JsonElement label : task.getResult().getAsJsonArray().get(0).getAsJsonObject().get("labelAnnotations").getAsJsonArray()) {
JsonObject labelObj = label.getAsJsonObject();
String text = labelObj.get("description").getAsString();
String entityId = labelObj.get("mid").getAsString();
float score = labelObj.get("score").getAsFloat();
}