How to resolve the algorithm Color quantization step by step in the C programming language

Published on 7 June 2024 03:52 AM
#C

How to resolve the algorithm Color quantization step by step in the C programming language

Table of Contents

Problem Statement

Color quantization is the process of reducing number of colors used in an image while trying to maintain the visual appearance of the original image. In general, it is a form of cluster analysis, if each RGB color value is considered as a coordinate triple in the 3D colorspace. There are some well know algorithms [1], each with its own advantages and drawbacks. Task: Take an RGB color image and reduce its colors to some smaller number (< 256). For this task, use the frog as input and reduce colors to 16, and output the resulting colors. The chosen colors should be adaptive to the input image, meaning you should not use a fixed palette such as Web colors or Windows system palette. Dithering is not required. Note: the funny color bar on top of the frog image is intentional.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Color quantization step by step in the C programming language

This C code implements the palette quantization algorithm known as Octree Quantization.

It takes an image represented as an array of pixels and a target number of colors as input, and outputs a reduced color palette that closely approximates the original image while reducing its file size by decreasing the number of colors used.

Here is a summary of the code:

  • Octree Node:
    • The oct_node_t struct represents an octree node.
    • Each node stores the sum of colors, count of pixels, and other metadata.
    • Octree nodes are organized into an eight-way tree structure.
  • Heap Operations:
    • The code uses a binary heap to store Octree nodes, maintaining them in a specific order based on their properties.
    • The cmp_node function defines the comparison criteria for the heap.
  • Tree Construction and Color Insertion:
    • The node_insert function inserts a color triple into the Octree.
    • It traverses the tree based on the pixel's color values, creating nodes as needed.
    • Each node accumulates the color information of pixels in its subtree.
  • Tree Folding:
    • The node_fold function removes leaf nodes from the Octree.
    • It combines their color information with their parent node and updates the parent's count and child list.
  • Color Replacement:
    • The color_replace function traverses the Octree based on the pixel's color values.
    • For each pixel, it replaces its color with the color stored in the corresponding Octree node.
  • Palette Quantization:
    • The color_quant function builds an Octree from the image and maintains a heap of leaf nodes.
    • It iteratively folds the first node in the heap (the one with the least significant color information) into its parent and adds the parent to the heap.
    • This process continues until the desired number of colors is reached.
    • Finally, the code assigns the average color of each leaf node as the quantized color and replaces the original pixel colors with these quantized colors.

Overall, this code provides an efficient and effective method for reducing the number of colors in an image while preserving its visual quality.

Source code in the c programming language

typedef struct oct_node_t oct_node_t, *oct_node;
struct oct_node_t{
	/* sum of all colors represented by this node. 64 bit in case of HUGE image */
	uint64_t r, g, b;
	int count, heap_idx;
	oct_node kids[8], parent;
	unsigned char n_kids, kid_idx, flags, depth;
};

/* cmp function that decides the ordering in the heap.  This is how we determine
   which octree node to fold next, the heart of the algorithm. */
inline int cmp_node(oct_node a, oct_node b)
{
	if (a->n_kids < b->n_kids) return -1;
	if (a->n_kids > b->n_kids) return 1;

	int ac = a->count * (1 + a->kid_idx) >> a->depth;
	int bc = b->count * (1 + b->kid_idx) >> b->depth;
	return ac < bc ? -1 : ac > bc;
}

/* adding a color triple to octree */
oct_node node_insert(oct_node root, unsigned char *pix)
{
#	define OCT_DEPTH 8
	/* 8: number of significant bits used for tree.  It's probably good enough
	   for most images to use a value of 5.  This affects how many nodes eventually
	   end up in the tree and heap, thus smaller values helps with both speed
	   and memory. */

	unsigned char i, bit, depth = 0;
	for (bit = 1 << 7; ++depth < OCT_DEPTH; bit >>= 1) {
		i = !!(pix[1] & bit) * 4 + !!(pix[0] & bit) * 2 + !!(pix[2] & bit);
		if (!root->kids[i])
			root->kids[i] = node_new(i, depth, root);

		root = root->kids[i];
	}

	root->r += pix[0];
	root->g += pix[1];
	root->b += pix[2];
	root->count++;
	return root;
}

/* remove a node in octree and add its count and colors to parent node. */
oct_node node_fold(oct_node p)
{
	if (p->n_kids) abort();
	oct_node q = p->parent;
	q->count += p->count;

	q->r += p->r;
	q->g += p->g;
	q->b += p->b;
	q->n_kids --;
	q->kids[p->kid_idx] = 0;
	return q;
}

/* traverse the octree just like construction, but this time we replace the pixel
   color with color stored in the tree node */
void color_replace(oct_node root, unsigned char *pix)
{
	unsigned char i, bit;

	for (bit = 1 << 7; bit; bit >>= 1) {
		i = !!(pix[1] & bit) * 4 + !!(pix[0] & bit) * 2 + !!(pix[2] & bit);
		if (!root->kids[i]) break;
		root = root->kids[i];
	}

	pix[0] = root->r;
	pix[1] = root->g;
	pix[2] = root->b;
}

/* Building an octree and keep leaf nodes in a bin heap.  Afterwards remove first node
   in heap and fold it into its parent node (which may now be added to heap), until heap
   contains required number of colors. */
void color_quant(image im, int n_colors)
{
	int i;
	unsigned char *pix = im->pix;
	node_heap heap = { 0, 0, 0 };

	oct_node root = node_new(0, 0, 0), got;
	for (i = 0; i < im->w * im->h; i++, pix += 3)
		heap_add(&heap, node_insert(root, pix));

	while (heap.n > n_colors + 1)
		heap_add(&heap, node_fold(pop_heap(&heap)));

	double c;
	for (i = 1; i < heap.n; i++) {
		got = heap.buf[i];
		c = got->count;
		got->r = got->r / c + .5;
		got->g = got->g / c + .5;
		got->b = got->b / c + .5;
		printf("%2d | %3llu %3llu %3llu (%d pixels)\n",
			i, got->r, got->g, got->b, got->count);
	}

	for (i = 0, pix = im->pix; i < im->w * im->h; i++, pix += 3)
		color_replace(root, pix);

	node_free();
	free(heap.buf);
}


  

You may also check:How to resolve the algorithm Find if a point is within a triangle step by step in the Dart programming language
You may also check:How to resolve the algorithm Roman numerals/Decode step by step in the Fortran programming language
You may also check:How to resolve the algorithm Arena storage pool step by step in the PARI/GP programming language
You may also check:How to resolve the algorithm Ackermann function step by step in the M4 programming language
You may also check:How to resolve the algorithm Element-wise operations step by step in the jq programming language