Mostrando entradas con la etiqueta MAV. Mostrar todas las entradas
Mostrando entradas con la etiqueta MAV. Mostrar todas las entradas

domingo, 7 de diciembre de 2025

MAV. OOo Basic


Presentación de secuencias de imágenes





Podemos realizar presentaciones de forma totalmente automatizada sin necesidad de crearlas previamente, con imágenes adaptadas al cumplimiento de determinadas condiciones. De este modo es posible automatizar la presentación del input, ajustándolo a necesidades específicas y cambiantes.


Está mal que utilice el icono de Impress para ilustrar el ítem de la entrada, ya que precisamente no es éste el servicio al que se accede (por cierto que por defecto). Tampoco se trata de presentaciones en sentido estricto, aunque se puede simular el funcionamiento básico de una presentación sencilla.

En realidad el servicio que se abre por defecto es Draw, ya que es éste el que en LibreOffice está asociado a los formatos más comunes de imágenes, de modo que, al cargar una imagen lo que sucede es que el sistema de LibreOffice recurre al servicio que tiene disponible.

Pero realmente esto no es lo importante. Sí lo es la utilidad que tiene este procedimiento y su versatilidad para adaptar el contenido (gráfico) del ítem que podemos presentar. No sólo no necesitamos tener preparada una determinada presentación, es que podemos adaptarla de forma automatizada (e interactiva) de acuerdo con las condiciones que deseemos, siempre que, claro está, hayamos organizado convenientemente los materiales gráficos.

Como siempre es mejor explicar sobre la base de un proyecto concreto, en este caso lo voy a hacer sobre el que me ha llevado hasta aquí, aunque ahora tome un breve desvío: la automatización de la presentación de las imágenes del test PLON-R Fonología.

Para simplificar me limito a una cuestión de base en este test: los ítem de trabajo estén en función de la edad del niño, aunque de forma acumulativa. Me explico: a los niños de tres años se les aplican los ítem de su grupo de edad, a los de cuatro los ítem de tres y los ítem de cuatro años, y así sucesivamente hasta llegar a los seis años, edad máxima de la batería PLON-R.

Realmente el procedimiento que estoy desarrollando en relación con ese test es más complejo y diferente en cuanto a prioridades, pero lo que aquí te presento ha surgido a raíz de ese trabajo, motivo por el que será ese nuestro actual contexto: generar presentaciones de imágenes en función de la edad del sujeto.

Se trata, en realidad, de una especie de simulacro (en miniatura) de aplicación de un test de Fonología, ya que no es posible utilizar las mismas imágenes y no tengo ningún interés en sobrecargar el trabajo con una extensa lista de imágenes. Pero estas limitaciones no suponen ninguna merma para la potencialidad de la propuesta.

Empezaré por describir los soportes: una hoja de cálculo como gestor, que incluyen el listado de componentes (hoja Listados) y el código, y una carpeta que contiene las imágenes que vamos a emplear, exactamente 15 imágenes, a razón de 5 por grupo de edad (3, 5 y años).


Aunque, lo verdaderamente interesante está oculto en el IDE de plon_lista.ods, también es importante el listado de edades y de palabras, ya que a ellos se acude para acceder a los contenidos. Este acceso se realiza desde código, ubicado con el IDE tal y como muestra la imagen que precede, concretamente en sus dos módulos (Edad y cargarImg).

El primero, el fundamental, contiene un único script (AccesoPorEdad) que es el que tienes que activar para que funciones el conjunto. En cargarImg se encuentran los auxiliares que hacen posible la carga de las imágenes: una subrutina (AbrirImagen) y una función (AbrirImg): la primera es llamada desde el script y la segunda desde la subrutina. Veamos primero el script.

Sub AccesoPorEdad

Dim oHoja As Object
Dim sHoja As String
Dim oCelda As Object
Dim col As String
Dim i As Integer

