Статьи

Как использовать Google Cloud Machine Learning Services для Android

Новости в эти дни полны модных слов, таких как автоматизация, искусственный интеллект и машинное обучение. Вероятно, поэтому все больше и больше пользователей смартфонов начинают искать более умные приложения. Однако, как обычному разработчику приложений для Android, вам, вероятно, не хватает ресурсов, необходимых для создания таких приложений с нуля.

К счастью, Google недавно запустил платформу Cloud Machine Learning , которая предлагает нейронные сети, которые были предварительно подготовлены для выполнения различных задач. Большинство его моделей не только отличаются высокой точностью, но и постоянно совершенствуются. И угадайте, что? Вы можете использовать их, просто сделав несколько вызовов REST API!

В этом руководстве я познакомлю вас с платформой Cloud Machine Learning и покажу, как использовать ее для создания интеллектуального приложения для Android, которое может распознавать объекты реального мира и называть их на нескольких языках.

Чтобы максимально использовать этот учебник, все что вам нужно это:

Чтобы иметь возможность использовать службы машинного обучения Google в своем приложении для Android, вам нужен ключ API. Вы можете получить его, создав новый проект в консоли Google Cloud Platform.

Начните с входа в консоль и нажатия кнопки « Создать новый проект» . В появившемся диалоговом окне дайте значимое имя проекту.

Создать новый проект

После того, как проект будет создан, перейдите в « Диспетчер API»> «Панель инструментов» и нажмите кнопку « Включить API» .

На следующем экране под заголовком Google Cloud Machine Learning вы увидите все доступные API-интерфейсы машинного обучения. В этом руководстве мы будем использовать только API Vision и Translation .

Библиотека машинного обучения

Чтобы включить Vision API, нажмите на его ссылку и нажмите кнопку « Включить» .

Включить Vision API

Аналогичным образом, чтобы включить API перевода, нажмите на его ссылку и нажмите кнопку « Включить» .

Вам понадобится всего один ключ, чтобы использовать оба API. Чтобы получить его, перейдите на вкладку « Учетные данные », нажмите кнопку « Создать учетные данные» и выберите ключ API .

Выберите ключ API

Теперь вы должны увидеть всплывающее окно, содержащее ваш секретный ключ API.

Запустите Android Studio и создайте новый проект с пустым действием. Я предлагаю вам выбрать как минимум уровень API 19 для минимально поддерживаемого SDK.

Хотя вам и не нужно, всегда полезно использовать надежную сетевую библиотеку для взаимодействия с платформой Google Cloud Machine Learning. В этом уроке мы будем использовать одну такую ​​библиотеку под названием Fuel . Добавьте его как зависимость compile в файл build.gradle модуля app :

1
compile ‘com.github.kittinunf.fuel:fuel-android:1.5.0’

Нажмите Sync Now, чтобы обновить ваш проект.

Затем нашему приложению потребуется разрешение INTERNET для связи с серверами Google. Поэтому добавьте следующую строку в файл манифеста проекта:

1
<uses-permission android:name=»android.permission.INTERNET»/>

Наконец, добавьте ваш ключ API в файл values ​​/ strings.xml :

1
<string name=»mykey»>ABCDEF12345-abcdef12345-123</string>

API Vision помогает создавать приложения, которые могут видеть и понимать среду пользователя. Обнаружение лица, обнаружение эмоций, оптическое распознавание символов и аннотирование изображений — вот некоторые из его многочисленных функций. На данный момент мы сосредоточимся только на мощной функции аннотации изображений, также называемой обнаружением меток, которая, на мой взгляд, очень полезна.

Как и следовало ожидать, API ожидает изображение в качестве одного из своих входных данных. Поэтому давайте теперь создадим экран, на котором пользователь сможет делать снимки с помощью камеры устройства.

