Comunidad Gambas-es
Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - Versión para impresión

+- Comunidad Gambas-es (https://gambas-es.org)
+-- Foro: Gambas (https://gambas-es.org/forum-3.html)
+--- Foro: Aplicaciones/Fragmentos de Código (https://gambas-es.org/forum-8.html)
+--- Tema: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. (/thread-1097.html)

Páginas: 1 2


Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - tincho - 17-11-2022

Hola amigos.
Estoy agregando la visualización de fotos en un panel en el que agrego por código una serie de picturebox donde les cargo la imagen de la foto pero es lento.
Estoy usando el siguiente código para lograr la carga de imágenes 128 o 256 pixeles de lado:
Código:
Public Function CanvasPic(sFile As String, iSize As Integer) As Picture
  Dim pic As Picture
  Dim img As Image
  Dim sc As Float
  Dim H, W As Integer
  If Exist(sFile) Then
    img = Image.Load(sFile)
    If img.W >= img.H Then
      sc = img.H / img.W
      W = iSize
      H = iSize * sc
    Else
      sc = img.W / img.H
      W = iSize * sc
      H = iSize
    Endif
    pic = img.Stretch(W, H).Picture
  Endif
  Return pic
End

Pero al ser tan lento no lo puedo implementar para representar muchas imágenes.
Alguien podría sugerir alternativas mas eficientes.
Muchas gracias de antemano.


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - gartumar2 - 17-11-2022

¿Cuanto "pesan" las imágenes antes de redimensionarlas?

He probado tu rutina y funciona de manera inmediata cargando imágenes .jpg con un peso de 500 K antes de redimensión.

Código:
Public Sub Button1_Click()

  Dim pic As Picture
  Dim img As Image
  Dim sc As Float
  Dim H, W As Integer
  Dim sFile As String
  Dim iSize As Integer = 256
  sFile = "/home/Comun/Fondos/187641361_4511982512149288_4569997526716770034_n.jpg"
  If Exist(sFile) Then
    Print Now
    img = Image.Load(sFile)
    Print Now
    Print "-------------------------------"
    If img.W >= img.H Then
      sc = img.H / img.W
      W = iSize
      H = iSize * sc
    Else
      sc = img.W / img.H
      W = iSize * sc
      H = iSize
    Endif
    Print Now
    pic = img.Stretch(W, H).Picture
    Print Now
    Print "== == == == == == == == == == == == == == == == == =="
    PictureBox1.Picture = pic
  Endif

End

Y el resultado ha sido así:

17/11/2022 10:24:15
17/11/2022 10:24:15
-------------------------------
17/11/2022 10:24:15
17/11/2022 10:24:15
== == == == == == ==

Es decir, el tiempo empleado es del orden de los milisegundos. Habría que cambiar Now por otra función más apropiada paa medir tiempos tan cortos.
Es evidente que el cuello de botella solo puede estar en el Load o en el Stretch, el resto son solo cálculos aritméticos.
No sé cuantas imágenes tienes que cargar por segundo, en caso de que sea un carrusel, o realmente si el problema es solo de carga de una en una.

Si te puedo ayudar en algo mas, encantado, Pídeme las pruebas que necesites.

Un saludo.


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - Shordi - 17-11-2022

Sin entrar al fondo del asunto, una puntualización: Hay veces que no hay maneras más rápidas de hacer algo y uno debe renunciar a la inmediatez. En el gbAmp me encontré con esta situación en el cálculo de la duración de los ficheros de sonido, que no era posible hacerla de manera inmediata, por tanto lo derivé a "proceso de fondo" y se va haciendo mientras el programa continúa. Es lo que hacen también nautilus y similares, con un directorio con tropecientas fotos, si cambias de vista de lista detalle a iconos, verás que la miniatura se va rellenando poco a poco. Puede ser una manera de enfocar el problema.

Saludos


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - gartumar2 - 17-11-2022

He cambiado Now por CFloat(Now) y este es el resultado:
2492005,40379675
2492005,40379747
---------------------------
2492005,40379747
2492005,40379751
== == == == == ==
2492005,40379751  <-- Este lo he añadido después de PictureBox1.Picture = pic (para ver el tiempo de dibujado).

Como ves la diferencia de tiempos es ridícula.

Espero te sea útil.

Saludos.


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - Shordi - 17-11-2022

(17-11-2022, 11:32)gartumar2 escribió:  ...
...
Habría que cambiar Now por otra función más apropiada paa medir tiempos tan cortos.
...
...

¿Has probado "Activar Perfilado" en el menú "Depuración"? Ahí tienes eso y mucho más. Sólo ejecuta tu programa y cierralo después.
(Por cierto había ciertos problemas con QT si se hacía esto y el IDE debía abrirse con GTK (botón derecho en el menú del sistema)... o al revés, no recuerdo)

Saludos


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - gartumar2 - 17-11-2022

Nueva prueba. He cargado una imagen gorda, gorda, 36,4 MB antes de redimension. Estos han sido los resultados.

2492005,41794984
2492005,41795524
-------------------
2492005,41795524
2492005,41795588
== == == == == == ==
2492005,41795595

Se llega a apreciar el retardo en la carga, pero menos de un segundo.

Saludos.

(17-11-2022, 11:46)Shordi escribió: ¿Has probado "Activar Perfilado" en el menú "Depuración"?

Acabo de aterrizar en Gambas y aún me estoy enterando poco a poco de lo que puedo (y no puedo) hacer. Gracias por la info. Voy a probar.

Saludos.

PD. Efectivamente la activación del perfilado me deja a Gambas frito, se queda bloqueado en Loading profiling file...... hasta que explota. Con Gtk funciona bien.


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - Shordi - 17-11-2022

Prueba a ejecutarlo con QT5. Yo lo que hago es abrirlo con un shellscript que ubico en ~/.local/bin con este contenido

Código:
#!/bin/sh
#Si no existe el archivo de seguridad del día, lo creamos
copia_diaria="/home/jorge/Datos/Gambas_seguridad/`date +%Y-%m-%d`.tar.gz"
if [ ! -f $copia_diaria ]
then
    #Creamos el comprimido de copia de seguridad
    tar -czvf $copia_diaria /home/jorge/gambas >/home/jorge/.local/bin/gambas_copias.log
fi
env GB_GUI=gb.qt5 gambas3

La última línea es la que abre gambas con qt5. Las otras me hacen una copia de seguridad al día de mi carpeta de proyectos. (Cierto que es algo que almacena un montón de copias redundantes y cada x tiempo tienes que limpiar... pero tener la copia de ayer, y anteayer y anteanteayer, es algo que me ha salvado el culo muchas veces).

Saludos


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - gartumar2 - 17-11-2022

(17-11-2022, 11:46)Shordi escribió: (Por cierto había ciertos problemas con QT si se hacía esto y el IDE debía abrirse con GTK (botón derecho en el menú del sistema)... o al revés, no recuerdo)

Por si te satisface la curiosidad: las primeras ejecuciones con Perfilado activado en Qt4 y Qt5 han colgado Gambas, Luego con Gtk ha funcionado. Y a partir de ahí ya funciona con Qt4 y Qt5.


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - tercoide - 17-11-2022

(17-11-2022, 11:41)Shordi escribió: Sin entrar al fondo del asunto, una puntualización: Hay veces que no hay maneras más rápidas de hacer algo y uno debe renunciar a la inmediatez. En el gbAmp me encontré con esta situación en el cálculo de la duración de los ficheros de sonido, que no era posible hacerla de manera inmediata, por tanto lo derivé a "proceso de fondo" y se va haciendo mientras el programa continúa. Es lo que hacen también nautilus y similares, con un directorio con tropecientas fotos, si cambias de vista de lista detalle a iconos, verás que la miniatura se va rellenando poco a poco. Puede ser una manera de enfocar el problema.

Saludos

Coincido con este aproach del problema. Ademas, podrías guardar las miniaturas como suelen hacer estos navegadores de archivos, con ello no tendrías que stretch-arlas  función que debe insumir algún tiempo.


RE: Cual es la forma mas rapida y eficiente de cargar imagenes en un control. - tincho - 17-11-2022

Estoy explorando el uso, a través de Extern, de la librería MagickWand para ello me sirvo de un ejemplo alojado en la wiki del foro gambas-it.org.
https://www.gambas-it.org/wiki/index.php/Ridimensionare_un%27immagine_mediante_le_funzioni_esterne_del_API_di_ImageMagick
Bien la pegunta es:
¿Como convertir la variable tipo Pointer "magickwand", que ya contiene un la imagen redimensionada, en una imagen en memoria que pueda usar gambas?
Ya que para el uso que deseo darle no es necesario que la imagen redimensionada sea guardada en el sistema de archivos.
Encontré que hay una Struct de la siguiente forma:
Código:
size_t     id
char     name [MagickPathExtent]
Image *     images
ImageInfo *     image_info
ExceptionInfo *     exception
MagickBooleanType     insert_before
MagickBooleanType     image_pending
MagickBooleanType     debug
size_t     signature
https://www.imagemagick.org/api/MagickWand/struct__MagickWand.html
Pero no se como asignar el puntero a dicha estructura.

¿La imagen esta en "Image *     images" ? en ese caso ¿Como la convierto en una Image de gambas?

Este es el código modificado para usarlo como función, que no funciona:

Código:
' Gambas module file
Library "libMagickWand-6.Q16:6.0.0"

Private Enum MagickFalse = 0, MagickTrue
Private Enum UndefinedFilter = 0, PointFilter, BoxFilter, TriangleFilter, HermiteFilter,
  HanningFilter, HammingFilter, BlackmanFilter, GaussianFilter, QuadraticFilter,
  CubicFilter, CatromFilter, MitchellFilter, JincFilter, SincFilter, SincFastFilter,
  KaiserFilter, WelshFilter, ParzenFilter, BohmanFilter, BartlettFilter, LagrangeFilter,
  LanczosFilter, LanczosSharpFilter, Lanczos2Filter, Lanczos2SharpFilter, RobidouxFilter,
  RobidouxSharpFilter, CosineFilter, SplineFilter, LanczosRadiusFilter, SentinelFilter

Public Struct Swand
  id As Integer
  char As String
  images As Pointer
  image_info As String
  exception As String
  insert_before As Boolean
  image_pending As Boolean
  bdebug As Boolean
  signature As Integer
End Struct

' void MagickWandGenesis(void)
' Initializes the MagickWand environment.
Private Extern MagickWandGenesis()

' MagickWand *NewMagickWand(void)
' Returns a wand required for all other methods in the API.
Private Extern NewMagickWand() As Pointer

' MagickBooleanType MagickReadImage(MagickWand *wand,const char *filename)
' Reads an image or image sequence.
Private Extern MagickReadImage(wand As Pointer, filename As String) As Boolean

' MagickBooleanType MagickResizeImage(MagickWand *wand, const size_t columns,const size_t rows,const FilterType filter)
' Scales an image to the desired dimensions with a filter.
Private Extern MagickResizeImage(wand As Pointer, columns As Long, rows As Long, filter As Integer, blur As Float) As Boolean

' MagickBooleanType MagickWriteImages(MagickWand *wand, const char *filename,const MagickBooleanType adjoin)
' Writes an image or image sequence.
Private Extern MagickWriteImages(wand As Pointer, filename As String, adjoin As Boolean) As Boolean

' MagickWand *DestroyMagickWand(MagickWand *wand)
' Deallocates memory associated with an MagickWand.
Private Extern DestroyMagickWand(wand As Pointer) As Pointer

' void MagickWandTerminus(void)
' Terminates the MagickWand environment.
Private Extern MagickWandTerminus()

Public Function Image(jpg As String) As Image

  Dim bo As Boolean
  Dim magickwand As Pointer
  Dim w As New Swand
  Dim fileimmagine, nuovofile As String
  Dim img As Image

  MagickWandGenesis()

  magickwand = NewMagickWand()

  bo = MagickReadImage(magickwand, jpg)
  If bo = MagickFalse Then
    Error.Raise("Impossibile caricare il file immagine !")
    Chiude(magickwand)
  Endif

  ' Ridimensiona l'immagine:
  MagickResizeImage(magickwand, 106, 80, LanczosFilter, 1.0)

  bo = MagickWriteImages(magickwand, nuovofile, MagickTrue)
  If bo = MagickFalse Then
    Error.Raise("Impossibile creare il nuovo file dell'immagine ridimensionata !")
    Chiude(magickwand)
  Else
    w = magickwand
    img = w.images
  Endif

  Chiude(magickwand)

  If img Then
    Return img
  Else
    Return Null
  Endif

End

Private Procedure Chiude(mw As Pointer)

  ' Libera la memoria e chiude la libreria "ImageMagick":
  DestroyMagickWand(mw)
  Wait 0.01
  MagickWandTerminus()

End

(17-11-2022, 11:41)Shordi escribió: con un directorio con tropecientas fotos, si cambias de vista de lista detalle a iconos, verás que la miniatura se va rellenando poco a poco.

Efectivamente, el esta sobre la mesa esa opción, de hecho tengo algo armado en otro programa que extrae las imágenes de un PDF para ello uso la clase Task de gambas que funciona súper bien para esto que decís.
De todas manera quisiera saber si esta forma que uso es mejorable.

(17-11-2022, 11:32)gartumar2 escribió: ¿Cuanto "pesan" las imágenes antes de redimensionarlas?

Las imágenes son de unos 4Mb son fotos de teléfonos y cámaras.
Esto es para el programa Photo organizer que hace un tiempo estoy desarrollando y en el feedback de Shordi y TercoIDE me instaron a que se puedan ver las fotos y se pueda buscar en una base de datos, así que acá estoy tratando de lograr el desafío. Dodgy
El objetivo es mostrar unas miniaturas de las imágenes de un directorio d importación y también mostrar las imágenes del disco duro al buscar por fecha o por alguna otra restricción.

(17-11-2022, 12:04)gartumar2 escribió: PD. Efectivamente la activación del perfilado me deja a Gambas frito, se queda bloqueado en Loading profiling file...... hasta que explota. Con Gtk funciona bien.

si, correcto, hay que usar QT para que funcione. Antes funcionaba con GTK pero algo cambio.