Libro "Code Faster in Delphi"

 
 En esta entrada de hoy les hablaré del libro "Code Faster in Delphi" de Alister Christie.

Alister Christie es MVP (profesional más valorado) de Delphi, consultor, desarrollador, formador, presentador y escritor de varios libros. Ha dado charlas en simposios y conferencias de Code Rage de ADUG (Australasian Delphi Users Group) durante muchos años. Es Licenciado en Matemáticas e Informática por la Universidad Victoria, Wellington. Vive cerca de Wellington, Nueva Zelanda.

En su canal de YouTube, ha creado muchas horas de tutoriales en video para ayudarnos a aprender la programación de Delphi de manera eficiente y efectiva.

Actualmente está escribiendo una trilogía de libros dedicada a mejorar nuestros hábitos al programar en Delphi y de paso aprender algunos trucos que esconde este magnífico entorno de programación, el primero de esta trilogía de libros es el que hoy os recomiendo.

La palabra que mejor define a Delphi es productividad, con Delphi es fácil construir componentes, es fácil usar esos componentes y hace fáciles las cosas que en otros lenguajes serían complicadas.

Y de eso trata el libro de Alister: mejorar nuestra productividad.  

En él aprenderemos atajos de teclado y trucos de productividad, manejar clases, etc, cómo editar código más rápido, cómo utilizar Code Insight, cómo escribir nuestros propios plugin, conoceremos en profundidad la RTL, los genéricos, programación paralela, trabajaremos con expresiones regulares, FireDAC, también nos enseñará algunas herramientas de terceros como cnPack, ModelMaker, CodeSite, GExpers, etc...

Un libro altamente recomendable y creo que imprescindible en nuestra biblioteca como programadores.

La definición de Alister de su libro es la siguiente:

Me gusta el título de un libro que tiene doble sentido, y este es uno de ellos.
En primer lugar, este libro tiene como objetivo acelerar la programación. Si lees y entiendes el contenido de este libro, ciertamente hará precisamente eso. En segundo lugar es la certeza de que programar en Delphi es más rápido, con lo que estoy totalmente de acuerdo. 
Se pueden crear aplicaciones potentes y útiles en Delphi sin demasiado esfuerzo,
con mucho menos esfuerzo que en otros lenguajes o herramientas.
Para aprovechar al máximo este libro, solo necesitará un conocimiento básico de Delphi,
sin embargo, este libro será útil tanto para principiantes como para expertos. No importa
cuánto tiempo ha estado usando Delphi, tendrá una gran cantidad de "No puedo
creer que Delphi hace eso ! ", y" He perdido tanto tiempo en el pasado haciendo esto manualmente! ”, y,“ ¿dónde estaba este libro hace años? ”. Si yo te hubiese
escuchado antes. Lo tienes ahora, y eso es lo que cuenta.
Este libro no le enseñará los fundamentos de cómo programar.
Este libro no le enseñará los fundamentos del lenguaje Delphi.
Este libro le enseñará a codificar más rápido en Delphi.
Este libro le hará más rápido en la codificación, pero no mejor en la programación en Delphi
- al menos no directamente - ese es mi próximo libro, "Code Better In Delphi".

 

Puedes comprar el libro desde www.learndelphi.tv

Y sus páginas en redes sociales:

Email: alister@learndelphi.tv
YouTube: https://www.youtube.com/user/codegearguru
Web: https://LearnDelphi.tv
LinkedIn: https://linkedin.com/in/alisterchristie/
Twitter: https://twitter.com/AlisterChristie
Facebook: https://www.facebook.com/LearnDelphitv/
Instagram: https://www.instagram.com/christiealister/

Algunos vídeos de Alister:








Instalar Rad Studio 10.4.1 sin problemas



 A continuación les voy a detallar los pasos que he seguido para configurar RAD Studio 10.4.1 en una máquina con Windows 10.

1) En mi caso tenía un PC sin sistema operativo por lo que lo primero que hay que hacer es instalar Windows 10.

Probé a instalarlo sobre una máquina virtual  que tenía en otro PC con Windows 8 utilizando VirtualBox, pero en un momento de la instalación se quedó colgado, repetí la instalación varias veces y no lo conseguí, por lo que me decidí a instalarlo en la partición C:

