b454. 請輸出這張圖片的 RGB 數值

contents

  1. 1. Problem
  2. 2. Sample Input
  3. 3. Sample Output
  4. 4. Solution
    1. 4.1. 前處理
    2. 4.2. 建表壓縮
      1. 4.2.1. 產生器
    3. 4.3. base64
    4. 4.4. huffman+base64
    5. 4.5. diff+huffman+base64

Problem

給一張 $64 \times 64$ 的經典 Lena 影像,共有 $4096$ 個像素,程式碼長度上限 10K,避開打表的長度限制,輸出一模一樣的圖形。

Sample Input

1
NO INPUT

Sample Output

1
2
64 64
153 153 153 151 151 151 148 148 148 (後面省略)

Solution

前處理

首先遇到的問題是將灰階圖片取出,變成一般常看到的數字格式,直接用 python 來完成,必要時需要安裝 install Python Imaging Library PIL,這是前處理,也可以用其他的 OpenCV 來完成。

1
$ python img2txt.py b454.png >in.txt
img2txt.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sys
import Image
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout)
for infile in sys.argv[1:]:
try:
im = Image.open(infile)
gray = im.convert('LA')
width = gray.size[0]
height = gray.size[1]
pix = gray.load()
print height, width
for i in range(height):
for j in range(width):
print pix[j, i][0],
print
except IOError:
pass

建表壓縮

由於 4096 個像素,若使用十六進制表示每一個 0 - 255 像素,只需要 2 個字元 00 - FF,程式碼長度大約會落在 8192 bytes,若使用十進制,需要使用 3 個字元表示,那麼大約是在 13KB,所以至少要用十六進制表示法。

還有更好的方式,如一般常在網頁上瀏覽的 base64,它充分利用 64 個可視字元進行編碼,相較於十六進制只使用 16 個可視字元來比較會更加地短。利用傳統的霍夫曼編碼 (huffman coding) 來壓縮,結果會短個 10% 到 20%,當每種像素次數分布相當懸殊時,效果就越好,但上傳時還要附加解壓縮的代碼,所以沒辦法短太多,還不如直接 base64。由於是圖片,漸層效果比較普遍,改用與前一個相鄰像素的差值來轉換,得到的分布會比較極端,帶入 huffman 的效果就會不錯。

最後,還有一種比較容易實作的 lz77 壓縮,比較類似最長平台,每一次的訊息為 (起始位置, 重疊長度, 補尾字元),設定一個 window 長度,然後 sliding 滑動,但是起始位置、長度需要選好,代碼中嘗試用 window size = 16,則起始位置和重疊長度可以用 8-bits 表示,越大不見得越好,太小也不是好事,但不管怎麼做,由於影像的重複 pattern 並不多,沒有像一般數學性質的數列來得強,壓縮實驗不管怎樣都大於 10KB。

關於實作細節,霍夫曼編碼儲存格式是 壓縮位元長度 bits length + 字典表 + 壓縮資料,對於字典表的儲存有很多種,由於是 complete binary tree (只會有兩個、零個子節點),可以用一個 0 / 1 前序走訪來完成,這會造成解壓縮代碼長度就會有點虧本,所以在代碼中直接使用一般的打表,所以要保證每一個壓縮完的最大 bit-length 小於 32 來方便型態 int32 操作。

方法 代碼長度 (bytes)
base64 6485
huffman+base64 7965
diff+huffman+base64 7717
lz77+base64 > 10K

