Машинное обучение Microsoft Azure — это мощный облачный сервис для предиктивной аналитики. Он позволяет вам определять модели и проводить эксперименты в облаке для анализа данных и поиска закономерностей.

Microsoft Azure Machine Learning Studio — это инструмент, предоставляющий интерфейс перетаскивания для создания моделей и быстрого их опробования. Вы можете поделиться своими моделями с сообществом, чтобы использовать или использовать то, чем поделились другие, через Cortana Intelligence Gallery.

Microsoft упаковала свои модели машинного обучения распознавания речи и лиц в Microsoft Cognitive Services (Project Oxford). Это не просто распознавание, API-интерфейсы могут извлекать гораздо больше контекста из данных, и поэтому приложения, которые мы создаем с их помощью, могут быть более продуктивными и персонализированными.

В этом эксперименте мы будем изучать Face API. Сначала мы поймем, что может предоставить служба, а затем рассмотрим вариант использования.

Майкрософт Фейс API,

Это часть Microsoft Cognitive Services, мощного облачного сервиса с функциями обнаружения и распознавания лиц.

  • Обнаружение лица означает поиск области лица на изображении. Он представлен прямоугольником. API также имеет функциональные возможности для поиска различных характеристик/атрибутов лица — возраста, пола, позы, очков и растительности на лице. Есть много онлайн-приложений, которые используют Face API в фоновом режиме.
  • Face Verification — это возможность найти сходство между двумя лицами. Этот API может сравнивать два лица, находить все похожие лица и тому подобное.

Чтобы понять, как использовать сервис, нам нужно сначала немного разобраться в организации данных. Иерархия объектов следующая:

Существуют группы пользователей, в которых может быть до 1000 человек. С каждым человеком может быть связано до 248 лиц. Изображение с лицом можно искать в группе людей, человеке или на фоне другого лица.

У вас может быть одна группа членов семьи, другая из друзей и тому подобное. Ваша группа членов семьи может иметь объект Person, и каждый из них представляет члена семьи. И тогда вы можете иметь несколько изображений лиц, связанных с каждым человеком. Он хорошо работает с изображениями лица и лица вблизи.

Поэтому, прежде чем мы начнем, активируйте бесплатную подписку на Face API в Microsoft Azure Cognitive Services. А здесь документация по API для ознакомления.

Давайте пойдем по принципу «сверху вниз»!

Группы лиц

Сначала мы создадим группу лиц. PersonGroup — это логическая группа лиц. Вы можете создать один универсальный набор или создать группы из нескольких человек в зависимости от варианта использования. Ограничение составляет 1000 объектов Person в одной PersonGroup.

public class PersonGroups
{
    public string personGroupId { get; set; }
    public string name { get; set; }
    public string userData { get; set; }
}

У PersonGroup есть идентификатор, который мы определяем, и мы предоставляем «имя». Мы также можем связать с ним некоторые «пользовательские данные». Например, описание или может быть что-то внутреннее для нашего использования.

public static async Task<string> AddPersonGroups(string personGroupId, string name, string userData)
{
    string uri = HttpHandler.BaseUri + "/" + personGroupId;
    string jsonString = "{\"name\":\"" + name + "\", \"userData\":\"" + userData + "\"}";
    HttpContent content = new StringContent(jsonString, Encoding.UTF8, "application/json");
    
    HttpResponseMessage response = await HttpHandler.client.PutAsync(uri, content);
    response.EnsureSuccessStatusCode();
    
    string responseBody = await response.Content.ReadAsStringAsync();
    return responseBody;
}
BaseUri: https://api.projectoxford.ai/face/v1.0/persongroups

лица

Объект person представляет человека в группе людей. Когда мы создаем новый объект человека, нам нужно связать его с personGroupId.

class Persons
{
    public string personId { get; set; }
    public string name { get; set; }
    public string userData { get; set; }
    public List persistedFaceIds { get; set; }
}

Нам нужно связать «имя» и можно связать «userData». PersonId возвращается при создании нового человека, но с ним не связано лицо.

