Páginas

martes, 28 de febrero de 2023

Entrenando una red neuronal para reconocimiento de imágenes con DeepLearning4J

En este ejemplo vamos a clasificar imágenes de 5 x 5 pixeles mediante una red neuronal que diferencie entre las que tienen una cruz:

 00100
 00100
 11111
 00100
 00100

Y las que no tienen una cruz:

10010
01000
10100
00010
01001

Las imágenes se aplanarán en un CSV con 25 parámetros de datos y una columna etiqueta con valores {0,1} para indicar que la imagen no tiene o sí tiene una cruz:

0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1

1,0,0,1,0,0,1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,1,0,0,1,0 

Para el ejemplo tenemos 500 imágenes, 450 imágenes aleatorias sin cruces y 50 imágenes con cruces. El código es el siguiente:

package org.dune;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;

import org.datavec.api.records.reader.RecordReader;
import org.datavec.api.records.reader.impl.csv.CSVRecordReader;
import org.datavec.api.split.FileSplit;
import org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator;
import org.deeplearning4j.eval.Evaluation;
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.layers.DenseLayer;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.deeplearning4j.nn.weights.WeightInit;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.dataset.SplitTestAndTrain;
import org.nd4j.linalg.dataset.api.DataSet;
import org.nd4j.linalg.dataset.api.iterator.DataSetIterator;
import org.nd4j.linalg.lossfunctions.LossFunctions;

/**
 *
 * @author egdepedro
 */
public class EntrenaRedNeuronal {
    
    // Ruta del fichero de datos
    public static final String DATA_SET_PATH = "./src/main/resources/datos.csv";
    // Número de columnas de artibutos (son 25 datos con ceros y unos para el valor de los pixeles de la imagen)
    public static final int FEATURES_COUNT = 25;
    // Columna en donde está la etiqueta (la etiqueta está en la columna 25 porque la matriz java comienza en la posición cero)
    public static final int LABEL_INDEX = 25;
    // Número de grupos en los que clasificar los datos, dos grupos, con cruz o sin cruz
    public static final int NUM_POSSIBLE_LABLES = 2;
    // Fijamos el % de los datos usados para entrenar
    public static final int TRAIN_TO_TEST_RATIO = 70;
    // Fijamos el número de líneas del fichero a cargar (
    public static final int BATCH_SIZE = 500;

    
    public static void main(String[] args) throws IOException, InterruptedException {
        
        //System.out.println("Generamos el fichero CSV con las imágenes etiquetadas");
        //GeneradorCSV(DATA_SET_PATH);

        System.out.println("Cargamos los datos etiquetados y los barajamos para mejorar el rendimiento del modelo");
        final DataSet allData = loadData(DATA_SET_PATH);
        allData.shuffle();

        System.out.println("Preparamos los datos etiquetados de entrenamiento y de test");
        SplitTestAndTrain testAndTrain = allData.splitTestAndTrain(TRAIN_TO_TEST_RATIO);
        DataSet trainingData = testAndTrain.getTrain();
        DataSet testData = testAndTrain.getTest();

        System.out.println("Configuramos la red neuronal");
        MultiLayerConfiguration configuration = new NeuralNetConfiguration.Builder()
                // Ajustamos el número de iteraciones con los datos de entrenamiento
                .iterations(2000)
                // Ajustamos la función de activación de cada nodo
                .activation(Activation.RELU)
                // Ajustamos los pesos iniciales
                .weightInit(WeightInit.RELU_UNIFORM).learningRate(0.05).regularization(true).l2(0.0001).list()
                .layer(0, new DenseLayer.Builder().nIn(FEATURES_COUNT).nOut(25).build())
                .layer(1, new DenseLayer.Builder().nIn(25).nOut(25).build())
                .layer(2, new DenseLayer.Builder().nIn(25).nOut(25).build())
                .layer(3, new DenseLayer.Builder().nIn(25).nOut(25).build())
                .layer(4,
                        new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
                                .activation(Activation.SOFTMAX).nIn(25).nOut(NUM_POSSIBLE_LABLES).build())
                .backprop(true).pretrain(false).build();

        System.out.println("Creamos la red neuronal y lanzamos el entrenamiento");
        MultiLayerNetwork model = new MultiLayerNetwork(configuration);
        model.init();
        model.fit(trainingData);

        System.out.println("Probamos la red entrenada");
        INDArray output = model.output(testData.getFeatures());
        Evaluation eval = new Evaluation(NUM_POSSIBLE_LABLES);
        eval.eval(testData.getLabels(), output);
        System.out.println(eval.stats());
    }

    private static DataSet loadData(String path) throws IOException, InterruptedException {
        DataSet allData;
        try (RecordReader recordReader = new CSVRecordReader(0, ',')) {
            recordReader.initialize(new FileSplit(new File(path)));
            DataSetIterator iterator = new RecordReaderDataSetIterator(recordReader, BATCH_SIZE, LABEL_INDEX, NUM_POSSIBLE_LABLES);
            allData = iterator.next();
        }
        return allData;
    }
    
    private static void GeneradorCSV(String path) throws IOException {
        
        //Tenemos la matriz de las 50 imágenes etiquetadas como con cruces
        final String[] matrizCruces =
        {"1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1",
        "0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1",
        "0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1",
        "0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1",
        "1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1",
        "0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1",
        "1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1",
        "0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1",
        "1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1",
        "0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1",
        "0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1",
        "1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1",
        "0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1",
        "0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1",
        "0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1",
        "1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1",
        "0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1",
        "0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1",
        "1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1",
        "0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1",
        "0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1",
        "1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1",
        "1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1",
        "0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1",
        "0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1",
        "0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,1,1,1,1,1,0,1,0,0,0,1",
        "0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,1,1,1,1,1,0,0,0,1,0,1",
        "0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1,1",
        "0,0,1,0,0,0,0,1,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1",
        "1,1,1,1,1,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1",
        "1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,1,1,1,1,1"};
        
        //Preparamos el generador aleatorio
        final Random r = new Random(System.currentTimeMillis());
                
        //Preparamos la matriz de las 450 imágenes con ruido (en las que nos la estamos jugando porque entendemos que no deberían aparecer cruces pero no lo controlamos)
        final String [] matrizRuido = new String [450];
        String imagen_ruido;
        for (int l=0;l<450;l++) {
            imagen_ruido = "";
            for (int c=0;c<25;c++) {
                imagen_ruido +=+r.nextInt(0,2)+",";
            }
            //Metemos la etiqueta en la última columna
            imagen_ruido += "0";
            matrizRuido [l] = imagen_ruido;
        }
        
        //Generamos una matriz con los datos mezclados
        final String [] matrizDatos = new String [BATCH_SIZE];
        int contadorCruces=0;
        int contadorRuido=0;
        for (int l=0;l<BATCH_SIZE;l++) {
            if (l%10==0) {
                matrizDatos[l]=matrizCruces[contadorCruces++];
            } else {
                matrizDatos[l]=matrizRuido[contadorRuido++];
            }
        }
        System.out.println("contadorCruces["+contadorCruces+"] contadorRuido ["+contadorRuido+"]" );
        
        //Volcamos los datos al fichero CSV recibido como parámetro
        File f = new File(path);
        FileWriter fw = new FileWriter(f);
        BufferedWriter bfw = new BufferedWriter(fw);
        for (int l=0;l<BATCH_SIZE;l++) {
            bfw.write(matrizDatos[l]+"\n");
        }
        bfw.flush();
        bfw.close();
    }
}


Probamos la red entrenada

Examples labeled as 0 classified by model as 0: 384 times

Examples labeled as 0 classified by model as 1: 5 times

Examples labeled as 1 classified by model as 0: 11 times

Examples labeled as 1 classified by model as 1: 30 times

==========================Scores========================================

# of classes: 2

Accuracy: 0,9628

Precision: 0,9146

Recall: 0,8594

F1 Score: 0,7895

========================================================================

viernes, 24 de febrero de 2023

Entrenar el modelo davinci de OpenAI para que realice sumas básicas

Tras revisar la documentación disponible en https://platform.openai.com/docs/guides/completion/prompt-design probamos
a entrenar el modelo davinci para que aprenda a realizar sumas básicas con datos del tipo:

    {"prompt": "7+5", "completion": "12"}
    {"prompt": "4+2", "completion": "6"}
    {"prompt": "3+7", "completion": "10"}

1) Instalamos Python:

sudo apt install python3-pip
pip install --upgrade pip
pip install aiohttp
pip install --upgrade openai
pip install openai[datalib]

2) Ajustamos $PATH en $HOME/.profile para que python sepa donde estan las librerías, añadiendo:

PATH="$HOME/.local/lib/python3.10/site-packages:$PATH"

3) Recargamos las variables de .profile en bash ejecutando:

. ~/.profile

4) Creamos una cuenta en OpenAI y obtenemos una API KEY:

https://platform.openai.com/account/api-keys

5) Instalamos la API KEY:

export OPENAI_API_KEY="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"

6) Generamos datos de entrenamiento del tipo:

    {"prompt": "<prompt text>", "completion": "<ideal generated text>"}
    {"prompt": "0+1", "completion": "1"}
    {"prompt": "0+2", "completion": "2"}
    ...
    {"prompt": "9+8", "completion": "17"}
    {"prompt": "9+9", "completion": "18"}
    {"prompt":"","completion":" 0+8\", \"completion\": \"8\"}"}

7) Para ello necesitaremos un programa por ejemplo en Java como el siguiente:

package org.dune;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;

public class GeneraDatosGPT {

    private static int NUMLINEAS = 5000;
    