2)  Ir a "Configuración de Windows" y pulsar sobre "Actualización y seguridad"

En el menú "Windows Update", pulsar el botón "Buscar actualizaciones" e instalar todas las actualizaciones que irá encontrando, seguramente necesitará reiniciar varias veces el equipo.

3) Instalar "Microsoft Office 2016"

4) Ir al paso "2" para buscar más actualizaciones, hasta que aparezca el mensaje ¡Todo está actualizado!

5) Instalar Java.

Java está certificado para Windows 10 desde la versión Java 8 Update 51.

4) Instalar Rad Studio.


 Seleccioné Delphi Windows Enterprise y Delphi Android Enterprise

En la pestaña "Additonal Options" recordar que hay que marcar "AdoptOpenJDK".

En algunos foros he leído que no marcar AdoptOpenJDK puede dar problemas al instalar o al compilar las aplicaciones.


Se pulsa el botón "Apply" y se van aceptando las sucesivas pantallas que irán apareciendo, hasta terminar la instalación del programa.

Una vez que pude ejecutar por primera vez Rad Studio y compilar uno de los programas que había realizado, me daba errores la conexión con Firedac, se cerraba el programa inesperadamente o se bloqueaba, pues para solucionarlo simplemente hay que añadir en el uses de la unit donde se establezca la conexión con la base de datos lo siguiente:

FireDAC.Phys.SQLiteWrapper.Stat

 por ejemplo:

UNIT BaseDeDatos;

INTERFACE

USES
  System.SysUtils, System.UITypes,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls,
  FireDAC.Stan.Def, FireDAC.Stan.Async, FireDAC.Stan.ExprFuncs,
  FireDAC.Phys.SQLiteDef, FireDAC.UI.Intf,
  FireDAC.FMXUI.Wait, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Error, FireDAC.Phys.Intf, FireDAC.Stan.Pool,
  FireDAC.Phys, FireDAC.Phys.SQLite, FireDAC.Stan.Param, FireDAC.DatS,
  FireDAC.DApt.Intf, FireDAC.DApt, Data.DB,
  FireDAC.Comp.DataSet, FireDAC.Comp.Client, FireDAC.Comp.UI,
  FMX.Objects,
  FMX.ScrollBox, FMX.Memo, FMX.Effects, FMX.Layouts,  FireDAC.Phys.SQLiteWrapper.Stat,
  System.Classes, FMX.Memo.Types;


Con eso las operaciones de búsqueda o inserción de registros, transacciones, mantenimiento de la base de datos, compactación y verificación, funcionaban perfectamente.

Otro error que me daba era al intentar ejecutar una app para Android en un dispositivo Xiaomi, me aparecía el mensaje:

INSTALL_FAILED_USER_RESTRICTED

Intenté ejecutar la app en modo "Developpment" y en modo "Application Store" y no funcionaba.

Para arreglarlo hay que activar las "Opciones de desarrollador" y hacer lo siguiente:

Ir a 

Settings -> Additional Settings -> Developer options ->

    Desactivar"MIUI optimization" y reiniciar el móvil.

    Activar "USB Debugging"

    Activar "Install via USB"

 




y aplicado la anterior ya me funcionaban las apps perfectamente en el nuevo Rad Studio 10.4.1.

Otro truco, si las apps compiladas con versiones anteriores a la 10.4 dan errores, borren el archivo AndroidMainfest.template.xml que está en la carpeta principal de su app y vuelvan a compilarla, esto provoca que Delphi vuelva a reconstruir ese archivo adaptándolo a la última versión disponible.

Espero que les haya servido de algo y que por lo menos no pierdan el tiempo que he perdido yo en buscar las soluciones a los problemas anteriores.



Acción solidaria relacionada con Delphi y con Al González


 
Hace unos días vimos una publicación en Twitter de Al haciendo un llamamiento de apoyo, de ayuda.

Pues para eso hemos creado una formación Solidaria donde los beneficios irán destinados a ayudarle, recuerda, hoy por ti, mañana por mi.

El curso se llama:
"Curso de Delphi: Nociones importantes del Lenguaje, del compilador y la depuración de código"