public static async Task<string> CreatePerson(string personGroupId, string name, string userData)
{
    string uri = HttpHandler.BaseUri + "/" + personGroupId + "/persons";
    string jsonString = "{\"name\":\"" + name + "\", \"userData\":\"" + userData + "\"}";
    HttpContent content = new StringContent(jsonString, Encoding.UTF8, "application/json");
    
    HttpResponseMessage response = await HttpHandler.client.PostAsync(uri, content);
    response.EnsureSuccessStatusCode();
    
    string responseBody = await response.Content.ReadAsStringAsync();
    return responseBody;
}

Лица

Объект лица связан с Person и personGroup. С человеком может быть связано до 248 лиц.

public class FaceData
{
    public string persistedFaceId { get; set; }
    public string userData { get; set; }
}

На изображении может быть только одно детектируемое лицо (за исключением, для краткости, отброшенного).

public static async Task<string> AddPersonFace(string personGroupId, string personId, string userData)
{
    string uri = HttpHandler.BaseUri + "/" + personGroupId + "/persons/" + personId + "/persistedFaces?userData=" + userData + "&userData=" + userData;
    string jsonString = "{\"url\":\"" + HttpHandler.storagePath + "originals/" + userData + "\"}";
    HttpContent content = new StringContent(jsonString, Encoding.UTF8, "application/json");
    
    HttpResponseMessage response = await HttpHandler.client.PostAsync(uri, content);
    response.EnsureSuccessStatusCode();
    
    string responseBody = await response.Content.ReadAsStringAsync();
    return responseBody;
}

Обучение

Прежде чем мы сможем запустить какое-либо сравнение, нам нужно обучить нашу систему. Обучение заключается в запуске модели машинного обучения для группы людей, которую мы определили.

public static async Task<string> TrainPersonGroups(string personGroupId)
{
    string uri = HttpHandler.BaseUri + "/" + personGroupId + "/train";
    
    HttpResponseMessage response = await HttpHandler.client.PostAsync(uri, null);
    response.EnsureSuccessStatusCode();
    
    string responseBody = await response.Content.ReadAsStringAsync();
    return responseBody;
}

Он собирается создать внутреннюю базу функций и связать ее с различными объектами. Для лиц, которые мы связали с каждым человеком, будут сохранены некоторые извлеченные данные. Это не означает полное изображение, а набор функций, которые ML будет использовать для сравнения.

Статус обучения можно запросить, после завершения мы можем запустить наш сравнительный запрос!

public static async Task<string> StatusPersonGroups(string personGroupId)
{
    string uri = HttpHandler.BaseUri + "/" + personGroupId + "/training";
    
    HttpResponseMessage response = await HttpHandler.client.GetAsync(uri);
    response.EnsureSuccessStatusCode();
    
    string responseBody = await response.Content.ReadAsStringAsync();
    return responseBody;
}

Searching

Теперь наша установка машинного обучения заполнена данными и обучена. Давайте запросим лицо, чтобы идентифицировать человека!

Шаги просты

  1. Загрузите изображение-кандидат в хранилище BLOB-объектов. Я объяснил процесс ранее.
  2. Найдите FaceId всех лиц на изображении-кандидате
  3. Для всех FaceId на изображении-кандидате выполните идентификационный запрос.
  4. Идентификационный запрос возвращает кандидатов с PersonId и уровнем достоверности
  5. Преобразовать PersonId в имя человека

Несколько классов для представления данных, которые мы будем отправлять и получать:

        public class FaceRectangle
        {
            public int top { get; set; }
            public int left { get; set; }
            public int width { get; set; }
            public int height { get; set; }
        }
        public class Visitors
        {
            public string faceId { get; set; }
            public FaceRectangle faceRectangle { get; set; }
        }
        public class FaceQueryPayload
        {
            public string personGroupId { get; set; }
            public List faceIds { get; set; }
            public int maxNumOfCandidatesReturned { get; set; }
            public double confidenceThreshold { get; set; }
        }
        public class Candidate
        {
            public string personId { get; set; }
            public double confidence { get; set; }
        }
        public class CandidateObject
        {
            public string faceId { get; set; }
            public List candidates { get; set; }
        }

