Manipulación de imágenes usando Quadtrees

Los quadtrees son un método eficaz para almacenar y localizar datos de puntos en un plano bidimensional. Otro uso efectivo de quadtrees es en el campo de la manipulación de imágenes.

A diferencia del almacenamiento de puntos, en la manipulación de imágenes obtenemos un quadtree completo con los Nodes hoja que consisten en píxeles individuales de la imagen. Debido a esto, podemos utilizar una array para almacenar los Nodes del árbol. Esto lleva a que se necesite menos memoria (en comparación con la representación vinculada) para el procesamiento.

El nivel más bajo del quadtree contendría N Nodes equivalentes al número de píxeles de la imagen. El siguiente nivel contendría N/4 Nodes.
Por lo tanto, el número total de Nodes necesarios se puede encontrar mediante: N + N / 4 + N / 16 + … + 1.
Para obtener un límite superior podemos usar la suma de la progresión geométrica hasta el infinito
Nodes = N / (1 – 1 / 4 ) = 4 / 3 * N
Este es el tamaño de la array necesaria.
Por lo tanto, la cantidad de memoria necesaria es O(N)

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
class Pixel(object):
    def __init__(self, color = [0, 0, 0], 
            topLeft = Point(0, 0), 
            bottomRight = Point(0, 0)):
        self.R = color[0]
        self.G = color[1]
        self.B = color[2]
        self.topLeft = topLeft
        self.bottomRight = bottomRight

El código anterior en Python que se muestra demuestra la definición de clase de un píxel.

Inserción
A diferencia de un quadtree clásico, para la manipulación de imágenes, podemos insertar todos los Nodes en complejidad de tiempo O(N).
Primero, insertamos todos los Nodes hoja directamente en las últimas N posiciones de la array. El siguiente fragmento de código demuestra esto:

# Store leaves into array
count = 0
for i in range(image.size[0] - 1, 0, -2):
    for j in range(image.size[1] - 1, 0, -2):
        self.tree[self.size - 1 - 4 * count] = Pixel(self.image[i, j], 
                Point(i, j), 
                Point(i, j))
        self.tree[self.size - 2 - 4 * count] = Pixel(self.image[i, j - 1], 
                Point(i, j - 1),
                Point(i, j - 1))
        self.tree[self.size - 3 - 4 * count] = Pixel(self.image[i - 1, j], 
                Point(i - 1, j), 
                Point(i - 1, j))
        self.tree[self.size - 4 - 4 * count] = Pixel(self.image[i - 1, j - 1], 
                Point(i - 1, j - 1), 
                Point(i - 1, j - 1))
        count += 1

En el fragmento anterior, self.tree es la array de Nodes, self.size es el tamaño total de la array y self.image son los píxeles de la imagen, se usa count para recorrer el árbol.

Para los Nodes que no son hojas, los valores R, G, B se calculan tomando el promedio de los valores de los hijos.
topLeft y bottomRight se obtienen tomando los valores máximo y mínimo de x e y de los hijos.

# Calculate and create parent nodes
for i in range(self.size - 4 * count - 1, -1, -1):
    self.tree[i] = Pixel(
        [(self.tree[4 * i + 1].R + self.tree[4 * i + 2].R + self.tree[4 * i + 3].R + self.tree[4 * i + 4].R) / 4,
         (self.tree[4 * i + 1].G + self.tree[4 * i + 2].G + self.tree[4 * i + 3].G + self.tree[4 * i + 4].G) / 4,
         (self.tree[4 * i + 1].B + self.tree[4 * i + 2].B + self.tree[4 * i + 3].B + self.tree[4 * i + 4].B) / 4],
        self.tree[4 * i + 1].topLeft,
        self.tree[4 * i + 4].bottomRight)

Aquí podemos ver, tomamos los valores de R, G, B de los cuatro niños y los dividimos por 4, obteniendo así el promedio.

El cálculo de estos valores ocurre en el tiempo O(1) asumiendo que se conocen los valores de los niños. Como nos estamos moviendo en orden inverso, los valores de los hijos se calculan antes que los de los padres.
Por lo tanto, la inserción se produce en O (N).