¿QUÉ INCLUYE LA FORMACIÓN?
- 2 horas en directo con Al
- Impartirá nociones básicas sobre el lenguaje de programación, cosas que no sueles caer en tu día a día.
- Nociones sobre el compilador que quizás, como yo, hayas tocado pocas veces.
- Nociones sobre la depuración de código, siempre hay algunas perlas que no habíamos pensado en ellas.
- También podrás conversar directamente con el formador y el resto de asistentes, entre todos se enriquecerá muchísimo más la formación.
- Tendrás acceso al curso para poder volver a verlo las veces que quieras, quedará grabado. 
- Certificado del curso a tu nombre, para que puedas adjuntarlo a Linkedin, a tu hoja de vida etc. 
- Encuesta de satisfacción
- Descuento del 40% para los componentes TMS Web Core, tanto para Delphi como para Visual Studio Code

¿CUANTO CUESTA?
Esta formación podría costar perfectamente más de 150 euros, sólo el descuento de TMS Web Core se ahorra mucho más de este dinero, pero hemos puesto un precio muy asequible para todos al ser una acción solidaria y sólo te costará 15 €.

¿CUANDO ES EL CURSO?
El curso será el próximo Martes 15 de septiembre a las 16:00 hora Madrid/España . Si no puedes asistir no te preocupes, apúntate, ayúdanos y visualiza el vídeo posteriormente.

¿QUIEN IMPARTE EL CURSO?
Detrás el curso está nuestra empresa Abatic Soluciones Tecnológicas, empresa responsable del Podcast No Solo Delphi, del Podcast Pildoras PostgreSQL, de plataformas formativas como abatic.net, nosolodelphi.com, emiliopm.com y todopostgresql.com
Pero claro, como es una formación solidaria, el formador no forma parte de nuestra empresa, es freelance y los beneficios del curso irán destinados para él, se llama Al González y es el creador de componentes como GHFreebrary.

¿Donde puedo apuntarme?
Aquí te dejo el enlace para apuntarte al curso, te damos las gracias desde el equipo Abatic por ayudarnos en esta causa en la cual podemos caer todos y cada uno de nosotros.

Memory leaks en delphi


 

¿Qué es un memory leak?

Un memory leak ocurre cuando un bloque de la memoria del ordenador no se libera, debido a un fallo del programa que se está ejecutando en ese momento, es decir en un determinado momento del programa necesitamos reservar un conjunto de bytes para almacenar datos o para hacer un cálculo, cuando se termina la operación tendremos que liberar ese bloque de bytes para que lo pueda usar otro módulo de nuestro programa

¿Que ocurre si no se libera?

Si vamos reservando bytes sin liberarlos al final agotaremos la memoria disponible del PC hasta llegar un momento en el que se bloqueará.

Esto provoca que el resto de las aplicaciones se ejecuten más lentamente, ya que cada vez tienen menos memoria disponible, el PC irá más lento.

Esto causa que algunas aplicaciones no lleguen a abrirse ya que no disponen de la memoria suficiente para su correcto funcionamiento.

¿Que tipo de liberación de memoria existe?

Puede ser manual o automática.

Manual:

En Delphi lo hacemos con la instrucción  Free / FreeAndNil  / Destroy  / Release (seguramente se me olvida alguna instrucción ...)

Automática:

En algunos lenguajes como java se utiliza lo que es un "recolector de basura".

También se utiliza un "conteo de referencias" que va contando el número de referencias a la zona bloqueada y cuando llega a 0 libera la memoria. Esto tiene un problema y es que si las referencias forman un ciclo la memoria no se liberará nunca.

Ejemplos de buenas prácticas

//CODIFICACIÓN INCORRECTA (No se libera la variable fich)

procedure obtenerdatos;
var
fich:tstringlist;
lista:string;

begin

lista:='';

fich:=tstringlist.create;

        for i:=1 to fich.count-1 do
        begin
        lista:=lista+fich.strings[i];
        end;

end;


//CODIFICACIÓN CORRECTA (siempre se va a liberar la variable fich)

procedure obtenerdatos;
var
fich:tstringlist;
lista:string;

begin

lista:='';

fich:=tstringlist.create; //siempre antes del try
try

        for i:=1 to fich.count-1 do
        begin
        lista:=lista+fich.strings[i];
        end;

finally
  fich.free;
end;