Отправка изображения-кандидата в хранилище BLOB-объектов

private async void btnTestPic_Click(object sender, RoutedEventArgs e)
        {
            btnTestPic.IsEnabled = false;
            FileOpenPicker filePicker = new FileOpenPicker();
            filePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
            filePicker.ViewMode = PickerViewMode.Thumbnail;
filePicker.FileTypeFilter.Clear();
            filePicker.FileTypeFilter.Add(".jpeg"); filePicker.FileTypeFilter.Add(".jpg");
            filePicker.FileTypeFilter.Add(".png"); filePicker.FileTypeFilter.Add(".gif");
StorageFile file = await filePicker.PickSingleFileAsync();
            
            CloudBlockBlob blob = null;
            string blobFileName = null;
            if (null != file)
            {
                BitmapImage bitmapImage = new BitmapImage();
                IRandomAccessStream fileStream = await file.OpenAsync(FileAccessMode.Read);
                bitmapImage.SetSource(fileStream);
                CapturedPhoto.Source = bitmapImage;
                CapturedPhoto.Tag = file.Path;
blobFileName = System.Guid.NewGuid() + "." + file.Name.Split('.').Last();
await HttpHandler.tempContainer.CreateIfNotExistsAsync();
                BlobContainerPermissions permissions = new BlobContainerPermissions();
                permissions.PublicAccess = BlobContainerPublicAccessType.Blob;
                await HttpHandler.tempContainer.SetPermissionsAsync(permissions);
                blob = HttpHandler.tempContainer.GetBlockBlobReference(blobFileName);
                await blob.DeleteIfExistsAsync();
                await blob.UploadFromFileAsync(file);
string uri = "https://api.projectoxford.ai/face/v1.0/detect?returnFaceId=true";
                string jsonString = "{\"url\":\"" + HttpHandler.storagePath + "visitors/" + blobFileName + "\"}";
                HttpContent content = new StringContent(jsonString, Encoding.UTF8, "application/json");
                HttpResponseMessage response = await HttpHandler.client.PostAsync(uri, content);
                response.EnsureSuccessStatusCode();
                string responseBody = await response.Content.ReadAsStringAsync();
List names = await VisitorCmds.CheckFace(responseBody, ((PersonGroups)cmbPersonGroup.SelectedItem).personGroupId);
                txtResponse.Text = string.Join(",", names.ToArray());
            }
            btnTestPic.IsEnabled = true;
        }
    }

Сравнение лиц

public static async Task<List> CheckFace(string responseString, string personGroupId)
        {
            List visitors = JsonConvert.DeserializeObject<List>(responseString);
            List faceIds = new List(); ;
            foreach (Visitors visitor in visitors)
            {
                faceIds.Add(visitor.faceId);
            }
FaceQueryPayload jsonPayLoad = new PhotoBooth.VisitorCmds.FaceQueryPayload();
            jsonPayLoad.personGroupId = personGroupId;
            jsonPayLoad.faceIds = faceIds;
            jsonPayLoad.maxNumOfCandidatesReturned = 1;
            jsonPayLoad.confidenceThreshold = 0.5;
string uri = "https://api.projectoxford.ai/face/v1.0/identify";
            string jsonString = JsonConvert.SerializeObject(jsonPayLoad);
            HttpContent content = new StringContent(jsonString, Encoding.UTF8, "application/json");
            HttpResponseMessage response = await HttpHandler.client.PostAsync(uri, content);
            response.EnsureSuccessStatusCode();
            string responseBody = await response.Content.ReadAsStringAsync();
            List candidates = JsonConvert.DeserializeObject<List>(responseBody);
List names = new List();
            foreach (CandidateObject candidate in candidates)
            {
                //get person from personId
                if (candidate.candidates.Count != 0)
                {
                    string name = await PersonCmds.GetPerson(personGroupId, candidate.candidates[0].personId);
                    names.Add(name);
                }
            }
            return names;
        }
    }

Я опубликую полный код на github вскоре после очистки и некоторой обработки ошибок. А пока, взломай!