Макет нашего экрана должен иметь виджет « Button пользователь может нажать, чтобы сделать снимок, виджет « ImageView для отображения изображения и виджет « TextView для отображения меток или аннотаций, сгенерированных API. Соответственно, добавьте следующий код в XML-файл макета вашей деятельности:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<Button android:text=»Take a picture»
    android:layout_width=»match_parent»
    android:layout_height=»wrap_content»
    android:id=»@+id/takePictureButton»
    android:onClick=»takePicture»/>
 
<ImageView
    android:layout_width=»wrap_content»
    android:layout_height=»wrap_content»
    android:layout_below=»@id/takePictureButton»
    android:id=»@+id/previewImage»
    android:layout_centerHorizontal=»true»/>
 
<TextView
    android:layout_width=»wrap_content»
    android:layout_height=»wrap_content»
    android:id=»@+id/resultsText»
    android:layout_below=»@id/previewImage»/>

Обратите внимание, что мы связали обработчик onClick с кнопкой. Мы определим это на следующем шаге.

Создав новое намерение с ACTION_IMAGE_CAPTURE действия ACTION_IMAGE_CAPTURE и передав его startActivityForResult() , вы можете попросить приложение камеры по умолчанию на устройстве пользователя сделать снимки и передать их в ваше приложение. Поэтому добавьте следующий код в ваш класс Activity :

1
2
3
4
5
6
public final static int MY_REQUEST_CODE = 1;
 
public void takePicture(View view) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(intent, MY_REQUEST_CODE);
}

Для получения изображений, снятых приложением камеры по умолчанию, необходимо переопределить метод onActivityResult() вашего класса Activity . Внутри метода вы получите доступ к объекту Bundle содержащему все данные изображения. Вы можете визуализировать данные изображения, просто преобразовав их в Bitmap и передав его в виджет ImageView .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
@Override
protected void onActivityResult(int requestCode, int resultCode,
                                Intent data) {
    if(requestCode == MY_REQUEST_CODE && resultCode == RESULT_OK) {
 
        // Convert image data to bitmap
        Bitmap picture = (Bitmap)data.getExtras().get(«data»);
 
        // Set the bitmap as the source of the ImageView
        ((ImageView)findViewById(R.id.previewImage))
                                .setImageBitmap(picture);
 
        // More code goes here
    }
}

Если вы запустите приложение сейчас и сделаете снимок, вы увидите, что размер картинки довольно мал. Впрочем, все в порядке — API Cloud Vision очень точен даже при использовании миниатюр изображений.

API Vision не может напрямую использовать объекты Bitmap . Вместо этого он ожидает закодированную Base64 строку сжатых данных изображения.

Чтобы сжать данные изображения, вы можете использовать метод compress() класса Bitmap . В качестве аргументов метод ожидает использования формата сжатия, требуемого качества вывода и объекта ByteArrayOutputStream . Следующий код сжимает растровое изображение в формате JPEG, а также обеспечивает достаточно высокое качество получаемого изображения:

1
2
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
picture.compress(Bitmap.CompressFormat.JPEG, 90, byteStream);

Теперь вы можете сгенерировать строку Base64 с помощью encodeToString() класса Base64 .

1
2
String base64Data = Base64.encodeToString(byteStream.toByteArray(),
                                         Base64.URL_SAFE);

После всей этой тяжелой работы у вас есть все, что вам нужно для взаимодействия с Vision API. Начните с создания строки, содержащей как URL-адрес API, так и ваш ключ API:

1
2
3
String requestURL =
       «https://vision.googleapis.com/v1/images:annotate?key=» +
        getResources().getString(R.string.mykey);

Чтобы отправить данные в API, необходимо создать запрос HTTP POST. Тело запроса должно быть документом JSON, содержащим данные изображения в кодировке Base64. Для обнаружения метки документ также должен иметь массив features содержащий значение LABEL_DETECTION . Вот формат документа JSON:

1
2
3
4
5
6
7
8
{«requests»: [{
      «image»: {
        «content»: <Base64-encoded image data>
      },
      «features»: [{
          «type»: «LABEL_DETECTION»
      }]
}]}