產生器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
#include <bits/stdc++.h>
using namespace std;
void lz77_encode(unsigned char *in, int n, unsigned char *out, int &m) {
m = 0;
if (n <= 16) {
for (int i = 0; i < n; i++)
out[m++] = in[i];
return ;
}
memcpy(out, in, 16);
m += 16;
while (n > 16) {
int mx = 0, offset, i, j;
for (i = 0; i < 16; i++) {
for (j = 0; i+j < 16 && j+16 < n && in[i+j] == in[j+16]; j++);
if (j > mx)
mx = j, offset = i;
}
if (mx <= 1) {
out[m++] = 0;
out[m++] = in[16];
in++, n--;
} else {
out[m++] = (offset<<4)|(mx-1);
in += mx, n -= mx;
}
}
}
void lz77_decode(unsigned char *in, int n, unsigned char *out, int &m) {
m = 0;
if (n <= 16) {
for (int i = 0; i < n; i++)
out[m++] = in[i];
return ;
}
memcpy(out, in, 16);
in += 16, n -= 16, m += 16;
int offset, mx;
while (n > 0) {
if (*in) {
offset = (*in)>>4, mx = ((*in)&0xf)+1;
memcpy(out + m, out + m - 16 + offset, mx);
m += mx;
in++, n -= 1;
} else {
out[m++] = in[1];
in += 2, n -= 2;
}
}
}
void base64_encode(unsigned char *in, int n, unsigned char *out, int &m) {
static char encode_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'};
static int mod_table[] = {0, 2, 1};
m = 4 * ((n + 2) / 3);
for (int i = 0, j = 0; i < n; ) {
unsigned int a, b, c, d;
a = i < n ? in[i++] : 0;
b = i < n ? in[i++] : 0;
c = i < n ? in[i++] : 0;
d = (a<<16)|(b<<8)|c;
out[j++] = encode_table[(d>>18)&0x3f];
out[j++] = encode_table[(d>>12)&0x3f];
out[j++] = encode_table[(d>>6)&0x3f];
out[j++] = encode_table[d&0x3f];
}
for (int i = 0; i < mod_table[n%3]; i++)
out[m - 1 - i] = '=';
}
void base64_decode(unsigned char *in, int n, unsigned char *out, int &m) {
static int decode_table[256];
for (int i = 'A'; i <= 'Z'; i++) decode_table[i] = i - 'A';
for (int i = 'a'; i <= 'z'; i++) decode_table[i] = i - 'a' + 26;
for (int i = '0'; i <= '9'; i++) decode_table[i] = i - '0' + 52;
decode_table['+'] = 62, decode_table['/'] = 63;
m = n/4*3;
if (in[n-1] == '=') m--;
if (in[n-2] == '=') m--;
for (int i = 0, j = 0; i < n; ) {
unsigned int a, val = 0;
for (int k = 3; k >= 0; i++, k--) {
a = in[i] == '=' ? 0 : decode_table[in[i]];
val |= a<<(k*6);
}
if (j < m) out[j++] = (val>>16)&0xff;
if (j < m) out[j++] = (val>>8)&0xff;
if (j < m) out[j++] = (val>>0)&0xff;
}
}
void huffman_encode(unsigned char *in, int n, unsigned char *out, int &m) {
m = 0;
struct Table {
int f, bit, bn;
} tables[512];
struct Node {
int f, tid;
Node *l, *r;
Node(int a = 0, int b = 0, Node *ls = NULL, Node *rs = NULL):
f(a), tid(b), l(ls), r(rs) {}
bool operator<(const Node &x) const {
return f < x.f;
}
} nodes[512];
struct Stack {
Node *node;
int bit, bn;
Stack(Node *a = NULL, int b = 0, int c = 0):
node(a), bit(b), bn(c) {}
} stacks[1024];
int size = 0, leaf;
multiset< pair<int, Node*> > S;
memset(tables, 0, sizeof(tables));
for (int i = 0; i < n; i++)
tables[in[i]].f++;
for (int i = 0; i < 256; i++) {
if (tables[i].f) {
nodes[size] = Node(tables[i].f, i);
S.insert({nodes[size].f, &nodes[size]});
size++;
}
}
leaf = size;
while (S.size() >= 2) {
pair<int, Node*> u, v;
u = *S.begin(), S.erase(S.begin());
v = *S.begin(), S.erase(S.begin());
int f = u.second->f + v.second->f;
nodes[size] = Node(f, -1, u.second, v.second);
S.insert({nodes[size].f, &nodes[size]});
size++;
}
int stkIdx = 0;
stacks[stkIdx++] = Stack(&nodes[size-1], 0, 0);
while (stkIdx) {
Stack u = stacks[--stkIdx], v;
if (u.bn >= 31) {
fprintf(stderr, "huffman error: bit length exceeded\n");
exit(0);
}
if (u.node->l == NULL) {
tables[u.node->tid].bit = u.bit;
tables[u.node->tid].bn = u.bn;
} else {
v = Stack(u.node->l, u.bit | (1<<u.bn), u.bn+1);
u.node = u.node->r, u.bn++;
stacks[stkIdx++] = u;
stacks[stkIdx++] = v;
}
}
int bits_cnt = 0;
for (int i = 0; i < 256; i++)
bits_cnt += tables[i].f * tables[i].bn;
fprintf(stderr, "huffman: %d bit length\n", bits_cnt);
memcpy(out+m, &bits_cnt, 4), m += 4;
memcpy(out+m, &leaf, 1), m += 1;
for (int i = 0; i < leaf; i++) {
Table t = tables[nodes[i].tid];
memcpy(out+m, &nodes[i].tid, 1), m += 1;
memcpy(out+m, &t.bn, 1), m += 1;
memcpy(out+m, &t.bit, (t.bn + 7)/8), m += (t.bn + 7)/8;
}
int cnt = 0, mask = 0;
for (int i = 0; i < n; i++) {
int bit = tables[in[i]].bit;
int bn = tables[in[i]].bn;
while (bn) { // LBS
int j = min(bn, 8 - cnt);
mask |= (bit&((1<<j)-1))<<cnt;
cnt += j, bn -= j, bit >>= j;
if (cnt == 8) {
memcpy(out+m, &mask, 1), m += 1;
cnt = 0, mask = 0;
}
}
}
if (cnt)
memcpy(out+m, &mask, 1), m += 1;
fprintf(stderr, "huffman encode length %d\n", m);
}
void huffman_decode(unsigned char *in, int n, unsigned char *out, int &m) {
m = 0;
map<int, int> R[64];
int bits_cnt = 0, leaf = 0;
memcpy(&bits_cnt, in, 4), in += 4, n -= 4;
memcpy(&leaf, in, 1), in += 1, n -= 1;
fprintf(stderr, "huffman: %d bit length\n", bits_cnt);
fprintf(stderr, "huffman: %d leaves\n", leaf);
for (int i = 0; i < leaf; i++) {
int tid = 0, bn = 0, bit = 0;
memcpy(&tid, in, 1), in += 1, n -= 1;
memcpy(&bn, in, 1), in += 1, n -= 1;
memcpy(&bit, in, (bn+7)/8), in += (bn+7)/8, n -= (bn+7)/8;
R[bn][bit] = tid;
}
int mask = 0, cnt = 0;
for (int i = 0; i < bits_cnt; i++) {
mask |= ((in[(i>>3)]>>(i&7))&1)<<cnt;
cnt++;
if (R[cnt].count(mask)) {
out[m++] = R[cnt][mask];
cnt = 0, mask = 0;
}
}
}
int main() {
int n, m;
int data[64][64];
while (scanf("%d %d", &n, &m) == 2) {
unsigned char in[32767], in2[32767];
int stat[256] = {};
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
scanf("%d", &data[i][j]);
in[i*m+j] = data[i][j];
stat[data[i][j]]++;
}
}
int lz77n, b64n, elz77n, tn, hufn, ehufn;
unsigned char buf[4][32767] = {}, test[32767] = {};
// Case 1
base64_encode(in, n*m, buf[1], b64n);
// Case 2
// huffman_encode(in, n*m, buf[0], hufn);
// base64_encode(buf[0], hufn, buf[1], b64n);
// base64_decode(buf[1], b64n, buf[2], ehufn);
// huffman_decode(buf[2], ehufn, test, tn);
// Case 3
// lz77_encode(in, n*m, buf[0], lz77n);
// base64_encode(buf[0], lz77n, buf[1], b64n);
// base64_decode(buf[1], b64n, buf[2], elz77n);
// lz77_decode(buf[2], elz77n, test, tn);
// Case 4
// huffman_encode(in, n*m, buf[0], hufn);
// lz77_encode(buf[0], hufn, buf[2], lz77n);
// base64_encode(buf[2], lz77n, buf[1], b64n);
// Case 5
// lz77_encode(in, n*m, buf[0], lz77n);
// huffman_encode(buf[0], hufn, buf[2], hufn);
// base64_encode(buf[2], lz77n, buf[1], b64n);
// Case 6
// for (int i = 0; i < n*m; i++)
// in2[i] = in[i] - (i ? in[i-1] : 0);
// huffman_encode(in2, n*m, buf[0], hufn);
// base64_encode(buf[0], hufn, buf[1], b64n);
for (int i = 0; i < b64n; i++)
printf("%c", buf[1][i]);
puts("");
}
return 0;
}