En algunos casos en el evento OnClose del Form también puedes poner "fich.free", pero yo prefiero el método anterior.

Otro ejemplo podría suceder cuando cargamos una dll externa usando LoadLibrary. Siempre hay que llamar al final a FreeLibrary para liberar la memoria ocupada.

 var
   dllHandle: THandle;
begin
   dllHandle: = Loadlibrary ('MyLibrary.DLL');
   // hacer algo con esta DLL
   if dllHandle <> 0 then FreeLibrary (dllHandle);
final;

 

Cómo encontrar fugas de memoria en nuestros programas

Mediante la instrucción 
System.ReportMemoryLeaksOnShutdown: = True;

Poniendo esta variable a true, el administrador de memoria irá escaneando el programa a medida que se va ejecutando e informará al usuario cuando el programa termine de las pérdidas de memoria que haya encontrado.

De la siguiente forma:

begin
  Application.Initialize;
  Application.MainFormOnTaskbar: = True;
  Application.CreateForm (TForm1, Form1);
  System.ReportMemoryLeaksOnShutdown: = True;
  Application.Run;
final .

Programas que detectan fugas de memoria

FASTMM
FastMM es en realidad el administrador de memoria incluido con Delphi desde hace algunos años. Es una obra original de Pierre le Riche. Aunque se incluye con Delphi, puede descargar el código fuente completo de SourceForge en http://fastmm.sourceforge.net . Para aprovechar al máximo la función FastMM, debe abrir el archivo FastMM4Options.inc y cambiar alguna directiva de compilación condicional. Este archivo está muy comentado para explicar todos los símbolos. Al desarrollar su aplicación, debe definir los símbolos "EnableMemoryLeakReporting" y "FullDebugMode". 


Memcheck
MemCheck busca pérdidas de memoria, corrupción de memoria, uso de un objeto después de su destrucción, llamadas a métodos en referencias de interfaz que se refieren a un objeto destruido, etc. MemCheck es un programa gratuito, con código fuente.
http://cc.embarcadero.com/item/16059
 

MadExcept
madExcept fue creado para ayudar a localizar memory leaks en su aplicación Delphi. Siempre que haya un bloqueo / excepción en su programa, madExcept lo detectará, analizará, recopilará mucha información útil y le dará al usuario final la posibilidad de enviarle un informe de error completo.


Eurekalog
EurekaLog es la nueva herramienta complementaria que le da a su aplicación Delphi / C ++ Builder (GUI, Consola, Web, ...) el poder de detectar cada excepción, generando un registro detallado de la pila de llamadas (con unidad, clase, método y número de línea), mostrándolo y enviándolo de vuelta por correo electrónico o mensaje web (HTTP-S / FTP).

LeakCheck (gratis)
Delphi LeakCheck es otra gran opción para detectar fugas de memoria. También es gratuito, de código abierto y tiene algunas ventajas sobre FastMM: es multiplataforma , lo que significa que puede verificar fugas directamente en aplicaciones móviles y Linux; y se integra muy bien con los marcos de prueba unitarios (a saber, DUnit y DUnitX ).

La forma de comenzar es similar a FastMM : agregue la unidad LeakCheck como la primera unidad utilizada en su cláusula de usos de dpr, y se agregará y estará lista para usar. Sin embargo, la configuración para las pruebas unitarias es un poco más complicada, pero eso es parte del juego.

Una pequeña desventaja es que para usarlo estás casi solo: el proyecto no ha recibido actualizaciones por un tiempo (lo cual no es necesariamente malo ya que está funcionando). Pero eso significa que probablemente no obtendrá mucha ayuda directamente del autor (nunca lo intenté, para ser justos). Tampoco hay mucha información al respecto en la web , solo encontré un solo artículo que explica cómo usarlo además de la descripción detallada en el repositorio oficial de Bitbucket.

Pros

Gratis;
Código fuente completo;
Plataforma cruzada;
Se integra bien con las pruebas unitarias (DUnit y DUnitX).
Contras

No hay mucha información sobre cómo usarlo;
Sin actualizaciones recientes, sin soporte oficial;

Deleaker

Delaker es una aplicación comercial que se dedica exclusivamente a detectar fugas de memoria . Eso se refleja en el producto, que le proporciona características realmente interesantes para detectar fugas de memoria.