Хотя можно вручную кодировать документ JSON, его создание программно менее подвержено ошибкам. В следующем коде показано, как это сделать с помощью классов JSONObject и JSONArray :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Create an array containing
// the LABEL_DETECTION feature
JSONArray features = new JSONArray();
JSONObject feature = new JSONObject();
feature.put(«type», «LABEL_DETECTION»);
features.put(feature);
 
// Create an object containing
// the Base64-encoded image data
JSONObject imageContent = new JSONObject();
imageContent.put(«content», base64Data);
 
// Put the array and object into a single request
// and then put the request into an array of requests
JSONArray requests = new JSONArray();
JSONObject request = new JSONObject();
request.put(«image», imageContent);
request.put(«features», features);
requests.put(request);
JSONObject postData = new JSONObject();
postData.put(«requests», requests);
 
// Convert the JSON into a
// string
String body = postData.toString();

На этом этапе мы можем использовать метод post() класса Fuel для выполнения HTTP-запроса POST. В качестве единственного аргумента метод ожидает URL-адрес API. Вы также должны включить в запрос заголовки content-length и content-type . Для этого используйте метод header() . Аналогично, чтобы добавить документ JSON в тело запроса POST, используйте метод post() .

Наконец, вызвав метод responseString() и передав ему новый экземпляр класса Handler , вы можете асинхронно получить ответ на запрос в виде строки. Соответственно добавьте следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
Fuel.post(requestURL)
   .header(
       new Pair<String, Object>(«content-length», body.length()),
       new Pair<String, Object>(«content-type», «application/json»)
       )
   .body(body.getBytes())
   .responseString(new Handler<String>() {
       @Override
       public void success(@NotNull Request request,
                           @NotNull Response response,
                           String data) {
           // More code goes here
       }
 
       @Override
       public void failure(@NotNull Request request,
                           @NotNull Response response,
                           @NotNull FuelError fuelError) {}
   });

