Skip to content

Latest commit

 

History

History
366 lines (255 loc) · 8.46 KB

README.md

File metadata and controls

366 lines (255 loc) · 8.46 KB

quadtree-image-compression

image image image image

Author: Andrew Gyakobo

Note

Each processed image file by default have to be in a form of a square in order to be processed

Warning

This project hasn't yet been tested as of late and is still in development. I'd personally love to add a pre-order traversal decoder to this project on par with the encoder program. I'd also love to compare to my other compression algorithm sparse matrix.

This project aims utilize a graph(a tree) as a form of two-dimensional image compression by implementing a special kind of data structure where it basically groups repeating variables into a node and ungroups non-repeating values into their own nodes.

Introduction

In this example specifically we shall be compressing a grayscale image. Given that pixels are usually stored as a series of integers, we would be using the int type to store said pixels.

Methodology

One method to compress an image is to store it in a tree data structure where each node would have up to 4 children. Each of them shall represent a select partition of the image in one of the four quartiles: northwest, northeast, southwest, and southeast. Needless to say, the root node marked with a -1 will either represent the entire image or another subset of partitions. The partitioning process will continue till each node in the partitioned subset carries the same color intensity.

  1. Let's consider the following image content from the given sample.txt:
3 3 3 3 2 2 1 1
3 3 3 3 2 2 1 1
3 3 3 3 0 5 1 0
3 3 3 3 0 0 0 2
1 1 1 1 1 1 0 3
1 1 1 1 1 1 0 0
1 1 1 1 5 5 0 0
1 1 1 1 5 5 0 0

Note

Each 8 lines in the sample file denotes a row in the image file

  1. Assuming all the pixels are not unique, we then partition the image into equal subsets of 4 by 4 smaller images as shown by the below:
3 3 3 3
3 3 3 3
3 3 3 3
3 3 3 3
2 2 1 1
2 2 1 1
0 5 1 0
0 0 0 2
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
1 1 0 3
1 1 0 0
5 5 0 0
5 5 0 0
  1. Afterwards we delve into the partitions more, and subsequently divide them into even smaller subsets, in this case 2 by 2 partitions. You may have noticed a recurring partain: each partition is in multiples of 2. From the getgo our partition is 8, then 4, then 2 and soon enough 1.
3 3 3 3
3 3 3 3
3 3 3 3
3 3 3 3
2 2
2 2
1 1
1 1
0 5
0 0
1 0
0 2
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
1 1
1 1
0 3
0 0
5 5
5 5
0 0
0 0
  1. As the last step we make quick work of all the last non-unique variables. Judging by the remaining 10 partitions there are only 3 subsets to worked on. This shall be our final partition.
3 3 3 3
3 3 3 3
3 3 3 3
3 3 3 3
2 2
2 2
1 1
1 1
0
5
0
0
1
0
0
2
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
1 1
1 1
0
3
0
0
5 5
5 5
0 0
0 0
  1. All this partitions can thereafter be presented in the following tree below. Each node the tree shall have four other nodes representing the four partitions (NW, NE, SW, and SE order). If however the partition fully consists of pixels of the same intensity, then the node would just be labeled with such intensity.

Note

Each circle denotes a node with 4 other leaf nodes. Each double circle is the 'end' node with no child leaf nodes(null references).

  1. Finally, the image can then be encoded in a sequence of integers by performing a preorder traversal of the corresponding:
-1 3 -1 2 1
-1 0 5 0 0
-1 1 0 0 2
1 -1 1 -1 0
3 0 0 5 0

You could of course change the traversal order by just switching the places of the preorder(root->children[n]) functions. Hence you could even easily simulate both inorder and postorder traversals or any other traversal you may come up with.

void preorder(struct Node *root) {
    printf("d: %d\n", root->intensity);

    if (root->intensity == -1) {
        preorder(root->children[0]);
        preorder(root->children[1]);
        preorder(root->children[2]);
        preorder(root->children[3]);
    } 
    else {
        free(root);
        return;
    } 
}

Code snippets

  • The code greets you with three structs: struct Bitmap, struct Node, struct Tree. The struct Bitmap allocates space for the image and stores all of the pixel values; the struct Node represents a node which references four other leaf nodes; lastly, the stuct Tree just represents the tree data structure with a pointer to the root of the tree.
struct Bitmap {
    int rows;
    int cols;
    short *pixels;
};

struct Node {
    short intensity;
    struct Node *children[4];
};

struct Tree {
    struct Node *root;
};
  • The struct Tree Tree_new(struct Bitmap *b) function along with the create_branches(...) function then creates the tree.

Performance

When it comes to performance the numbers pretty much vary. If for instance an image has little to no unique values then the compression would be at its best and utmost performance. Otherwise, the compression would have varying results.

In our case the input file weights 128 bytes, and when compressed, the output file weights just 56 bytes. Thus so far the compressed file halved the size of the original.

Important

Once again it's important to note that this compression algorithm is most prevalent when there are repeating elements in the image.

License

MIT