Mostrando entradas con la etiqueta Imágenes. Mostrar todas las entradas
Mostrando entradas con la etiqueta Imágenes. 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.

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...) 

viernes, 17 de octubre de 2025

MAV. Python

Pillow

Usos básicos (IV)



Esta entrada complementa [la anterior], incluso puede que debiera ser previa pero en todo caso considero que no debe faltar en esta exposición de recursos para el tratamiento de imágenes ya que en ella trato sobre una función básica en Pillow relativa al cambio de tipo de imagen.

 

Me estoy refiriendo al cambio de modelo de imagen según sus atributos de color, tema este de cierta complejidad, pero que aquí vamos a resumir de forma drástica a las cuestiones que considero tienen mayor interés, quedando el resto para que tú profundices en ellas.

Por mi parte me limitaré a decir que podemos trabajar con imágenes en blanco y negro, imágenes en escala de grises (esta conversión ya la vimos en la entrada antes referida) e imágenes en color. Y dentro de estas últimas podemos distinguir entre imágenes formadas por combinaciones de colores básicos (formato RBG, extensión jpg) e imágenes que añaden a lo anterior información sobre niveles de transparencia (formato RGBA, extensión png). No son los únicos, pero por ahora es suficiente.

Empecemos por lo de siempre...

from PIL import Image

... es suficiente para lo que tenemos que hacer, no siendo necesarios (al contrario que antes) más módulos. Cargamos una imagen en formato png (por eso de trabajar con el formato más complejo)...

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

#Imagen original
img = Image.open(img_dir)

... y visualizamos su atributo modo...

print(img.mode) -> Devuelve RGBA

Y para finalizar este proceso (ya conocido) podemos visualizar la imagen

img.show()

A partir de aquí empezaremos con las transformaciones que partirán siempre de la imagen original para facilitar la comparación.

