forked from deepakkrish212/images-design
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathImage.cpp
More file actions
340 lines (285 loc) · 10.7 KB
/
Copy pathImage.cpp
File metadata and controls
340 lines (285 loc) · 10.7 KB
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
332
333
334
335
336
337
338
339
340
/*
* Deepak Krishnaa Govindarajan
* Marcus Naess
* Soobin Rho
* Fall, 2022
* COSC 226: C++ Programming
*
* Hw: A Container class for handling images.
*/
#include "Image.h"
// --------------------------------------------------------------------
// Class operator overloading
// --------------------------------------------------------------------
Image& Image::operator=(Image&& image) {
pgmType = image.pgmType;
totalColumn = image.totalColumn;
totalRow = image.totalRow;
maxValue = image.maxValue;
// Move the pointer to the new values and delete the old values
delete[] values;
values = image.values;
return *this;
}
bool operator==(const Image& image1, const Image& image2) {
// Check the magic number and the image dimension
if (image1.pgmType!=image2.pgmType ||
image1.totalColumn!=image2.totalColumn ||
image1.totalRow!=image2.totalRow ||
image1.maxValue!=image2.maxValue) {
return false;
}
// Compare all values
else {
for (int i=0; i<image1.size(); ++i) {
if (image1[i]!=image2[i]) return false;
}
}
return true;
}
bool operator!=(const Image& image1, const Image& image2) {
return !(image1==image2);
}
std::ostream& operator<<(std::ostream& ost, const Image& image) {
/*
* OUTPUT STREAM OPERATOR OVERLOADING
*
* Two possibilities:
* 1. pgmType is P2. In this case, print all values in ASCII.
* 2. pgmType is P5. In this case, print all values as bytes.
*/
// Output the magic number, image dimension, and maximum value
ost<<image.pgmType<<'\n'
<<image.totalColumn<<' '<<image.totalRow<<'\n'
<<image.maxValue<<'\n';
// Output all color values in ASCII
if (image.pgmType=="P2") {
for (int i=0; i<image.size(); ++i) {
if (i%image.totalColumn==0 && i!=0) ost<<'\n';
ost<<image.values[i]<<' ';
}
}
// Output all color values as bytes
else if (image.pgmType=="P5") {
for (int i=0; i<image.size(); ++i) {
ost<<char(image.values[i]);
}
}
// Add a whitespace at the end for better readability
ost<<'\n';
return ost;
}
std::istream& operator>>(std::istream& ist, Image& image) {
/*
* INPUT STREAM OPERATOR OVERLOADING
*
* Two possibilities:
* 1. pgmType is P2. In this case, read color values in ASCII.
* 2. pgmType is P5. In this case, read color values as bytes.
*
*/
// First of all, parse pgmType, image dimensions
// (column * row), and the maximum color value.
// Ignore any line starting with `#`.
const int NUM_OF_CONFIGS {4};
int numOfConfigsFound {0};
bool isFound {false};
std::string linesBuffer;
std::stringstream lineContainingAllConfigs;
while (!isFound && std::getline(ist,linesBuffer)) {
if (linesBuffer[0]!='#') {
// Break the line into words so that we can count
// how many configs we've parsed
std::stringstream lineBuffer {linesBuffer};
std::string wordBuffer;
while (lineBuffer>>wordBuffer) {
++numOfConfigsFound;
lineContainingAllConfigs<<wordBuffer<<' ';
// Exit the loop when we found all configs
if (numOfConfigsFound>=NUM_OF_CONFIGS) {
isFound = true;
break;
}
}
}
}
// Assign the config values
lineContainingAllConfigs>>image.pgmType
>>image.totalColumn
>>image.totalRow
>>image.maxValue;
// Reset the current color values
image.values = new int[image.size()];
// ---------------------------------------------------------------
// 1. If pgmType is "P2", parse color values as string
// ---------------------------------------------------------------
if (image.pgmType=="P2") {
for (int i=0; i<image.size(); ++i) {
ist>>image.values[i];
}
}
// ---------------------------------------------------------------
// 2. If pgmType is "P5", parse color values as bytes
// ---------------------------------------------------------------
else if (image.pgmType=="P5") {
int8_t value;
void* valueAddress = &value;
for (int i=0; i<image.size(); ++i) {
ist.read(static_cast<char*>(valueAddress),sizeof(char));
image.values[i] = value;
}
}
else std::cout<<"[ERROR] pgmType should be either P2 or P5.";
return ist;
}
// --------------------------------------------------------------------
// Constructor by an existing file definition
// --------------------------------------------------------------------
Image::Image(std::string fileName) {
// Open the file
std::ifstream file {fileName,std::ios_base::binary};
if (!file) {
std::cout<<"[ERROR] Can't open the file.\n";
return;
}
// Read the color values from the file
file>>*this;
}
// --------------------------------------------------------------------
// Class member functions definitions
// --------------------------------------------------------------------
std::vector<int> Image::getHistogram() const {
// Count occurences of each color value. Initialize a vector with
// `maxValue+1` elements set to 0.
std::vector<int> counts(maxValue+1, 0);
for (int i=0; i<size(); ++i) ++counts[values[i]];
return counts;
}
void Image::setBrightness(double scale, int offset) {
/*
* A function for setting the brightness of the image
*/
for (int i=0; i<size(); ++i) {
// Apply scale and offset
values[i] = static_cast<int>(values[i]*scale+offset);
// Range check
if (values[i]>maxValue) values[i]=maxValue;
else if (values[i]<0) values[i]=0;
}
}
Image Image::subset(int edge1X, int edge1Y, int edge2X, int edge2Y) {
std::vector<int> subset;
int locationOne = edge1X+(totalColumn*(edge1Y - 1));
int locationTwo = edge2X+(totalColumn*(edge2Y - 1));
int columnCounter = edge1X;
int rowCounter = edge1Y;
for(int i = locationOne - 1; i <= locationTwo - 1; i++) {
if(columnCounter >= edge1X && columnCounter <= edge2X) {
subset.push_back(values[i]);
}
if(columnCounter >= totalColumn) {
columnCounter = 1;
}
else {
columnCounter++;
}
}
Image subsetImage {(edge2X - edge1X)+1, edge2Y - edge1Y, maxValue, pgmType};
for (int i = 0; i <= subsetImage.size(); i++) {
subsetImage.values[i] = subset[i];
}
return subsetImage;
}
Image Image::downsample(bool smothing) {
int columnSampleTotal;
int rowSampleTotal;
int boxAverage;
(totalColumn % 2 == 0) ? columnSampleTotal = (totalColumn/2)-1 : columnSampleTotal = (totalColumn/2);
(totalRow % 2 == 0) ? rowSampleTotal = (totalRow/2)-1 : rowSampleTotal = (totalRow/2);
Image returnImage {columnSampleTotal, rowSampleTotal, maxValue, pgmType};
int pushback = 0;
for (int i = 0; i < totalRow-1; i++) {
for(int j = 0; j < totalColumn-1; j++) {
if(i%2 == 1 && j%2 == 1) {
if (smothing) {
boxAverage = (values[((i*totalColumn) + j) - totalColumn - 1] + values[((i*totalColumn) + j) - totalColumn] + values[((i*totalColumn) + j) - totalColumn + 1] +
values[((i*totalColumn) + j) - 1] + values[((i*totalColumn) + j)] + values[((i*totalColumn) + j) + 1] +
values[((i*totalColumn) + j) + totalColumn - 1] + values[((i*totalColumn) + j) + totalColumn] + values[((i*totalColumn) + j) + totalColumn + 1])/9;
returnImage.values[pushback] = boxAverage;
}
else { returnImage.values[pushback] = values[(i*totalColumn) + j]; }
pushback++;
}
}
}
return returnImage;
}
// --------------------------------------------------------------------
// Helper functions definitions
// --------------------------------------------------------------------
void pgmPrintHistogram(const Image& image) {
std::vector<int> counts = image.getHistogram();
// Print the results. Align all values neatly.
// Find how many widths are required for alignment.
const int howManyTens=std::floor(std::log10(image.maxValue))+1;
for (int i=0; i<image.maxValue+1; ++i) {
std::cout<<std::setw(howManyTens)<<i<<':';
// Calculate percentage and print '*' the same amount
double percentage = static_cast<double>(counts[i])/image.size()*100;
percentage = std::round(percentage);
for (int count=0; count<percentage; ++count) { std::cout<<'*'; }
// End of histogram
std::cout<<'\n';
}
}
void pgmSaveAsFile(const Image& image, std::string fileName) {
std::ofstream ofs {fileName};
if (!ofs) std::cout<<"[ERROR] Failed to initiate an output stream.\n";
else ofs<<image;
}
void pgmSaveAsFile(Image& image, std::string fileName, std::string pgmType) {
/*
* TWO POSSIBILITES
* 1. If the pgmType in the funciton call is not the same as
* the pgmType that the image already has.
* In this case, change the pgmType to that one temporarily
* and then change it back to the original when all is done.
* 2. If the pgmType in the function call is the same.
* In this case, just proceed as normal.
*/
// 1. If the pgmType in the function call is not the same
if (image.pgmType!=pgmType) {
std::string pgmTypeBackup = image.pgmType;
image.pgmType = pgmType;
// Save as a file
pgmSaveAsFile(image,fileName);
// Restore the original pgmType
image.pgmType = pgmTypeBackup;
}
// 2. If the pgmType in the function call is the same.
else if (image.pgmType==pgmType) {
// Save as a file
pgmSaveAsFile(image,fileName);
}
}
void readFileAndPrintWhiteSpaces(std::string fileName) {
/*
* A function for printing all whitespaces in a file.
* This function is used only for debugging.
*/
std::ifstream file {fileName,std::ios_base::binary};
if (!file) {
std::cout<<"[ERROR] Can't open the file.\n";
return;
}
int8_t value;
void* valueAddress = &value;
while (file.read(static_cast<char*>(valueAddress),sizeof(char))) {
if (value==' ') {std::cout<<"*";}
else if (value=='\n') {std::cout<<"^";}
else std::cout<<value;
}
}
//NOTES
//Need to make sure user can choose to smooth or not
//Only works for P2 needs to work for P5