Breno Ferreira

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

Posts Tagged ‘JavaScript

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

Primeiros passos com NodeJS

leave a comment »

Olá pessoal.

Hoje irei falar sobre um assunto um pouco diferente do que costumo escrever por aqui. Não é um assunto relacionado (nem tanto) ao mundo Microsoft. Irei falar hoje sobre uma tecnologia que tem gerado um “buzz” bem grande recentemente e que vem chamando a atenção de muitos desenvolvedores. Trata-se de Node.JS, ou simplesmente Node.

O que é Node.JS

Node.js is a platform built on Chrome’s JavaScript runtime for easily building fast, scalable network applications. (Fonte: http://nodejs.org/).

Duas coisas na descrição chamam a atenção:

Built on Chrome’s JS Engine (V8): Node permite que aplicações que rodem no server-side sejam construidas utilizando como linguagem o Javascript. Isso mesmo, Javascript. Isso é possível por que o Node interpreta seu código utilizando a engine de Javascript criada pelo Google, o V8. Talvez voce pense que Javascript é uma linguagem exclusiva para o mundo client-side, rodando em páginas web dentro do browser, mas com Node, suas habilidades adquiridas escrevendo código no lado do cliente poderão ser usadas no lado do servidor (a não ser que a única coisa que voce faz é amarrar eventos e Ajax simples via Jquery).

Fast e Scalable: A fundamental diferença entre Node e outros web-frameworks (além da linguagem, é claro), é que Node utiliza uma outra forma de tratar requests que chegam ao servidor. Frameworks como RubyOnRails, ASP.NET MVC, PHP (Zend, Cake, etc.), Django e outros, rodam sobre um WebServer, como Apache ou o IIS. E esses webservers utilizam Threads para tratar os requests. Então, para cada request que chega, o servidor aloca uma Thread (geralmente de uma ThreadPool) para tratar aquele request. Enquanto o processamento daquele request não terminar, aquela Thread fica “travada”, não podendo responder a outros requests. Por exemplo, muitas aplicações web implementam o que chamamos de Request-Database-Response, ou seja, um request chega, procuramos alguma informação no banco de dados e retornamos uma resposta pro cliente. Porém, o gargalo dessa execução está justamente na espera pelo banco de dados responder com as informações requeridas. Enquanto o BD não retorna, a Thread alocada ficará travada.

O Node por sua vez, utiliza um outra maneira de tratar requisições. Ele utiliza um recurso chamado Evented IO, que não faz uso de Threads, mas sim de uma pilha de eventos que são executados. Isso funciona da seguinte maneira: quando uma requisição chega ao servidor, o framework começa a tratar a requisição. A partir do momento que alguma operação de IO (seja para fazer alguma query no banco de dados, ler um arquivo, etc.) é executada, ela é executada de maneira assíncrona, e um evento (ou callback) é registrado. Enquanto essa operação de IO está executando, o processo do Node volta a ser capaz de continuar tratando outras requisições, resultando no que chamamos de Non-Blocking-IO. Quando a operação de IO anterior terminar, o callback registrado é colocado na pilha de execução e quando chegar sua vez, ele será executado, continuando a execução do request anterior, podendo assim, devolver uma resposta para o cliente. Em código, seria algo parecido com isso:

function EventLoop(){
	while (true){
		var callback = executionStack.Pop();
		callback();
	}
}

//called whenever an async IO operation is performed
function PushToExecutionStack(callback){
	executionStack.Push(callback);
}

Esse é o ingrediente fundamental que possibilita que aplicações escritas com Node sejam rápidas e escaláveis. Como o servidor nunca fica bloqueado esperando uma requisição terminar, ele pode servir milhares de requisições.

Mas por que Javascript?

Javascript é uma das poucas linguagens que, naturalmente, não oferece um modelo de programação síncrono. O conceito de callback é algo tão natural em Javascript, e todos estão tão acostumados a esse modelo de programação (ninguém faz AJAX de maneira síncrona, certo?) com essa linguagem, que Javascript é uma linguagem muito apropriada a esse modelo de programação orientado a eventos.

Exemplo

Abaixo temos um exemplo que cria um servidor que escuta na porta 8080 e que quando uma requisição chega, retorna o conteúdo do arquivo index.html.

var http = require('http');
var fileSystem = require('fs');

var server = http.createServer(function(req, resp){
	fileSystem.readFile('./index.html', function(error, fileContent){
		if(error){
			resp.writeHead(500, {'Content-Type': 'text/plain'});
			resp.end('Error');
		}
		else{
			resp.writeHead(200, {'Content-Type': 'text/html'});
			resp.write(fileContent);
			resp.end();
		}
	});
});

server.listen(8080);

console.log('Listening at: localhost:8080');

Primeiramente, chamamos a função require(‘http’) que carrega o módulo HTTP do Node. Com esse módulo, podemos chamar a função createServer, que recebe como parâmetro um callback, que será chamado toda vez que um request chegar para ser executado. A função de callback tem dois parâmetros, request e response. O parâmetro request possui os dados sobre o request, como headers, querystrings, content, etc.. O parâmetro response pode ser usado para enviar uma resposta para o cliente.

Na função responsável por tratar os requests, utilizamos a API do FileSystem para lermos o conteúdo do arquivo index.html, e novamente, passamos um callback que será chamado quando a leitura do arquivo terminar, e esse callback será chamado, devolvendo o conteúdo do arquivo, ou um erro, caso algum tenha ocorrido. Enfim, retornaremos uma resposta ao cliente, seja ela uma mensagem de erro, ou o conteúdo do arquivo. Fazemos isso chamando o método writeHead e write do objeto response. O método writeHead escreve um Header, nesse caso, passamos o Status-Code de 200 ou 500 e Content-Type sendo text/plain. O método write escreve o conteúdo da resposta, nesse caso o conteúdo do arquivo index.html

Note que, enquanto a leitura do arquivo está sendo feita, o processo do Node é capaz de continuar tratando outras requisições que chegarem.

Para executar essa aplicação, salve o código com algum nome, por exemplo “server.js”, e rode o seguinte comando no Terminal: node server.js. O código completo voce pode ver aqui. Não esqueça de criar o arquivo index.html com algum HTML de exemplo.

Era isso pessoal, até a próxima.

Breno

Written by Breno Ferreira

27/02/2012 at 14:00

Posted in Node.JS

Tagged with ,