-
Notifications
You must be signed in to change notification settings - Fork 0
/
Compression.cpp
331 lines (302 loc) · 8.8 KB
/
Compression.cpp
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <cmath>
#include <fstream>
#define PI 3.14159265
using namespace cv;
using namespace std;
int min(int a, int b)
{
return (a < b) ? a : b;
}
int max(int a, int b)
{
return (a > b) ? a : b;
}
class compression
{
//Contains namely Mat Image for storing for 3 channel pixel values of the original image
Mat image, inputBlock, Channel[3];
int rows, cols;
Mat compressed_image;
Mat decompressed_image;
public:
compression()
{
string image_path;
cout << "Image Name\n";
cin >> image_path;
image = imread(image_path);
//width of the Image
rows = image.rows;
//Height of the Image
cols = image.cols;
//Mat to store final Compressed Image
compressed_image = Mat::zeros(rows - rows % 8, cols - cols % 8, CV_8UC3);
}
void zig_zag(int zigzag[], int result[][8])
{
//Entropy Coding of the Mat done by zig_zag traversal of the Matrix and storing the values so traversed into single D array 'zig_zag'
int m, n;
m = n = 8;
int j = 0;
for (int i = 0; i < m + n - 1; i++){
if (i % 2 == 1){
for (int y = min(i, n - 1); y >= max(0, i - m + 1); y--){
zigzag[j++] = result[i - y][y];
}
}
else{
for (int x = min(i, m - 1); x >= max(0, i - n + 1); x--){
zigzag[j++] = result[x][i - x];
}
}
}
}
void compress()
{
//for each r g and b component of the image
for (int rgb = 0; rgb < 3; rgb++)
{
//Scans till last mutiple of 8 in row as well as column
//have to modify to consider even the left pixel parts in row and column
for (int l = 0; l <= rows - rows % 8 - 8; l += 8)
{
for (int m = 0; m <= cols - cols % 8 - 8; m += 8)
{
ofstream file; //Stores the final compressed values
cout << "ROWS : " << l << " COLS : " << m << endl;
Mat roi_img(image(cvRect(m, l, 8, 8))); //8*8 block extracted from Image
split(roi_img, Channel); //8*8 block split into different channels (R,G,B)
Channel[rgb].convertTo(inputBlock, CV_8S); //Copying into Mat data structure of suitable type
file.open("encoded.txt", fstream::app);
Mat Q = (Mat_<double>(8, 8) << 16, 11, 10, 16, 24, 40, 51, 61, //Base Quantization Matrix
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77,
24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99);
Mat generatedMatrix(8, 8, CV_64FC1);
Mat MeanBlock(8, 8, CV_8S);
// Subtracting 128 from each pixel of 8*8 block to get the values centered around 0
add(inputBlock, -128, MeanBlock);
//DCT performed on 8*8 Block
double K = 1 / 4.0;
double mul_fact = PI / 16;
double alpha_X, alpha_Y;
double sum = 0, cosx, cosy;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
alpha_X = alpha_Y = 1;
if (i == 0)
alpha_X = 1 / sqrt(2);
if (j == 0)
alpha_Y = 1 / sqrt(2);
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
cosx = cos((2 * x + 1) * i * mul_fact);
cosy = cos((2 * y + 1) * j * mul_fact);
sum = sum + (double)(MeanBlock.at<schar>(x, y)) * cosx * cosy;
}
}
generatedMatrix.at<double>(i, j) = K * alpha_X * alpha_Y * sum;
sum = 0;
}
}
//Divide element wise by Quantization Matrix
compressed_image = generatedMatrix / Q;
int result[8][8];
int result_zig_zag[64];
//Storing the Mat into 2D Result Array
for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++) {
result[i][j] = (int)compressed_image.at<double>(i, j);
}
}
//zig_zag returns result_zig_zag containing Matrix values read in zig_zag manner
zig_zag(result_zig_zag, result);
//Finding the the begining part of consecutive zeros at the end
int zero_index = 63;
for (int i = 63; i > 0; i--)
{
if (result_zig_zag[i] != 0)
break;
else
zero_index = i;
}
//writing the compressed values into file with last consecutive zeros at the end skipped
for (int i = 0; i < zero_index; i++)
file << result_zig_zag[i] << " ";
file << "\n";
}
}
}
return;
}
void zig_zag_reader(vector<double> inputs, Mat inputblock)
{
//converts the vector of double numbers read in zig_zag manner in right form into inputblock
int j = 0;
int m = 8, n = 8;
for (int i = 0; i < m + n - 1; i++)
{
if (i % 2 == 1)
{
for (int y = min(i, n - 1); y >= max(0, i - m + 1); y--){
inputblock.at<double>(i - y, y) = (double)inputs[j++];
}
}
else
{
for (int x = min(i, m - 1); x >= max(0, i - n + 1); x--){
inputblock.at<double>(x, i - x) = (double)inputs[j++];
}
}
}
}
void decompressor()
{
ifstream file1;
file1.open("encoded.txt"); // Read the file
double After_DCT[8][8];
vector<double> Array;
vector<double>::iterator it;
Mat Q = (Mat_<double>(8, 8) << 16, 11, 10, 16, 24, 40, 51, 61, //Base Quantization Matrix
12, 12, 14, 19, 26, 58, 60, 55,
14, 13, 16, 24, 40, 57, 69, 56,
14, 17, 22, 29, 51, 87, 80, 62,
18, 22, 37, 56, 68, 109, 103, 77,
24, 35, 55, 64, 81, 104, 113, 92,
49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99);
int k = 0, n = 0;
Mat channel[3];
vector<Mat> final;
split(decompressed_image, channel); //Splits the compressed image into its respective channels
// Initializing respective channels with value 0
channel[0] = Mat::zeros(rows - rows % 8, cols - cols % 8, CV_8UC1);
channel[1] = Mat::zeros(rows - rows % 8, cols - cols % 8, CV_8UC1);
channel[2] = Mat::zeros(rows - rows % 8, cols - cols % 8, CV_8UC1);
int r = 0, c = 0;
Mat inputblock(8, 8, CV_64FC1);
int i = 0, j = 0;
int count = 0;
int rgb = 0;
string numbers;
while (!file1.eof()) {
//Reading a line of numbers from the encoded file
count++;
getline(file1, numbers);
vector<double> inputs;
istringstream in(numbers);
copy(istream_iterator<double>(in), istream_iterator<double>(), back_inserter(inputs));
vector<double>::iterator it;
//Adding 0 to fill the places where the last consecutive zeros were removed in compress() method
for (int i = inputs.size(); i < 64; i++)
inputs.push_back(0);
//get the right matrix from numbers in zig_zag form
zig_zag_reader(inputs, inputblock);
Mat Perform_DCT(8, 8, CV_64FC1);
Perform_DCT = inputblock.mul(Q);
double K = 1 / 4.0;
double mul_fact = PI / 16;
double alpha_u, alpha_v;
double sum = 0, cosx, cosy;
//Perform Inverse DCT
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 8; y++)
{
for (int u = 0; u < 8; u++)
{
for (int v = 0; v < 8; v++)
{
alpha_u = alpha_v = 1;
if (u == 0)
alpha_u = 1 / sqrt(2);
if (v == 0)
alpha_v = 1 / sqrt(2);
cosx = cos((2 * x + 1) * u * mul_fact);
cosy = cos((2 * y + 1) * v * mul_fact);
sum = sum + alpha_u * alpha_v * Perform_DCT.at<double>(u, v)* cosx * cosy;
}
}
After_DCT[x][y] = K * sum;
sum = 0;
}
}
// Making the matrix to be centered around 128 from being centered around 0
for (int x = 0; x < 8; x++){
for (int y = 0; y < 8; y++)
After_DCT[x][y] += 128;
}
Mat A = Mat(8, 8, CV_8UC1);
//Converting to Mat form
for (int x = 0; x < 8; x++){
for (int y = 0; y < 8; y++)
A.at<uchar>(x, y) = (int)After_DCT[x][y];
}
//Fill the 8*8 blocks into respective channel of final Decompressed Image
A.copyTo(channel[rgb](Rect(c, r, 8, 8)));
//Keeping track of row and column number
c += 8;
if (c == (cols - cols % 8)){
r += 8;
c = 0;
}
if (r >= (rows - rows % 8))
{
c = r = 0;
rgb++; //next channel in RGB
if (rgb == 3)
{
cout << "END" << endl;
file1.close();
final.push_back(channel[0]);
final.push_back(channel[1]);
final.push_back(channel[2]);
//Merging all the channels into compressed_image
merge(final, decompressed_image);
//Display of compressed Image
imshow("Compressed", decompressed_image);
//Display of original Image
imshow("Original", image);
waitKey(0);
return;
}
}
cout << c << " " << r << " Channel no : " << rgb + 1 << " row : " << count << endl;
}
}
};
int main()
{
//Create a object of compression class
compression Object;
cout << "Enter : \n1 to compress\n2 to Decompress\n3 to Exit\n";
int choice;
cin >> choice;
switch (choice)
{
case 1:
//Call Compressing member function
Object.compress();
break;
case 2:
//Call Decompressing member function
Object.decompressor();
break;
case 3:
exit(0);
}
return 0;
}