Nueva versión de la app GPS TOTAL RUN



 

Ya pueden descargar la nueva versión de la app gratuita y sin publicidad GPS TOTAL RUN (Android).
Para los que aún no la conozcan es una app que he programado con RAD Studio 10.3.3 que sirve para grabar las rutas que realizamos cuando hacemos deporte, vamos en bici, hacemos un viaje,etc.
Permite utilizar dispositivos Bluetooth LE, para obtener las pulsaciones por minuto, o la cadencia de pedaleo cuando hacemos rutas en bicicleta.
Se pueden hacer estadísticas por dias, semanas, meses y cálculo de tiempos por tramos en 27 actividades distintas.

Tiene más de 30 utilidades:
Actualiza datos AGPS, información de 15 parámetros del GPS, cálculo de UTM, brújula, hora en la que amanece, anochece en un calendario perpetuo, altímetro, mapas del Instituto Geográfico Nacional de España, creación de puntos de interés, mapas offline, información de los sensores del dispositivo, etc...

En la última versión incorporo varias novedades:

- Permite compartir la ruta en tiempo real a tus contactos, por email, por whastapp, etc... 

- Se puede enviar la ruta en formato kml, para que se pueda abrir desde Google Earth directamente.

- Cientos de rutas realizadas en varios países (Brasil, Argentina, Chile, México, Perú, Colombia, Francia, Italia, Portugal, etc...) con mapas interactivos de openstreetmap y opentopomap.

- Permite medir distancias y calcular áreas sobre mapas.

- Se mejora el módulo que calcula el sobrepeso, mostrando cuántos kilos nos sobran para llegar a un peso normal.

- Medida del riesgo cardiovascular: indica el tanto por ciento de posibilidades de sufrir un accidente cardio-vascular, en función de varios parámetros (sexo, edad, diabetes, fuma, presión arterial, etc...).

 


Si les gusta la app y creen que puede ser de utilidad les agradecería que la difundan en sus redes sociales.

Pantallas de la app: 


Solucionar error en el componente tInAppPurchase




En la última versión de Rad Studio 10.3.3 se produce un error al utilizar el componente tInAppPurchase en Android de 64 bits, cuando el usuario pulsa sobre la app para comprar una suscripción. 

Una forma de capturar el error en este componente sería desde el evento OnError:

 PROCEDURE TFPublicidad.InAppPurchase1Error(Sender: TObject;  
  ErrorKind: TFailureKind; CONST ErrorMessage: STRING);  
 BEGIN  
  IF ErrorKind = TFailureKind.ProductsRequest THEN  
  BEGIN  
   Memo2.Lines.Add(' ERROR al solicitar los productos ' + ErrorMessage);  
  END;  
  IF ErrorKind = TFailureKind.Purchase THEN  
  BEGIN  
   Memo2.Lines.Add(' ERROR en la compra ' + ErrorMessage);  
  END;  
 END;  

En caso de que se la compra del producto online sea correcta hay que tratarla desde el evento OnPurchaseCompleted, de la siguiente forma:

 PROCEDURE TFPublicidad.InAppPurchase1PurchaseCompleted(Sender: TObject;  
  CONST ProductID: STRING; NewTransaction: Boolean);  
 BEGIN  
  Memo2.Lines.Add('*** ' + ProductID + ' EL Producto ha sido comprado');  
  IF ItemAComprar1 = ProductID THEN  
   showmessage ('se ha comprado el producto 1');  
  IF ItemAComprar2 = ProductID THEN  
   showmessage ('se ha comprado el producto 2');  
 END;  


tInAppPurchase se usa para proporcionar acceso a la app a los servicios de pago de Google Play o de App Store, que permiten vender contenido digital, en forma de suscripciones o compra de productos.

El problema es que se omitió cambiar un tipo de datos "Cardinal" (mismo tamaño en todas las plataformas) a "NativeUInt" (específico de la plataforma). Esto se logra utilizando el tipo tFmxHandle.

Para resolver este problema hay que :

- Acceder al archivo FMX.Helpers.Android.pas
Está en C: \ Archivos de programa (x86) \ Embarcadero \ Studio \ 20.0 \ source \ fmx 

- Copiar el archivo y pegarlo en la carpeta del proyecto

- Después hay que cambiar la línea nº 250 de esa unit y donde dice:

FTimerHandle: Cardenal;

