NÃO reinvente a roda! Treine e faça predições na Visão Personalizada da Azure

Nos dois artigos anteriores — o primeiro referente aos Projetos e, o segundo referente as Tags —, tratamos da construção das primeiras partes para usufruirmos da Visão Personalizada da Azure. Agora iremos fazer predições na Visão Personalizada da Azure.

Para fazermos uma predição, precisamos:

1) dar uma imagem ao Serviço Cognitivo da Azure de Visão Personalizada, dizendo o que ela é, de acordo com as tags cadastradas;

2) treinar o projeto com as imagens devidamente categorizadas;

3) fornecer uma imagem para a predição.

Este artigo é uma continuação imediata do anterior, NÃO reinvente a roda! Gerencie suas tags na Visão Personalizada da Azure, que dividimos em três partes, sendo esta a terceira e última parte, além de continuar a série NÃO reinvente a roda!

Visão Personalizada no Azure

Assim como o Serviço de Visão Computacional, o Serviço de Visão Personalizada da Azure também é usado para analisar e detectar informações relevantes em imagens, com a diferença de ser completamente customizável para o seu domínio e ainda sem precisar de expertise em machine learning.

Este artigo foi dividido em três partes devido a quantidade de operações que podem ser feitas na Visão Personalizada, sendo esta a terceira e última parte, que tratará da predição nos Projetos de tal Serviço Cognitivo criados na primeira parte do artigo e continuado no artigo anterior. O projeto, em si, foi iniciado no primeiro artigo técnico da série NÃO reinvente a roda! Serviços cognitivos da Azure: Face e demos continuidade em todos os da série NÃO reinvente a roda!. Portanto, confere lá os outros artigos e fique por dentro de tudo que estamos fazendo.

Este artigo terá, finalmente, as predições na Visão Personalizada da Azure, referente aos Projetos e Tags criadas nos artigos passados.

Todo o código está disponível no github.

Então, vamos a mão na massa!

1. Tenha seu projeto

Criamos um projeto do tipo ASP.NET Core Web Application, com o template Web Application para ter o projeto criado voltado para o Razor Pages. Demos a ele o nome de AzureCognitiveServices. Confira no primeiro passo do primeiro artigo técnico da série e crie o seu projeto também.

O projeto CustomVision, do tipo Class Library (.NET Core) dentro da Solution Folder nomeada de Vision foi criado na primeira parte do artigo.

É importante que seu projeto esteja assim:

2. Configure a Azure e obtenha as informações do portal de Visão Personalizada

Para prosseguir, você vai precisar da chave e do endpoint do recurso de treinamento e do recurso de predição da Visão Computacional Personalizada do Azure, bem como as informações de dentro do portal de Visão Computacional.

Como obter tais informações estão detalhadas no artigo sobre como criar projetos para a Visão Personalizada da Azure e é imprescindível a leitura do mesmo, bem como a aquisição dessas informações.

3. Conecte a Predição do Serviço de Visão Personalizada ao seu projeto

Voltando ao projeto no Visual Studio abra o Package Manager Console e execute o seguinte comando para adicionar as bibliotecas do Serviço Cognitivo de Visão Personalizada ao projeto que chamamos de CustomVision.

dotnet add .CustomVision package Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction --version 1.0.0

4. Implemente os métodos de treinamento na classe Treinamento.cs

Essa classe já possui seus usings, variáveis e métodos. Porém, agora, vamos adicionar mais uma variável de instância, logo após a private const string _endpoint. Suas variáveis devem ficar assim:

private const string _chaveDeTreinamento = \"<sua chave de treinamento>\";
private const string _endpoint = \"<seu endpoint>\";
private const string _idDoRecursoDePredicao = \"<id do recurso de predição>\";

private readonly ICustomVisionTrainingClient _servicoCognitivoDeVisaoPersonalizadaTreinamento;

Adicionamos apenas uma variável a mais, que é referente ao id do recurso de predição criados na primeira parte do artigo. Não esqueça de trocar todas as informações que estão <> pelas suas informações.

Após isso, encontre o método ExcluirProjeto(), pois vamos modificá-lo.