Aplicación en la manipulación de imágenes
Digamos que deseamos convertir una imagen de alta calidad en una miniatura . Una vez que hemos creado un quadtree para la imagen, seleccionando una altura del quadtree podemos seleccionar la calidad de la imagen que obtenemos. Si la altura es igual a la altura del quadtree, conservamos la imagen original. A menor altura obtenemos imágenes de menor calidad.

En caso de que no queramos reducir la calidad de la imagen, podemos intentar comprimir la imagen mediante lo que se conoce como “poda”. En este se eliminan las hojas con colores cercanos al de sus progenitores. Esto se hace continuamente hasta que no se puedan quitar más hojas. Luego se toma el nivel más bajo para formar la imagen, utilizando solo Nodes de hoja. Si bien esto no reduce drásticamente la calidad de la imagen, puede conducir a una pequeña compresión de la imagen.

Código completo:

from PIL import Image
import math
class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
  
class Pixel(object):
    def __init__(self, color = [0, 0, 0], 
            topLeft = Point(0, 0), 
            bottomRight = Point(0, 0)):
        self.R = color[0]
        self.G = color[1]
        self.B = color[2]
        self.topLeft = topLeft
        self.bottomRight = bottomRight
  
class quadtree():
    def __init__(self, image):
  
        # Total number of nodes of tree
        self.size = 0
  
        # Store image pixelmap
        self.image = image.load()
  
        # Array of nodes
        self.tree = []
        self.x = image.size[0]
        self.y = image.size[1]
  
        # Total number of leaf nodes
        size = image.size[0] * image.size[1]
  
        # Count number of nodes
        while(size >= 1):
            self.size += size
            size /= 4
  
        size = image.size[0] * image.size[1]
  
        # Initialize array elements
        for i in range(0, self.size):
            self.tree.append(Pixel())
  
        # Store leaves into array
        count = 0
        for i in range(image.size[0] - 1, 0, -2):
            for j in range(image.size[1] - 1, 0, -2):
                self.tree[self.size - 1 - 4 * count] = Pixel(self.image[i, j], 
                        Point(i, j), 
                        Point(i, j))
                self.tree[self.size - 2 - 4 * count] = Pixel(self.image[i, j - 1], 
                        Point(i, j - 1),
                        Point(i, j - 1))
                self.tree[self.size - 3 - 4 * count] = Pixel(self.image[i - 1, j], 
                        Point(i - 1, j), 
                        Point(i - 1, j))
                self.tree[self.size - 4 - 4 * count] = Pixel(self.image[i - 1, j - 1], 
                        Point(i - 1, j - 1), 
                        Point(i - 1, j - 1))
                count += 1
  
        # Calculate and create parent nodes
        for i in range(self.size - 4 * count - 1, -1, -1):
            self.tree[i] = Pixel(
                [(self.tree[4 * i + 1].R + self.tree[4 * i + 2].R + self.tree[4 * i + 3].R + self.tree[4 * i + 4].R) / 4,
                 (self.tree[4 * i + 1].G + self.tree[4 * i + 2].G + self.tree[4 * i + 3].G + self.tree[4 * i + 4].G) / 4,
                 (self.tree[4 * i + 1].B + self.tree[4 * i + 2].B + self.tree[4 * i + 3].B + self.tree[4 * i + 4].B) / 4],
                self.tree[4 * i + 1].topLeft,
                self.tree[4 * i + 4].bottomRight)
  
    def disp(self, level):
        start = 0
  
        # Calculate position of starting node of height
        for i in range(0, level):
            start = 4 * start + 1
  
        # Invalid height given
        if (start > self.size):
            return
  
        # Create a new image
        img = Image.new("RGB", (self.x, self.y), "black")
        pixels = img.load()
  
        # Move from starting to last node on given height
        for i in self.tree[start : 4 * start]:
            x1 = i.topLeft.x
            y1 = i.topLeft.y
            x2 = i.bottomRight.x
            y2 = i.bottomRight.y
            for x in range(x1, x2 + 1):
                for y in range(y1, y2 + 1):
  
                    # Set colour
                    pixels[x, y] = (i.R, i.G, i.B)
  
        # Display image
        img.show()

Este artículo es una contribución de Aditya Kamath . Si le gusta GeeksforGeeks y le gustaría contribuir, también puede escribir un artículo usando contribuya.geeksforgeeks.org o envíe su artículo por correo a contribuya@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.

Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *