Dynamic Planning - Example 7. Image Compression

1. Title Description

A digitized image is an n*n array of pixels. Assuming that each pixel has a gray value of 0-255, it requires at most 8 bits to store a pixel.
To reduce storage space, a variable-length mode is used, that is, different pixels are stored in different bits, as follows:

  • Image Linearization: Convert a nn-dimensional image to an N2 vector {p1,p2,...pn^2}
  • Segmentation: divide the pixels into consecutive m-segments s1,s2,...sm so that the pixel storage bits in each segment are the same. Each segment is a collection of adjacent pixels and each segment contains a maximum of 256 pixels. If the pixels of the same bit exceed 256, it is represented by more than two segments.
  • Create three tables
    l: l[i] stores the length of section i, each item in the table is 8 bits long
    B: b[i] stores the number of bits of pixels in the first segment, each of which is 3 bits long in the table.
    P: {p1,...p n2} Binary string of pixels stored in variable length format.

If m segments are created, the space required to store the pixels in segment I is: **l[i] * b[i] + 11 **
(l[i] * b[i] denotes the information required by the segment itself, and 11 denotes the length of the segment, l[i], and how many bits each of the pixels represents b[i], that is, 3 + 8 = 11 bits)
The total storage space is 11m+l[i]*b[i];
The problem requires an optimal segment to be found. Minimize storage space.

Example:
Examine 4*4 images

Let's optimize step by step:
1. No segmentation, no variable length compression.
Gray value sequence P:
00001010 00001001 00001100 00111000 00110010 00100011...
Total storage is:
16 * 8 = 128 bits

(2) Segments: [10, 9, 12], [40, 50, 35], [15, 12, 8, 10, 9, 15, 11], [130, 160, 240]
L={3,3, 7,3};B={4,6,4,8};
P contains 16 gray values, of which the first three are stored in 4 bits, the next three in 6 bits, the next seven in 4 bits, and the last three in 8 bits.
1010 1001 1100 111000 110010 100011 ...

The three tables require storage space:
L: 32 bits (4 * 8)
B: 12 bits (3 * 4)
P: 82 bits (4 * 3 + 6 * 3 + 4 * 7 + 8 * 3)
Total storage: 32 + 12 + 82 = 126 bits

(3) If we merge paragraphs 1 and 2, the corresponding three tables are updated to:
L={6, 7, 3}; B={6, 4, 8};
P: 001010 001001 001100 111000 110010 100011...
The rest remains unchanged.
The storage space of the three tables becomes:
L: 24 bits (3 * 8)
B: 9 bits (3 * 3)
P: 88 bits (6 * 6 + 4 * 7 + 8 * 3)
Total storage: 24 + 9 + 88 = 121 bits (reduced by 5 bits)

Therefore, the problem of image compression is to find an optimal segmentation scheme to minimize the total storage space.

2. Solving problems

The idea of the algorithm is to turn it into a multi-step decision-making process, which first finds the optimal segment with one segment, then finds the optimal segment with two segments, and then finds the optimal segment with three segments.

1. Optimal solution structure:

Set the best merge of N segments to C. If in merge C, the n-th segment is merged with n-1,n-2,...n-k+1 segments, then the merge of n-1,n-2,...n-k+1 segments is also optimal.
The space required for optimal merge C is:
Optimal merge space + Lsum(n-k+1,n) * Bmax(n-k+1,n)+11 for the first to n-k segments
Where Lsum(a, b)= , Bmax(a, b) = max{ B[a],..., B[b] }

2. Recursive calculation of optimal values

If s[i] i s the best combined storage number for the first I segments, then:
s[i] =
{s[i-k]+ lsum(i-k+1,i)*bmax(i-k+1,i) } +11

3. Constructing the Optimal Solution

Construct the optimal solution: Note the value of K when k[i] i s the minimum value of s[i], and the corresponding optimal solution can be constructed from the value of K.
Compress uses l[i] and b[i] to record the information needed for the most segmentation. The length of the last segment of the most segmentation and the number of pixels are stored i n l[n], b[n], and the length and number of pixels of the previous segment are stored i n l[n-l[n], and b[n-l[n], respectively.
By analogy, l and b computed by the algorithm can construct corresponding optimal solutions in O(n) time.

The code is as follows:

// image compression
#include<bits/stdc++.h>
using namespace std;
int length(int i)  //The number of storage bits required to calculate the pixel point p
{
    int k = 1;
    i = i/2;
    while(i>0)
    {
        k++;
        i = i/2;
    }
    return k;
}
void Compress(int n, int p[], int s[], int l[], int b[])  //Make s[i] the optimal combined number of storage bits for the first I segments
{ 
    int Lmax = 256, header = 11;
    s[0] = 0;
    for(int i=1; i<=n; i++)  //i denotes the first few paragraphs
    {
        b[i] = length(p[i]); //The number of storage bits required to calculate the pixel point p
        int bmax = b[i];
        cout<<i<<"bmax: "<<bmax<<endl;
        s[i] = s[i-1] + bmax;  //So the following j starts from 2  
        l[i] = 1;
        for(int j=2; j<=i && j<=Lmax; j++)   //Recursive relationships: s[i]= min (1<=j<=i) (lsum(i-j+1,i)<=256) {s[i-j]+ lsum(i-j+1,i)*bmax(i-j+1,i)} + 11
        {
            if(bmax < b[i-j+1])
                bmax = b[i-j+1];
            if(s[i] > s[i-j] + j*bmax)   //Since all sequences are not segmented at first, it can be seen that each segment is a number, so lsum(i-j+1, i) = j;
            {
                s[i] = s[i-j] + j*bmax;
                l[i] = j;   //Optimal disconnect position, l[i] indicates that the optimal segment scheme for the first I segment should be disconnected at i-j such as l[5] = 3, which means that the optimal segment for the first five segments should be disconnected at (5-3=2), s[5] = s[2] + 3*bmax   
                            //That is, 12 | 345, and so on, l[n]; Then l[n] backtraces forward when constructing the optimal solution
            }
        }
        s[i] += header;
    }
}
void Traceback(int n, int &m, int s[], int l[])
{
    if(n == 0) return;
    Traceback(n-l[n], m, s, l);
    s[m++] = n-l[n];  //Re-assign s[] array to store segment location
}
void Output(int s[], int l[], int b[], int n)
{
    cout<<"The optimal value is "<<s[n]<<endl;
    int m = 0;
    Traceback(n, m, s, l);
    s[m] = n;
    cout<<"Decompose into "<<m<<" segments "<<endl;
    for(int j=1; j<=m; j++)
    {
        l[j] = l[s[j]];
        b[j] = b[s[j]];
    }
    for(int j=1; j<=m; j++)
        cout<<"Segment Length:"<<l[j]<<" Required number of storage bits:"<<b[j]<<endl;
}
int main()
{
    int n;
    while(cin>>n && n)
    {
        int p[n+1];
        int s[n+1], l[n+1], b[n+1];
        for(int i=1; i<=n; i++)
            cin>>p[i];
        Compress(n, p, s, l, b);
        int m=0;
        Traceback(n, m, s, l);
        Output(s, l, b, n);
        memset(p, sizeof(p), 0);
    }
    system("pause");
    return 0;
}

Run result:

This article refers to my teacher Bi Fangming's Courseware Algorithmic Design and Analysis.
Welcome to my personal blog-- George's programming cabin And join me in offer ing for the big factory!

Tags: Dynamic Programming

Posted on Tue, 30 Nov 2021 16:32:25 -0500 by Mr_Mako