base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <bits/stdc++.h>
using namespace std;
unsigned char data[] = "mZeUkpampXpOX2FhYGlxd ... ignore";
void base64_decode(unsigned char *in, int n, unsigned char *out, int &m) {
static int R[256];
for (int i = 'A'; i <= 'Z'; i++) R[i] = i - 'A';
for (int i = 'a'; i <= 'z'; i++) R[i] = i - 'a' + 26;
for (int i = '0'; i <= '9'; i++) R[i] = i - '0' + 52;
R['+'] = 62, R['/'] = 63;
m = n/4*3;
if (in[n-1] == '=') m--;
if (in[n-2] == '=') m--;
for (int i = 0, j = 0; i < n; ) {
unsigned int a, val = 0;
for (int k = 3; k >= 0; i++, k--) {
a = in[i] == '=' ? 0 : R[in[i]];
val |= a<<(k*6);
}
if (j < m) out[j++] = (val>>16)&0xff;
if (j < m) out[j++] = (val>>8)&0xff;
if (j < m) out[j++] = (val>>0)&0xff;
}
}
int main() {
unsigned char d[32767] = {};
int n;
base64_decode(data, sizeof(data), d, n);
printf("%d %d\n", 64, 64);
for (int i = 0; i < 64; i++) {
for (int j = 0; j < 64; j++) {
printf("%d %d %d%c", d[i*64+j], d[i*64+j], d[i*64+j], j == 63 ? '\n' : ' ');
}
}
return 0;
}