Tiene una interfaz gráfica de usuario amigable para que pueda configurar el entorno y ver los resultados, que se puede usar de forma independiente o integrada en Delphi IDE. También puede detectar muchos más tipos de fugas de memoria : fugas de GDI, fugas de objetos y identificadores de USUARIO de Windows, fugas en API de Windows, en DLL de terceros, etc. Exactamente por eso, proporciona opciones para que usted ignore fácilmente varios tipos de Fugas : si no lo hace, obtendrá una gran cantidad de fugas en una ejecución normal de la aplicación.

Otra característica interesante es la capacidad de tomar instantáneas de la asignación de memoria . Esto le permite detectar fugas no solo durante la vida útil de toda su aplicación, sino en algunas operaciones específicas de la misma.






 

Promoción de Delphi 10.4, últimos 3 días

Imagen
Disfrute de un desarrollo de Windows sin igual con Rad Studio 10.4

Mejoras significativas de Windows. Entrega aplicaciones visualmente deslumbrantes con elementos de IU con alto DPI de gran nitidez en monitores 4K y una nueva y versátil compatibilidad con estilos para los controles visuales. Integra tecnologías web modernas y seguras a través del nuevo WebView2 de Microsoft basado en Chromium. Crea barras de título modernas y mejoradas similares a las de Office, Explorer, Google Chrome y otros. Significativas mejoras de estabilidad, con un nuevo depurador para Windows 64 bits C++.

Mayor productividad. Incrementa la productividad con rápido completamiento de código en el IDE. Mejor compatibilidad con el código existente y codificación más sencilla a través de la administración de memoria unificada. Vincula datos rápidamente con la arquitectura optimizada y el rendimiento mejorado de Visual LiveBindings. Accede fácilmente a bibliotecas populares de C++ como ZeroMQ, SDL2, SOCI, libSIMDpp y Nematode. Más amplia compatibilidad con la nube de Amazon AWS.

Mejoras de rendimiento y calidad. Más de 1000 mejoras de calidad y rendimiento. Logra una mejor eficiencia del código a través de los nuevos registros de administración personalizada. Ejecuta el código más rápido con las tareas paralelas mejoradas en procesadores multinúcleo modernos. Experimenta un mejor rendimiento del renderizado en pantalla para macOS e iOS y compatibilidad con la API de Metal. Mayor compatibilidad con el código existente y codificación más sencilla a través de la administración de memoria unificada.

!Embarcadero está llevando a cabo un ENORME esfuerzo en la promoción de la versión 10.4! ¡ Póngase en contacto con su representante de ventas o socios para obtener más información!
Aproveche estos últimos 3 días 
para tener un gran descuento





Efectos con Scanline


 How to use ScanLine property for 24-bit bitmaps? - Stack Overflow

Tbitmap.scanline es una propiedad indexada de solo lectura que devuelve un puntero a una fila de pixeles de un bitmap.
Es la forma más rápida de acceder a los píxeles de una imagen aunque depende del formato de mapa de bits que se establezca desde la propiedad Pixelformat de la unit Graphics.

TPixelFormat = (pfDevice, pf1bit, pf4bit, pf8bit, pf15bit, pf16bit, pf24bit, pf32bit, pfCustom);

Ejemplo de carga un bitmap:

VAR
  bmp: tbitmap;
BEGIN
  bmp := tbitmap.Create;
  TRY
    IF OpenDialog1.Execute THEN
    BEGIN
      bmp.LoadFromFile(OpenDialog1.FileName);
      bmp.PixelFormat := pf24bit;
      Invalidate; { Mostrar la imagen }
    END;
  FINALLY
    bmp.Free;
  END;
END;
 

Cuando creamos un mapa de bits cada pixel se inicializa al máximo valor, es decir a 255 por eso el mapa de bits es blanco, ese el formato de pixel predeterminado.
Un pixel en un mapa de bits de 24 bits se describe con 3 valores rojo, verde y azul, que se almacenan en la memoria en orden inverso: azul, verde y rojo.
El puntero a la matriz de bytes que hace scanline se vería de la siguiente forma:
 




Para obtener el resultado anterior hay que asignar el resultado de Scanline a un puntero de bytes: pByteArray

  VAR
    p: PByteArray;
  BEGIN
    p := FImage.ScanLine[0];
  END;

