-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathNearestNeighbourUpscaleDriver.c
264 lines (230 loc) · 8.9 KB
/
NearestNeighbourUpscaleDriver.c
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
/*
Cole L - 5th January 2022 - https://github.com/cole8888/Nearest-Neighbour-Upscale
Driver program to use the NearestNeighbourUpscale code from a commandline program.
Parses the command line arguments, reads in the image from disk, passes it to the appropriate upscaler function and saves the upscaled image to disk.
*/
#include "NearestNeighbourUpscaleDriver.h"
// Calculate the amount of time in seconds between the provided start and finish timespec structs.
double getElapsedTime(struct timespec start, struct timespec finish){
return (finish.tv_sec - start.tv_sec) + (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
}
// Functions to determine whether or not a string starts with or ends with a particular string.
bool endsWith(char *str, char *toCheck){
int n = strlen(str);
int cl = strlen(toCheck);
if(n < cl){
return false;
}
char *subbuff = (char *)malloc(cl * sizeof(char));
memcpy(subbuff, &str[n - cl], cl);
return (strcmp(subbuff, toCheck) == 0);
}
bool startsWith(char *str, char* toCheck){
int n = strlen(str);
int cl = strlen(toCheck);
if(n < cl){
return false;
}
char *subbuff = (char *)malloc(cl * sizeof(char));
memcpy(subbuff, &str[0 - cl], cl);
return (strcmp(subbuff, toCheck) == 0);
}
// Construct a filepath from two strings. sizeFile is the number of valid characters in the file string.
char *path_join(char* dir, char* file, int sizeFile){
int size = strlen(dir) + sizeFile + 2;
char *buf = (char*)malloc(size*sizeof(char));
if(NULL == buf){
return NULL;
}
strcpy(buf, dir);
// If the path does not end with the path seperator then we need to add it.
if(!endsWith(dir, PATH_SEPERATOR)){
strcat(buf, PATH_SEPERATOR);
}
// If the file starts with the path seperator, ignore it.
if(startsWith(file, PATH_SEPERATOR)){
char *filecopy = strdup(file);
if(NULL == filecopy){
free(buf);
return NULL;
}
strcat(buf, filecopy + 1);
free(filecopy);
}
else{
strcat(buf, file);
}
return buf;
}
// Determine how many digits are in a base 10 integer.
int getIntDigits(int num){
int digits = 0;
while(num != 0){
num /= 10;
++digits;
}
return digits;
}
// Save the image, do not overwrite any previous images.
void saveImg(u_char *img, int dimX, int dimY){
char *cwd = get_current_dir_name(); // Current working directory.
char ext[] = ".png"; // File extention. Must be .PNG
char buff[FILENAME_BUFFER_SIZE]; // Buffer to store the filename while we figure out what the file should be called.
char *file; // Once we settle on a filename, this will hold it.
// See if an image with this name exists. If so, append a number and keep incrementing that number until we find one where that name is not in use.
sprintf(buff, "%s%s", DEFAULT_FILENAME, ext);
if(access(path_join(cwd, buff, strlen(DEFAULT_FILENAME) + strlen(ext)), F_OK) == 0){
// File with this filename already exists. Let's try another one.
int num = 2; // Start by trying to append the number 2.
int digits = 1; // 2 has one digit.
sprintf(buff, "%s%d%s", DEFAULT_FILENAME, num, ext);
// Increment the appended number until we find a name that is not in use.
while(access(path_join(cwd, buff, digits + strlen(DEFAULT_FILENAME) + strlen(ext)), F_OK) == 0){
num++;
digits = getIntDigits(num);
sprintf(buff, "%s%d%s", DEFAULT_FILENAME, num, ext);
}
// Save the filename with the number at the end.
sprintf(buff, "%s%d%s", DEFAULT_FILENAME, num, ext);
file = path_join(cwd, buff, digits + strlen(DEFAULT_FILENAME) + strlen(ext));
}
else{
// File with this name does not exist, lets use this name for the new file.
sprintf(buff, "%s%s", DEFAULT_FILENAME, ext);
file = path_join(cwd, buff, strlen(DEFAULT_FILENAME) + strlen(ext));
}
// If the filename is NULL for some reason, let the user know, but still try to save saving the image. (Since this might have taken a long time to compute and there is no need to exit for this error.)
if(NULL == file){
fprintf(stderr, "\nError when making filename, it is NULL. Will attempt saving anyway using backup filename\n");
file = BACKUP_FILENAME;
}
// Start the timer and then save the image.
struct timespec start, finish;
clock_gettime(CLOCK_MONOTONIC, &start);
unsigned error;
if(CHANNELS_PER_PIXEL == 4){
error = lodepng_encode32_file(file, img, dimX, dimY);
}
else if(CHANNELS_PER_PIXEL == 3){
error = lodepng_encode24_file(file, img, dimX, dimY);
}
else{
fprintf(stderr, "CHANNELS_PER_PIXEL in the header file is not a valid value. Must be 3 or 4.\n");
exit(EXIT_FAILURE);
}
clock_gettime(CLOCK_MONOTONIC, &finish);
// See if there was an issue when saving the image.
if(error){
fprintf(stderr, "\nUnable to save the image, lodepng returned an error.\nError %u: %s\n", error, lodepng_error_text(error));
}
else{
printf("Saved to %s (%f secs)\n", file, getElapsedTime(start, finish));
}
}
// Load the image from disk. (Handles RGBA (32bit) and RGB (24bit) without issue, so use by default).
bool loadImgRGBA(u_char** img, u_int* width, u_int* height, char* filename){
u_char *png = 0;
size_t pngsize;
LodePNGState state;
lodepng_state_init(&state);
unsigned error = lodepng_load_file(&png, &pngsize, filename); // Load the file from disk into a temporary buffer in memory.
if(!error){
error = lodepng_decode(img, width, height, &state, png, pngsize);
if(error){
printf("Lodepng encountered an error.\nError: %u, %s\n", error, lodepng_error_text(error));
return false;
}
free(png);
lodepng_state_cleanup(&state);
return true;
}
else{
printf("Lodepng encountered an error.\nError: %u, %s\n", error, lodepng_error_text(error));
return false;
}
}
// Load the image from disk. RGBA images will be stripped of their alpha channel.
bool loadImgRGB(u_char** img, u_int* width, u_int* height, char* filename){
u_char *png = 0;
size_t pngsize;
unsigned error = lodepng_load_file(&png, &pngsize, filename); // Load the file from disk into a temporary buffer in memory.
if(!error){
error = lodepng_decode24(img, width, height, png, pngsize);
if(error){
printf("Lodepng encountered an error.\nError: %u, %s\n", error, lodepng_error_text(error));
return false;
}
free(png);
return true;
}
else{
printf("Lodepng encountered an error.\nError: %u, %s\n", error, lodepng_error_text(error));
return false;
}
}
// Parse the arguments and coordinate the calling of other functions.
int main(int argc, char *argv[]){
// Make sure there are exactly 2 arguments.
if(argc != 3){
fprintf(stderr, "No input filename, and/or no scale provided.\nUsage: ./upscale <INPUT_IMAGE> <SCALE>\n");
return EXIT_FAILURE;
}
char *filename = argv[1]; // Read in the input image filename from the commandline arguments.
int scale = 0;
// Read in the scale from the command line args.
char *temp;
long value = strtol(argv[2], &temp, 10);
if(temp != argv[2] && *temp == '\0'){
scale = (int)value;
if(scale < 1){
fprintf(stderr, "Invalid data in scale argument. Must be a non-zero integer.\nUsage: ./upscale <INPUT_IMAGE> <SCALE>\n");
return EXIT_FAILURE;
}
}
else{
fprintf(stderr, "Invalid data in scale argument. Must be a non-zero integer.\nUsage: ./upscale <INPUT_IMAGE> <SCALE>\n");
return EXIT_FAILURE;
}
u_char *img = 0; // Place to hold the original image we read from disk.
u_int dimX = 0; // Width of the original image.
u_int dimY = 0; // Height of the original image.
// Load the image from disk.
if(CHANNELS_PER_PIXEL == 4){
// Image will be read as RGBA. If image is actually RGB it's okay, the alpha channel for all pixel will just be filled with 255.
if(!loadImgRGBA(&img, &dimX, &dimY, filename)){
return EXIT_FAILURE;
}
}
else if(CHANNELS_PER_PIXEL == 3){
// Image will be read as RGB. If image is actually RGBA then the alpha channel will be ignored.
if(!loadImgRGB(&img, &dimX, &dimY, filename)){
return EXIT_FAILURE;
}
}
else{
fprintf(stderr, "CHANNELS_PER_PIXEL in the header file is not a valid value. Must be 3 or 4.\n");
return EXIT_FAILURE;
}
// Allocate memory for the upscaled image.
u_char *upscaledImg = (u_char *)malloc((dimX*scale) * (dimY*scale) * CHANNELS_PER_PIXEL * sizeof(u_char));
if(NULL == upscaledImg){
fprintf(stderr, "Unable to allocate upscaledImg array... May have run out of RAM.\n");
return EXIT_FAILURE;
}
printf("Start upscaling the image...\n");
struct timespec start, finish;
clock_gettime(CLOCK_MONOTONIC, &start); // Start the timer.
if(CHANNELS_PER_PIXEL == 4){
upscaleNN_RGBA(img, upscaledImg, (int)dimX, (int)dimY, scale); // Upscale the RGBA image.
}
else if(CHANNELS_PER_PIXEL == 3){
upscaleNN_RGB(img, upscaledImg, (int)dimX, (int)dimY, scale); // Upscale the RGB image.
}
clock_gettime(CLOCK_MONOTONIC, &finish); // Stop the timer.
printf("Finished upscaling the image.\t(%f secs)\n", getElapsedTime(start, finish));
free(img); // Free the original image.
printf("\nStart saving the image...\n");
saveImg(upscaledImg, dimX*scale, dimY*scale); // Save the upscaled image to disk.
free(upscaledImg); // Free the upscaled image,
return EXIT_SUCCESS;
}