Primero convertimos la imagen a escala de grises. Esto ya lo sabemos hacer con el módulo ImageOps y la función grayscale() (img_gris = ImageOps.grayscale(img)), pero ahora lo haremos desde el módulo principal Image mediante la función convert() (la verdadera artífice de esta conversión, que requiere como parámetro el identificador del modo color, concretamente L para la conversión a escala de grises (img_gris = img.convert('L'))

El paquete completo de instrucciones incluye la identificación del modo, el visionado de la nueva imagen (que podremos comparar con la original) y su archivo como imagen.

#Escala de grises
img_gris = img.convert('L')
print(img_gris.mode)             -> Devuelve L
img_gris.show()
img_gris.save('img/grafgris.png') #Guardar imagen

Si tratamos de invertir el proceso (convertir L a color, lógicamente obtenemos de nuevo una imagen en escala de grises, pero su modo sí se modifica al especificado en la conversión:

  • convert('RGBA') -> Genera RGBA
  • convert('RGB') -> Genera RGB
Finalmente convertiremos RGBA a RGB como paso previo a convertir la imagen de extensión png (con transparencias) a jpg (sin transparencias)...

# De RGBA a RGB
img_cv = img.convert('RGB')
print(img_cv.mode)                 -> Devuelve RGB
img_cv.show()
img_cv.save('img/grafcv.jpg') #Guardar imagen

... obteniendo el mismo efecto que antes (escala de grises) si realizamos la conversión inversa, aunque ahora la diferencia entre la original RGBA y la RGBA reformulada es menos evidente a la vista.

MAV. Python


Pillow

Usos básicos (III)


Ahora vamos a trabajar con el color, la luz y la nitidez. Para ello necesitamos algunos módulos, concretamente ImageChops,  ImageOps e ImageEnhance.

 

No será ésta la única entrada que dediquemos a esta temática, dado que se presentan múltiples opciones, lo cual puede tener el efecto negativo de distraernos de nuestros objetivos, ya que sobrepasa de largo nuestras necesidades pero resulta muy atrayente. Trataré de minimizar este efecto, pero difícil será no caer en excesos.

Para no empezar pecando de palabrería, presento el script donde desarrollo cómo generar ciertos efectos relativos al color, la luminosidad y la nitidez. Después comentaré algunas cuestiones. Por cierto, la fuente está disponible en [este enlace]. Te recomiendo visualizarlo.

from PIL import Image, ImageChops, ImageEnhance, ImageOps

#Cargar imagen base
img_dir = 'img/grafico.jpg'
img = Image.open(img_dir)

#Leer sus atributos
print(img.format, img.size, img.mode)

#Ver imagen base
img.show()

#Modificaciones -------------------------------------------------------------

#Invertir colores
img_inv = ImageChops.invert(img)
img_inv.show()

#Escala de grises
img_gris = ImageOps.grayscale(img)
img_gris.show()

#Resaltar luces
img_luz = ImageEnhance.Brightness(img).enhance(5)
img_luz.show()

#Contraste
img_cont = ImageEnhance.Contrast(img).enhance(5)
img_cont.show()

#Disminuir nitidez
img_nitida = ImageEnhance.Sharpness(img).enhance(-10)
img_nitida.show()

Trato en él cinco efectos: la inversión del color, la conversión a escala de grises, la luminosidad, el contraste y la nitidez de la imagen. Para ello hacemos uso de los módulos ImageChops, ImageOps e ImageEnhance, éste último para las tres modificaciones de luz, contraste y nitidez mediante funciones que reciben como parámetro la imagen, pero con el añadido de una segunda función con un parámetro numérico de efecto ordinal o de grado de intensidad (del efecto).



jueves, 16 de octubre de 2025

MAV. Python


Pillow

Usos básicos (II)



Aprendimos a cargar y guardar una imagen, así que ahora nos toca aprender a realizar algunas manipulaciones que pueden sernos de utilidad cuando trabajamos con imágenes. Se me ocurren algunas.

   


Dando por resueltos los pasos previos y la carga de la imagen, empezaré por cambiar su tamaño (grande a pequeño, claro)...

img_pq = img.resize((400,300))
img_pq.show()
img_pq.save('img/paisaje_pq.png')

... para lo que empleamos la función resize() que contiene una tupla como parámetro, la cual define el tamaño de la imagen resultante (img_pq = img.resize((400,300)))

También disponemos de una función que genera directamente una imagen en miniatura, aunque también requiere definir el tamaño de esa miniatura.

mini = (100,100)
img.thumbnail(mini)
img.save('img/paisaje_mini.png')

Puede que necesitemos rotar la imagen, tarea para la que disponemos de varias funciones, posiblemente la más versátil rotate() que recibe como parámetro los grados de rotación.

img_giro = img.rotate(50)
img_giro.show()
img_giro.save('img/paisaje_giro.png')

También podemos rotar la imagen en espejo, aunque para ello necesitamos trabajar con el módulo ImageOps que deberemos importar (from PIL Import Image, ImageOps).

img_espejo = ImageOps.mirror(img)
img_espejo.show()
img_espejo.save('img/paisaje_espejo.png')

Otra acción que nos puede interesar es recortar un trozo de la imagen. Para ello definimos una tupla indicando la zona a recortar y después aplicamos la función crop(), usando la tupla como parámetro.

zona = (100,100,500,500)
parte_img = img.crop(zona)
parte_img.show()
parte_img.save('img/paisaje_parte.png')

Finalmente vamos a unir dos imágenes mediante el método paste(). Este procedimiento requiere una explicación un poco más elaborada que los anteriores, por lo que lo voy a presentar en un script nuevo.

from PIL import Image

#Creamos la imagen de base

final = Image.new("RGB",(1800,1600),"black")

#Cargar primera imagen

img_dir1 = 'img/paisaje.png'

img_a = Image.open(img_dir1)

#Cargar segunda imagen

img_dir2 = 'img/cara.png'

img_b = Image.open(img_dir2)

#Pegamos las imágenes

final.paste(img_a,(0,650))

final.paste(img_b,(1000,0))

#Mostramos la imagen resultante y la guardamos 

final.show()

final.save('img/unir.png')

Observa que empezamos creando una imagen (con función de contenedor, en realidad) mediante el método new() (final = Image.new("RGB",(1800,1600),"black")), el cual requiere varios parámetro: el sistema de color ("RGB"), el tamaño ((1800,1600)) y el color de fondo ("black").

Después cargamos las dos (en este caso) imágenes que deseamos unir (vg. img_a = Image.open(img_dir1)) y las pegamos sucesivamente sobre el contenedor (vg final.paste(img_a,(0,650))), para lo que empleamos el método paste() que recibe como parámetros la imagen a pegar (img_a) y la posición en la que se sitúa su ángulo superior izquierdo ((0,650)).

Espero que estas manipulaciones te sean de interés. Pillow dispone de muchas más, algunas de las cuales veremos en próximas entradas.