Por ejemplo para poner el primer pixel de la primera fila de la imagen de color negro y el segundo de color blanco habría que hacer:


VAR
  p: PByteArray;
BEGIN
  p := FImage.ScanLine[0]; { lee la primera fila }
  p[0] := 0; { primer pixel de color negro }
  p[1] := 0;
  p[2] := 0;
  p[3] := 255; { segundo pixel de color blanco }
  p[4] := 255;
  p[5] := 255;
  Invalidate;
END;


También podemos usar la siguiente estructura:


 type
  TRGBTriple = packed record
    rgbtBlue: Byte;
    rgbtGreen: Byte;
    rgbtRed: Byte;
  end;

Para conseguir que la segunda fila del bitmap sea de color negro:

type
  PRGBTripleArray = ^TRGBTripleArray;
  TRGBTripleArray = array[0..4095] of TRGBTriple;

procedure TForm1.Button1Click(Sender: TObject);
var
  I: Integer;
  Bitmap: TBitmap;
  Pixels: PRGBTripleArray;
begin
  Bitmap := TBitmap.Create;
  try
    Bitmap.Width := 3;
    Bitmap.Height := 2;
    Bitmap.PixelFormat := pf24bit;
    // get pointer to the second row's raw data
    Pixels := Bitmap.ScanLine[1];
    // iterate our row pixel data array in a whole width
    for I := 0 to Bitmap.Width - 1 do
    begin
      Pixels[I].rgbtBlue := 0;
      Pixels[I].rgbtGreen := 0;
      Pixels[I].rgbtRed := 0;
    end;
    Bitmap.SaveToFile('c:\Image.bmp');
  finally
    Bitmap.Free;
  end;
end;
 

De lo anterior podemos observar que el primer pixel del bitmap se encuentra en el offset 0 de la primera fila, el segundo en el offset 3, el tercero en el offset 6 esto quiere decir que el byte en la ubicación x*3 es el componente azul, el byte de la ubicación x*3+1 es el componente verde y el x*3+2 es el rojo.
Entonces si quisiéramos poner una imagen en color negro habría que hacer lo siguiente:

VAR
  p: PByteArray;
  x: Integer;
  y: Integer;
BEGIN { Iterar entre todas las líneas}
  FOR y := 0 TO Pred(FImage.Height) DO
  BEGIN
    p := FImage.ScanLine[y];
    FOR x := 0 TO Pred(FImage.Width) DO
    BEGIN
      p[x * 3] := 0;
      p[x * 3 + 1] := 0;
      p[x * 3 + 2] := 0;
    END;
 END;
END;
 

Ahora vamos a profundizar un poco más en este aspecto, hemos visto que cambiando los valores de los bytes de un pixel podemos conseguir los diferentes tipos de colores y si vamos un poco más allá conseguiremos espectaculares efectos visuales como los siguientes:

- Solarize

VAR
  p: PByteArray;
  x: Integer;
  y: Integer;
BEGIN
  FOR y := 0 TO Pred(FImage.Height) DO
  BEGIN
    p := FImage.ScanLine[y];
    FOR x := 0 TO Pred(FImage.Width) DO
    BEGIN
      IF p[x * 3] > 127 THEN
        p[x * 3] := 255 - p[x * 3];
      IF p[x * 3 + 1] > 127 THEN
        p[x * 3 + 1] := 255 - p[x * 3 + 1];
      IF p[x * 3 + 2] > 127 THEN
        p[x * 3 + 2] := 255 - p[x * 3 + 2];
    END;
  END;
END;


- Invertir colores
 
VAR
  p: PByteArray;
  x: Integer;
  y: Integer;
BEGIN
  FOR y := 0 TO Pred(FImage.Height) DO
  BEGIN { get the pointer to the y line }
    p := FImage.ScanLine[y];
    FOR x := 0 TO Pred(FImage.Width) DO
    BEGIN { modificar el color azul }
      p[x * 3] := 255 - p[x * 3]; { modificar el color verde  }
      p[x * 3 + 1] := 255 - p[x * 3 + 1]; { modificar el color rojo  }
      p[x * 3 + 2] := 255 - p[x * 3 + 2];
    END;
  END;