huffman+base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <bits/stdc++.h>
using namespace std;
unsigned char data[] = "RHkAAOoAC4kCAguWBwYMFggIDBYA ... ignore";
void base64_decode(unsigned char *in, int n, unsigned char *out, int &m) {
static int R[256];
for (int i = 'A'; i <= 'Z'; i++) R[i] = i - 'A';
for (int i = 'a'; i <= 'z'; i++) R[i] = i - 'a' + 26;
for (int i = '0'; i <= '9'; i++) R[i] = i - '0' + 52;
R['+'] = 62, R['/'] = 63;
m = n/4*3;
if (in[n-1] == '=') m--;
if (in[n-2] == '=') m--;
for (int i = 0, j = 0; i < n; ) {
unsigned int a, val = 0;
for (int k = 3; k >= 0; i++, k--) {
a = in[i] == '=' ? 0 : R[in[i]];
val |= a<<(k*6);
}
if (j < m) out[j++] = (val>>16)&0xff;
if (j < m) out[j++] = (val>>8)&0xff;
if (j < m) out[j++] = (val>>0)&0xff;
}
}
void huffman_decode(unsigned char *in, int n, unsigned char *out, int &m) {
m = 0;
map<int, int> R[64];
int bits_cnt = 0, leaf = 0;
memcpy(&bits_cnt, in, 4), in += 4, n -= 4;
memcpy(&leaf, in, 1), in += 1, n -= 1;
for (int i = 0; i < leaf; i++) {
int tid = 0, bn = 0, bit = 0;
memcpy(&tid, in, 1), in += 1, n -= 1;
memcpy(&bn, in, 1), in += 1, n -= 1;
memcpy(&bit, in, (bn+7)/8), in += (bn+7)/8, n -= (bn+7)/8;
R[bn][bit] = tid;
}
int mask = 0, cnt = 0;
for (int i = 0; i < bits_cnt; i++) {
mask |= ((in[(i>>3)]>>(i&7))&1)<<cnt;
cnt++;
if (R[cnt].count(mask)) {
out[m++] = R[cnt][mask];
cnt = 0, mask = 0;
}
}
}
int main() {
unsigned char d[2][32767] = {};
int n, m;
base64_decode(data, sizeof(data), d[0], n);
huffman_decode(d[0], n, d[1], m);
printf("%d %d\n", 64, 64);
for (int i = 0; i < 64; i++) {
for (int j = 0; j < 64; j++) {
printf("%d %d %d%c", d[1][i*64+j], d[1][i*64+j], d[1][i*64+j], j == 63 ? '\n' : ' ');
}
}
return 0;
}