    public static void main(String[] args) throws IOException {
        
        //Iniciamos el generador aleatorio de enteros
        final Random r = new Random();
        r.setSeed(System.currentTimeMillis());
        
        //Preparamos un fichero de salida
        final File f = new File("/home/egdepedro/datos-sumas.jsonl");
        final FileWriter fw = new FileWriter(f);
        final BufferedWriter bfw = new BufferedWriter(fw);
        
        //Preparamos las variables para cada operación
        int operando1;
        int operando2;
        int resultado;
        
        //Preparamos la línea
        StringBuilder sbLinea;
        for (int c=0;c<NUMLINEAS;c++) {
            //Generamos la operacion aleatoria
            operando1 = r.nextInt(0, 9);
            operando2 = r.nextInt(0, 9);
            resultado = operando1 + operando2;
            //Montamos la línea de datos
            sbLinea = new StringBuilder("{\"prompt\": \"");
            sbLinea.append(operando1);
            sbLinea.append("+");
            sbLinea.append(operando2);
            sbLinea.append("\", \"completion\": \"");
            sbLinea.append(resultado);
            sbLinea.append("\"}");
            
            //Escribimos la línea en el fichero de salida
            bfw.write(sbLinea.toString());
            bfw.newLine();
            
            //Reseteamos la línea
            sbLinea.setLength(0);
        }
        
        //Cerramos los manejadores
        bfw.flush();
        bfw.close();
        fw.close();
    }
}

8) Entrenamos el modelo seleccionado (davinci, curie, babbage, ada) con:

openai api fine_tunes.create -t datos-sumas.jsonl -m davinci

9) Tendremos una salida del tipo:

Upload progress: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 187k/187k [00:00<00:00, 307Mit/s]
Uploaded file from datos-sumas.jsonl: file-JkbUfKOhPX7hbdkQt7AXUglq
Created fine-tune: ft-k1iukuO7r3pCmTDMwV7supOi
Streaming events until fine-tuning is complete...

(Ctrl-C will interrupt the stream, but not cancel the fine-tune)
[2023-02-24 11:49:11] Created fine-tune: ft-k1iukuO7r3pCmTDMwV7supOi

Stream interrupted (client disconnected).
To resume the stream, run:

  openai api fine_tunes.follow -i ft-k1iukuO7r3pCmTDMwV7supOi

10) Relanzamos el comando indicado:

 openai api fine_tunes.follow -i ft-k1iukuO7r3pCmTDMwV7supOi

11) Nos quedamos con el ID asignado al modelo --> ft-k1iukuO7r3pCmTDMwV7supOi

12) Revisamos la lista de modelos:

openai api fine_tunes.list
    
13) Comprobamos el estado de nuestro modelo, que puede ser: pending, running, succeeded, or failed

    openai api fine_tunes.get -i ft-k1iukuO7r3pCmTDMwV7supOi

  Si lo vamos ejecutando cada cierto tiempo podemos ver como avanza:
    
  "id": "ft-k1iukuO7r3pCmTDMwV7supOi",
  "model": "davinci",
  "object": "fine-tune",
  "organization_id": "org-PTX4TjDLRbTu7lnZTDjil0hG",
  "result_files": [],
  "status": "pending",
  ...
    
  "id": "ft-k1iukuO7r3pCmTDMwV7supOi",
  "model": "davinci",
  "object": "fine-tune",
  "organization_id": "org-PTX4TjDLRbTu7lnZTDjil0hG",
  "result_files": [],
  "status": "running",
  ...

  "id": "ft-k1iukuO7r3pCmTDMwV7supOi",
  "model": "davinci",
  "object": "fine-tune",
  "organization_id": "org-PTX4TjDLRbTu7lnZTDjil0hG",
  "result_files": [
    {
      "bytes": 114982,
      "created_at": 1677248683,
      "filename": "compiled_results.csv",
      "id": "file-vp1v2ozODA9O4r3853X9yUIN",
      "object": "file",
      "purpose": "fine-tune-results",
      "status": "processed",
      "status_details": null
    }
  ],
  "status": "succeeded",

 
  Nota. Entre los datos generados hasta llegar al 'succeeded' podremos ir viendo cómo avanzan las tandas de entrenamiento, que parecen ser 4:
 
  "message": "Completed epoch 1/4",
  ...
  "message": "Completed epoch 2/4",
  ...
  "message": "Completed epoch 3/4",

14) Cuando tras chequear el status tenemos succeeded debemos sacar el ID del Modelo:

openai api fine_tunes.list
{
  "data": [
    {
      "created_at": 1677235751,
      "fine_tuned_model": "davinci:ft-personal-2023-02-24-14-24-41",

15) Recogiendo el ID del modelo generado podemos probarlo con:

openai api completions.create -m davinci:ft-personal-2023-02-24-14-24-41 -M 1 -p 4+1
Obteniendo: 4+15
openai api completions.create -m davinci:ft-personal-2023-02-24-14-24-41 -M 1 -p 9+9
Obteniendo: 9+918
   
15) Si el modelo no ha sido creado tendríamos un aviso del tipo: Error: That model does not exist (HTTP status code: 404)

viernes, 30 de abril de 2021

Geoposicionamiento indoor con EddyStone


Tecnología BLE, protocolo de Google soportado por Apple. Alcance 50 metros.

Los beacon se colocan estratégicamente en distintas ubicaciones y se ajusta para emitir con cierta frecuencia una señal de baliza que contiene una mezcla de 3 bloques de datos:

  • Eddystone-UID. Emite un identificador único universal del beacon, que es equivalente a la funcionalidad de iBeacon (UUID 10 bytes + MajorMinor 6 bytes + TX), ver entrada anterior.
  • Eddystone-EID. Es equivalente a Eddystone-UID pero los identificadores se cifran con una clave AES rotatoria que sólo conocen el beacon y la App. De este modo nadie puede copiar el UUID para ponerlo en sus propios beacons y falsificar los datos de posicionamiento.
  • Eddystone-URL. Emite una URL, que es la URL del sitio de internet donde se ofrece la audioguía, eliminamos por tanto la necesidad de que el usuario tenga instalada la App turística, dado que el SO del móvil (Android >=4.4) genera directamente la notificación PUSH con el mensaje de aviso y la URL
  • Eddystone-TLM. Contiene datos sobre el estado del propio beacon: nivel de batería, temperatura dispositivo, tiempo desde el reseteo, número de veces que se ha emitido el anuncio.

En la configuración del beacon se decide qué tipo de mensajes se envían (UID + TLM, EID + TLM, URL + TLM) y con qué frecuencia de modo que se mantenga la calidad del servicio ahorrando batería.

Es esencial destacar que los mensajes TLM no tienen UUID ni URL por lo que se depende de que en el cliente se asocien con los mensajes anteriores, presuponiéndose que el UUID es el mismo. Aquí veo un mar de complicaciones derivado de la posible existencia de beacons próximos de NN marcas o con frecuencias de envío suficientemente bajas como para que el dueño del móvil se mueva de una zona a otra...

Para que el sistema funcione es indispensable que el usuario tenga:

  •     Un teléfono inteligente
  •     Activado el Bluetooth
  •     App instalada y activa (sólo si manejamos mensajes Eddystone-UID)

El SO recibe los mensajes:

  • Si es un mensaje Eddystone-UID, identifica el UUID, se lo pasa a la App asociada a ese UUID. La App envía en ese momento a cierto servidor propio el mensaje. El servidor analiza el ID de la zona (Major), el ID de la ubicación exanca (Minor) y la potencia (TX) y genera un mensaje de respuestqa con, por ejemplo, la URL de cierto portal donde hay una audioguía que explica algo sobre el cuadro que sabemos que está a menos de 1 metro (TX era por ejemplo 1)
  • Si es un mensaje Eddystone-URL, el SO genera la notificación PUSH para mostrar esa URL al usuario (sin necesidad de una App)
  • Si es un mensaje Eddystone-TLM, si hubo mensaje UID se le pasa la telemetría al App asociado, si hubo mensaje URL entiendo que se envía la telemetría a esa URL (tampoco he visto aclaraciones sobre este punto)

Importante, en 2018 Google dejo de dar soporte a Eddystone y a las Eddystone-URL...

Alernativas:
    NFC. La pega es que su alcance es de 20 cm
    Seeketing. Es una alternativa muy seria dado que no precisamos de App instalada
    EddyStone. Es un protocolo impulsado por Google con ciertas diferencias y mejoras

Enlaces:
    https://en.wikipedia.org/wiki/Eddystone_(Google)
    https://www.mokosmart.com/es/eddystone-protocol-and-specifications/
    https://os.mbed.com/teams/Bluetooth-Low-Energy/code/BLE_EddystoneBeacon_Service/file/dfb7fb5a971b/Eddystone.h/
    https://www.novelbits.io/eddystone-beacons-zephyr-nrf52/#eddystonetlm-eddystoneeid-aaa85f04-9e47-4eb4-bc6c-6832e289639c
    https://github.com/google/eddystone/tree/master/eddystone-tlm

Geoposicionamiento indoor con iBeacon

Tecnología BLE, protocolo de Apple que también se soporta en Android. Alcance 50 metros.

Los beacon se colocan estratégicamente en distintas ubicaciones y se ajusta para emitir con cierta frecuencia una señal de baliza que contiene el siguiente mensaje:

  • Un identificador con 3 partes:
    • UUID. Son 16 bits que quedarían asociados a la "marca", en nuestro caso Diputación Alicante
    • Major. Son 2 bits que se asocian por ejemplo a la zona, en nuestro caso el Museo Marq o la ruta GR-330
    • Minor. Son 2 bits que se asocian por ejemplo a un punto concreto en el museo o a un punto concreto en la ruta.
  • Un indicador de la potencia de emisión (TX). Si el beacon se ha ajustado para emitir con 1 metro de alcance, el valor 1, pero si se ha ajustado para emitir con 5 metros de alcance, el valor valor será 5.

Para que el sistema funcione es indispensable que el usuario tenga:

  •     Un teléfono inteligente
  •     Activado el Bluetooth
  •     Instalada y activada la aplicación receptora que escucha esos IDs y sabe con qué servidor hablar.

El SO recibe los mensajes, identifica los UUID y se los pasa a la App asociada a cada "marca". La App envía a su servidor asociado el mensaje. El servidor analiza el ID de la zona (Major), el ID de la ubicación exanca (Minor) y la potencia (TX) y genera un mensaje de respuesta con, por ejemplo, la URL de cierto portal donde hay una audioguía que explica algo sobre el cuadro que sabemos que está a menos de 1 metro (TX era por ejemplo 1)

Alernativas:
    NFC. La pega es que su alcance es de 20 cm
    Seeketing. Es una alternativa muy seria dado que no precisamos de App instalada
    EddyStone. Es un protocolo impulsado por Google con ciertas diferencias y mejoras

Enlaces:
    https://es.wikipedia.org/wiki/IBeacon
    https://developer.apple.com/ibeacon/

martes, 13 de abril de 2021

Pruebas con OpenCV y una webcam

Se ha probado la librería OpenCV.js para la identificación de diferentes patrones en un vídeo: caras, ojos, coches, el cuerpo humano, etc. El ejemplo se ha montado con sobre una página web 'index.html' y un pequeño código 'script.js'.

index.html

<!DOCTYPE html>
<html lang="es">

<head>
<meta charset="utf-8">
<title>Prueba de OpenCV.js usando (https://github.com/opencv) y (https://huningxin.github.io/opencv.js)</title>
</head>

<body>
<table>
<tr>
<td><video id="video"></video></td>
</tr>
<tr>
<td><canvas id="canvasOutput" width=320 height=240></canvas></td>
</tr>
</table>
</body>

<script>
var Module = {
wasmBinaryFile: 'https://huningxin.github.io/opencv.js/build/wasm/opencv_js.wasm',
preRun: [function () {
Module.FS_createPreloadedFile('/',
'haarcascade_frontalface_default.xml',
'https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml',
true, false);
Module.FS_createPreloadedFile('/',
'haarcascade_eye.xml',
'https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_eye.xml',
true, false);
Module.FS_createPreloadedFile('/',
'haarcascade_fullbody.xml',
'https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_fullbody.xml',
true, false);
Module.FS_createPreloadedFile('/',
'cas4.xml',
'https://raw.githubusercontent.com/abhi-kumar/CAR-DETECTION/master/cas4.xml',
true, false);
}],
_main: function () { opencvIsReady(); }
};
</script>

<script async src="https://huningxin.github.io/opencv.js/build/wasm/opencv.js"></script>
<script src="./script.js"></script>

</html>

script.js

/**
* Instanciamos variables
*/
let hayWebcam = false; // Indicador de la existencia de señal de vídeo desde la cámara
let videoWidth; // Anchura de la imagen de vídeo
let videoHeight; // Altura de la imagen de vídeo
let stream = null; // Stream de vídeo
let detector = null; // Detector de objetos en las imágenes
let canvasInput = null; // Panel de dibujo
let canvasInputCtx = null;
/**
* Capturamos los componentes <html> asociados al vídeo
*/
let video = document.getElementById('video');
let canvasOutput = document.getElementById('canvasOutput');
let canvasOutputCtx = canvasOutput.getContext('2d');

/**
* Captura la webcam y su señal de stream, volcando esa imagen
* en el componente <video>
* @returns
*/
function startCamera() {

if (hayWebcam) {
// Si se ha detectado ya la webcam salimos
return;
}
// Capturamos la señal de la webcam
navigator.mediaDevices.getUserMedia({ video: true, audio: false })
.then(function (s) {
stream = s;
video.srcObject = s;
video.play();
})
.catch(function (err) {
console.log("Error: " + err);
});
// Lanzamos el procesamiento de la señal de vídeo
video.addEventListener("canplay", function (ev) {
if (!hayWebcam) {
videoWidth = video.videoWidth;
videoHeight = video.videoHeight;
canvasOutput.width = videoWidth;
canvasOutput.height = videoHeight;
hayWebcam = true;
}
startVideoProcessing();
}, false);
}

/**
* Prepara lo necesario para el procesamiento inicial del vídeo
* @returns
*/
function startVideoProcessing() {

// Si no hay señal de vídeo terminamos
if (!hayWebcam) {
console.warn("Webcam no detectada...");
return;
}
// Ajustamos el canvas de la página
canvasInput = document.createElement('canvas');
canvasInput.width = videoWidth;
canvasInput.height = videoHeight;
canvasInputCtx = canvasInput.getContext('2d');

// Preparamos las matrices
srcMat = new cv.Mat(videoHeight, videoWidth, cv.CV_8UC4);
grayMat = new cv.Mat(videoHeight, videoWidth, cv.CV_8UC1);

// Instanciamos el detector y cargamos el patrón que deseemos
detector = new cv.CascadeClassifier();
detector.load('haarcascade_frontalface_default.xml'); // caras
//detector.load('haarcascade_eye.xml'); // ojos
//detector.load('haarcascade_fullbody.xml'); // cuerpo humano
//detector.load('cas4.xml'); // coches

// Lanzamos el procesamiento del vídeo
requestAnimationFrame(processVideo);
}

/**
* Realiza el procesamiento de la señal de vídeo para detectar patrones
*/
function processVideo() {

// Preparamos el panel donde se vocarán los fotogramas con el vídeo
canvasInputCtx.drawImage(video, 0, 0, videoWidth, videoHeight);
let imageData = canvasInputCtx.getImageData(0, 0, videoWidth, videoHeight);
srcMat.data.set(imageData.data);
cv.cvtColor(srcMat, grayMat, cv.COLOR_RGBA2GRAY);
let patronesDetectados = [];
let size;
// Inicializa la matriz de coordenadas de los patrones
let faceVect = new cv.RectVector();
// Inicializa la matriz de patrones
let faceMat = new cv.Mat();
cv.pyrDown(grayMat, faceMat);
cv.pyrDown(faceMat, faceMat);
size = faceMat.size();
// Lanza el detector de patrones
detector.detectMultiScale(faceMat, faceVect);
// Recorre la matriz de coordenadas de los patrones detectados y dibuja los cuadrados
for (let i = 0; i < faceVect.size(); i++) {
let face = faceVect.get(i);
patronesDetectados.push(new cv.Rect(face.x, face.y, face.width, face.height));
}
// Borra las matrices de patrones y de coordenadas
faceMat.delete();
faceVect.delete();
// Dibuja cada frame con patrones en el panel resultante
canvasOutputCtx.drawImage(canvasInput, 0, 0, videoWidth, videoHeight);
// Dibuja los rectangulos en cada frame en el panel resultante
dibujaRectangulos(canvasOutputCtx, patronesDetectados, 'yellow', size);
// Continúa el procesamiento de los siguientes frames
requestAnimationFrame(processVideo);
}

/**
* Dibuja un rectangulo en cada patrón detectado
*/
function dibujaRectangulos(ctx, patronesDetectados, color, size) {

for (let i = 0; i < patronesDetectados.length; ++i) {
let rect = patronesDetectados[i];
let xRatio = videoWidth / size.width;
let yRatio = videoHeight / size.height;
ctx.lineWidth = 1;
ctx.strokeStyle = color;
ctx.strokeRect(rect.x * xRatio, rect.y * yRatio, rect.width * xRatio, rect.height * yRatio);
}
}

/**
* Cuando se carga la librería y el fichero de entrenamiento para la detección de patrones
* se lanza la función que desencadena la activación de la webcam y el procesamiento del
* stream de vídeo
*/
function opencvIsReady() {

console.log('OpenCV.js cargado');
startCamera();
}

sábado, 10 de abril de 2021

BrainJS. Entrenando y probando una red neuronal prealimentada

Entrenamos una red neuronal básica de clasificación basada en retropropagación con 2 capas ocultas de 4 y 2 neuronas.

Generamos 500 puntos de datos en el amarilla (0,0) a (5,5) indicando para cada punto si está o no dentro del cuadrado rojo.

Tras el entrenamiento la red podrá clasificar los puntos del plano que caigan dentro del cuadrado ((2,2),(3,3))

 

Generando en la consola cosas como:

El punto (4.891284625420421,0.7771611636075099) está en el rectangulo ((2,2),(3,3)) --> 0.0035132323391735554
El punto (2.754181240075061,2.371697018062347) está en el rectangulo ((2,2),(3,3)) --> 0.9642634391784668

El código es el siguiente:

<!DOCTYPE html>
<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Red neuronal de clasificación que aprende si un cierto punto {coordX: x, coordY: y} está o no dentro de un
cuadrado {pointA: pointA, pointB:pointB}</title>
<script src="https://unpkg.com/brain.js@2.0.0-beta.2/dist/brain-browser.min.js"></script>
</head>

<body>

<form>
<input type="button" value="GeneraDatos" onclick="generaDatosEntrenamiento()">
<input type="button" value="EntrenaRed" onclick="realizaEntrenamiento()">
<input type="button" value="GeneraPrediccion" onclick="generaPrediccion()">
</form>

<script type="text/javascript">

/*
Area donde se generarán puntos para los datos de entrenamiento
*/
const nubePuntos = { puntoA: { coordX: 0, coordY: 0 }, puntoB: { coordX: 5, coordY: 5 } };

/*
La red aprende si un punto está dentro de este cuadrado
*/
const cuadradoEntrenamiento = { puntoA: { coordX: 2, coordY: 2 }, puntoB: { coordX: 3, coordY: 3 } };

/*
Función que determina si un punto está dentro de un cierto cuadrado
*/
function estaDentroCuadrado(punto, cuadrado) {
if (punto.coordX >= cuadrado.puntoA.coordX && punto.coordX <= cuadrado.puntoB.coordX &&
punto.coordY >= cuadrado.puntoA.coordY && punto.coordY <= cuadrado.puntoB.coordY) {
return 1;
}
return 0;
}

/*
Función que genera en consola los datos de entenamiento.
Primero genera puntos en una nube aleatoria y después
analiza si dichos puntos caen dentro del cuadrado asociado
al entrenamiento
*/
function generaDatosEntrenamiento() {

let datos = [];
for (i = 0; i < 500; i++) {
x = (Math.random() * nubePuntos.puntoB.coordX) + nubePuntos.puntoA.coordX;
y = (Math.random() * nubePuntos.puntoB.coordY) + nubePuntos.puntoA.coordY;
output = { dentro: estaDentroCuadrado({ coordX: x, coordY: y }, cuadradoEntrenamiento) };
input = { coordX: x, coordY: y };
datos.push({ input, output });
}
var datosJSON = JSON.stringify(datos);
console.log(datosJSON.replace(/"/g, ""));
}

/*
Datos de entrenamiento generados por el código anterios. Hay 24 positivos entre los 500 puntos.
*/
var datosEntrenamiento = [
{input:{coordX:2.591158582085052,coordY:2.042299833846048},output:{dentro:1}},
{input:{coordX:3.0261573771835253,coordY:4.656101063432621},output:{dentro:0}},
{input:{coordX:4.859026562809468,coordY:1.6802148560277903},output:{dentro:0}},
{input:{coordX:0.8688027560553258,coordY:2.9602023180901864},output:{dentro:0}},
{input:{coordX:2.6305403349206795,coordY:1.3342552750053605},output:{dentro:0}},
{input:{coordX:0.39465399884823116,coordY:2.9874843889901372},output:{dentro:0}},
{input:{coordX:4.584064088772152,coordY:0.8300798971447471},output:{dentro:0}},
{input:{coordX:1.7191849022771426,coordY:0.682554313161095},output:{dentro:0}},
{input:{coordX:2.2805414453890753,coordY:0.9334049896908847},output:{dentro:0}},
{input:{coordX:0.4592871750273897,coordY:2.92232926674274},output:{dentro:0}},
{input:{coordX:2.511581676916764,coordY:4.894463455666016},output:{dentro:0}},
{input:{coordX:3.4005691362617285,coordY:3.815651368039598},output:{dentro:0}},
{input:{coordX:1.2471487205648546,coordY:1.3183864324593353},output:{dentro:0}},
{input:{coordX:0.8556602927236767,coordY:1.459276194560442},output:{dentro:0}},
{input:{coordX:4.338815460259784,coordY:3.1474897062307914},output:{dentro:0}},
{input:{coordX:2.1195972310817446,coordY:0.9427627021985485},output:{dentro:0}},
{input:{coordX:2.76802886127951,coordY:3.790134057556593},output:{dentro:0}},
{input:{coordX:4.450454654639632,coordY:4.533901809674175},output:{dentro:0}},
{input:{coordX:4.574229214461389,coordY:0.03408808266700203},output:{dentro:0}},
{input:{coordX:4.6489084985771685,coordY:3.70315149257231},output:{dentro:0}},
{input:{coordX:0.6770676688487298,coordY:0.7995059524010956},output:{dentro:0}},
{input:{coordX:4.646182374334192,coordY:2.4573025129351853},output:{dentro:0}},
{input:{coordX:0.16890281391721884,coordY:0.16608932338141424},output:{dentro:0}},
{input:{coordX:0.7726563178868362,coordY:1.4625243951445444},output:{dentro:0}},
{input:{coordX:2.2855944936244406,coordY:2.1153893158643595},output:{dentro:1}},
{input:{coordX:3.5461546657770278,coordY:1.9070773542434099},output:{dentro:0}},
{input:{coordX:2.2178848951721304,coordY:0.8957765957427388},output:{dentro:0}},
{input:{coordX:0.4627348937373926,coordY:0.5736146880651605},output:{dentro:0}},
{input:{coordX:2.3555675994938463,coordY:2.9698624393694506},output:{dentro:1}},
{input:{coordX:0.8122106633258735,coordY:0.9805200136814568},output:{dentro:0}},
{input:{coordX:3.257621549205135,coordY:2.9930513841845787},output:{dentro:0}},
{input:{coordX:3.7147177482498765,coordY:0.9562403840943867},output:{dentro:0}},
{input:{coordX:3.6895664234884533,coordY:3.5023604710766723},output:{dentro:0}},
{input:{coordX:0.32007061667000347,coordY:0.8730843949345857},output:{dentro:0}},
{input:{coordX:1.2785186287056733,coordY:2.666098662617977},output:{dentro:0}},
{input:{coordX:4.868305613066592,coordY:1.2725352247386745},output:{dentro:0}},
{input:{coordX:3.992499517104606,coordY:0.6301677792938759},output:{dentro:0}},
{input:{coordX:4.537690914812764,coordY:4.413608189244301},output:{dentro:0}},
{input:{coordX:4.528591584029364,coordY:3.366803765438953},output:{dentro:0}},
{input:{coordX:4.996360201662469,coordY:4.569639654197882},output:{dentro:0}},
{input:{coordX:1.1891569653725986,coordY:3.614126471025652},output:{dentro:0}},
{input:{coordX:0.4377865579997414,coordY:4.772961789835246},output:{dentro:0}},
{input:{coordX:4.508371516434325,coordY:0.5232989625136752},output:{dentro:0}},
{input:{coordX:3.699765806871148,coordY:3.3244219003345634},output:{dentro:0}},
{input:{coordX:4.647829486171694,coordY:4.195646692351189},output:{dentro:0}},
{input:{coordX:1.4426875559930785,coordY:1.1372394670006107},output:{dentro:0}},
{input:{coordX:4.129268527924347,coordY:2.154283561684247},output:{dentro:0}},
{input:{coordX:3.0307522856923415,coordY:1.154206395321602},output:{dentro:0}},
{input:{coordX:1.0115229254328324,coordY:4.8773896890339135},output:{dentro:0}},
{input:{coordX:4.514556383398797,coordY:4.8066113479240435},output:{dentro:0}},
{input:{coordX:1.503406160616429,coordY:0.12331365391142768},output:{dentro:0}},
{input:{coordX:3.5502173022206556,coordY:3.33952133351752},output:{dentro:0}},
{input:{coordX:0.1964478156412358,coordY:2.5426592821816425},output:{dentro:0}},
{input:{coordX:3.4718932109813543,coordY:2.84786987658752},output:{dentro:0}},
{input:{coordX:3.022656198603906,coordY:4.852187888866357},output:{dentro:0}},
{input:{coordX:4.12481585909126,coordY:1.413256725342091},output:{dentro:0}},
{input:{coordX:1.1938537566386676,coordY:0.7950300833621438},output:{dentro:0}},
{input:{coordX:0.6329632465600227,coordY:0.42135035907095},output:{dentro:0}},
{input:{coordX:0.3221356696554589,coordY:3.0991561780590358},output:{dentro:0}},
{input:{coordX:2.8998121371390564,coordY:3.6250659878024423},output:{dentro:0}},
{input:{coordX:2.8569843965067996,coordY:1.7136424252402134},output:{dentro:0}},
{input:{coordX:1.7783262146184486,coordY:0.23603740474950008},output:{dentro:0}},
{input:{coordX:1.2042264768117557,coordY:0.6134693021262111},output:{dentro:0}},
{input:{coordX:3.23444842503202,coordY:0.4688557629150153},output:{dentro:0}},
{input:{coordX:2.3695864235707456,coordY:4.083342251621617},output:{dentro:0}},
{input:{coordX:3.2820433975456775,coordY:1.7752579693852106},output:{dentro:0}},
{input:{coordX:3.986810383953949,coordY:4.141298227960039},output:{dentro:0}},
{input:{coordX:3.335070183676475,coordY:1.6580988344070318},output:{dentro:0}},
{input:{coordX:2.5315106252200312,coordY:4.772586872813491},output:{dentro:0}},
{input:{coordX:2.4990349665349187,coordY:2.3295824687469713},output:{dentro:1}},
{input:{coordX:0.12828964256510422,coordY:3.340531337878872},output:{dentro:0}},
{input:{coordX:4.722001884885581,coordY:4.303809877436634},output:{dentro:0}},
{input:{coordX:2.2652293980087035,coordY:2.2465434331052094},output:{dentro:1}},
{input:{coordX:1.7879142582321688,coordY:2.9506118273092734},output:{dentro:0}},
{input:{coordX:4.408619729893482,coordY:2.9860324361282364},output:{dentro:0}},
{input:{coordX:1.1648443073019066,coordY:0.04664074787817718},output:{dentro:0}},
{input:{coordX:1.1614285878923303,coordY:0.19277172136332954},output:{dentro:0}},
{input:{coordX:2.9544552908326738,coordY:3.5728596480170296},output:{dentro:0}},
{input:{coordX:2.9525793254027324,coordY:4.05580460021049},output:{dentro:0}},
{input:{coordX:3.0271123423790645,coordY:3.9043302859360414},output:{dentro:0}},
{input:{coordX:4.842860307346679,coordY:4.674812183882458},output:{dentro:0}},
{input:{coordX:3.1599326128975562,coordY:0.29846574268705184},output:{dentro:0}},
{input:{coordX:0.6102122467164806,coordY:3.2455042824668623},output:{dentro:0}},
{input:{coordX:1.1252857446476872,coordY:2.2662565994396724},output:{dentro:0}},
{input:{coordX:0.19651972687378283,coordY:4.567336121693078},output:{dentro:0}},
{input:{coordX:3.497059258387798,coordY:1.869572264271337},output:{dentro:0}},
{input:{coordX:2.802017822766399,coordY:2.0450265802649636},output:{dentro:1}},
{input:{coordX:1.0693147997366297,coordY:4.994694608471769},output:{dentro:0}},
{input:{coordX:1.2255545493324198,coordY:1.131059782715944},output:{dentro:0}},
{input:{coordX:1.3759590004161164,coordY:1.0873737033491748},output:{dentro:0}},
{input:{coordX:4.0566311657843315,coordY:2.4126195829409207},output:{dentro:0}},
{input:{coordX:4.822085561417683,coordY:2.227405205706166},output:{dentro:0}},
{input:{coordX:3.133692808689351,coordY:3.0703550480179516},output:{dentro:0}},
{input:{coordX:0.6526543504184695,coordY:2.510283262875781},output:{dentro:0}},
{input:{coordX:0.3788131657184035,coordY:4.9863507647425465},output:{dentro:0}},
{input:{coordX:4.454252568839932,coordY:1.3536210680951104},output:{dentro:0}},
{input:{coordX:1.0797310165066654,coordY:1.0305142946159322},output:{dentro:0}},
{input:{coordX:0.12343201243169899,coordY:3.924519812183516},output:{dentro:0}},
{input:{coordX:1.0002062213634715,coordY:0.6708008974302682},output:{dentro:0}},
{input:{coordX:2.964984464260022,coordY:3.102376387371992},output:{dentro:0}},
{input:{coordX:2.719686041372456,coordY:0.30746515304745425},output:{dentro:0}},
{input:{coordX:2.4931787449307,coordY:2.5502514138024104},output:{dentro:1}},
{input:{coordX:1.1253108707716102,coordY:2.9311631623755345},output:{dentro:0}},
{input:{coordX:4.827212707222049,coordY:3.149451034597},output:{dentro:0}},
{input:{coordX:3.4549645088598253,coordY:0.10394653511317298},output:{dentro:0}},
{input:{coordX:3.6363424686748314,coordY:4.65661141378061},output:{dentro:0}},
{input:{coordX:0.1884508433924903,coordY:2.7820506841773254},output:{dentro:0}},
{input:{coordX:2.2139997106122684,coordY:2.4534714002295215},output:{dentro:1}},
{input:{coordX:4.719510725097681,coordY:1.812211850872144},output:{dentro:0}},
{input:{coordX:3.2360525565511393,coordY:1.6076557675323921},output:{dentro:0}},
{input:{coordX:4.4802775208883325,coordY:4.494509825444161},output:{dentro:0}},
{input:{coordX:1.881487250916762,coordY:1.309964288741844},output:{dentro:0}},
{input:{coordX:2.844044233791611,coordY:4.63429000968908},output:{dentro:0}},
{input:{coordX:1.4784204143516488,coordY:4.449629748570722},output:{dentro:0}},
{input:{coordX:2.2804551716397925,coordY:3.802103886514118},output:{dentro:0}},
{input:{coordX:2.0010265040334407,coordY:1.6580850821679345},output:{dentro:0}},
{input:{coordX:1.542268940110384,coordY:0.9888049784803432},output:{dentro:0}},
{input:{coordX:4.046825000230106,coordY:0.7013135654771213},output:{dentro:0}},
{input:{coordX:0.5134317831107094,coordY:4.330518057506569},output:{dentro:0}},
{input:{coordX:2.0799136779317533,coordY:4.944250791785447},output:{dentro:0}},
{input:{coordX:2.379233669383507,coordY:0.9762506495844975},output:{dentro:0}},
{input:{coordX:3.097867857296471,coordY:3.159473813391509},output:{dentro:0}},
{input:{coordX:0.9516003832855952,coordY:1.1248671040871243},output:{dentro:0}},
{input:{coordX:2.7729341514204076,coordY:1.7416475490250005},output:{dentro:0}},
{input:{coordX:1.5401166777293223,coordY:0.6195253222461872},output:{dentro:0}},
{input:{coordX:3.99292833250286,coordY:3.4324301227992238},output:{dentro:0}},
{input:{coordX:2.646802641558202,coordY:3.1671619790492245},output:{dentro:0}},
{input:{coordX:0.6434863523090179,coordY:2.2091534561215087},output:{dentro:0}},
{input:{coordX:2.441131123539017,coordY:2.992076148678822},output:{dentro:1}},
{input:{coordX:2.537002301911306,coordY:0.6331317797210118},output:{dentro:0}},
{input:{coordX:1.530887461546147,coordY:4.042357940796142},output:{dentro:0}},
{input:{coordX:2.640285776442436,coordY:2.077450640362153},output:{dentro:1}},
{input:{coordX:2.144317605868238,coordY:0.3745379249097147},output:{dentro:0}},
{input:{coordX:2.6534373037077934,coordY:1.7057412044393643},output:{dentro:0}},
{input:{coordX:4.612283611931763,coordY:2.191754158105044},output:{dentro:0}},
{input:{coordX:1.474009689673761,coordY:0.4452474713236032},output:{dentro:0}},
{input:{coordX:3.2641893900643715,coordY:4.160327255918491},output:{dentro:0}},
{input:{coordX:0.4873651158345277,coordY:2.714631430986165},output:{dentro:0}},
{input:{coordX:2.205211548697456,coordY:0.5697480043809128},output:{dentro:0}},
{input:{coordX:3.302099530862276,coordY:0.41257830760690173},output:{dentro:0}},
{input:{coordX:4.038077115313566,coordY:1.0012554140117365},output:{dentro:0}},
{input:{coordX:3.505468105375011,coordY:0.3395151851650219},output:{dentro:0}},
{input:{coordX:4.036153690311485,coordY:3.2053996151954567},output:{dentro:0}},
{input:{coordX:0.31621120057998053,coordY:0.6696052330883773},output:{dentro:0}},
{input:{coordX:2.2892052635157207,coordY:1.2260431261314158},output:{dentro:0}},
{input:{coordX:0.8361540826463293,coordY:2.5578836985029496},output:{dentro:0}},
{input:{coordX:2.4126310593705242,coordY:1.0381328646504135},output:{dentro:0}},
{input:{coordX:4.820974048439909,coordY:4.019341586227395},output:{dentro:0}},
{input:{coordX:4.437471321217095,coordY:3.9285272180602737},output:{dentro:0}},
{input:{coordX:1.9235825270606748,coordY:2.80672294644726},output:{dentro:0}},
{input:{coordX:0.49187184951079377,coordY:3.815047897619695},output:{dentro:0}},
{input:{coordX:3.1671294238885084,coordY:0.5371133021968755},output:{dentro:0}},
{input:{coordX:0.30866302906460696,coordY:1.8028667354149874},output:{dentro:0}},
{input:{coordX:3.322844931918139,coordY:4.4428220052612275},output:{dentro:0}},
{input:{coordX:2.5006771808360444,coordY:0.7361300107916569},output:{dentro:0}},
{input:{coordX:1.3467979180968215,coordY:2.3365418736839065},output:{dentro:0}},
{input:{coordX:2.1037178011498074,coordY:3.573568855027767},output:{dentro:0}},
{input:{coordX:3.145264338020147,coordY:1.2577400217239865},output:{dentro:0}},
{input:{coordX:3.556844362660763,coordY:0.2438256141143752},output:{dentro:0}},
{input:{coordX:3.6096180341380664,coordY:0.8115602761520224},output:{dentro:0}},
{input:{coordX:3.8630900174630716,coordY:4.1252679207308365},output:{dentro:0}},
{input:{coordX:2.8286522753927867,coordY:4.414246896078119},output:{dentro:0}},
{input:{coordX:1.3976321093575306,coordY:0.14529072299913348},output:{dentro:0}},
{input:{coordX:1.206371642850963,coordY:2.9025858407059606},output:{dentro:0}},
{input:{coordX:3.036101595749428,coordY:0.6299660099446497},output:{dentro:0}},
{input:{coordX:3.9112927431083264,coordY:0.004482649606867839},output:{dentro:0}},
{input:{coordX:4.50589350221354,coordY:2.7235805875277217},output:{dentro:0}},
{input:{coordX:3.6274924449728676,coordY:0.7104132143966158},output:{dentro:0}},
{input:{coordX:0.46392992873701966,coordY:0.2441132995551143},output:{dentro:0}},
{input:{coordX:2.3637735876144212,coordY:1.0054523513262077},output:{dentro:0}},
{input:{coordX:3.7422785206760993,coordY:4.570449686331192},output:{dentro:0}},
{input:{coordX:3.256347422415451,coordY:4.480529572348397},output:{dentro:0}},
{input:{coordX:3.31120747482528,coordY:2.6113518089710865},output:{dentro:0}},
{input:{coordX:2.694622163466229,coordY:3.136643734147006},output:{dentro:0}},
{input:{coordX:0.15722435217951136,coordY:2.304940862603928},output:{dentro:0}},
{input:{coordX:3.1441067586517013,coordY:1.7185257495174033},output:{dentro:0}},
{input:{coordX:3.198464126190431,coordY:0.4032328507488142},output:{dentro:0}},
{input:{coordX:4.046195344741124,coordY:0.6242624120002577},output:{dentro:0}},
{input:{coordX:2.4930878331018618,coordY:4.279834549855128},output:{dentro:0}},
{input:{coordX:3.3204596222810583,coordY:0.9691690755862736},output:{dentro:0}},
{input:{coordX:4.79682369132332,coordY:3.5857033941607535},output:{dentro:0}},
{input:{coordX:4.881737220735893,coordY:1.1497013701453125},output:{dentro:0}},
{input:{coordX:1.1399663907900743,coordY:3.1945853733278193},output:{dentro:0}},
{input:{coordX:3.193139924783383,coordY:3.084946420385367},output:{dentro:0}},
{input:{coordX:3.3300531036310432,coordY:1.3865524875040647},output:{dentro:0}},
{input:{coordX:3.2986598941143708,coordY:1.5886990725219297},output:{dentro:0}},
{input:{coordX:2.912909505311174,coordY:2.5970774698332195},output:{dentro:1}},
{input:{coordX:3.218838960999734,coordY:0.4622839543502122},output:{dentro:0}},
{input:{coordX:1.1686157253516445,coordY:3.9733161388806018},output:{dentro:0}},
{input:{coordX:0.761516023534804,coordY:3.3518358028088335},output:{dentro:0}},
{input:{coordX:2.0161924111919975,coordY:0.7450015365413842},output:{dentro:0}},
{input:{coordX:2.154483334794091,coordY:4.935563041107962},output:{dentro:0}},
{input:{coordX:2.0003437495773073,coordY:0.9976917254277257},output:{dentro:0}},
{input:{coordX:3.7943900763777054,coordY:2.9690857457658835},output:{dentro:0}},
{input:{coordX:0.040052876368935064,coordY:1.1006471923568757},output:{dentro:0}},
{input:{coordX:1.252292086332711,coordY:4.841139022671863},output:{dentro:0}},
{input:{coordX:1.6003916414581076,coordY:4.2317218044703555},output:{dentro:0}},
{input:{coordX:2.033912728395202,coordY:3.665385555664264},output:{dentro:0}},
{input:{coordX:0.03368854486543116,coordY:3.9525858833283687},output:{dentro:0}},
{input:{coordX:3.949721716085767,coordY:3.9754011884828433},output:{dentro:0}},
{input:{coordX:2.879666811003276,coordY:1.6771767219553952},output:{dentro:0}},
{input:{coordX:0.23351737905658976,coordY:0.4313530243815328},output:{dentro:0}},
{input:{coordX:3.4824057667906745,coordY:4.460747124603172},output:{dentro:0}},
{input:{coordX:0.33272984294137553,coordY:1.2188632155980246},output:{dentro:0}},
{input:{coordX:0.5320065971881122,coordY:0.7836628149419234},output:{dentro:0}},
{input:{coordX:0.22101749819515848,coordY:4.624565660438144},output:{dentro:0}},
{input:{coordX:3.503921210863719,coordY:2.303683104955617},output:{dentro:0}},
{input:{coordX:0.416091577986476,coordY:3.8789269488151996},output:{dentro:0}},
{input:{coordX:4.28457935950948,coordY:4.005992278098774},output:{dentro:0}},
{input:{coordX:2.99918373651031,coordY:0.3977901126214567},output:{dentro:0}},
{input:{coordX:0.7078986063750276,coordY:1.2191764574720438},output:{dentro:0}},
{input:{coordX:4.11357897804483,coordY:1.2533169884420359},output:{dentro:0}},
{input:{coordX:3.150575300833957,coordY:0.7410961429714324},output:{dentro:0}},
{input:{coordX:2.5363201713319516,coordY:1.8184284470722463},output:{dentro:0}},
{input:{coordX:0.011505880489067755,coordY:4.813799663548135},output:{dentro:0}},
{input:{coordX:1.86717239017355,coordY:4.7884573230054315},output:{dentro:0}},
{input:{coordX:0.25027252781804954,coordY:2.223235236335243},output:{dentro:0}},
{input:{coordX:3.822850020406821,coordY:0.24177433228934897},output:{dentro:0}},
{input:{coordX:0.5022527096697582,coordY:3.5510296730010236},output:{dentro:0}},
{input:{coordX:4.137924602625344,coordY:0.8568680640564086},output:{dentro:0}},
{input:{coordX:4.146156843839235,coordY:3.593501765137938},output:{dentro:0}},
{input:{coordX:4.995397174773309,coordY:3.737491514409732},output:{dentro:0}},
{input:{coordX:2.5723499113065267,coordY:2.4986412543460226},output:{dentro:1}},
{input:{coordX:4.970715814736831,coordY:0.6447003748166957},output:{dentro:0}},
{input:{coordX:4.31821764575692,coordY:2.381087291549834},output:{dentro:0}},
{input:{coordX:3.246404064839174,coordY:3.3208643352057443},output:{dentro:0}},
{input:{coordX:0.5352960894640924,coordY:1.353182257896115},output:{dentro:0}},
{input:{coordX:4.277189498701176,coordY:3.606249216975504},output:{dentro:0}},
{input:{coordX:0.454836144248838,coordY:2.9529998012745082},output:{dentro:0}},
{input:{coordX:0.33488936907839273,coordY:3.948701506298655},output:{dentro:0}},
{input:{coordX:2.9866254154135543,coordY:2.9221584426968024},output:{dentro:1}},
{input:{coordX:4.750680594542428,coordY:4.787429272176158},output:{dentro:0}},
{input:{coordX:2.151498127386546,coordY:4.834940253828296},output:{dentro:0}},
{input:{coordX:3.2629541105014677,coordY:1.8259205187534415},output:{dentro:0}},
{input:{coordX:4.411250519093598,coordY:1.186695656815004},output:{dentro:0}},
{input:{coordX:3.1064641630743592,coordY:2.1711842215351123},output:{dentro:0}},
{input:{coordX:4.191850973644078,coordY:1.303591139830238},output:{dentro:0}},
{input:{coordX:2.5082742617950426,coordY:1.126237024756986},output:{dentro:0}},
{input:{coordX:3.1775890184818985,coordY:3.5607832447605543},output:{dentro:0}},
{input:{coordX:4.183438224086873,coordY:0.4496393496862089},output:{dentro:0}},
{input:{coordX:2.5697407634505702,coordY:4.070894131287156},output:{dentro:0}},
{input:{coordX:4.256449670465758,coordY:0.09912408449155641},output:{dentro:0}},
{input:{coordX:3.0310959000939777,coordY:3.5923076601993635},output:{dentro:0}},
{input:{coordX:0.22955381724616641,coordY:2.4188592045079895},output:{dentro:0}},
{input:{coordX:4.8379796481911095,coordY:0.37691843036169403},output:{dentro:0}},
{input:{coordX:0.9452190725101245,coordY:1.5058882038269494},output:{dentro:0}},
{input:{coordX:2.854452757418428,coordY:4.882024402446001},output:{dentro:0}},
{input:{coordX:1.7283906365844282,coordY:2.6019626293170055},output:{dentro:0}},
{input:{coordX:2.3946540350760834,coordY:4.944569298771339},output:{dentro:0}},
{input:{coordX:3.46222936432613,coordY:3.134662092769997},output:{dentro:0}},
{input:{coordX:4.727327224346942,coordY:3.1262198155266803},output:{dentro:0}},
{input:{coordX:3.4565853301276013,coordY:2.822639788738913},output:{dentro:0}},
{input:{coordX:3.2161094614609897,coordY:4.065903005940312},output:{dentro:0}},
{input:{coordX:2.413619433881861,coordY:1.082963068106384},output:{dentro:0}},
{input:{coordX:1.0552911167009378,coordY:2.5836197375907384},output:{dentro:0}},
{input:{coordX:3.0243001959386477,coordY:1.1017774869634929},output:{dentro:0}},
{input:{coordX:1.7313984034258234,coordY:2.4225443952318635},output:{dentro:0}},
{input:{coordX:3.1514998666952607,coordY:0.47263095966346036},output:{dentro:0}},
{input:{coordX:1.846534212618428,coordY:2.1761976894795665},output:{dentro:0}},
{input:{coordX:3.7095033382025204,coordY:2.2509886772604313},output:{dentro:0}},
{input:{coordX:2.871397027577804,coordY:4.71824263205623},output:{dentro:0}},
{input:{coordX:3.4333672098193793,coordY:2.0280758682721434},output:{dentro:0}},
{input:{coordX:0.38999607711460993,coordY:1.8010781318643816},output:{dentro:0}},
{input:{coordX:2.4422268241896514,coordY:2.258242014970323},output:{dentro:1}},
{input:{coordX:1.9982811745797924,coordY:4.219450372874788},output:{dentro:0}},
{input:{coordX:4.760082642005266,coordY:1.0048022940451562},output:{dentro:0}},
{input:{coordX:3.9185192287659163,coordY:2.50046710500977},output:{dentro:0}},
{input:{coordX:1.8383590968817893,coordY:2.241336708255544},output:{dentro:0}},
{input:{coordX:1.672780733597653,coordY:4.442214257082721},output:{dentro:0}},
{input:{coordX:4.534574581273852,coordY:4.099166887610133},output:{dentro:0}},
{input:{coordX:0.7250089614436694,coordY:4.597144115647885},output:{dentro:0}},
{input:{coordX:2.3891505078918414,coordY:2.831674410266637},output:{dentro:1}},
{input:{coordX:2.750287656349484,coordY:2.8478848161280834},output:{dentro:1}},
{input:{coordX:0.09833623258234947,coordY:4.082909184899686},output:{dentro:0}},
{input:{coordX:3.0248953254039375,coordY:3.5556588801160878},output:{dentro:0}},
{input:{coordX:1.671987955240977,coordY:4.129193284230019},output:{dentro:0}},
{input:{coordX:4.001849266486557,coordY:1.205280682448181},output:{dentro:0}},
{input:{coordX:4.988518499029045,coordY:1.6172403460486855},output:{dentro:0}},
{input:{coordX:0.39433722436549745,coordY:4.134609678419109},output:{dentro:0}},
{input:{coordX:1.6279242499664321,coordY:2.2423089202814666},output:{dentro:0}},
{input:{coordX:1.498675549396427,coordY:4.772349084010053},output:{dentro:0}},
{input:{coordX:1.8825744525280708,coordY:2.687542928153671},output:{dentro:0}},
{input:{coordX:1.755234128903217,coordY:1.9025219265307665},output:{dentro:0}},
{input:{coordX:4.147593757800891,coordY:0.3432282760240063},output:{dentro:0}},
{input:{coordX:3.7887258327953437,coordY:0.902728804604454},output:{dentro:0}},
{input:{coordX:1.1936113231132488,coordY:2.155712709315931},output:{dentro:0}},
{input:{coordX:3.421023892412625,coordY:4.916681499580208},output:{dentro:0}},
{input:{coordX:3.288115641115418,coordY:4.5970540310107335},output:{dentro:0}},
{input:{coordX:4.533764672960644,coordY:4.789473605485444},output:{dentro:0}},
{input:{coordX:4.828645855102723,coordY:1.5112685326148028},output:{dentro:0}},
{input:{coordX:0.0665248101011845,coordY:0.34126186475430975},output:{dentro:0}},
{input:{coordX:0.307723025102668,coordY:2.9380005852475435},output:{dentro:0}},
{input:{coordX:3.5950851671157675,coordY:2.6620872143468284},output:{dentro:0}},
{input:{coordX:2.9504010786501427,coordY:3.8790247353185476},output:{dentro:0}},
{input:{coordX:4.025137723700105,coordY:2.795196196983265},output:{dentro:0}},
{input:{coordX:1.2449694961049467,coordY:3.275432726548206},output:{dentro:0}},
{input:{coordX:3.1021302807289426,coordY:1.1744864807639517},output:{dentro:0}},
{input:{coordX:3.200603192006696,coordY:3.6608763835311824},output:{dentro:0}},
{input:{coordX:2.3195276421809363,coordY:1.813223488125033},output:{dentro:0}},
{input:{coordX:2.660040509828435,coordY:4.840427036649345},output:{dentro:0}},
{input:{coordX:1.6258742690438894,coordY:1.7961097206426606},output:{dentro:0}},
{input:{coordX:2.049903192678877,coordY:0.5866029709552256},output:{dentro:0}},
{input:{coordX:2.78741217893133,coordY:4.780111852106793},output:{dentro:0}},
{input:{coordX:0.1180835190308116,coordY:1.0899039760151552},output:{dentro:0}},
{input:{coordX:2.333138832319985,coordY:3.3348087584193347},output:{dentro:0}},
{input:{coordX:3.4633221130741947,coordY:3.5287400212424935},output:{dentro:0}},
{input:{coordX:2.8366954367253445,coordY:1.3383212242396354},output:{dentro:0}},
{input:{coordX:4.6273906976068275,coordY:0.496176958727344},output:{dentro:0}},
{input:{coordX:3.054215585904709,coordY:2.1395688538840334},output:{dentro:0}},
{input:{coordX:1.31847708625209,coordY:2.3410940743886592},output:{dentro:0}},
{input:{coordX:3.590641116967948,coordY:2.989056166712384},output:{dentro:0}},
{input:{coordX:2.6756745397350605,coordY:4.554317848095392},output:{dentro:0}},
{input:{coordX:3.647364715736279,coordY:0.1058577787673598},output:{dentro:0}},
{input:{coordX:3.707137939394021,coordY:4.222866820422489},output:{dentro:0}},
{input:{coordX:3.238564234227374,coordY:0.8927061454089508},output:{dentro:0}},
{input:{coordX:2.999237564565057,coordY:1.5289433609290237},output:{dentro:0}},
{input:{coordX:2.0985021502716976,coordY:1.4642674988332387},output:{dentro:0}},
{input:{coordX:2.51621133652343,coordY:4.929886109933333},output:{dentro:0}},
{input:{coordX:1.8556572350771505,coordY:1.0719176303325395},output:{dentro:0}},
{input:{coordX:3.425478432353419,coordY:3.102679441623286},output:{dentro:0}},
{input:{coordX:0.41167584585873496,coordY:3.8663846945881764},output:{dentro:0}},
{input:{coordX:0.8965408920314183,coordY:1.4575264136632582},output:{dentro:0}},
{input:{coordX:4.994210666532178,coordY:0.8236130787701851},output:{dentro:0}},
{input:{coordX:0.24557567529333857,coordY:1.6788237096976482},output:{dentro:0}},
{input:{coordX:2.0989248433309493,coordY:4.430512686526004},output:{dentro:0}},
{input:{coordX:3.1416912026767245,coordY:3.4922492988964597},output:{dentro:0}},
{input:{coordX:1.2713864371899586,coordY:2.3794997565802496},output:{dentro:0}},
{input:{coordX:4.014567142933735,coordY:3.594328475774727},output:{dentro:0}},
{input:{coordX:4.511238842845852,coordY:3.3506271098514926},output:{dentro:0}},
{input:{coordX:4.600119723310383,coordY:3.883046947792987},output:{dentro:0}},
{input:{coordX:0.37390914436509937,coordY:2.6344773257270093},output:{dentro:0}},
{input:{coordX:0.7272090384174301,coordY:0.6746688657659816},output:{dentro:0}},
{input:{coordX:2.1269642313890484,coordY:2.252073039426059},output:{dentro:1}},
{input:{coordX:4.672389556980399,coordY:3.1919761716461736},output:{dentro:0}},
{input:{coordX:2.9560991716385643,coordY:1.6663216255287516},output:{dentro:0}},
{input:{coordX:0.8837634359141988,coordY:3.1424538323464626},output:{dentro:0}},
{input:{coordX:2.2893724505889335,coordY:2.471645135815102},output:{dentro:1}},
{input:{coordX:2.1306296443369077,coordY:3.1664514401787107},output:{dentro:0}},
{input:{coordX:2.8966249758362905,coordY:4.603741465914915},output:{dentro:0}},
{input:{coordX:0.6705076787055486,coordY:3.623100512580319},output:{dentro:0}},
{input:{coordX:3.9960952180183744,coordY:0.37022458105847866},output:{dentro:0}},
{input:{coordX:0.271360780257367,coordY:0.6740279913960523},output:{dentro:0}},
{input:{coordX:4.95545177469501,coordY:2.7772374551303534},output:{dentro:0}},
{input:{coordX:0.227031745196285,coordY:1.6416025512419523},output:{dentro:0}},
{input:{coordX:1.7886929664995184,coordY:0.44374386575040003},output:{dentro:0}},
{input:{coordX:4.368299267899269,coordY:0.6269238286883916},output:{dentro:0}},
{input:{coordX:1.1022744747982012,coordY:2.2140661814682296},output:{dentro:0}},
{input:{coordX:2.67874172901384,coordY:2.1888754043158754},output:{dentro:1}},
{input:{coordX:3.2662597891772487,coordY:3.599337675400642},output:{dentro:0}},
{input:{coordX:4.948487435120824,coordY:3.4917670820572173},output:{dentro:0}},
{input:{coordX:2.220943679125865,coordY:3.6455507321366536},output:{dentro:0}},
{input:{coordX:3.5448817481984274,coordY:2.1270447444504237},output:{dentro:0}},
{input:{coordX:1.3592060553992384,coordY:4.719597239702673},output:{dentro:0}},
{input:{coordX:1.5499117181863353,coordY:3.1711100247741184},output:{dentro:0}},
{input:{coordX:1.029551413175539,coordY:4.035969410149136},output:{dentro:0}},
{input:{coordX:2.54693629565058,coordY:3.2514804429681377},output:{dentro:0}},
{input:{coordX:3.9787461895983753,coordY:0.9234690811565277},output:{dentro:0}},
{input:{coordX:4.253705244577155,coordY:3.197895880452895},output:{dentro:0}},
{input:{coordX:2.3292450408078613,coordY:4.158559465516157},output:{dentro:0}},
{input:{coordX:3.1941095693528103,coordY:3.9699825582636765},output:{dentro:0}},
{input:{coordX:2.680734540599063,coordY:1.1804360350373373},output:{dentro:0}},
{input:{coordX:4.739025955467791,coordY:3.7409626538414233},output:{dentro:0}},
{input:{coordX:0.45148369946130806,coordY:0.9598385678731469},output:{dentro:0}},
{input:{coordX:0.7461122863797703,coordY:3.98188180916935},output:{dentro:0}},
{input:{coordX:4.884921412156257,coordY:1.4321874136006625},output:{dentro:0}},
{input:{coordX:3.565608477710658,coordY:3.3532173233980385},output:{dentro:0}},
{input:{coordX:4.340647739349781,coordY:1.5630285729068922},output:{dentro:0}},
{input:{coordX:4.277372022901472,coordY:1.1746815064860683},output:{dentro:0}},
{input:{coordX:4.766191042075224,coordY:0.9700108006229174},output:{dentro:0}},
{input:{coordX:2.919863298452019,coordY:1.7344582952930299},output:{dentro:0}},
{input:{coordX:4.807696739086878,coordY:2.019152650459564},output:{dentro:0}},
{input:{coordX:0.7417761840958709,coordY:3.4553999871909618},output:{dentro:0}},
{input:{coordX:3.738633394324265,coordY:1.895530581423091},output:{dentro:0}},
{input:{coordX:3.1526476999049406,coordY:4.465967072548411},output:{dentro:0}},
{input:{coordX:1.7805742833604865,coordY:0.08747846236699663},output:{dentro:0}},
{input:{coordX:2.982231750919021,coordY:0.3591406019284782},output:{dentro:0}},
{input:{coordX:0.4959449445858849,coordY:4.699963026938086},output:{dentro:0}},
{input:{coordX:0.832252668597066,coordY:3.9061065466153186},output:{dentro:0}},
{input:{coordX:3.869800544345307,coordY:0.647215598308189},output:{dentro:0}},
{input:{coordX:1.9218927981567369,coordY:1.7780153795564435},output:{dentro:0}},
{input:{coordX:3.2658499715899705,coordY:0.4926158989086815},output:{dentro:0}},
{input:{coordX:3.1240904560098235,coordY:4.654173320378876},output:{dentro:0}},
{input:{coordX:1.4279706244345465,coordY:2.235806917519983},output:{dentro:0}},
{input:{coordX:1.2781411679504262,coordY:3.779353169939204},output:{dentro:0}},
{input:{coordX:4.805044709120289,coordY:3.2626649734362587},output:{dentro:0}},
{input:{coordX:3.2090238294446345,coordY:2.415468003293078},output:{dentro:0}},
{input:{coordX:1.524379546378432,coordY:0.5515283072152294},output:{dentro:0}},
{input:{coordX:0.4751328078447159,coordY:3.9129917205026916},output:{dentro:0}},
{input:{coordX:1.2205797239881848,coordY:3.821920040399079},output:{dentro:0}},
{input:{coordX:0.23137792890822795,coordY:0.519961061558088},output:{dentro:0}},
{input:{coordX:4.0447993654194425,coordY:1.4740451077101435},output:{dentro:0}},
{input:{coordX:3.753308531638151,coordY:0.6559863978165315},output:{dentro:0}},
{input:{coordX:1.4969906902239498,coordY:4.932712468561701},output:{dentro:0}},
{input:{coordX:0.33432620232231813,coordY:4.081187197094801},output:{dentro:0}},
{input:{coordX:4.890517470769416,coordY:1.6774173916783957},output:{dentro:0}},
{input:{coordX:0.43398734463220345,coordY:1.95215550033502},output:{dentro:0}},
{input:{coordX:1.8640920896959274,coordY:2.7157971915714985},output:{dentro:0}},
{input:{coordX:2.4225985086238726,coordY:1.505669809661161},output:{dentro:0}},
{input:{coordX:1.1200523379612626,coordY:3.3031502152049645},output:{dentro:0}},
{input:{coordX:2.2090002540375604,coordY:1.5665411285599746},output:{dentro:0}},
{input:{coordX:2.8179226139307674,coordY:1.9291115756940724},output:{dentro:0}},
{input:{coordX:2.2271394845984274,coordY:4.55624876643785},output:{dentro:0}},
{input:{coordX:2.255329087746336,coordY:2.594092432861203},output:{dentro:1}},
{input:{coordX:3.251584925507827,coordY:0.13390733361676066},output:{dentro:0}},
{input:{coordX:0.5636862542265947,coordY:1.7955829173234128},output:{dentro:0}},
{input:{coordX:2.0630915418907403,coordY:3.9989760203002382},output:{dentro:0}},
{input:{coordX:3.197635002936414,coordY:2.072906496478418},output:{dentro:0}},
{input:{coordX:1.0961606569742255,coordY:4.034524577506496},output:{dentro:0}},
{input:{coordX:1.6151064503072194,coordY:3.0739513536834324},output:{dentro:0}},
{input:{coordX:1.3260942625891676,coordY:3.0339323681599675},output:{dentro:0}},
{input:{coordX:2.2338696700641476,coordY:3.2164610646001064},output:{dentro:0}},
{input:{coordX:0.4194043987493806,coordY:3.1831756255162684},output:{dentro:0}},
{input:{coordX:3.7071049093684345,coordY:2.9985138956249493},output:{dentro:0}},
{input:{coordX:4.477201953850042,coordY:4.129854063605965},output:{dentro:0}},
{input:{coordX:0.5125325959845889,coordY:4.52640348391554},output:{dentro:0}},
{input:{coordX:4.049999868098492,coordY:3.8716199332232195},output:{dentro:0}},
{input:{coordX:3.5479924787141934,coordY:4.795601160206046},output:{dentro:0}},
{input:{coordX:3.1310637776342825,coordY:1.3366593153490243},output:{dentro:0}},
{input:{coordX:4.0205599719074305,coordY:3.7930738527521575},output:{dentro:0}},
{input:{coordX:0.7016805157645617,coordY:0.9449368100933692},output:{dentro:0}},
{input:{coordX:3.1040046582812604,coordY:1.4122060573308866},output:{dentro:0}},
{input:{coordX:2.8271145613440396,coordY:3.398917089825412},output:{dentro:0}},
{input:{coordX:4.544253819702218,coordY:4.323677296349116},output:{dentro:0}},
{input:{coordX:3.4962194464068848,coordY:3.2689087094522233},output:{dentro:0}},
{input:{coordX:4.102193621776122,coordY:4.611669788888176},output:{dentro:0}},
{input:{coordX:4.997049151723314,coordY:0.2794161632636022},output:{dentro:0}},
{input:{coordX:1.33858290131045,coordY:3.7445465284387525},output:{dentro:0}},
{input:{coordX:2.729239438924534,coordY:0.7735631464107229},output:{dentro:0}},
{input:{coordX:3.2381764212503166,coordY:0.8514218702916992},output:{dentro:0}},
{input:{coordX:0.9279247480518238,coordY:4.558972384450469},output:{dentro:0}},
{input:{coordX:0.7394544460347868,coordY:4.023262686383106},output:{dentro:0}},
{input:{coordX:2.3566146701100417,coordY:1.9953854547146173},output:{dentro:0}},
{input:{coordX:2.054817875181384,coordY:1.4834009664539543},output:{dentro:0}},
{input:{coordX:0.4933732058254081,coordY:3.2032420025194277},output:{dentro:0}},
{input:{coordX:2.136597080390867,coordY:0.41991905155767617},output:{dentro:0}},
{input:{coordX:4.3030400873925565,coordY:2.4402787543454996},output:{dentro:0}},
{input:{coordX:1.5430509198256726,coordY:0.9606699845822458},output:{dentro:0}},
{input:{coordX:4.5119951049059095,coordY:4.9554526035262105},output:{dentro:0}},
{input:{coordX:2.253097692288741,coordY:4.099423556469653},output:{dentro:0}},
{input:{coordX:4.167193230994263,coordY:0.993167180692906},output:{dentro:0}},
{input:{coordX:2.311181250881049,coordY:1.520240717354377},output:{dentro:0}},
{input:{coordX:3.28199430505302,coordY:3.296735718770625},output:{dentro:0}},
{input:{coordX:1.4995615727056688,coordY:2.0236379818272536},output:{dentro:0}},
{input:{coordX:2.2471801476337383,coordY:0.14460599478739178},output:{dentro:0}},
{input:{coordX:4.377237215738093,coordY:2.498013090218118},output:{dentro:0}},
{input:{coordX:1.0351243272338617,coordY:0.7548151855702734},output:{dentro:0}},
{input:{coordX:4.412203297179589,coordY:2.355626544806471},output:{dentro:0}},
{input:{coordX:3.720429526453717,coordY:0.9455404457139083},output:{dentro:0}},
{input:{coordX:2.336449582288887,coordY:4.528508605502948},output:{dentro:0}},
{input:{coordX:1.6254932591424605,coordY:0.6164549153681359},output:{dentro:0}},
{input:{coordX:4.49380686815539,coordY:2.5211863212537717},output:{dentro:0}},
{input:{coordX:3.7597246346082507,coordY:0.020583138825610026},output:{dentro:0}},
{input:{coordX:0.05115960862027591,coordY:1.4364777031655795},output:{dentro:0}},
{input:{coordX:3.9871446638734884,coordY:2.111390129614364},output:{dentro:0}},
{input:{coordX:1.9950946192167591,coordY:4.57866988593504},output:{dentro:0}},
{input:{coordX:4.798235513276436,coordY:2.1277106129002576},output:{dentro:0}},
{input:{coordX:4.299775004376528,coordY:3.072982298521683},output:{dentro:0}},
{input:{coordX:1.396158590229456,coordY:3.481023179010794},output:{dentro:0}},
{input:{coordX:4.779033954603933,coordY:2.466185284709279},output:{dentro:0}},
{input:{coordX:0.26439393835574254,coordY:1.885797943516509},output:{dentro:0}},
{input:{coordX:3.2823271322072594,coordY:3.030845566553784},output:{dentro:0}},
{input:{coordX:0.34107780983853864,coordY:2.652408872320339},output:{dentro:0}},
{input:{coordX:0.5213379651005984,coordY:0.5140642505388165},output:{dentro:0}},
{input:{coordX:1.6406783294651839,coordY:4.557718775695475},output:{dentro:0}},
{input:{coordX:0.23698988044482727,coordY:4.663855255308955},output:{dentro:0}},
{input:{coordX:4.9514124421119075,coordY:1.259779507773426},output:{dentro:0}},
{input:{coordX:3.2736006579511994,coordY:3.4914439826145167},output:{dentro:0}},
{input:{coordX:2.508944225928846,coordY:4.098182012696347},output:{dentro:0}},
{input:{coordX:1.273992133968006,coordY:4.687894559254873},output:{dentro:0}},
{input:{coordX:2.8893312233364736,coordY:2.6899517475118015},output:{dentro:1}},
{input:{coordX:4.320996916641976,coordY:2.130341242566682},output:{dentro:0}},
{input:{coordX:2.0836867639801526,coordY:2.880668594141188},output:{dentro:1}},
{input:{coordX:4.781259554625619,coordY:3.961648188683438},output:{dentro:0}},
{input:{coordX:0.05192975171759706,coordY:2.298103838389153},output:{dentro:0}},
{input:{coordX:3.699664559067774,coordY:3.9710371489329015},output:{dentro:0}},
{input:{coordX:1.9379422597637759,coordY:1.9795332735461062},output:{dentro:0}},
{input:{coordX:2.9827201428670587,coordY:2.0421336279085835},output:{dentro:1}},
{input:{coordX:4.033246160332309,coordY:3.0147896279047313},output:{dentro:0}},
{input:{coordX:4.879898567404818,coordY:2.8209783958217223},output:{dentro:0}},
{input:{coordX:1.769418329425339,coordY:0.9339906131353476},output:{dentro:0}},
{input:{coordX:3.581311554585021,coordY:1.4880419720269822},output:{dentro:0}},
{input:{coordX:1.6672503889681023,coordY:1.2023637703973333},output:{dentro:0}},
{input:{coordX:4.544350908311962,coordY:1.3774645135283736},output:{dentro:0}},
{input:{coordX:1.218505818085045,coordY:4.985360006361043},output:{dentro:0}},
{input:{coordX:1.08040155676751,coordY:3.132499666911508},output:{dentro:0}},
{input:{coordX:1.9542061443805454,coordY:4.535984912465059},output:{dentro:0}},
{input:{coordX:4.381667665532988,coordY:0.7010143649992212},output:{dentro:0}},
{input:{coordX:2.80509326779298,coordY:1.8227783652570717},output:{dentro:0}},
{input:{coordX:1.2855833826654406,coordY:1.4194398926039202},output:{dentro:0}},
{input:{coordX:0.13487614651416846,coordY:4.215753472095731},output:{dentro:0}},
{input:{coordX:2.1914905013367565,coordY:4.011557456139474},output:{dentro:0}},
{input:{coordX:1.5795839891593444,coordY:2.7304006630094046},output:{dentro:0}},
{input:{coordX:2.0412993376204023,coordY:2.6366345780502303},output:{dentro:1}},
{input:{coordX:0.4738599212409206,coordY:3.6693455984587815},output:{dentro:0}},
{input:{coordX:2.726355257039625,coordY:0.8812589711208973},output:{dentro:0}},
{input:{coordX:2.599918474810678,coordY:0.23845141657989566},output:{dentro:0}},
{input:{coordX:0.6535655605884888,coordY:0.8286517868744792},output:{dentro:0}},
{input:{coordX:0.39767226120502763,coordY:4.522678283900609},output:{dentro:0}},
{input:{coordX:3.963658393499523,coordY:4.04192358594597},output:{dentro:0}},
{input:{coordX:0.34810241261076347,coordY:3.915220873167395},output:{dentro:0}}
];

/*
Instanciamos una red neuronal con 2 capas ocultas,
la primera con 4 nodos y la segunda con 2 nodos
valores elegidos de forma completamente aleatoria
*/
var net = new brain.NeuralNetwork({
hiddenLayers: [4, 2]
});

/*
Entenamos la red con los datos preparados
*/
function realizaEntrenamiento() {

net.train(datosEntrenamiento, {
iterations: 60000, // the maximum times to iterate the training data --> number greater than 0
log: true, // true to use console.log, when a function is supplied it is used --> Either true or a function
errorThresh: 0.008
}); // the acceptable error percentage from training data --> number between 0 and 1
console.log("Entrenamiento finalizado");
}

/*
Realizamos una predicción para ver si el punto ((2.5),(2.5)) está en el rectangulo ((2,2),(3,3))
*/
function generaPrediccion() {

x = (Math.random() * nubePuntos.puntoB.coordX) + nubePuntos.puntoA.coordX;
y = (Math.random() * nubePuntos.puntoB.coordY) + nubePuntos.puntoA.coordY;
var output = net.run({ coordX: x, coordY: y });
console.log("El punto ("+x+","+y+") está en el rectangulo ((2,2),(3,3)) --> " + output.dentro);
}
</script>
</body>

</html>