Breno Ferreira

Opniões e códigos de um desenvolvedor de software .NET.

Posts Tagged ‘webapi

Handwriting Recognition com HTML5 Canvas

leave a comment »

Olá pessoal,

Uma das grandes novidades do HTML5 é o elemento Canvas. Ele permite que uma determinada area da página seja utilizada para desenhar, renderizar imagens e gráficos. Algumas aplicações bem legais do elemento Canvas são o SketchPad, Cloth Simulation, e até jogos são possíveis. Se voce ainda não conhece como utilizar as APIs do Canvas, recomendo ir ao Mozilla Developer Network e ver como acessar as funcionalidades básicas.

Um uso bacana que pensei foi a possibilidade de fazer o reconhecimento de escrita utilizando o elemento Canvas. Isso seria bastante útil em dispositivos móveis com telas touch-screen como tablets e smartphones.

Como fazer?

Para fazermos o reconhecimento da escrita precisamos fazer duas coisas: guarder os dados de input do usuário, conforme ele vai “escrevendo”, em seguida, mandar esses dados para o servidor para realizar o processo de reconhecimento da escrita.

 Client-Side

Primeiramente definimos o elemento Canvas, e um botão que, quando clicado, irá enviar os dados para o servidor.

<canvas id="myCanvas"></canvas>

<button id="submitBtn">Submit</button>

Depois, definimos um objeto que irá ser responsável por desenhar os traços na tela.

board = {
    isDrawing: false,
    surface: null,

    setup: function () {
        var myCanvas = document.getElementById('myCanvas')
        myCanvas.width = window.innerWidth - 25;
        myCanvas.height = window.innerHeight - 25;
        this.surface = myCanvas.getContext('2d');
        this.surface.lineWidth = 5;
        this.surface.strokeStyle = '#0000ff';
    },

    beginDraw: function (x, y) {
        this.surface.beginPath();
        this.surface.moveTo(x, y);

        this.isDrawing = true;
    },

    draw: function (x, y) {
        this.surface.lineTo(x, y);
        this.surface.stroke();
    },

    endDraw: function () {
        this.isDrawing = false;
    }
};

Primeiramente, no método Setup() definimos algumas propriedades básicas do Canvas como altura e largura na tela, tamanho do traço a ser desenhado e sua cor, e guardamos uma referência para a superficie de desenho, chamando o método getContext(‘2d’).

Em seguida, temos as funções beginDraw(x, y), draw(x, y) e endDraw(). Essas funções serão chamadas quando o usuário começar a escrever alguma coisa, enquanto ele estiver escrevendo, e quando ele terminar de escrever, respectivamente. Os métodos são bem simples, na função beginDraw, desloco o contexto de desenho para o ponto XY onde o usuário começou a escrever e seto um flag indicando que o usuário está escrevendo. Na função draw, traço uma linha até o ponto XY, e no método endDraw, simplesmente seto o flag para false, indicando que o usuário terminou de escrever.

Em seguida, associamos os eventos de input do usuário e chamamos as funções do objeto definido acima.

var strokesHistory = {
    Strokes: []
};

var currentStroke = {
    Points: []
};

board.setup();

var myCanvas = document.getElementById('myCanvas');

myCanvas.onmousedown = myCanvas.ontouchstart = function (e) {
    var xy = getInputCoordinates(e);

    currentStroke.Points.push({ X: xy.x, Y: xy.y });

    board.beginDraw(xy.x, xy.y);
};

myCanvas.onmousemove = myCanvas.ontouchmove = function (e) {
    if (board.isDrawing === true) {
        var xy = getInputCoordinates(e);

        currentStroke.Points.push({ X: xy.x, Y: xy.y });
        board.draw(xy.x, xy.y);
    }
};

myCanvas.onmouseup = myCanvas.ontouchend = function (e) {
    strokesHistory.Strokes.push(currentStroke);
    currentStroke = { Points: [] };
    board.endDraw();
};

function getInputCoordinates(e) {
    var x, y;

    if (e.changedTouches) {
        var touchData = e.changedTouches[0];
        x = touchData.clientX;
        y = touchData.clientY;
        e.preventDefault();
    }
    else {
        x = e.clientX;
        y = e.clientY;
    }

    return { x: x, y: y };
}

Repare que definimos callbacks para ambos os eventos do mouse e também para os de touch. Também criamos um objeto, strokesHistory, que guardará os dados de input (coordenadas X e Y dos traços) do usuário, assim, podemos enviar os dados para o servidor depois. Por fim, criamos uma função, getInputCoordinates(e), que retorna a coordenada XY de onde o usuário está escrevendo. A partir desse momento, o usuário já consegue escrever livremente com o elemento Canvas.