Modifique o método e adicione o outro método abaixo:

public void ExcluirProjeto(string idDoProjeto)
{
    ExcluirIteracoes(idDoProjeto);
    _servicoCognitivoDeVisaoPersonalizadaTreinamento.DeleteProject(new Guid(idDoProjeto));
}

public void ExcluirIteracoes(string idDoProjeto)
{
    var iteracoes = _servicoCognitivoDeVisaoPersonalizadaTreinamento.GetIterations(new Guid(idDoProjeto));
    foreach (var iteracao in iteracoes.Where(i => !string.IsNullOrWhiteSpace(i.PublishName)))
        _servicoCognitivoDeVisaoPersonalizadaTreinamento.UnpublishIteration(new Guid(idDoProjeto), iteracao.Id);
}

Agora, como vamos treinar nossos projetos, teremos iterações. Cada iteração equivale a um treinamento realizado e publicado.

Para excluir um projeto é necessário, antes, excluir suas iterações publicadas e é isto que estamos fazendo.

Ao final da classe, após o último método (public Tag CarregarTag(Project projeto, string idDaTag)), adicione o código a seguir:

public void AdicionarImagemPorUrl(string idDoProjeto, string url, IEnumerable<string> idDastags)
{
    var urls = new List<ImageUrlCreateEntry>(1)
    { new ImageUrlCreateEntry(url) };

    _servicoCognitivoDeVisaoPersonalizadaTreinamento.CreateImagesFromUrls(
        new Guid(idDoProjeto),
        new ImageUrlCreateBatch(urls, idDastags.Select(id => new Guid(id)).ToList()));
}

public void AdicionarImagemPorArquivo(string idDoProjeto, string localDoArquivo, IEnumerable<string> idDastags)
{
    var arquivo = new FileStream(localDoArquivo, FileMode.Open);

    _servicoCognitivoDeVisaoPersonalizadaTreinamento.CreateImagesFromData(
        new Guid(idDoProjeto),
        arquivo,
        idDastags.Select(id => new Guid(id)).ToList());
}