diff+huffman+base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <bits/stdc++.h>
using namespace std;
unsigned char data[] = "nG0AAPkABQIBBRICBRoDBRMEBQ0FB ... ignore";
void base64_decode(unsigned char *in, int n, unsigned char *out, int &m) {
static int R[256];
for (int i = 'A'; i <= 'Z'; i++) R[i] = i - 'A';
for (int i = 'a'; i <= 'z'; i++) R[i] = i - 'a' + 26;
for (int i = '0'; i <= '9'; i++) R[i] = i - '0' + 52;
R['+'] = 62, R['/'] = 63;
m = n/4*3;
if (in[n-1] == '=') m--;
if (in[n-2] == '=') m--;
for (int i = 0, j = 0; i < n; ) {
unsigned int a, val = 0;
for (int k = 3; k >= 0; i++, k--) {
a = in[i] == '=' ? 0 : R[in[i]];
val |= a<<(k*6);
}
if (j < m) out[j++] = (val>>16)&0xff;
if (j < m) out[j++] = (val>>8)&0xff;
if (j < m) out[j++] = (val>>0)&0xff;
}
}
void huffman_decode(unsigned char *in, int n, unsigned char *out, int &m) {
m = 0;
map<int, int> R[64];
int bits_cnt = 0, leaf = 0;
memcpy(&bits_cnt, in, 4), in += 4, n -= 4;
memcpy(&leaf, in, 1), in += 1, n -= 1;
for (int i = 0; i < leaf; i++) {
int tid = 0, bn = 0, bit = 0;
memcpy(&tid, in, 1), in += 1, n -= 1;
memcpy(&bn, in, 1), in += 1, n -= 1;
memcpy(&bit, in, (bn+7)/8), in += (bn+7)/8, n -= (bn+7)/8;
R[bn][bit] = tid;
}
int mask = 0, cnt = 0;
for (int i = 0; i < bits_cnt; i++) {
mask |= ((in[(i>>3)]>>(i&7))&1)<<cnt;
cnt++;
if (R[cnt].count(mask)) {
out[m++] = R[cnt][mask];
cnt = 0, mask = 0;
}
}
}
int main() {
unsigned char d[2][32767] = {};
int n, m;
base64_decode(data, sizeof(data), d[0], n);
huffman_decode(d[0], n, d[1], m);
for (int i = 1; i < m; i++)
d[1][i] = d[1][i] + d[1][i-1];
printf("%d %d\n", 64, 64);
for (int i = 0; i < 64; i++) {
for (int j = 0; j < 64; j++) {
printf("%d %d %d%c", d[1][i*64+j], d[1][i*64+j], d[1][i*64+j], j == 63 ? '\n' : ' ');
}
}
return 0;
}