{"id":19989758,"url":"https://github.com/inspiaaa/quadtreeimagecompression","last_synced_at":"2025-07-25T02:38:51.332Z","repository":{"id":108960339,"uuid":"584864404","full_name":"Inspiaaa/QuadTreeImageCompression","owner":"Inspiaaa","description":"Powerful image compression algorithm based on quadtrees","archived":false,"fork":false,"pushed_at":"2023-08-30T21:12:59.000Z","size":36276,"stargazers_count":29,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-30T14:05:06.722Z","etag":null,"topics":["binary","compression","image-compression","image-processing","numpy","python","quadtree","scikit-image"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Inspiaaa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-01-03T18:00:25.000Z","updated_at":"2025-04-01T05:15:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"395c0889-a419-4540-9782-6b652126e398","html_url":"https://github.com/Inspiaaa/QuadTreeImageCompression","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Inspiaaa/QuadTreeImageCompression","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FQuadTreeImageCompression","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FQuadTreeImageCompression/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FQuadTreeImageCompression/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FQuadTreeImageCompression/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Inspiaaa","download_url":"https://codeload.github.com/Inspiaaa/QuadTreeImageCompression/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Inspiaaa%2FQuadTreeImageCompression/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266945059,"owners_count":24010491,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-25T02:00:09.625Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["binary","compression","image-compression","image-processing","numpy","python","quadtree","scikit-image"],"created_at":"2024-11-13T04:50:02.228Z","updated_at":"2025-07-25T02:38:51.322Z","avatar_url":"https://github.com/Inspiaaa.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Quadtree Image Compression\n\nThis library implements an image compression algorithm that is based on quadtrees. It can radically reduce the size of images while still preserving detail.\n\nFeatures:\n\n- **Compressing** images and **rendering** the simplified version\n\n- **Encoding** the compressed data to a compact binary representation\n\n- **Decoding** the binary and reconstructing the image\n\nThe algorithm works by starting with an empty image and **incrementally adding detail** where it is important. At the beginning the compressed image is filled with the average color of the original image. Then, it recursively **subdivides** the regions that have the most detail into 4 quads that each have the average color of the area they represent in the original image.\n\nhttps://user-images.githubusercontent.com/28511584/211117608-29ff4349-64de-4250-a7fa-931b76a1392b.mp4\n\nHow does the algorithm determine the **amount of detail** in a given quad region? The metric used is the **standard deviation** of the colors of the pixels in the region, multiplied by the **size** of the region (simply the number of pixels `width * height`). If all the pixels have the same color, then the standard deviation is 0, meaning that it does not need to be divided any further. If there are many different colors over a large area, then the detail metric will have a high value.\n\n## Examples\n\n| 100 Iterations           | 1,000 Iterations          | 20,000 Iterations          |\n| ------------------------ | ------------------------- | -------------------------- |\n| ![](docs/night_100.jpg)  | ![](docs/night_1000.jpg)  | ![](docs/night_20000.jpg)  |\n| ![](docs/plant_100.jpg)  | ![](docs/plant_1000.jpg)  | ![](docs/plant_20000.jpg)  |\n| ![](docs/sunset_100.jpg) | ![](docs/sunset_1000.jpg) | ![](docs/sunset_20000.jpg) |\n\nTo see the compressed size of these images and other interesting facts about them, scroll down to the *Benchmark* section.\n\n## Usage\n\nTo use the quadtree image compression algorithm, simply copy the `quad_tree_compression.py` file and import it into your scripts. It requires `numpy`, `Pillow`, `tqdm` and `sortedcontainers` to be installed. If you also want to run the image benchmark (`benchmark.py`), in addition you will need `tabulate` and `scikit-image` (which is used for analysing the input image).\n\nThe `quad_tree_compression` file provides easy helper functions for performing common operations (such as compressing and loading images) but also gives you access to the underlying classes.\n\n**Compressing and loading an image:**\n\n```python\nfrom quad_tree_compression import compress_image_file, reconstruct_image_from_file\n\n# Compress the image and encode is a binary file (any file extension can be chosen)\ncompress_image_file(\"input/mountain.jpg\", \"output/mountain_qt.qid\", iterations=20_000)\n\n# Reconstruct the image from the binary file. (Returns a PIL.Image object)\nimage = reconstruct_image_from_file(\"output/mountain_qt.qid\")\nimage.show()\n```\n\n**Using the compressed image data (a numpy array) directly:**\n\n```python\nfrom quad_tree_compression import compress_image_data\nfrom PIL import Image\nimport numpy as np\n\n# Load the image and convert it to a numpy array\nimage = Image.open(\"input/mountain.jpg\")\nimage_data = np.array(image)\n\n# Compress the image\ncompressed_data = compress_image_data(image_data, iterations=20_000)\n\n# Show the simplified image\ncompressed_image = Image.fromarray(compressed_data)\ncompressed_image.show()\n```\n\n**Working with the binary representation directly:**\n\n```python\nfrom quad_tree_compression import compress_and_encode_image_data, reconstruct_image_data\nfrom PIL import Image\nimport numpy as np\n\n# Load the image and convert it to a numpy array\nimage = Image.open(\"input/mountain.jpg\")\nimage_data = np.array(image)\n\n# Compress the image and encode it to the binary representation (a \"bytes\" object).\ncompressed_binary = compress_and_encode_image_data(image_data, iterations=20_000)\n\n# Decode the compressed binary and convert it to a numpy array\ncompressed_image_data = reconstruct_image_data(compressed_binary)\n\n# Show the simplified image\ncompressed_image = Image.fromarray(compressed_image_data)\ncompressed_image.show()\n```\n\n**Advanced: interacting with the image compressing class directly:**\n\n```python\nfrom quad_tree_compression import ImageCompressor\nfrom PIL import Image\nimport numpy as np\n\n# Load the image and convert it to a numpy array\nimage = Image.open(\"input/mountain.jpg\")\nimage_data = np.array(image)\n\n# Create a new ImageCompressor which allows you to incrementally add detail\ncompressor = ImageCompressor(image_data)\n\n# Perform 10000 iterations\ncompressor.add_detail(10_000)\n\n# Render the compressed image to a numpy array and display it\ncompressed_data = compressor.draw()\ncompressed_image = Image.fromarray(compressed_data)\ncompressed_image.show()\n\n# Perform another 50000 iterations (total is 60000 then)\ncompressor.add_detail(50_000)\n\n# Convert the output to a compressed binary representation\ncompressed_binary = compressor.encode_to_binary()\n```\n\n**Advanced: interacting with the underlying quadtree data structure:**\n\nInternally, there are three classes that are used for compressing and reconstructing images. The base class `QuadTreeNode` takes care of positioning, sizing and subdividing. When compressing the image, the `CompressNode` class is used (which inherits from `QuadTreeNode`). When reconstructing the image, the `ReconstructNode` class is used (which also inherits from `QuadTreeNode`).\n\n```python\nfrom quad_tree_compression import ImageCompressor, reconstruct_quadtree\nfrom PIL import Image\nimport numpy as np\n\n# ...\n\ncompressor = ImageCompressor(image_data)\ncompressor.add_detail(10_000)\n\n# You can access the compression quadtree directly \n# (type: CompressNode which is a subclass of the QuadTreeNode class)\ntree = compressor.root_node\nprint(tree)\nprint(tree.top_left_node)\nprint(tree.color)\n\n# ...\n\n# When you are loadinng the tree from the binary representation, you \n# can also access the quadtree:\n# (type: ReconstructNode which is a subclass of the QuadTreeNode class)\ntree = reconstruct_quadtree(compressed_binary_data)\nprint(tree)\n```\n\n## Binary Representation\n\nThis library uses a **custom binary representation** to minimise the output file size. To reconstruct the image, it needs to store the structure of the quadtree (which nodes are subdivided, their positions, ...) and the colors of the nodes.\n\nHowever, a few tricks can be used to minimise the resulting file size:\n\n- The algorithm only needs to store **whether each node in the tree is subdivided or not**. Their exact **position and size can be reconstructed** from the structure of the tree when loading the tree. The algorithm simply performs a preorder traversal over the quadtree, storing their `is_subdivided` flag. As this is a boolean, using an entire byte to store it would be incredibly inefficient, wasting 87.5% of the space. Therefore they are stored as individual bits of a **bitset**.\n\n- **Only the leaf nodes of the tree are drawn**. Therefore only the colors of these need to be stored.\n\n- The combined data can be further compressed using **general-purpose compression algorithms** (`lzma` in this case).\n\nIn the end, the following information is stored (before general-purpose compression):\n\n- **Width** of the image (4 bytes)\n\n- **Height** of the image (4 bytes)\n\n- **Bitset** containing the `is_subdivided` flags (4 bytes for the length, and 1 byte per 8 nodes)\n\n- **Colors** of the leaf nodes (3 bytes for RGB per leaf node)\n\n## Benchmark\n\nHow good is the quadtree algorithm at compressing images? To try to answer this question, we can have a look at different aspects and test the algorithm on a variety of images.\n\nTo measure the **compression ratio**, we can compare it with the size of a PNG or JPEG file storing the same image.\n\nFurthermore, it would be interesting to quantify the **compression \"quality\"**, seeing how similar it is to the original image. The benchmark (`benchmark.py`) uses the average of the **mean absolute error** (MAE) of each channel (red, green and blue). This value is easy to interpret, as it shows how far the red, green and blue values of each pixel are from the original on average (the color values range from 0-255).\n\nHowever, this value can be **misleading**, as some compressed images have a better (lower) MAE value than other compressed images which subjectively look better. For example, if an image only uses red colors (one of the three channels) the MAE at a low iteration count will have a comparatively small value. The MAE of an image that uses all three channels but was compressed using a higher iteration count may be higher (worse!) than that of the \"simpler\" image.\n\nTherefore it helps to estimate the **image difficulty**. There are two aspects that influence how challenging an image is to compress:\n\n- The usage of a wide range of different **colors**, a high dynamic range, ..., which is measured as the **entropy of the histogram** of the image (more precisely: the average entropy of the histogram of each channel).\n\n- Furthermore, the complexity of the structures and arrangement of colors plays an important role. Although an image with white noise has the same entropy value regarding its histogram as a smooth gradient image, they are clearly not equally easy to compress.\n  \n  | Noise                   | Gradient               |\n  | ----------------------- | ---------------------- |\n  | ![](docs/noise.jpg)     | ![](docs/gradient.jpg) |\n  | Histogram Entropy: 7.99 | Histogram Entropy: 8.0 |\n  \n  Therefore, the benchmark calculates the **local entropy of each region** in the image using `scikit-image` and computes the average.\n  \n  | Noise                       | Gradient                       |\n  | --------------------------- | ------------------------------ |\n  | ![](docs/noise_entropy.jpg) | ![](docs/gradient_entropy.jpg) |\n  | Mean Local Entropy: 6.02    | Mean Local Entropy: 3.26       |\n  \n  The following image shows the local entropy map of a more realistic test image depicting a mountain:\n  \n  ![](docs/mountain_entropy.jpg)\n  \n  The mean local entropy score calculated from this image is 1.339.\n\n### Benchmark Results\n\n| Original             | 80,000 Iterations          |\n| -------------------- | -------------------------- |\n| ![](docs/branch.jpg) | ![](docs/branch_80000.jpg) |\n\n```\n----------  ------\n     Width    3712\n    Height    4640\nResolution  17.2MP\n----------  ------\n```\n\nMetrics of difficulty (0 = empty image; the higher, the more difficult):\n\n```\n------------------  -----\nMean Local Entropy  4.305\nHistogram Entropy   7.607\n------------------  -----\n```\n\n```\n        File Type    Size (KB)\n-----------------  -----------\n              PNG     17,372.9\nJPG (90% quality)      3,811.6\n```\n\n```\n  Iterations    Compressed    Mean Average    Size Reduction    Size Reduction    Compression    Compression\n                 Size (KB)           Error           PNG (%)           JPG (%)     Factor PNG     Factor JPG\n------------  ------------  --------------  ----------------  ----------------  -------------  -------------\n         100          0.93           14.04             99.99             99.98       18720.8         4107.28\n        1000          7.68            8.79             99.96             99.8         2260.92         496.04\n       20000         93.09            5.76             99.46             97.56         186.63          40.95\n       80000        307.03            5.17             98.23             91.94          56.58          12.41\n```\n\n| Original               | 80,000 Iterations            |\n| ---------------------- | ---------------------------- |\n| ![](docs/mountain.jpg) | ![](docs/mountain_80000.jpg) |\n\n```\n----------  ------\n     Width    5472\n    Height    3648\nResolution  20.0MP\n----------  ------\n```\n\n```\n------------------  -----\nMean Local Entropy  1.339\nHistogram Entropy   6.69\n------------------  -----\n```\n\n```\n        File Type    Size (KB)\n-----------------  -----------\n              PNG      5,611.4\nJPG (90% quality)        993.6\n```\n\n```\n  Iterations    Compressed    Mean Average    Size Reduction    Size Reduction    Compression    Compression\n                 Size (KB)           Error           PNG (%)           JPG (%)     Factor PNG     Factor JPG\n------------  ------------  --------------  ----------------  ----------------  -------------  -------------\n         100          0.94            7.77             99.98             99.91        5995.12        1061.51\n        1000          7.85            4.15             99.86             99.21         714.65         126.54\n       20000        134               1.62             97.61             86.51          41.88           7.41\n       80000        471.68            1.01             91.59             52.53          11.9            2.11\n```\n\n| Original             | 80,000 Iterations          |\n| -------------------- | -------------------------- |\n| ![](docs/hiking.jpg) | ![](docs/hiking_80000.jpg) |\n\n```\n----------  ------\n     Width    3744\n    Height    5616\nResolution  21.0MP\n----------  ------\n```\n\n```\n------------------  -----\nMean Local Entropy  5.039\nHistogram Entropy   7.452\n------------------  -----\n```\n\n```\n        File Type    Size (KB)\n-----------------  -----------\n              PNG     34,908.6\nJPG (90% quality)      7,372.6\n```\n\n```\n  Iterations    Compressed    Mean Average    Size Reduction    Size Reduction    Compression    Compression\n                 Size (KB)           Error           PNG (%)           JPG (%)     Factor PNG     Factor JPG\n------------  ------------  --------------  ----------------  ----------------  -------------  -------------\n         100          0.92           19.67            100                99.99       38109.8         8048.69\n        1000          7.44           15.8              99.98             99.9         4694.54         991.47\n       20000        127.7            12.58             99.63             98.27         273.36          57.73\n       80000        469.73           11.36             98.65             93.63          74.32          15.7\n```\n\n| Original             | 80,000 Iterations          |\n| -------------------- | -------------------------- |\n| ![](docs/sunset.jpg) | ![](docs/sunset_80000.jpg) |\n\n```\n----------  ------\n     Width    3957\n    Height    6240\nResolution  24.7MP\n----------  ------\n```\n\n```\n------------------  -----\nMean Local Entropy  2.475\nHistogram Entropy   5.321\n------------------  -----\n```\n\n```\n        File Type    Size (KB)\n-----------------  -----------\n              PNG     15,051.9\nJPG (90% quality)      3,259.4\n```\n\n```\n  Iterations    Compressed    Mean Average    Size Reduction    Size Reduction    Compression    Compression\n                 Size (KB)           Error           PNG (%)           JPG (%)     Factor PNG     Factor JPG\n------------  ------------  --------------  ----------------  ----------------  -------------  -------------\n         100          0.86            4.78             99.99             99.97       17502.3         3790.03\n        1000          5.75            2.99             99.96             99.82        2618.64         567.05\n       20000         71.04            2.62             99.53             97.82         211.87          45.88\n       80000        254.64            2.55             98.31             92.19          59.11          12.8\n```\n\n| Original              | 80,000 Iterations           |\n| --------------------- | --------------------------- |\n| ![](docs/squares.png) | ![](docs/squares_80000.jpg) |\n\n```\n----------  ------\n     Width    5472\n    Height    3648\nResolution  20.0MP\n----------  ------\n```\n\n```\n------------------  -----\nMean Local Entropy  0.014\nHistogram Entropy   1.374\n------------------  -----\n```\n\n```\n        File Type    Size (KB)\n-----------------  -----------\n              PNG         68.4\nJPG (90% quality)        393.4\n```\n\n```\n  Iterations    Compressed    Mean Average    Size Reduction    Size Reduction    Compression    Compression\n                 Size (KB)           Error           PNG (%)           JPG (%)     Factor PNG     Factor JPG\n------------  ------------  --------------  ----------------  ----------------  -------------  -------------\n         100          0.56            5.24             99.19             99.86         122.97         707.58\n        1000          1.58            0.62             97.69             99.6           43.38         249.63\n       20000          3.58            0                94.77             99.09          19.12         110.01\n       80000          3.58            0                94.77             99.09          19.12         110.01\n```\n\n| Original            | 80,000 Iterations         |\n| ------------------- | ------------------------- |\n| ![](docs/night.jpg) | ![](docs/night_80000.jpg) |\n\n```\n----------  ------\n     Width    4160\n    Height    6240\nResolution  26.0MP\n----------  ------\n```\n\n```\n------------------  -----\nMean Local Entropy  3.438\nHistogram Entropy   5.722\n------------------  -----\n```\n\n```\n        File Type    Size (KB)\n-----------------  -----------\n              PNG     25,385.6\nJPG (90% quality)      3,886.6\n```\n\n```\n  Iterations    Compressed    Mean Average    Size Reduction    Size Reduction    Compression    Compression\n                 Size (KB)           Error           PNG (%)           JPG (%)     Factor PNG     Factor JPG\n------------  ------------  --------------  ----------------  ----------------  -------------  -------------\n         100          0.88            7.73            100                99.98       28716.8         4396.62\n        1000          6.64            4.54             99.97             99.83        3825.44         585.69\n       20000         83.59            2.73             99.67             97.85         303.7           46.5\n       80000        286.11            2.4              98.87             92.64          88.73          13.58\n```\n\n| Original            | 80,000 Iterations         |\n| ------------------- | ------------------------- |\n| ![](docs/plant.jpg) | ![](docs/plant_80000.jpg) |\n\n```\n----------  ------\n     Width    4000\n    Height    6000\nResolution  24.0MP\n----------  ------\n```\n\n```\n------------------  -----\nMean Local Entropy  2\nHistogram Entropy   5.868\n------------------  -----\n```\n\n```\n        File Type    Size (KB)\n-----------------  -----------\n              PNG     13,039.2\nJPG (90% quality)      1,774.4\n```\n\n```\n  Iterations    Compressed    Mean Average    Size Reduction    Size Reduction    Compression    Compression\n                 Size (KB)           Error           PNG (%)           JPG (%)     Factor PNG     Factor JPG\n------------  ------------  --------------  ----------------  ----------------  -------------  -------------\n         100          0.83           11.48             99.99             99.95       15672.1         2132.64\n        1000          7.06            6.8              99.95             99.6         1847.96         251.47\n       20000        128.21            2.66             99.02             92.77         101.7           13.84\n       80000        485.07            1.67             96.28             72.66          26.88           3.66\n```\n\n| Original               | 80,000 Iterations            |\n| ---------------------- | ---------------------------- |\n| ![](docs/penguins.jpg) | ![](docs/penguins_80000.jpg) |\n\n```\n----------  ------\n     Width    4039\n    Height    6058\nResolution  24.5MP\n----------  ------\n```\n\n```\n------------------  -----\nMean Local Entropy  3.044\nHistogram Entropy   6.514\n------------------  -----\n```\n\n```\n        File Type    Size (KB)\n-----------------  -----------\n              PNG     23,573.7\nJPG (90% quality)      5,023.5\n```\n\n```\n  Iterations    Compressed    Mean Average    Size Reduction    Size Reduction    Compression    Compression\n                 Size (KB)           Error           PNG (%)           JPG (%)     Factor PNG     Factor JPG\n------------  ------------  --------------  ----------------  ----------------  -------------  -------------\n         100          0.92           20.28            100                99.98       25735.5         5484.15\n        1000          7.46           12.98             99.97             99.85        3161.71         673.75\n       20000        132.92            9.16             99.44             97.35         177.36          37.79\n       80000        504.67            7.21             97.86             89.95          46.71           9.95\n```\n\n## Credits\n\nAll sample images sourced and credited to Unsplash. See `input/credits.txt` for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finspiaaa%2Fquadtreeimagecompression","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finspiaaa%2Fquadtreeimagecompression","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finspiaaa%2Fquadtreeimagecompression/lists"}