END;


- Convertir a escala de grises

 Se hace con la siguiente fórmula
Gray = (Red * 3 + Blue * 4 + Green * 2) div 9

VAR
  p: PMyPixelArray;
  x: Integer;
  y: Integer;
  gray: Integer;
BEGIN
  FOR y := 0 TO Pred(FImage.Height) DO
  BEGIN
    p := FImage.ScanLine[y];
    FOR x := 0 TO Pred(FImage.Width) DO
      WITH p[x] DO
      BEGIN
        gray := (Red * 3 + Blue * 4 + Green * 2) DIV 9;
        Blue := gray;
        Red := gray;
        Green := gray;
      END;
  END;
END;



- Hacer un espejo vertical

  procedure TForm1.Button1Click(Sender: TObject);

   procedure EspejoVertical(Origen,Destino:TBitmap);
   var
      x,y          : integer;
      Alto         : integer;
      Po,Pd        : PByteArray;
      tmpBMP       : TBitmap;
      LongScan     : integer;
   begin
     {Si es un modo raro... pasamos}
     case Origen.PixelFormat of
       pfDevice,
       pfCustom:
         Raise exception.create( 'Formato no soportado'+#13+
                                 'Bitmap Format not valid');
     end;

     {Calculamos variables intermedias}
     Alto :=Image1.Picture.Bitmap.Height-1;
     try
       {Calculo de cuánto ocupa un scan}
       LongScan:= Abs( Integer(Origen.ScanLine[0])-
                       Integer(Origen.ScanLine[1]) );
     except
       Raise exception.create( 'ScanLine Error...');
     end;

     {Cremos un bitmap intermedio}
     tmpBMP:=TBitmap.Create;
     with tmpBMP do
     begin
       {Lo asignamos, así se copia la paleta si la hay}
       Assign(Origen);
       {Esto es para que sea un bitmap nuevo... es un bug de D3 y D4}
       Canvas.Pixels[0,0]:=Origen.Canvas.Pixels[0,0];
     end;

     {Damos la vuelta al bitmap}
     for y:=0 to Alto do
     begin
       Po := Origen.ScanLine[y];
       Pd := tmpBMP.ScanLine[Alto-y];
       for x := 0 to LongScan-1 do
       begin
           Pd^[X]:=Po^[X];
       end;
     end;
     {Lo asignamos al bitmap destino}
     Destino.Assign(tmpBMP);
     {Esto es para parchear un bug de Delphi 3 y Delphi4...}
     Destino.Canvas.Pixels[0,0]:=tmpBMP.Canvas.Pixels[0,0];
     tmpBMP.Free;
   end;
 begin
   EspejoVertical( Image1.Picture.Bitmap,
                   Image1.Picture.Bitmap);
   Image1.Refresh;
 end;



- Ajustar el brillo  

 Se hace con la siguiente fórmula:
NewPixel = OldPixel + (1 * Percent) div 200


FUNCTION IntToByte(AInteger: Integer): Byte; INLINE;
BEGIN
  IF AInteger > 255 THEN
    Result := 255
  ELSE IF AInteger < 0 THEN
    Result := 0
  ELSE
    Result := AInteger;
END;

PROCEDURE TMainForm.AdjustBrightness(Percent: Integer);
VAR
  p: PMyPixelArray;
  x: Integer;
  y: Integer;
  amount: Integer;
BEGIN
  amount := (255 * Percent) DIV 200;
  FOR y := 0 TO Pred(FImage.Height) DO
  BEGIN
    p := FImage.ScanLine[y];
    FOR x := 0 TO Pred(FImage.Width) DO
      WITH p[x] DO
      BEGIN
        Blue := IntToByte(Blue + amount);
        Green := IntToByte(Green + amount);
        Red := IntToByte(Red + amount);
      END;
  END;
  Invalidate;
END;

PROCEDURE TMainForm.ScanLineBrightnessClick(Sender: TObject);
VAR
  amount: Integer;
BEGIN
  amount := StrToInt(InputBox('Brightness Level', 'Enter a value from -100 to 100:', '50'));
  AdjustBrightness(amount);
END;

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 también hace 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...
lo que te da seguridad en caso de que ocurra algún problema.

- 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, 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