public bool Treinar(string idDoProjeto, IEnumerable<Tag> tags)
{
    if (!PodeTreinarProjeto(tags)) return false;

    var treinamento = _servicoCognitivoDeVisaoPersonalizadaTreinamento.TrainProject(new Guid(idDoProjeto));

    while (\"Training\".Equals(treinamento.Status))
    {
        Thread.Sleep(1000);

        treinamento = _servicoCognitivoDeVisaoPersonalizadaTreinamento.GetIteration(new Guid(idDoProjeto), treinamento.Id);
    }

    EnviarResultadosParaPedicao(idDoProjeto, treinamento);

    return true;
}

private static bool PodeTreinarProjeto(IEnumerable<Tag> tags) =>
    tarsal(t => t.ImageCount >= 5);

private void EnviarResultadosParaPedicao(string idDoProjeto, Iteration treinamento)
{
    var nomeDePublicacao = $\"pub{DateTime.Now:ddMMyyHHmmss}\";
    _servicoCognitivoDeVisaoPersonalizadaTreinamento.PublishIteration(new Guid(idDoProjeto), treinamento.Id, nomeDePublicacao, _idDoRecursoDePredicao);
}

public bool PodePredizer(string idDoProjeto) =>
    _servicoCognitivoDeVisaoPersonalizadaTreinamento.GetIterations(new Guid(idDoProjeto)).Count > 0;

public string RetornarUltimaIteracaoRealizada(string idDoProjeto) =>
    _servicoCognitivoDeVisaoPersonalizadaTreinamento.GetIterations(new Guid(idDoProjeto))
    .Where(i => !string.IsNullOrWhiteSpace(i.PublishName))
    .OrderByDescending(i => i.Created)
    .FirstOrDefault()
    .PublishName;

Os primeiros dois métodos que criamos são similares aos dos outros Serviços Cognitivos da Azure, enviamos uma foto ao Serviço com duas opções diferentes, uma por URL e outra por arquivo.

Em seguida, temos o método de treinamento que recebe o id do Projeto que iremos treinar, bem como suas Tags. As Tags serão utilizadas para verificar se o Projeto pode, ou não, executar um treino. Para executar um treino, o Projeto precisa que todas suas Tags tenham, pelo menos, 5 imagens cada.

Depois, há os métodos responsáveis por publicar o resultado do treinamento — a iteração — e, assim, estarem disponíveis para a predição.

Para predizer, é necessário que exista pelo menos uma iteração publicada e é essa verificação que o método bool PodePredizer(string idDoProjeto) faz. Já o método string RetornarUltimaIteracaoRealizada(string idDoProjeto) retorna a última iteração realizada, pois precisamos dizer, na hora de predizer, qual a iteração que utilizaremos e, para isso, utilizaremos a última iteração publicada.

A classe inteira pode ser visualizada no github.

5. Crie o arquivo AdicaoDeFotos.cshtml no projeto AzureCognitiveServices

Dentro do projeto AzureCognitiveServices utilize a pasta CustomVision, dentro da pasta Vision que está dentro de Pages para adicionar uma Razor Page e a chame de AdicaoDeFotos.cshtml. Lembrando que essa pasta foi criada na primeira parte do artigo.

Nela, criaremos o formulário responsável por ter adicionar fotos aos Projetos e às Tags que ele contém. Para isso, temos os campos para adicionar uma foto por URL, ou por arquivo, além da possibilidade de selecionar as Tags que definem a imagem que será enviada.

Vamos, primeiro, editar o código do servidor. Para isso, clique com o botão direito no arquivo aberto e vá em Go to PageModel ou aperte o F7.

Esse arquivo é responsável pelo código C# desta página. Comece colocando os usings.

using CustomVision;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training.Models;
using System.Collections.Generic;
using System.IO;
using System.Linq;

Adicione agora as variáveis que utilizaremos na classe.

namespace AzureCognitiveServices
{
    public class TreinamentoModel : PageModel
    {
        private readonly IWebHostEnvironment _environment;
        private readonly Treinamento _treinamento;

        public Project Projeto { get; private set; }

        public IEnumerable<Tag> TagsDoProjeto { get; private set; }

        public string MensagemFotos { get; private set; }

        public bool ErroFotos { get; private set; }

        public string MensagemTreinamento { get; private set; }

        public bool ErroTreinamento { get; private set; }

        [BindProperty]
        public IFormFile Arquivo { get; set; }

        [BindProperty]
        public IList<string> Tags { get; set; }

Temos diversas variáveis que utilizaremos nessa classe, mas todas bem simples de compreender.

  • IWebHostEnvironment _environment é responsável por informações sobre o ambiente onde a aplicação está rodando. Utilizaremos ela pra copiar o arquivo enviado e criar uma pasta no servidor;
  • Treinamento _treinamento é responsável por acessar os métodos que criamos em 4. Implemente os métodos de treinamento na classe Treinamento.cs;
  • Project Projeto é o Projeto ao qual estamos adicionando as fotos;
  • IEnumerable<Tag> TagsDoProjeto são as Tags que identificam as fotos que estamos enviando;
  • public string MensagemFotos e public bool ErroFotos são responsáveis por exibir e controlar as mensagens de sucesso e/ou erro ao enviar fotos
  • public string MensagemTreinamento e public bool ErroTreinamento são responsáveis por exibir e controlar as mensagens de sucesso e/ou erro ao tentar treinar o Projeto;
  • public IFormFile Arquivo e public IList<string> Tags marcadas com [BindProperty], são, por fim, responsáveis por capturar as informações do formulário HTML referentes ao arquivo possivelmente enviado e as Tags marcadas para aquela imagem.

Crie o método construtor.

public TreinamentoModel(IWebHostEnvironment environment)
{
    _environment = environment;
    _treinamento = new Treinamento();
}

Aqui, no método construtor, estamos usando injeção de dependência para capturarmos as informações de ambiente, além de criamos uma nova instância de Treinamento() para ser usada no restante da classe.

Agora crie o método get que será executado sempre que esta página for aberta e o método que ele chama.

public void OnGet(string idDoProjeto) =>
    CarregarProjeto(idDoProjeto);

private void CarregarProjeto(string idDoProjeto)
{
    Projeto = _treinamento.ListarProjetos().FirstOrDefault(p => idDoProjeto.Equals(p.Id.ToString()));
    TagsDoProjeto = _treinamento.ListarTags(Projeto);
}

O método get apenas recebe o id do Projeto por parâmetro, na URL e chama o método CarregarProjeto(), este, como o nome sugere, carrega as informações referentes ao Projeto e lista todas as Tags do mesmo.

Por último, crie o método post, assim como método que ele chama e não esqueça de fechar todas as chaves da classe.

public void OnPost(string idDoProjeto)
{
    if (ErroAoPreencherCampos())
    {
        CarregarProjeto(idDoProjeto);
        return;
    }

    if (Arquivo is null)
    {
        var url = Request.Form[\"url\"];
        _treinamento.AdicionarImagemPorUrl(idDoProjeto, url, Tags);
    }
    else
    {
        var pasta = Path.Combine(_environment.ContentRootPath, \"imagens\");
        if (!Directory.Exists(pasta))
            Directory.CreateDirectory(pasta);

        var caminhoDoArquivo = Path.Combine(_environment.ContentRootPath, \"imagens\", Arquivo.FileName);

        using (var fileStream = new FileStream(caminhoDoArquivo, FileMode.Create))
            Arquivo.CopyTo(fileStream);

        _treinamento.AdicionarImagemPorArquivo(idDoProjeto, caminhoDoArquivo, Tags);

        Directory.Delete(pasta, true);
    }

    CarregarProjeto(idDoProjeto);

    MensagemFotos = \"Imagem adicionada com sucesso!\";

    if (!_treinamento.Treinar(idDoProjeto, TagsDoProjeto))
    {
        ErroTreinamento = true;
        MensagemTreinamento = \"Porém, não é possível TREINAR o modelo sem que TODAS as tags tenham, ao menos, 5 fotos cada.\";
        return;
    }

    MensagemTreinamento = \"Treinamento realizado com sucesso!\";
}

private bool ErroAoPreencherCampos()
{
    var url = Request.Form[\"url\"];

    if (string.IsNullOrWhiteSpace(url) && Arquivo is null)
    {
        MensagemFotos = \"É necessário enviar uma imagem por URL OU por arquivo.\";
        return ErroFotos = true;
    }

    if (!string.IsNullOrWhiteSpace(url) && !(Arquivo is null))
    {
        MensagemFotos = \"É necessário escolher apenas UM método de envio de imagem.\";
        return ErroFotos = true;
    }

    if (!Tags.Any())
    {
        MensagemFotos = \"É necessário escolher pelo menos uma tag.\";
        return ErroFotos = true;
    }

    return ErroFotos = false;
}
}
}

O método post também recebe o id do Projeto como parâmetro para saber de qual Projeto estamos tratando, depois ele chama o método responsável por verificar erro nos preenchimentos dos campos, caso exista algum erro, ele para a execução com a instrução return, depois de carregar as informações do Projeto novamente para exibí-las na tela.

Como teremos dois formulários no HTML, seguimos verificando qual deles estamos usando para poder chamar o método correto para adicionar a foto.

Portanto, caso a interface IFormFile Arquivo seja nula, significa que o usuário optou por enviar uma URL, então simplesmente capturamos a informação enviada pelo campo url do formulário HTML e o enviamos para a adição através da url.

Caso a interface não seja nula, temos alguns passos a mais. Primeiro começamos copiando a imagem enviada e colocando no servidor, numa pasta própria. Depois pegamos o caminho do arquivo criado e enviamos para adição através do método que analisa um arquivo, lembrando que este método pega o caminho e o converte para um FileStream. Por último deletamos a pasta criada e todo seu conteúdo.

Após isso, carregamos as informações do Projeto novamente, pois ele acabou de sofrer modificações com a adição da foto e, para informar o usuário, colocamos uma mensagem de sucesso.

Por fim, tentamos treinar o Projeto. Caso o método de treinamento retorne um false, significa que ainda não temos a quantidade necessária de fotos para o treino e, aí, exibimos uma mensagem de erro e paramos a execução do resto do método. Caso retorne um true, exibimos uma mensagem de sucesso para o treinamento também.

O método criado após o post (ErroAoPreencherCampos()), verifica se há erros nos preenchimentos dos campos, preenche com uma mensagem de erro explicativa e, além de retornar true ou false, preenche a variável ErroFotos com o valor equivalente.

Isso encerra o código da parte do servidor. Vamos voltar a parte do HTML, altere o código inicial para isto:

@page
@model AzureCognitiveServices.TreinamentoModel
@{
    ViewData[\"Title\"] = \"Treinamento\";

    var corDoAlertaFotos = \"success\";
    if (Model.ErroFotos)
        corDoAlertaFotos = \"danger\";

    var corDoAlertaTreinamento = \"success\";
    if (Model.ErroTreinamento)
        corDoAlertaTreinamento = \"danger\";
}

<h1>Treinamento do projeto @Model.Projeto.Name</h1>

Esta parte do começo do HTML está um pouco diferente de todas as anteriores, pois temos declaração de variáveis C# logo no começo do código. Declaramos e fizemos verificações com elas aí para saber qual a cor dos alertas exibiremos para o usuário.

Uma verificação para o alerta de adição de fotos, e outra para o de treinamento, verificando as variáveis de erro criadas na parte do servidor.

Coloque o seguinte código HTML no restante do arquivo:

<form method=\"post\" enctype=\"multipart/form-data\">
    <fieldset>
        <legend>Envie uma imagem por URL <strong>OU</strong> por arquivo</legend>
        <div>
            <div>
                <div>
                    <label for=\"url\">URL de uma imagem:</label>
                    <input type=\"url\" name=\"url\" id=\"url\" />
                </div>
            </div>

            <div>
                <div>
                    <label for=\"arquivo\">Arquivo de uma imagem:</label>
                    <input type=\"file\" asp-for=\"Arquivo\" id=\"arquivo\" />
                </div>
            </div>
        </div>
    </fieldset>

    <br />
    <br />

    <div>
        <fieldset>
            <legend>Selecione todas as tags que se relacionam com a imagem</legend>
            <div>
                <div>
                    @foreach (var tag in Model.TagsDoProjeto)
                    {
                        <div>
                            <input type=\"checkbox\" id=\"@tag.Id\" name=\"Tags\" value=\"@tag.Id\">
                            <label for=\"@tag.Id\">@tag.Name (@tag.ImageCount fotos)</label>
                        </div>
                    }
                </div>
            </div>
        </fieldset>
    </div>

    <br />
    <br />

    <div>
        <button type=\"submit\">Adicionar foto</button>
    </div>
</form>

@if (!string.IsNullOrWhiteSpace(Model.MensagemFotos))
{
    <div role=\"alert\">
        @Model.MensagemFotos
        <button type=\"button\" data-dismiss=\"alert\" aria-label=\"Close\">
            <span aria-hidden=\"true\">&times;</span>
        </button>
    </div>
}

@if (!string.IsNullOrWhiteSpace(Model.MensagemTreinamento))
{
    <div role=\"alert\">
        @Model.MensagemTreinamento
        <button type=\"button\" data-dismiss=\"alert\" aria-label=\"Close\">
            <span aria-hidden=\"true\">&times;</span>
        </button>
    </div>
}

O restante do código HTML dessa página é simples. Criamos um formulário com dois campos, uma para enviar uma URL e outro para enviar um arquivo.

Logo abaixo listamos todas as Tags que existem no Projeto e a quantidade de fotos que existe em cada Tag.

Por último, verificamos se há mensagem, tanto para a adição quanto para a o treinamento, e as exibimos, colocando o valor da cor do alerta de acordo com as variáveis que declaramos no começo do HTML.

Os arquivos inteiros podem ser visualizados no github, tanto o arquivo de HTML, quando a classe do servidor.

6. Implemente a Predição da Visão Personalizada na classe Predicao.cs

Dentro do projeto CustomVision crie uma nova classe e a chame de Predicao.cs.

Essa classe terá os métodos responsáveis por fazer as predições na Visão Personalizada da Azure.

Comece colocando todos os Usings necessários para o funcionamento correto da classe:

using Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction;
using Microsoft.Azure.CognitiveServices.Vision.CustomVision.Prediction.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

Depois adicione as variáveis de instância que utilizaremos.

namespace CustomVision
{
    public class Predicao
    {
        private const string _chaveDePredicao = \"<sua chave de predição>\";
        private const string _endpoint = \"<seu endpoin>\";

        private readonly ICustomVisionPredictionClient _servicoCognitivoDeVisaoPersonalizadaPredicao;

        private readonly Treinamento _treinamento;

Lembre de substituir \"<sua chave de predição>\" e \"<seu endpoint>\" pelos valores obtidos na primeira parte do artigo.

A interface ICustomVisionPredictionClient é, justamente, a interface que utilizaremos para chamar os métodos que o Serviço Cognitivo de Visão Personalizada nos disponibiliza.

Já a variável Treinamento _treinamento é a que acessa os métodos de treinamento necessários para capturar a última iteração realizada e usá-la na predição

Crie um método construtor para que, sempre que essa classe for instanciada, a variável _servicoCognitivoDeVisaoPersonalizadaPredicao e a variável _treinamento sejam instanciadas também.

public Predicao()
{
    _servicoCognitivoDeVisaoPersonalizadaPredicao = new CustomVisionPredictionClient()
    {
        ApiKey = _chaveDePredicao,
        Endpoint = _endpoint
    };

    _treinamento = new Treinamento();
}

Adicione os códigos abaixo e não esqueça de fechar todas as chaves da classe:

public IList<string> ClassificarPorUrl(string idDoProjeto, string url)
{
    var resultadoDaClassificacao = _servicoCognitivoDeVisaoPersonalizadaPredicao
        .ClassifyImageUrl(
        new Guid(idDoProjeto),
        UltimaIteracaoRealizada(idDoProjeto),
        new ImageUrl(url));

    return AnalisarResultadoDaClassificacao(resultadoDaClassificacao);
}

public IList<string> ClassificarPorArquivo(string idDoProjeto, string localDoArquivo)
{
    var arquivo = new FileStream(localDoArquivo, FileMode.Open);

    var resultadoDaClassificacao = _servicoCognitivoDeVisaoPersonalizadaPredicao
        .ClassifyImage(
        new Guid(idDoProjeto),
        UltimaIteracaoRealizada(idDoProjeto),
        arquivo);

    return AnalisarResultadoDaClassificacao(resultadoDaClassificacao);
}

private string UltimaIteracaoRealizada(string idDoProjeto) =>
    _treinamento.RetornarUltimaIteracaoRealizada(idDoProjeto);

private static IList<string> AnalisarResultadoDaClassificacao(ImagePrediction resultadoDaClassificacao) =>
    resultadoDaClassificacao.Predictions
        .Select(resultado => $\"Pode ser \"{resultado.TagName}\" com {resultado.Probability:P2} de probabilidade\").ToList();
}
}

Os métodos da classe de predição são simples e curtos.

Os dois primeiros métodos são semelhantes, ambos classificam uma imagem recebida, recebendo o id do Projeto para realizar a predição e a imagem. A diferença é que o primeiro recebe uma URL e, o segundo, recebe um arquivo para tal.

O método UltimaIteracaoRealizada apenas consulta a classe _treinamento para verificar qual a última iteração foi realizada e, por fim, o método AnalisarResultadoDaClassificacao analisa o que foi retornado da classificação e devolve uma lista com um texto formatado.

A classe inteira pode ser visualizada no github.

7. Crie o arquivo Predicao.cshtml no projeto AzureCognitiveServices

Dentro do projeto AzureCognitiveServices use a pasta CustomVision para criar uma Razor Page e a chame de Predicao.cshtml.

Ela será responsável por receber uma imagem, por URL ou por arquivo, e chamar os métodos de predição para classificar a imagem de acordo com a configuração do Projeto.

Vamos, primeiro, editar o código do servidor. Para isso, clique com o botão direito no arquivo aberto e vá em Go to PageModel ou aperte o F7.

Esse arquivo é responsável pelo código C# desta página. Comece colocando os usings.

using CustomVision;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Azure.CognitiveServices.Vision.CustomVision.Training.Models;
using System.Collections.Generic;
using System.IO;

Adicione agora as variáveis que utilizaremos na classe.

namespace AzureCognitiveServices
{
    public class PredicaoModel : PageModel
    {
        private readonly IWebHostEnvironment _environment;
        private readonly Treinamento _treinamento;

        public Project Projeto { get; private set; }

        public bool PodePredizer { get; private set; }

        public IList<string> Mensagens { get; private set; }

        [BindProperty]
        public IFormFile Arquivo { get; set; }

Há poucas variáveis nessa classe.

Temos:

  • IWebHostEnvironment _environment que é responsável por informações do ambiente da aplicação;
  • Treinamento _treinamento, para acessarmos os métodos desta classe que utilizaremos aqui;
  • Project Projeto, para ter informações do Projeto ao qual vamos predizer;
  • bool PodePredizer, para sabermos se o Projeto pode receber predições;
  • IList<string> Mensagens, para ter as mensagens retornadas;
  • IFormFile Arquivo, marcada com [BindProperty], para receber a foto enviada como arquivo.

Crie o método construtor.

public PredicaoModel(IWebHostEnvironment environment)
{
    _environment = environment;
    _treinamento = new Treinamento();
}

Aqui, no método construtor, estamos usando injeção de dependência novamente para capturarmos as informações de ambiente, além de criamos uma nova instância de Treinamento() para ser usada no restante da classe.

Agora crie o método get que será executado sempre que esta página for aberta e o método que ele chama.

public void OnGet(string idDoProjeto)
{
    CarregarProjeto(idDoProjeto);

    if (!_treinamento.PodePredizer(idDoProjeto))
    {
        Mensagens = new List<string> { \"Não é possível predizer neste projeto, pois ainda não há nenhum treinamento realizado.\" };
        return;
    }

    PodePredizer = true;
}

private void CarregarProjeto(string idDoProjeto) =>
    Projeto = _treinamento.CarregarProjeto(idDoProjeto);

O método get apenas recebe o id do Projeto por parâmetro, na URL e chama o método CarregarProjeto(), este, como o nome sugere, carrega as informações referentes ao Projeto.

Após isso é verificado se o Projeto pode, ou não, realizar predições. Caso não possa, uma mensagem é exibida e o método é interrompido. Caso possa realizar, apenas mudamos o valor da variável PodePredizer para true, fazendo com que o código HTML referente a página possa ser exibido.

Por último, crie o método post e não esqueça de fechar todas as chaves da classe.

public void OnPost(string idDoProjeto)
{
    if (Arquivo is null)
    {
        var url = Request.Form[\"url\"];
        Mensagens = new Predicao().ClassificarPorUrl(idDoProjeto, url);
    }
    else
    {
        var pasta = Path.Combine(_environment.ContentRootPath, \"imagens\");
        if (!Directory.Exists(pasta))
            Directory.CreateDirectory(pasta);

        var caminhoDoArquivo = Path.Combine(_environment.ContentRootPath, \"imagens\", Arquivo.FileName);

        using (var fileStream = new FileStream(caminhoDoArquivo, FileMode.Create))
            Arquivo.CopyTo(fileStream);

        Mensagens = new Predicao().ClassificarPorArquivo(idDoProjeto, caminhoDoArquivo);

        Directory.Delete(pasta, true);
    }

    PodePredizer = true;

    CarregarProjeto(idDoProjeto);
}
}
}

O método post também recebe o id do Projeto como parâmetro para saber de qual Projeto estamos tratando e, como teremos dois formulários no HTML, seguimos verificando qual deles estamos usando para poder chamar o método correto para predizer.

Portanto, caso a interface IFormFile Arquivo seja nula, significa que o usuário optou por enviar uma URL, então simplesmente capturamos a informação enviada pelo campo url do formulário HTML e o enviamos para a predição através da url.

Caso a interface não seja nula, temos alguns passos a mais. Primeiro começamos copiando a imagem enviada e colocando no servidor, numa pasta própria. Depois pegamos o caminho do arquivo criado e enviamos para adição através do método que analisa um arquivo, lembrando que este método pega o caminho e o converte para um FileStream. Por último deletamos a pasta criada e todo seu conteúdo.

Após isso, carregamos as informações do Projeto novamente e modificamos o valor da variável PodePredizer para true para que o usuário consiga realizar novas predições.

Isso encerra o código da parte do servidor. Vamos voltar a parte do HTML, altere o código inicial para isto:

@page
@model AzureCognitiveServices.PredicaoModel
@{
    ViewData[\"Title\"] = \"Predição\";
    var corDoAlerta = \"danger\";
    if(Model.PodePredizer)
        corDoAlerta = \"info\";
}

<h1>Predição Do Projeto @Model.Projeto?.Name</h1>

Nesse início também estamos utilizando variáveis criadas na parte do HTML para controlar a cor do alerta que é exibido para o usuário, de acordo com a possibilidade de poder, ou não, predizer.

Coloque o seguinte código HTML no restante do arquivo:

@if (Model.PodePredizer)
{
    <div>
        <div>
            <fieldset>
                <legend>Predizer com uma URL</legend>
                <form method=\"post\">
                    <div>
                        <label for=\"url\">URL de uma imagem :</label>
                        <input type=\"url\" name=\"url\" id=\"url\" required />
                    </div>
                    <button type=\"submit\">Predizer</button>
                </form>
            </fieldset>
        </div>

        <div>
            <fieldset>
                <legend>Predizer com um arquivo</legend>
                <form method=\"post\" enctype=\"multipart/form-data\">
                    <div>
                        <label for=\"arquivo\">Arquivo de uma imagem:</label>
                        <input type=\"file\" asp-for=\"Arquivo\" id=\"arquivo\" required />
                    </div>
                    <button type=\"submit\">Predizer</button>
                </form>
            </fieldset>
        </div>
    </div>
}

@if (!(Model.Mensagens is null))
{
    @foreach (var mensagem in Model.Mensagens)
    {
        <div role=\"alert\">
            @mensagem
            <button type=\"button\" data-dismiss=\"alert\" aria-label=\"Close\">
                <span aria-hidden=\"true\">&times;</span>
            </button>
        </div>
    }
}

Começamos com uma verificação. Caso seja possível predizer, o código HTML contendo os formulários para enviar imagem é exibido.

O primeiro formulário envia uma imagem por URL e o outro por arquivo.

Logo abaixo há o código que verifica se há alguma mensagem para exibir. Caso exista, ele exibe todas as mensagens respeitando a cor que escolhemos no começo do código HTML.

Os arquivos inteiros podem ser visualizados no github, tanto o arquivo de HTML, quando a classe do servidor.

Conclusão

Este artigo, devido às várias opções do Serviço Cognitivo de Visão Personalizada, foi dividido em três partes, sendo esta a terceira e última parte falando sobre as predições na Visão Personalizada da Azure. Justamente por isso, não precisamos adicionar as páginas criadas ao menu, pois a página principal já está lá.

Com isso, a estrutura atual do seu projeto deve estar se parecendo assim:

Estrutura atual do projeto

Agora que finalizamos as três partes referentes a Visão Personalizada da Azure, percebemos que o Serviço Cognitivo citado consegue se moldar, de forma rápida, fácil e eficiente as necessidades do domínio em que está sendo aplicado.

Toda essa possibilidade de molde faz com que ele seja uma excelente opção de uso.

Além disso, há a possibilidade de tudo ser feito através do portal de Visão Personalizada, o que nos poupa ainda mais tempo, e nos reduz o custo.

Todo o projeto está disponível no github com uma licença que possibilita colaboração. Sinta-se a vontade.

Siga-nos em nossas redes sociais!

Posts recentes

Ultimas do blog