Когда вы используете функцию обнаружения меток, Vision API возвращает документ JSON, содержащий метки. Вместе с каждым ярлыком вы также получаете оценку, указывающую, насколько точным является ярлык. Документ выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
{«responses»:[{
  «labelAnnotations»: [
    {
      «mid»: «/m/id1»,
      «description»: «label1»,
      «score»: 0.91
    },
    {
      «mid»: «/m/id2»,
      «description»: «label2»,
      «score»: 0.90
    },
  …
}]}

А пока давайте просто labelAnnotations по всем объектам в массиве labelAnnotations и добавим значение каждого ключа description в виджет TextView нашего макета. Вот как вы можете сделать это внутри метода success() класса Handler :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
// Access the labelAnnotations arrays
JSONArray labels = new JSONObject(data)
                    .getJSONArray(«responses»)
                    .getJSONObject(0)
                    .getJSONArray(«labelAnnotations»);
 
String results = «»;
 
// Loop through the array and extract the
// description key for each item
for(int i=0;i<labels.length();i++) {
    results = results +
              labels.getJSONObject(i).getString(«description») +
              «\n»;
}
 
// Display the annotations inside the TextView
((TextView)findViewById(R.id.resultsText)).setText(results);

Теперь вы можете запустить приложение, сфотографировать любой объект поблизости и увидеть метки, которые Vision API генерирует для него.

Ярлыки, созданные для ноутбука

Cloud Translation API, как следует из его названия, может переводить текст на более чем 100 языков. Эффективно используя его, вы можете создавать умные приложения, которые могут общаться с вашими пользователями на их собственных языках.

На предыдущем шаге вы видели, что ярлыки, которые генерирует наше приложение, написаны на английском языке. Давайте теперь добавим кнопку для перевода этих меток на немецкий язык.

Добавьте виджет « Button в конец макета своей деятельности, используя следующий код:

1
2
3
4
5
6
<Button
   android:layout_width=»match_parent»
   android:layout_height=»wrap_content»
   android:layout_below=»@+id/resultsText»
   android:text=»in German»
   android:onClick=»translateToGerman»/>

Обратите внимание, что эта кнопка также имеет обработчик события onClick, который должен быть определен в вашем классе Activity .

Использование API перевода гораздо проще, чем использование Vision API, в первую очередь потому, что вам не нужно создавать документ JSON для определения вашего запроса. Вместо этого вы можете просто передать ему параметры в строке запроса.

Создайте метод translateToGerman() и внутри него создайте строку, содержащую URL-адрес API перевода.

1
2
3
4
5
6
public void translateToGerman(View view) {
    String requestURL =
        «https://translation.googleapis.com/language/translate/v2»;
 
    // More code goes here
}

Чтобы добавить поля в строку запроса указанного выше URL, мы можем использовать объекты List of Pair . Важны следующие поля:

  • key , указав ваш секретный ключ API
  • source указанием языка, с которого вы хотите перевести
  • target , указав язык, на который вы хотите перевести
  • q , указав текст, который вы хотите перевести

Следующий код показывает, как настроить API для перевода с английского на немецкий:

1
2
3
4
5
6
7
8
List<Pair<String, String>> params = new ArrayList<>();
// Add API key
params.add(new Pair<String, String>(«key»,
                        getResources().getString(R.string.mykey)));
 
// Set source and target languages
params.add(new Pair<String, String>(«source», «en»));
params.add(new Pair<String, String>(«target», «de»));

Поскольку строка запроса может содержать несколько полей q , мы добавим по одному для каждой метки, присутствующей в виджете TextView нашего макета. Вот как:

1
2
3
4
5
6
String[] queries = ((TextView)findViewById(R.id.resultsText))
                    .getText().toString().split(«\n»);
 
for(String query:queries) {
    params.add(new Pair<String, String>(«q», query));
}

Наконец, вы можете вызвать метод get() класса Fuel чтобы сделать HTTP-запрос GET к API перевода. На этот раз вы также можете использовать метод responseString() для асинхронного получения ответа в виде строки.

01
02
03
04
05
06
07
08
09
10
11
12
13
Fuel.get(requestURL, params).responseString(new Handler<String>() {
    @Override
    public void success(@NotNull Request request,
                        @NotNull Response response,
                        String data) {
        // More code here
    }
 
    @Override
    public void failure(@NotNull Request request,
                        @NotNull Response response,
                        @NotNull FuelError fuelError) { }
});

Ответ API перевода — это документ JSON, содержащий массив переводов. Он имеет следующий формат:

1
2
3
4
5
6
7
8
9
{ «data»: { «translations»: [
  {
    «translatedText»: «….»
  },
  {
    «translatedText»: «….»
  },
  …
] } }

А пока, внутри метода success() класса Handler , давайте просто пройдемся по массиву translations вышеприведенного документа JSON и обновим содержимое виджета TextView используя значения, связанные с ключами translatedText .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
// Access the translations array
JSONArray translations = new JSONObject(data)
                        .getJSONObject(«data»)
                        .getJSONArray(«translations»);
 
// Loop through the array and extract the translatedText
// key for each item
String result = «»;
for(int i=0;i<translations.length();i++) {
    result += translations.getJSONObject(i)
                .getString(«translatedText») + «\n»;
}
 
// Update the contents of the TextView widget
((TextView)findViewById(R.id.resultsText)).setText(result);

Если вы сейчас запустите приложение, сгенерируете метки для картинки и нажмете вторую кнопку, вы сможете увидеть метки на немецком языке.

Этикетки на немецком языке

В этом руководстве вы узнали, как использовать API-интерфейсы Cloud Vision и Cloud Translation, которые являются частью платформы Google Cloud Machine Learning, в приложении для Android. Платформа предлагает еще много таких API. Вы можете узнать о них больше, обратившись к официальной документации .

Пока вы здесь, ознакомьтесь с некоторыми другими нашими учебниками о том, как использовать машинное обучение и облачные сервисы в своих приложениях для Android!

  • Android вещи
    Android вещи и машинное обучение
    Пол Требилкокс-Руис
  • Android SDK
    Начните с Firebase для Android
  • Android SDK
    Бессерверные приложения с облачными функциями Firebase
    Чике Мгбемена