Por fim, quando o usuário clicar no botão, iremos enviar os dados via POST para o servidor, formatados como JSON. Note que iremos simplesmente exibir um alert com o resultado do reconhecimento.

$('#submitBtn').click(function () {
    $.post('/api/recognition/recognize',
            { strokes: JSON.stringify(strokesHistory)},
            function (result) {
                alert(result);
            });
});

Server-Side

No lado do servidor, iremos criar uma WebAPI, novo recurso do ASP.NET MVC 4. Caso voce ainda não conheça como funciona essa nova feature, o Elemar Jr. tem um post no seu blog explicando bem sucintamente o que é o ASP.NET WebAPI e como funciona.

Iremos chamar nosso Controller de RecognitionController, e nele teremos uma Action que aceita o verbo POST.

public class RecognitionController : ApiController
{
    [HttpPost]
    public HttpResponseMessage<String> Recognize(string strokes)
    {
        var strokePointsData = JsonConvert.DeserializeObject<dynamic>(strokes);

        var strokeCollection = GetStrokeCollectionFromPoints(strokePointsData);

        var inkAnalyzer = new InkAnalyzer();

        inkAnalyzer.AddStrokes(strokeCollection);

        var analysisStatus = inkAnalyzer.Analyze();

        if (analysisStatus.Successful)
        {
            var recognizedString = inkAnalyzer.GetRecognizedString();
            return new HttpResponseMessage<string>(recognizedString);
        }
        else
            return new HttpResponseMessage<string>("Data not recognized");
    }
}

O processo de reconhecimento é bem simples. Utilizamos uma API do Windows Presentation Foundation (WPF) chamada InkAnalyser, que, dado como input dados (coordenadas) de traços escritos pelo usuário, ela devolve uma String reconhecida no processo de análise. Porém, como estamos enviando os dados formatados como JSON, precisamos converter para uma estrutura de dados requerida pelo InkAnalyser. O método abaixo irá converter os dados deserializados do JSON para um objeto do tipo StrokeCollection.

private StrokeCollection GetStrokeCollectionFromPoints(dynamic strokePoints)
{
    var strokeCollection = new StrokeCollection();

    foreach (var stroke in strokePoints.Strokes)
    {
        var points = new StylusPointCollection();

        foreach (var point in stroke.Points)
        {
            var x = (float)point.X;
            var y = (float)point.Y;

            points.Add(new StylusPoint(x, y));
        }

        strokeCollection.Add(new Stroke(points));
    }

    return strokeCollection;
}

Agora se executarmos a aplicação, poderemos escrever alguma palavra no canvas (seja com o mouse, ou em algum dispositivo touch-screen), e clicarmos no botão no canto inferior esquerdo e esperar o resultado do reconhecimento.

 

O código dessa aplicação está no Github.

Abraços

Breno

Advertisements

Written by Breno Ferreira

14/03/2012 at 20:01

Criando Scaffolder customizado para o WCF WebAPI

leave a comment »

Olá pessoal,

No último post falei um pouco sobre como customizar um template do MvcScaffolding. Hoje irei explicar como podemos criar um Scaffolder totalmente novo, com funcionalidades nada relacionadas ao ASP.NET MVC. Irei demonstrar rapidamente como voce pode criar um Scaffolder para gerar código para serviços REST utilizando o WCF WebAPI.

Para quem não conhece o WCF WebAPI, recomendo ler um post do Israel Aece que explica muito bem alguns conceitos básicos.

Criando um Custom Scaffolder

Para criarmos um Custom Scaffolder, basta executarmos o seguinte comando no Package Manager Console (depois de ter adicionado o pacote MvcScaffolding é claro):

Gerar Custom Scaffolder
  1. Scaffold CustomScaffolder RestfulService

Esse comando basicamente é um Scaffolder de Scaffolders, ou seja, ele gera um Scaffolder customizado.

No Solution Explorer, agora teremos a seguinte estrutura de arquivos:

image_thumb1

Os arquivos marcados em amarelo foram gerados pelo MvcScaffolding. Um arquivo é o template T4 que possui o template do arquivo de saída, e outro é um arquivo Powershell (.ps1) que irá ser chamado quando executarmos o comando para utilizarmos esse Scaffolder.

O código PowerShell ficou assim:

