| 27 | | Note: if you have a different camera or need other resolutions, let me know ( fsluiter_at_gmail.com). I can generate the input files if you sent me a sample frame. |
| 28 | | |
| | 27 | Note: for questions about these examples, let me know ( fsluiter_at_gmail.com). |
| | 28 | |
| | 29 | How to generate PGM files: |
| | 30 | |
| | 31 | {{{ |
| | 32 | #!div style="font-size: 80%" |
| | 33 | {{{#!c |
| | 34 | |
| | 35 | #include <stdio.h> |
| | 36 | #include <stdlib.h> |
| | 37 | #include <libgen.h> |
| | 38 | #include <string.h> |
| | 39 | #include <getopt.h> |
| | 40 | #include <math.h> |
| | 41 | |
| | 42 | |
| | 43 | |
| | 44 | /* Compile with: gcc projection.c -Wall -o project -lm |
| | 45 | * math.h does not like to be linked directly... |
| | 46 | */ |
| | 47 | |
| | 48 | /* Flag set by ‘--verbose’. */ |
| | 49 | static int verbose_flag; |
| | 50 | |
| | 51 | typedef struct double2{ |
| | 52 | double x; |
| | 53 | double y; |
| | 54 | }double2; |
| | 55 | typedef struct double3{ |
| | 56 | double x; |
| | 57 | double y; |
| | 58 | double z; |
| | 59 | }double3; |
| | 60 | |
| | 61 | |
| | 62 | enum CameraMode { |
| | 63 | FRONT, |
| | 64 | FRONT235, |
| | 65 | BACK, |
| | 66 | UP, |
| | 67 | DOWN, |
| | 68 | SAMSUNG_GEAR_360, |
| | 69 | THETAS |
| | 70 | }; |
| | 71 | |
| | 72 | typedef struct configuration { |
| | 73 | char* xmap_filename; |
| | 74 | char* ymap_filename; |
| | 75 | int xmap_set; |
| | 76 | int ymap_set; |
| | 77 | int rows; // target |
| | 78 | int cols; // target |
| | 79 | int height; //source |
| | 80 | int width;//source |
| | 81 | int rows_set; |
| | 82 | int cols_set; |
| | 83 | int height_set; |
| | 84 | int width_set; |
| | 85 | int crop; |
| | 86 | enum CameraMode mode; |
| | 87 | } configuration; |
| | 88 | |
| | 89 | configuration parse_options(int argc, char **argv){ |
| | 90 | int c; |
| | 91 | configuration po; //to hold parsed options |
| | 92 | po.xmap_filename=NULL; |
| | 93 | po.ymap_filename=NULL; |
| | 94 | po.xmap_set=0; |
| | 95 | po.ymap_set=0; |
| | 96 | po.rows=0; |
| | 97 | po.cols=0; |
| | 98 | po.rows_set=0; |
| | 99 | po.cols_set=0; |
| | 100 | po.height_set=0; |
| | 101 | po.width_set=0; |
| | 102 | po.mode=FRONT; //default |
| | 103 | |
| | 104 | while (1) |
| | 105 | { |
| | 106 | static struct option long_options[] = |
| | 107 | { |
| | 108 | /* These options set a flag. */ |
| | 109 | {"verbose", no_argument, &verbose_flag, 1}, |
| | 110 | {"brief", no_argument, &verbose_flag, 0}, |
| | 111 | |
| | 112 | /* These options don’t set a flag. |
| | 113 | We distinguish them by their indices. */ |
| | 114 | {"help", no_argument, 0, 'q'}, |
| | 115 | |
| | 116 | /* options with arg*/ |
| | 117 | {"xmap", required_argument, 0, 'x'}, |
| | 118 | {"ymap", required_argument, 0, 'y'}, |
| | 119 | {"rows", required_argument, 0, 'r'},//target |
| | 120 | {"cols", required_argument, 0, 'c'},//target |
| | 121 | {"height", required_argument, 0, 'h'},//source |
| | 122 | {"width", required_argument, 0, 'w'},//source |
| | 123 | {"mode", required_argument, 0, 'm'}, |
| | 124 | {"crop", required_argument, 0, 'b'}, |
| | 125 | |
| | 126 | {0, 0, 0, 0} |
| | 127 | }; |
| | 128 | /* getopt_long stores the option index here. */ |
| | 129 | int option_index = 0; |
| | 130 | |
| | 131 | c = getopt_long (argc, argv, "qx:y:r:c:h:w:m:b:", |
| | 132 | long_options, &option_index); |
| | 133 | |
| | 134 | /* Detect the end of the options. */ |
| | 135 | if (c == -1) |
| | 136 | break; |
| | 137 | |
| | 138 | switch (c) |
| | 139 | { |
| | 140 | case 0: |
| | 141 | /* If this option set a flag, do nothing else now. */ |
| | 142 | if (long_options[option_index].flag != 0) |
| | 143 | break; |
| | 144 | printf ("option %s", long_options[option_index].name); |
| | 145 | if (optarg) |
| | 146 | printf (" with arg %s", optarg); |
| | 147 | printf ("\n"); |
| | 148 | break; |
| | 149 | |
| | 150 | case 'x': |
| | 151 | po.xmap_filename = optarg; |
| | 152 | po.xmap_set++; |
| | 153 | break; |
| | 154 | |
| | 155 | case 'y': |
| | 156 | po.ymap_filename = optarg; |
| | 157 | po.ymap_set++; |
| | 158 | break; |
| | 159 | case 'h': |
| | 160 | po.height = atoi(optarg); |
| | 161 | po.height_set++; |
| | 162 | break; |
| | 163 | case 'w': |
| | 164 | po.width = atoi(optarg); |
| | 165 | po.width_set++; |
| | 166 | break; |
| | 167 | case 'c': |
| | 168 | po.cols = atoi(optarg); |
| | 169 | po.cols_set++; |
| | 170 | break; |
| | 171 | case 'r': |
| | 172 | po.rows = atoi(optarg); |
| | 173 | po.rows_set++; |
| | 174 | break; |
| | 175 | case 'b': |
| | 176 | po.crop = atoi(optarg); |
| | 177 | break; |
| | 178 | case 'm': |
| | 179 | if (strcmp(optarg, "front") == 0) |
| | 180 | { |
| | 181 | po.mode = FRONT; |
| | 182 | } |
| | 183 | else if (strcmp(optarg, "front235") == 0) |
| | 184 | { |
| | 185 | po.mode = FRONT235; |
| | 186 | } |
| | 187 | else if (strcmp(optarg, "down") == 0) |
| | 188 | { |
| | 189 | po.mode = DOWN; |
| | 190 | } |
| | 191 | else if (strcmp(optarg, "samsung_gear_360") == 0) |
| | 192 | { |
| | 193 | po.mode = SAMSUNG_GEAR_360; |
| | 194 | } |
| | 195 | else if (strcmp(optarg, "thetas") == 0) |
| | 196 | { |
| | 197 | po.mode = THETAS; |
| | 198 | } |
| | 199 | /* more else if clauses */ |
| | 200 | else /* default: */ |
| | 201 | { |
| | 202 | printf("Camera mode %s not implemented \n",optarg); exit(1); |
| | 203 | } |
| | 204 | break; |
| | 205 | |
| | 206 | case '?': |
| | 207 | /* getopt_long already printed an error message. */ |
| | 208 | case 'q': |
| | 209 | printf ("Usage: %s -x|--xmap FILE_x.pgm -y|--ymap FILE_y.pgm -h|--height 300 -w|--width 400 -r|--rows 600 -c|--cols 800 \n", argv[0]); |
| | 210 | printf ("h,w is source size, r,c is targetsize \n"); |
| | 211 | exit(1); |
| | 212 | break; |
| | 213 | |
| | 214 | default: |
| | 215 | abort (); |
| | 216 | } |
| | 217 | } |
| | 218 | |
| | 219 | /* Instead of reporting ‘--verbose’ |
| | 220 | and ‘--brief’ as they are encountered, |
| | 221 | we report the final status resulting from them. */ |
| | 222 | if(verbose_flag){ |
| | 223 | switch(po.mode){ |
| | 224 | case FRONT: printf("Camera: Front proj\n"); break; |
| | 225 | case FRONT235: printf("Camera: Front 235 proj\n"); break; |
| | 226 | case DOWN: printf("Camera: Down proj\n"); break; |
| | 227 | case SAMSUNG_GEAR_360: printf("Camera: samsung_gear_360\n"); break; |
| | 228 | case THETAS: printf("Camera: Theta S\n"); break; |
| | 229 | default: printf("Camera mode not in verbose, exiting\n"); exit(1); |
| | 230 | } |
| | 231 | } |
| | 232 | /* Print any remaining command line arguments (not options). */ |
| | 233 | if (optind < argc) |
| | 234 | { |
| | 235 | printf ("ERROR: non-option ARGV-elements: "); |
| | 236 | while (optind < argc) |
| | 237 | printf ("%s ", argv[optind++]); |
| | 238 | putchar ('\n'); |
| | 239 | exit(1); |
| | 240 | } |
| | 241 | |
| | 242 | if(po.xmap_set!=1||po.ymap_set!=1){ printf("ERROR: Xmap and ymap are mandatory arguments and have to appear only once!\ntry --help for help\n\n ");exit(-1);} |
| | 243 | if(po.rows_set!=1||po.cols_set!=1){ printf("ERROR: Target Rows and Cols are mandatory arguments and have to appear only once!\ntry --help for help\n\n ");exit(-1);} |
| | 244 | if(po.height_set!=1||po.width_set!=1){ printf("ERROR: Source Height and Width are mandatory arguments and have to appear only once!\ntry --help for help\n\n ");exit(-1);} |
| | 245 | return po; |
| | 246 | |
| | 247 | |
| | 248 | #define MAXROWS 4500 |
| | 249 | #define MAXCOLS 4500 |
| | 250 | |
| | 251 | int pgmWrite_ASCII(char* filename, int rows,int cols, |
| | 252 | int **image,char* comment_string) { |
| | 253 | FILE* file; /* pointer to the file buffer */ |
| | 254 | // int maxval; /* maximum value in the image array */ |
| | 255 | long nwritten = 0; /* counter for the number of pixels written */ |
| | 256 | long x,y; /* for loop counters */ |
| | 257 | |
| | 258 | /* return 0 if the dimensions are larger than the image array. */ |
| | 259 | if (rows > MAXROWS || cols > MAXCOLS) { |
| | 260 | printf ("ERROR: row/col specifications larger than image array:\n"); |
| | 261 | return (0); |
| | 262 | } |
| | 263 | |
| | 264 | /* open the file; write header and comments specified by the user. */ |
| | 265 | if ((file = fopen(filename, "w")) == NULL) { |
| | 266 | printf("ERROR: file open failed\n"); |
| | 267 | return(0); |
| | 268 | } |
| | 269 | fprintf(file,"P2\n"); |
| | 270 | |
| | 271 | if (comment_string != NULL) fprintf(file,"# %s \n", comment_string); |
| | 272 | |
| | 273 | /* write the dimensions of the image */ |
| | 274 | fprintf(file,"%i %i \n", cols, rows); |
| | 275 | |
| | 276 | /* NOTE: MAXIMUM VALUE IS WHITE; COLOURS ARE SCALED FROM 0 - */ |
| | 277 | /* MAXVALUE IN A .PGM FILE. */ |
| | 278 | |
| | 279 | /* WRITE MAXIMUM VALUE TO FILE */ |
| | 280 | fprintf(file, "%d\n", (int)65535); |
| | 281 | |
| | 282 | /* Write data */ |
| | 283 | |
| | 284 | for (y = 0; y<rows;y++){ |
| | 285 | for (x = 0; x < cols; x++){ |
| | 286 | fprintf(file,"%i ",image[y][x]); |
| | 287 | nwritten++; |
| | 288 | } |
| | 289 | fprintf(file,"\n"); |
| | 290 | } |
| | 291 | fprintf(file,"\n"); |
| | 292 | |
| | 293 | |
| | 294 | printf ("\nNumber of pixels total(from rows*cols): %i\n",rows*cols); |
| | 295 | printf ("Number of pixels written in file %s: %ld\n\n",filename,nwritten); |
| | 296 | |
| | 297 | fclose(file); |
| | 298 | return(1); |
| | 299 | } |
| | 300 | |
| | 301 | |
| | 302 | |
| | 303 | |
| | 304 | /* So, to get the x’,y’ position for the circular image we will have to first pass the |
| | 305 | * coordinates x,y from the rectangular output image to spherical coordinates using the |
| | 306 | * first coordinate system, then those to the second shown spherical coordinate system, |
| | 307 | * then those to the polar projection and then pass the polar system to cardinal x’,y’. |
| | 308 | */ |
| | 309 | double2 evaluatePixel_Front(double2 outCoord, double2 srcSize) |
| | 310 | { |
| | 311 | double2 o; |
| | 312 | double theta,phi; |
| | 313 | double3 sphericCoords; |
| | 314 | double phi2_over_pi; |
| | 315 | double theta2; |
| | 316 | double2 inCentered; |
| | 317 | |
| | 318 | //convert outcoords to radians (180 = pi, so half a sphere) |
| | 319 | o.x = outCoord.x / srcSize.x; |
| | 320 | o.y = outCoord.y / srcSize.y; |
| | 321 | theta = (1.0-o.x) * M_PI; |
| | 322 | phi = o.y * M_PI; |
| | 323 | |
| | 324 | //Convert outcoords to spherical (x,y,z on unisphere) |
| | 325 | sphericCoords.x = cos(theta)*sin(phi); |
| | 326 | sphericCoords.y = sin(theta)*sin(phi); |
| | 327 | sphericCoords.z = cos(phi); |
| | 328 | |
| | 329 | //Convert spherical to input coordinates... |
| | 330 | theta2 = atan2(-sphericCoords.z,sphericCoords.x); |
| | 331 | phi2_over_pi = acos(sphericCoords.y)/(M_PI); |
| | 332 | |
| | 333 | inCentered.x = ((phi2_over_pi*cos(theta2))+0.5)*srcSize.x; |
| | 334 | inCentered.y = ((phi2_over_pi*sin(theta2))+0.5)*srcSize.y; |
| | 335 | |
| | 336 | return inCentered; |
| | 337 | } |
| | 338 | |
| | 339 | |
| | 340 | void gen_front_maps(configuration cfg,int** image_x,int** image_y ){ |
| | 341 | int x,y; |
| | 342 | |
| | 343 | printf("Front proj\n"); |
| | 344 | for (y = 0; y<cfg.rows;y++){ |
| | 345 | for (x = 0; x < cfg.cols; x++){ |
| | 346 | double2 o = evaluatePixel_Front((double2){((double)x/((double)cfg.cols)) * ((cfg.width)-(2*cfg.crop)), |
| | 347 | ((double)y/(double)cfg.rows) * ((cfg.height) -(2*cfg.crop))}, |
| | 348 | (double2){(cfg.width)-(2*cfg.crop), cfg.height-(2*cfg.crop)}); |
| | 349 | image_x[y][x] = (int)round(o.x)+cfg.crop; |
| | 350 | image_y[y][x] = (int)round(o.y)+cfg.crop; |
| | 351 | } |
| | 352 | } |
| | 353 | } |
| | 354 | |
| | 355 | |
| | 356 | int |
| | 357 | main (int argc, char **argv) |
| | 358 | { |
| | 359 | int y; |
| | 360 | int** image_x; |
| | 361 | int** image_y; |
| | 362 | configuration cfg = parse_options(argc,argv); |
| | 363 | |
| | 364 | |
| | 365 | if(cfg.xmap_filename) printf("xmapfile: %s\n",cfg.xmap_filename); |
| | 366 | if(cfg.ymap_filename) printf("ymapfile: %s\n",cfg.ymap_filename); |
| | 367 | |
| | 368 | image_x = malloc((cfg.rows)*sizeof(*image_x)); |
| | 369 | for(y=0;y<(cfg.rows);y++) image_x[y]= malloc((cfg.cols)*sizeof(*(image_x[y]))); |
| | 370 | image_y = malloc((cfg.rows)*sizeof(*image_y)); |
| | 371 | for(y=0;y<(cfg.rows);y++) image_y[y]= malloc((cfg.cols)*sizeof(*(image_y[y]))); |
| | 372 | |
| | 373 | |
| | 374 | switch(cfg.mode){ |
| | 375 | case FRONT: gen_front_maps(cfg,image_x,image_y); break; |
| | 376 | /* case FRONT235: gen_front235_maps(cfg,image_x,image_y); break; |
| | 377 | case DOWN: gen_down_maps(cfg,image_x,image_y); break; |
| | 378 | case SAMSUNG_GEAR_360: gen_samsung_gear_360_maps(cfg,image_x,image_y); break; |
| | 379 | case THETAS: gen_thetas_maps(cfg,image_x,image_y); break; |
| | 380 | */ |
| | 381 | default: printf("Camera mode not implemented\n"); exit(1); |
| | 382 | |
| | 383 | } |
| | 384 | |
| | 385 | |
| | 386 | |
| | 387 | |
| | 388 | printf("Writing files\n"); |
| | 389 | pgmWrite_ASCII(cfg.ymap_filename, cfg.rows,cfg.cols,image_y,cfg.ymap_filename); |
| | 390 | pgmWrite_ASCII(cfg.xmap_filename, cfg.rows,cfg.cols,image_x,cfg.xmap_filename); |
| | 391 | |
| | 392 | if(image_y){ |
| | 393 | for (y = 0; y<cfg.rows;y++) free(image_y[y]); |
| | 394 | free(image_y); |
| | 395 | } |
| | 396 | if(image_x){ |
| | 397 | for (y = 0; y<cfg.rows;y++) free(image_x[y]); |
| | 398 | free(image_x); |
| | 399 | } |
| | 400 | |
| | 401 | |
| | 402 | exit (0); |
| | 403 | } |
| | 404 | |
| | 405 | }}} |
| | 406 | }}} |