'Matrices para acceso a datos (EDAD y PALABRAS)
Dim mEdad(14) As Integer
Dim edad As Integer
Dim mPal(14) As string
Dim Pal As string
 
'Acceso al libro (ThisComponent), y a la hoja (sHoja)

sHoja = "Listados"
oHoja =ThisComponent.getSheets().getByName(sHoja)

'Acceso al contenidos de las listas

For i = 0 to UBound(mEdad())
'Acceso a los datos de edad
oCelda =  oHoja.getCellRangeByName( "A" & i+1)
edad = oCelda.getValue()
mEdad(i) = edad
'Acceso a los datos Palabras
oCelda =  oHoja.getCellRangeByName( "B" & i+1)
Pal = oCelda.getString()
mPal(i) = Pal
Next

'Muestra de elementos (palabras) en función de la edad

Dim edad_sujeto As Integer

edad_sujeto = CInt(InputBox("Edad del alumno/alumna (sólo LOS años)"))

For i = 0 To UBound(mEdad())
If mEdad(i) <= edad_sujeto Then
AbrirImagen(mPal(i)) 'Llamada a subrutina
End If
Next

End Sub
Tras acceder al libro y a la hoja que contiene las tablas (oHoja =ThisComponent.getSheets().getByName(sHoja)), mediante un bucle (For i = 0 to UBound(mEdad())) accedemos al contenido de las listas (esto es, a las celdas) para pasar los datos a sendas matrices (Dim mEdad(14) As Integer Dim mPal(14) As string), mediante un procedimiento sencillo y repetitivo pero muy útil, que se realiza en tres pasos:
  • Acceso a celda: oCelda =  oHoja.getCellRangeByName( "A" & i+1)
  • Asignar el contenido de la celda a variable: edad = oCelda.getValue()
  • Y paso del contenido de la variable a la matriz: mEdad(i) = edad
Una vez que ya hemos pasado los datos a las matrices toca el turno de establecer la condición que va a determinar la segunda parte del procedimiento (la carga de las imágenes).

En este caso, lo que determina la condición en la edad del alumno, así que la solicitamos al usuario (edad_sujeto = CInt(InputBox("Edad del alumno/alumna (sólo LOS años)"))).

