四叉树是在二维平面中存储和定位点数据的有效方法。四叉树的另一种有效用途是在图像处理领域。
与点存储不同,在图像处理中,我们得到一个完整的四叉树,其叶节点由图像的各个像素组成。因此,我们可以利用数组来存储树的节点。这样可以减少处理所需的内存(与链接表示相比)。
四叉树的最低级别将包含N个节点,这些节点等于图像中的像素数。下一级将包含N / 4个节点。
因此,可以通过以下公式找到所需的节点总数:N + N / 4 + N / 16 +…+ 1。
要获得上限,我们可以使用几何级数加和到无穷大的和
节点= N /(1 – 1/4)= 4/3 * N
这是必需的数组大小。
因此,所需的内存量为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
上面显示的Python中的代码演示了像素的类定义。
插入方式
与经典四叉树不同,对于图像处理,我们可以以O(N)时间复杂度插入所有节点。
首先,我们将所有叶节点直接插入数组的最后N个位置。以下代码段演示了这一点:
# 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
在上面的代码片段中,self.tree是节点的数组,self.size是数组的总大小,self.image是图像的像素,count用于遍历树。
对于不是叶子的节点,R,G,B值通过取子代值的平均值来计算。
通过获取子代x和y的最大值和最小值来获得topLeft和bottomRight。
# 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)
在这里我们可以看到,我们取了四个孩子的R,G,B的值,然后除以4,得到平均值。
假设孩子的值是已知的,这些值的计算将在O(1)时间中进行。由于我们以相反的顺序移动,因此,先计算孩子的值,再计算父母的值。
因此,插入发生在O(N)中。
在图像处理中的应用
让我们说我们希望将高质量的图像转换为缩略图。为图像创建四叉树后,通过选择四叉树的高度,我们可以选择所获得图像的质量。如果高度等于四叉树的高度,则我们保留原始图像。在较低的高度,我们获得的图像质量较差。
如果我们不希望降低图像质量,我们可以尝试通过所谓的“修剪”来压缩图像。在这种情况下,颜色接近其父本的叶子将被移除。持续进行此操作,直到无法移除其他叶子为止。然后仅使用叶节点获取最低级别的图像以形成图像。虽然这不会显着降低图像质量,但会导致图像压缩程度较小。
完整的代码:
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()