cambiarlo por 

FTimerHandle: tFmxHandle;

Aquí tienen una imagen del código



Referencia: blog Marco Cantú


También te puede interesar:

Que hacer para no perder ingresos con vuestras apps
Cómo dejar que Google gestione la clave de firma de las apps
Solucionando problemas con los componentes Iot
Cómo generar un Android App Bundle con Delphi




Reconocimiento facial con los componentes MITOV en Delphi

He estado probando los componentes MITOV y he quedado asombrado con lo que pueden hacer, tienen un nivel de abstracción muy alto, con pocas instrucciones se pueden realizar programas complejos (algo parecido al Livebinding de Delphi) lo que a los programadores seguro nos facilitará mucho el desarrollo de software.
En palabras de Boian Mitov, propietario y CEO de Mitov. Software. "Estos componentes ofrecen una combinación única de compilación multiplataforma nativa y características avanzadas de lenguaje moderno como RTTI avanzado por esto Delphi ha permitido implementar componentes de inteligencia artificial multiplataforma potentes y de alto rendimiento".
Jim McKeeth, jefe de desarrollo e ingeniero de Embarcadero dijo "Me encanta que IntelligenceLab de Mitov Software tome algo tan complejo como las redes neuronales y la inteligencia artificial y lo haga accesible a través de componentes reutilizables, ahora todos nuestros desarrolladores pueden agregar redes neuronales avanzadas y otros clasificadores de IA a sus aplicaciones multiplataforma". 

La licencia es gratuita para uso educativo y no comercial.

Seguidamente les mostraré cómo se hace un programa de reconocimiento facial utilizando los componentes basados en inteligencia artificial de Mitov.
Primero tenemos que descargarnos el paquete "Intelligence lab" desde aquí  (IntelligenceLab_VCL_7.8.2.288.zip)
Lo guardamos en una carpeta, lo descomprimimos y ejecutamos el setup.exe, aquí hay que tener paciencia ya que es un archivo de casi 2 Gb.
Nos pedirá que tipo de licencia tenemos: free, comercial, with source code, etc...
Pulsamos aceptar y si no hay ningún problema en la instalación tendremos en el IDE los componentes de este pack.


 
Creamos un nuevo proyecto y añadimos el componente
ILRadialBasisFunctionNetwork
Del cual sólo tenemos que modificar la propiedad MaxInfluencefield = 300
En función de este valor tendremos una mayor o menor precisión en el reconocimiento de caras.
Las imágenes de prueba las guardamos en la carpeta "faces" y las dividimos en 2 grupos uno para hacer el entrenamiento de la red y otro para hacer un test en el que verificaremos la precisión del reconocimiento facial.
En total hay 40 personas distintas y de cada una de ellas se han hecho 9 fotos, como ven en la siguiente imagen las fotos destinadas a entrenar la red se llaman "Train_(1..9)_(1..40)" y las destinadas a test se llaman "Reco_(1..40)"

 

El código que entrena la red y que se pone en el evento Onclick es:

procedure TForm1.TrainButtonClick(Sender: TObject);
var
  I               : Integer;
  J               : Integer;
  K               : Integer;
  ABitmap         : TBitmap;
  AFeaturesBuffer : ISLRealBuffer;
  AInPtr          : PByte;
  AOutPtr         : PReal;
  AItem           : TListItem;

begin
  ABitmap := TBitmap.Create();
  ProgressBar1.Max := 40;
  AFeaturesBuffer := TSLRealBuffer.CreateSize( 32 * 32 );

  ListView1.Items.BeginUpdate();
  for I := 1 to 40 do
    begin
    for J := 1 to 9 do
      begin
      ABitmap.LoadFromFile( '.\Faces\Train_' + IntToStr( I ) + '_' + IntToStr( J ) + '.bmp' );
      ImageList1.Add( ABitmap, NIL );
      AItem := ListView1.Items.Add();
      AItem.ImageIndex := ImageList1.Count - 1;
      AItem.Caption := IntToStr( I );
      AInPtr := ABitmap.ScanLine[ ABitmap.Height - 1 ];
      AOutPtr := AFeaturesBuffer.Write();
      for K := 0 to AFeaturesBuffer.Size - 1 do
        begin
        AOutPtr^ := AInPtr^ / 256;
        Inc( AOutPtr );
        Inc( AInPtr );
        end;

      ILRadialBasisFunctionNetwork1.Train( AFeaturesBuffer, I );
      end;
      
    ProgressBar1.Position := I;
    end;

  ListView1.Items.EndUpdate();
  ABitmap.Free();
  RecognizeButton.Enabled := True;
  TrainButton.Enabled := False;
  TrainLabel.Caption := IntToStr( ILRadialBasisFunctionNetwork1.Neurons.Count ) + ' Neurons used.';
  ILRadialBasisFunctionNetwork1.SaveToFile('redneuronal1');