Default Custom Scaffolder
  1. [T4Scaffolding.Scaffolder(Description = "Enter a description of RestfulService here")][CmdletBinding()]
  2. param(        
  3.     [string]$Project,
  4.     [string]$CodeLanguage,
  5.     [string[]]$TemplateFolders,
  6.     [switch]$Force = $false
  7. )
  8.  
  9. $outputPath = "ExampleOutput"
  10. $namespace = (Get-Project $Project).Properties.Item("DefaultNamespace").Value
  11.  
  12. Add-ProjectItemViaTemplate $outputPath -Template RestfulServiceTemplate `
  13.     -Model @{ Namespace = $namespace; ExampleValue = "Hello, world!" } `
  14.     -SuccessMessage "Added RestfulService output at {0}" `
  15.     -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force

O Comando mais importante aqui é o Add-ProjectItemViaTemplate, que irá gerar o código baseado no Template T4. Esse comando recebe alguns parâmetros importantes:
$outputPath: caminho onde o arquivo será gerado
-Template: Nome do template T4
-Model: dados que são passados para o template T4 como parâmetros para gerar o código
 
Para gerar o serviço com o WebAPI, precisamos de alguns parâmetros para o comando PowerShell:
Service Scaffolder Parameters
  1. [T4Scaffolding.Scaffolder(Description = "Creates a Restful service using WCF WebApi")][CmdletBinding()]
  2. param(
  3.     [parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$ModelType,
  4.     [string]$DbContextType,        
  5.     [string]$Project,
  6.     [string]$CodeLanguage,
  7.     [switch]$NoChildItems = $false,
  8.     [string[]]$TemplateFolders,
  9.     [switch]$Force = $false
  10. )

O primeiro parâmetro é o tipo do nosso Model, o segundo é o tipo de algum DbContext do EntityFramework já existente. Note que o primeiro parâmetro é obrigatório.

Não vou entrar em todos os detalhes do script Powershell, senão o post ficaria muito longo, mas o comando para gerarmos o código do serviço é o seguinte:

Gerando o c&amp;amp;#243;digo do servi&amp;amp;#231;o
  1. Add-ProjectItemViaTemplate $outputPath -Template RestfulServiceTemplate `
  2.     -Model @{
  3.         ServiceName = $serviceName;
  4.         RepositoryName = $repositoryName;
  5.         ModelType = [MarshalByRefObject]$foundModelType;
  6.         PrimaryKey = [string]$primaryKey;
  7.         DefaultNamespace = $defaultNamespace;
  8.         ModelTypeNamespace = $modelTypeNamespace;
  9.         ServiceNamespace = $serviceNamespace;
  10.          } `
  11.     -SuccessMessage "Added RestfulService output at {0}" `
  12.     -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force

Passamos como informações para o template T4 os seguintes dados:

  • ServiceName: Nome do tipo do nosso serviço (ex: PersonService)
  • RepositoryName: Nome do tipo do repositório (ex: PersonRepository)
  • ModelType: Tipo do modelo (ex: Person)
  • Primary Key: Propriedade que representa a chave primária no banco (ex: ID)
  • Default Namespace: Namespace padrão do projeto
  • ModelTypeNamespace: Namespace onde o tipo do nosso Model está localizado
  • ServiceNamespace: Namespace onde o serviço estará localizado (DefaultNamespace + “Services”)

Esse comando irá ler o template T4 e gerar um arquivo de saída com o serviço WCF WebAPI.

Uma parte desse template pode ser vista no código abaixo:

Code Snippet
  1.  [ServiceContract]
  2. public class <#= Model.ServiceName #> : I<#= Model.ServiceName #>
  3. {
  4.      private readonly I<#= repositoryName #> <#= repositoryVariableName #>;
  5.      
  6.      //if you are using Dependency Injection, you can delete the following constructor.
  7.      public <#= Model.ServiceName #> ()
  8.          : this(new <#= repositoryName #>())
  9.      {
  10.      }
  11.      
  12.      public <#= Model.ServiceName #> (I<#= repositoryName #> <#= repositoryVariableName #>)
  13.      {
  14.          this.<#= repositoryVariableName #> = <#= repositoryVariableName #>;
  15.      }
  16.      
  17.      [WebGet(UriTemplate="")]
  18.      public IQueryable< <#= modelName #> > GetAll()
  19.      {
  20.          return this.<#= repositoryVariableName #>.All;
  21.      }

Para quem quiser ver maiores detalhes do Scaffolder, criei um projeto no Codeplex e o código fonte complate está disponível lá. Basta entrar no site: http://servicescaffolder.codeplex.com/

Quem quiser utilizar esse Scaffolder, pode fazer uso do Nuget e referenciar o pacote:

Install-Package ServiceScaffolder

Scaffold RestfulService <Model>

Abraços

Breno

Written by Breno Ferreira

14/08/2011 at 15:16

Posted in Dev

Tagged with , , ,