Con este dato disponible iniciamos un segundo bucle (For i = 0 To UBound(mEdad())) que va a llamar reiteradamente a la subrutina (AbrirImagen(mPal(i))) pasando como parámetro el nombre de la imagen (siempre que se cumpla el criterio establecido (la edad, menor o igual) mediante un condicional (If mEdad(i) <= edad_sujeto Then)

Lo que sucede a continuación es responsabilidad de la subrutina y de la función a la que ésta llama, pero está todo ello presidido por el cumplimiento de la condición que establecemos en el script y en la reiteración de ese proceso mientras se cumpla esa condición. Veamos esos auxiliares:

Sub AbrirImagen(img as String)
Dim sDocum As String
sDocum = "D:/Docap/presenta_plon/img/" & img &".png"
AbrirImg(sDocum) 'Llama a la función
End Sub
La subrutina se encarga de establecer la ruta absoluta en la que se encuentra la imagen (en este caso un directorio de un soporte externo de memoria: sDocum = "D:/Docap/presenta_plon/img/" & img &".png") y de llamar a la función que se encarga de cargar la imagen cuyo identificador hemos pasados desde el listado.

Function AbrirImg(cRuta As String,Optional aOpciones()) As Object
Dim oDocumento As Object
cRuta = ConvertToURL(cRuta)
If IsMissing (aOpciones) Then aOpciones = Array()
              AbrirCualquierDoc = StarDesktop.LoadComponentFromURL(cRuta,"_blank",0, aOpciones())
      oDocumento = AbrirCualquierDoc  
wait 1000
oDocumento.close(True)

End Function
El correcto funcionamiento de este conjunto depende del escrupuloso respeto del código de esta función, así que debes copiarla tal cual, a excepción del tiempo de espera (wait 1000) que puedes no establecer o hacerlo de mayor o menor duración, y del cierre de los documentos Draw  que se van abriendo y que mediante esta instrucción (oDocumento = AbrirCualquierDoc -> oDocumento.close(True)), que también puedes omitir, se cierran automáticamente transcurrido el tiempo establecido.

Documentos. Este [archivo comprimido] contiene el DocAp, incluyendo la carpeta de imágenes. Recuerda que si lo guardas en otra estructura de carpetas debes adaptar la instrucción correspondiente (subrutina AbrirImagen() instrucción asociada a sDocum).




miércoles, 22 de octubre de 2025

MAV. Python.

Biblioteca OpenCV


Introducción, instalación y documentación



Para el tratamiento de imágenes disponemos en Python de [varias herramientas]. Ya conocemos Pillow, pero no podemos hablar de este tema sin mencionar la que posiblemente es la mejor y más completa en la actualidad: OpenCV, que no está desarrolla en Python ni se limita a este lenguaje, aunque es frecuente trabajar con ella desde Python puesto que son plenamente compatibles.



Son muchas las posibilidades que ofrece el trabajo con imágenes mediante OpenCV, desde procesos de manipulación simples hasta desarrollo de IA como la visión por ordenador, el reconocimiento biométrico, el análisis de contenido de vídeos y un largo etc... que incluye el OCR. Por resumir, todo lo que podemos hacer con Pillow y mucho más; motivo más que sobrado para que nos pongamos límites bastante estrictos para no perdernos por caminos tan atractivos como secundarios para los fines de este blog.

Por concretar intereses, dentro del amplio y complejo mundo de la manipulación de imágenes, OpenCV nos va a ayudar como herramienta complementaria (o alternativa) a Pillow para la manipulación de imágenes y como auxiliar para el trabajo con imágenes que contienen texto.

La instalación de OpenCV, paso necesario, no ofrece mayor dificultad: accedemos a [esta página] y copiamos el enlace pip en nuestro cmd. La [manipulación básica] es simple, al menos para los usos que podemos tener inicialmente en mente, pero también muy complejos, cuanto los objetivos también lo son. De hecho te recomiendo la lectura de algunos documentos sobre el tema para que te hagas una idea de las posibilidades de esta biblioteca. Yo estoy en ello.

MAV. Python

Biblioteca OpenCV

Algunos usos básicos


Después de aprender algunas de las posibilidades de trabajo que nos ofrece Pillow, no pretendo (por ahora) más que echar un vistazo a lo básico que OpenCV nos puede ofrecer, así que dedicaré esta entrada a trastear un poco con el código.

 

Aprenderemos cómo usar OpenCV desde Python (ya que no es una biblioteca Python pero sí accesible y manejable desde este lenguaje) algunas de las funciones más sencillas sin más pretensión. Para ello remito a [esta web] y a [este manual]. Empecemos.

Lo primero será importar la biblioteca (import cv2) y lo siguiente identificar la imagen con la que queremos trabajar...
 


... para lo cual emplearemos identificaremos la ubicación de la imagen (img_dir ='img/paisaje.png') y aplicaremos la función imread() (img = cv2.imread(img_dir))

Pero para evitar sorpresas tal vez nos interese comprobar que CV2 está correctamente instalado. Para ello y para saber qué versión tenemos instalada aplicaremos la siguiente instrucción: print ('Version cv2: ' + cv2.__version__) 

Si todo funciona correctamente podremos trabajar con nuestra imagen, por ejemplo, mostrándola en pantalla...

cv2.imshow("Imagen color", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

... o mostrando sus dimensiones (obtenemos una matriz de datos)

print('Dimensiones de la matriz de la imagen ' + str(img.shape)) 

También podemos modificar la imagen, por ejemplo convirtiéndola a escala de grises...

img_gris = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

... o modificando su tamaño

img_pq = cv2.resize(img, (100, 100))

Las opciones disponibles son muchas y muy interesantes, pero tendremos que volver sobre ellas más adelante, ya que lo que nos interesa de forma más inmediata es el uso de OpenCV como recurso para el desarrollo de funcionalidades OCR. A esta cuestión dedicaremos las próximas entradas y volveremos sobre OpenCV más adelante para incrementar nuestros conocimientos sobre manipulación automatizada de imágenes.

martes, 21 de octubre de 2025

MAV. Python.

Pillow

Matriz a partir de imagen


En la [entrada anterior] creamos una imagen a partir de los datos de una matriz. Ahora haremos el proceso inverso: obtener los datos numéricos que definen una imagen dada, lo que equivale a generar una matriz a partir de esos datos.

 

Lo interesante de lo que estamos haciendo es que podemos trabajar con datos numéricos para crear imágenes y viceversa: obtener los datos numéricos de los que se compone una imagen digitalizada. A partir de esto podemos realizar manipulaciones generales o específicas del color y, a partir de éste, también del dibujo y de las formas que conforman la imagen.

En el script que sigue obtenemos la matriz de datos de una imagen dada y, partiendo de ésta, volvemos a construir la imagen, en este caso, sin modificación alguna respecto a la original.

from PIL import Image
import numpy as np

#Cargar imagen

img_dir = 'img/Paisaje.jpg'
img = Image.open(img_dir)

#Visualizamos la imagen cargada

img.show()

#Convertimos la imagen en matriz

array_img = np.array(img)

#Visualizamos la matriz

print(array_img)

#Convertimos de nuevo la matriz en imagen

img_array = Image.fromarray(array_img)

#Visualizamos la imagen creada

img_array.show()

Para ello ha sido suficiente con importar ambas bibliotecas (Pillow y NumPy) y usar dos funciones específicas, una asociada a Numpy (array_img = np.array(img)) y otra a Pillow (img_array = Image.fromarray(array_img)). La primera para convertir la imagen en una matriz y la segunda para invertir el procedimiento.

A partir de aquí se nos abren todas las opciones que derivan del trabajo con matrices numéricas. Todo un mundo.


MAV. Python.


Pilow

Imagen a partir de una matriz


Cuando creamos por primera vez una imagen mediante Pilow lo primero que pensé fue que poco interés tenía la cosa; cuando sobre esa base unimos dos imágenes para componer una tercera, ya me pareció más interesante y a la vista del interés que tiene para la descomposición de la imagen en colores parece que el tema va ganando en interés. Añado ahora una razón más para valorar positivamente la creación de imágenes en Pilow.


Me refiero a la construcción de una imagen (sencilla, eso sí) no mediante la función ya conocida (img_base = Image.new("L",(d_x,d_y),"white")), sino mediante una nueva función que ilustra la relación entre Pilow y NumPy: la función  fromarray(array_base). Pero no adelantemos acontecimientos.

from PIL import Image
import numpy as np

#Crear array  de base
array_base = np.zeros([550,750,3], dtype = np.uint8)

#Agregar colores
array_base[:,0:300] = [255,128,0]
array_base[:,300:] = [0,0,255]

#Crear la imagen a partir del arreglo
img_array = Image.fromarray(array_base)

#Mostrar la imagen creada
img_array.show()

Mediante este script he creado una sencilla imagen a partir de datos numéricos organizados en una matriz tridimensional (array_base = np.zeros([550,750,3], dtype = np.uint8)) mediante la función np.zeros(), que recibe como primer parámetro una tupla con tres valores: ancho, alto y 3 canales de color como tercer dimensión.

Los dos primeros, además de definir el tamaño de la matriz, también nos sirven como referencias necesarias para concretar las posiciones de las diferentes configuraciones de color que compondrán la imagen final...

vg. array_base[:,0:300] = [255,128,0]

... atribuye al primer elemento la posición 0-0 a 0:300 (ejes x-y) y como color asignado la tupla [R.G.B] [255,128,0].

Posteriormente se construye la imagen a partir de esos datos mediante el método fromarray() que recibe como parámetro la variable imagen (img_array = Image.fromarray(array_base)).

Si en lugar de una imagen RGB quisiéramos crear una imagen en escala de grises deberíamos usar un script como el siguiente:

from PIL import Image
import numpy as np

#Crear array  de base
array_base = np.zeros([550,750], dtype = np.uint8)

#Agregar grises
array_base[:,0:300] = [50]
array_base[:,300:] = [235]

#Crear la imagen a partir del arreglo
img_array = Image.fromarray(array_base)

#Mostrar la imagen
img_array.show()

Puedes apreciar el cambio en la construcción del array (ahora array_base = np.zeros([550,750], dtype = np.uint8)) y en el procedimiento para agregar el color (vg array_base[:,0:300] = [50]). El resto es igual.

MAV. Python.


Pillow

Filtros a partir de la separación de colores


Hablando de filtros, toda imagen se puede entender como una mas o menos compleja combinación de los tres colores básicos (rojo-verde-azul), cuando de una imagen en color se trata, y de la menor o mayor intensidad de la presencia de cada uno de ellos, siendo su valor mínimo 0 y máximo 255. El blanco resulta de la máxima intensidad de los tres (255,255,255) y el negro de la mínima (0,0,0). A partir de aquí, podemos trabajar con una imagen como con una matriz de datos numéricos, lo que abre interesantes posibilidades de trabajo. La primera de ellas generar filtros basados en la manipulación de las intensidades de los colores básicos de la imagen.


La consecuencia de lo anterior, lo que realmente nos interesa, es el trabajo con la imagen como matriz de datos, siendo la cuestión de su uso como filtro más bien secundaria. Se trata, en realidad, de estudiar la interesante combinación del trabajo con Pillow y con NumPy, como recurso para facilitar el manejo de imágenes mediante código. Ahora nos centraremos en uno de los procedimientos básicos y seguiremos trabajando en esta dirección en las entradas que siguen.

Dicho esto empezaremos por importar las dos bibliotecas

from PIL import Image
import numpy as np

... y cargando una imagen con la que trabajar

img_dir = 'img/formas.jpg'
img = Image.open(img_dir)

Obtenemos sus dimensiones (ancho y alto)...

d_x, d_y = img.size

... para poder crear otra imagen de esas mismas dimensiones, la cual nos servirá de base para generar los filtros de color (observa que creamos la imagen de tipo escala de grises ("L") y le damos un fondo, en este caso blanco ("white")

img_base = Image.new("L",(d_x,d_y),"white")

Lo más interesante del procedimiento es la obtención de los datos de los tres canales de color de la imagen y su conversión en una matriz mediante el método splitz(), así de sencillo:

col_R, col_G , col_B = img.split()

A partir de aquí, podremos aplicar el filtro de color (R, G, B) sobre la imagen original combinándola con la imagen de base que creamos en blanco (también podría ser en negro), siendo suficiente con introducir el canal de color deseado y mantener el original de la imagen de base ocupando el lugar de los otros dos, v.g. para el rojo...

img_R = Image.merge('RGB',(col_R, img_base, img_base))

Si utilizamos los tres canales, el resultado es la imagen original

img_R = Image.merge('RGB',(col_R,col_G,col_B))



sábado, 18 de octubre de 2025

MAV. Python.

Pillow

Filtros


Avanzando en el manejo de imágenes trataré en esta entrada sobre una forma compleja de modificarlas. Me refiero al uso de filtros. Trataremos algunos de ellos mediante el módulo ImageFilter.



Un filtro de imágenes es el procesamiento de la imagen para su mejora o modificación, a fin de resultar cierto contenido o mejorar los valores de la imagen desde diferentes perspectivas (luminosidad, modificación del color, nitidez...).

El uso de filtros nos interesa, pues, por los efectos técnicos y artísticos de los mismos, pero también (y es lo que aquí nos trae) por motivos relativos al propio procesamiento, digamos que como preparación previa para posteriores desarrollos, como el reconocimiento automatizado de cierto contenido en las imágenes.

No obstante, en estos momentos nos limitaremos a explicar un número determinado (y limitado) de filtros y mostrar sus efectos. Se trata, pues, de completar la colección de herramientas que hemos ido viendo en entradas precedentes. Además nos sirve de enlace con la temática que desarrollaremos posteriormente, la cual, aunque no hablemos de ella ahora, es la que posibilita los procedimientos que trataremos.

El primer paso para aplicar filtros es importar los módulos...

from PIL import Image, ImageFilter

... y cargar la imagen (vg)...

#Directorio de la imagen
img_dir = 'img/cuadro.png'

#Imagen original
img = Image.open(img_dir)

... la cual, por motivos didácticos, nos interesa ahora que sea compleja y de calidad. En todo caso, el efecto de algunos filtros es fácilmente detectable, pero el de otros no lo es o depende de las características de la imagen original; por eso te recomiendo que pruebes con diferentes imágenes.

Por otra parte, la sintaxis es muy simple:  función filter() aplicada a la variable asociada al archivo (vg. img.filter()) que contiene como parámetro la clase ImageFilter seguida del atributo NOMBRE_DEL_FILTRO, todo ello asociado a una nueva variable (img_FiltroX = img.filter(ImageFilter.CONTOUR)).

Te dejo a continuación el script con los filtros de mayor interés del módulo ImageFilter, aunque sobre eso hay opiniones. El código de cada filtro está comentado (por eso lo dejo en tinta negra) para que puedas activarlos (y visualizarlos) de forma sucesiva.

from PIL import Image, ImageFilter

#Directorio de la imagen
img_dir = 'img/formas.png'

#Imagen original
img = Image.open(img_dir)

img.show() #Ver imagen

#Modificaciones mediante filtros  -----------------------------------------------

#Filtro BLUR (difuminar)
'''
img_Blur = img.filter(ImageFilter.BLUR)
img_Blur.show()
'''
#Filtro CONTOUR (contorno)
'''
img_Cont = img.filter(ImageFilter.CONTOUR)
img_Cont.show()
'''
# Filtro DETAIL (detalle)
'''
img_Det = img.filter(ImageFilter.DETAIL)
img_Det.show()
'''
#Filtros EDGE_ENHANCE y EDGE_ENHANCE_MORE  (realzar bordes)
'''
img_Bor1 = img.filter(ImageFilter.EDGE_ENHANCE)
img_Bor1.show()
'''
'''
img_Bor2 = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
img_Bor2.show()
'''
#Filtro EMBOSS (efecto grabado)
'''
img_Emb = img.filter(ImageFilter.EMBOSS)
img_Emb.show()
'''
#Filtro FIND_EDGES (detección de bordes)
'''
img_BorDetec = img.filter(ImageFilter.FIND_EDGES)
img_BorDetec.show()
'''
#Filtro SHARPEN (agudizar)
'''
img_Ag = img.filter(ImageFilter.SHARPEN)
img_Ag.show()
'''
#Filtros SMOOTH y SMOOTH_MORE (suavizar)
'''
img_Su1 = img.filter(ImageFilter.SMOOTH)
img_Su1.show()
'''
'''
img_Su2 = img.filter(ImageFilter.SMOOTH_MORE)
img_Su2.show()
'''

El esperado efecto de cada filtro queda explicado en el comentario que antecede, así que no es necesaria más explicación. Lo que sí te sugiero desde el punto de vista técnico-estético, que es el que ahora nos interesa, es que pruebes con diferentes imágenes (tamaño, calidad, formato, complejidad compositiva...)