Новости в эти дни полны модных слов, таких как автоматизация, искусственный интеллект и машинное обучение. Вероятно, поэтому все больше и больше пользователей смартфонов начинают искать более умные приложения. Однако, как обычному разработчику приложений для Android, вам, вероятно, не хватает ресурсов, необходимых для создания таких приложений с нуля.
К счастью, Google недавно запустил платформу Cloud Machine Learning , которая предлагает нейронные сети, которые были предварительно подготовлены для выполнения различных задач. Большинство его моделей не только отличаются высокой точностью, но и постоянно совершенствуются. И угадайте, что? Вы можете использовать их, просто сделав несколько вызовов REST API!
В этом руководстве я познакомлю вас с платформой Cloud Machine Learning и покажу, как использовать ее для создания интеллектуального приложения для Android, которое может распознавать объекты реального мира и называть их на нескольких языках.
Предпосылки
Чтобы максимально использовать этот учебник, все что вам нужно это:
- последняя версия Android Studio
- устройство под управлением Android 4.4 или выше
- и аккаунт Google Cloud Platform
1. Получение ключа API
Чтобы иметь возможность использовать службы машинного обучения Google в своем приложении для Android, вам нужен ключ API. Вы можете получить его, создав новый проект в консоли Google Cloud Platform.
Начните с входа в консоль и нажатия кнопки « Создать новый проект» . В появившемся диалоговом окне дайте значимое имя проекту.
После того, как проект будет создан, перейдите в « Диспетчер API»> «Панель инструментов» и нажмите кнопку « Включить API» .
На следующем экране под заголовком Google Cloud Machine Learning вы увидите все доступные API-интерфейсы машинного обучения. В этом руководстве мы будем использовать только API Vision и Translation .
Чтобы включить Vision API, нажмите на его ссылку и нажмите кнопку « Включить» .
Аналогичным образом, чтобы включить API перевода, нажмите на его ссылку и нажмите кнопку « Включить» .
Вам понадобится всего один ключ, чтобы использовать оба API. Чтобы получить его, перейдите на вкладку « Учетные данные », нажмите кнопку « Создать учетные данные» и выберите ключ API .
Теперь вы должны увидеть всплывающее окно, содержащее ваш секретный ключ API.
2. Создание нового проекта Android
Запустите 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>
|
3. Использование Vision API
API Vision помогает создавать приложения, которые могут видеть и понимать среду пользователя. Обнаружение лица, обнаружение эмоций, оптическое распознавание символов и аннотирование изображений — вот некоторые из его многочисленных функций. На данный момент мы сосредоточимся только на мощной функции аннотации изображений, также называемой обнаружением меток, которая, на мой взгляд, очень полезна.
Как и следовало ожидать, API ожидает изображение в качестве одного из своих входных данных. Поэтому давайте теперь создадим экран, на котором пользователь сможет делать снимки с помощью камеры устройства.
Шаг 1: создайте макет
Макет нашего экрана должен иметь виджет « 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
с кнопкой. Мы определим это на следующем шаге.
Шаг 2. Создание намерения
Создав новое намерение с 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 очень точен даже при использовании миниатюр изображений.
Шаг 3: закодируйте изображение
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);
|
Шаг 4: обработать изображение
После всей этой тяжелой работы у вас есть все, что вам нужно для взаимодействия с 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 генерирует для него.
4. Использование API перевода
Cloud Translation API, как следует из его названия, может переводить текст на более чем 100 языков. Эффективно используя его, вы можете создавать умные приложения, которые могут общаться с вашими пользователями на их собственных языках.
На предыдущем шаге вы видели, что ярлыки, которые генерирует наше приложение, написаны на английском языке. Давайте теперь добавим кнопку для перевода этих меток на немецкий язык.
Шаг 1: Обновите макет
Добавьте виджет « 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
.
Шаг 2: переведите метки
Использование 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!