end;


Al final del entrenamiento se crea el fichero "redneuronal1" que contiene todas las características y pesos de la red.

ILRadialBasisFunctionNetwork1.SaveToFile('redneuronal1');

El siguiente paso es probar su fiabilidad, para ello colocamos otro botón en nuestro proyecto y en el evento OnClick lo primero que hacemos es cargar la red almacenada antes:

ILRadialBasisFunctionNetwork1.LoadFromFile('redneuronal1');
 
 
El código que reconoce las imágenes de test es el siguiente:
procedure TForm1.RecognizeButtonClick(Sender: TObject);
var
  I               : Integer;
  K               : Integer;
  ABitmap         : TBitmap;
  AFeaturesBuffer : ISLRealBuffer;
  AInPtr          : PByte;
  AOutPtr         : PReal;
  AItem           : TListItem;

begin

ILRadialBasisFunctionNetwork1.LoadFromFile('redneuronal1');


  FTotalCount := 0;
  FErrorCount := 0;

  ABitmap := TBitmap.Create();
  ProgressBar1.Max := 40;
  AFeaturesBuffer := TSLRealBuffer.CreateSize( 32 * 32 );

  ListView2.Items.BeginUpdate();
  for I := 1 to 40 do
    begin
    FExpectedID := I;
    ABitmap.LoadFromFile( '.\Faces\Reco_' + IntToStr( I ) + '.bmp' );
    ImageList1.Add( ABitmap, NIL );
    AItem := ListView2.Items.Add();
    AItem.ImageIndex := ImageList1.Count - 1;
    AInPtr := ABitmap.ScanLine[ ABitmap.Height - 1 ];
    AOutPtr := AFeaturesBuffer.Write();
    for K := 0 to AFeaturesBuffer.Size - 1 do
      begin
      AOutPtr^ := AInPtr^ / 256;
      Inc( AOutPtr );
      Inc( AInPtr );
      end;

    ILRadialBasisFunctionNetwork1.Predict( AFeaturesBuffer );

    if( FResultCathegory <> I ) then
      begin
      if( FResultCathegory = 0 ) then
        AItem.Caption := 'Error - Can''t recognize'

      else
        AItem.Caption := 'Error - Expected:' + IntToStr( I ) +
         ' recognized as: ' + IntToStr( FResultCathegory ) + ' Distance: ' + FloatToStr( FDistance );

      end

    else
      AItem.Caption := 'Correct: ' + IntToStr( I ) + ' Distance: ' + FloatToStr( FDistance );

    ProgressBar1.Position := I;
    end;

  ListView2.Items.EndUpdate();
  ABitmap.Free();

  RecognizeLabel.Caption := IntToStr( FErrorCount ) + ' errors of ' + IntToStr( FTotalCount ) + ' faces - ' + FloatToStr( ( FTotalCount - FErrorCount ) * 100 / FTotalCount ) + '% correct';
end;



Al ejecutar el código anterior comprobamos que ha reconocido 35 caras de 40 lo que representa un 87,5% de fiabilidad, ¡no está nada mal!


Hay que saber que lo primero que hay que hacer es un preprocesamiento de las imágenes, ya que todas tienen que tener las mismas medidas, hay que convertirlas a b/n, tener el mismo formato, etc.. y que cuanto más imágenes se tengan de cada persona mejor...siempre dentro de un límite para no llegar al sobreentrenamiento de la red, ya que en ese caso se produciría una mayor tasa de error.
Como ven es sencillo hacer un reconocimiento facial con los componentes MITOV.
Espero que les haya gustado el post.

Código fuente y fotos de prueba



También te puede interesar:

Reconocimiento facial con Delphi Tokyo en Android
Reconocimiento de caras con Delphi
Ejemplo de uso de kinect con Delphi
Seguimiento de personas con OpenCV