/* to compile: cc ppmswarp.c -lm to run: a.out -w -x ___ -y ___ -a ___ -b ___ -c ___ -o pork.s pork.pgm help: a.out (isatty is used to determine that nothing is piped in, etc. pipe usage: cat pork.pgm | a.out > pork.s if one unnamed argument given, that's the input file if output is not a tty, that's pipe out based on elwin's swarp.c, intille's i/o, sourabh's linear interp, jywang + eero's bicubic interp Reads a ppm or pgm image file and applies a coordinate transformation to it, based on a radial taylor series Should later work for float, etc. images, defined by the .par format . everything needed is in this one file, no need to link to anything but -lm which exists on just about any platform. */ /* ppmswarp [-w] [-z] [-l int] [-a int] [-b int] [-c int] [-d] [-e outfile] [-o outfile] [infile] -w : Allow outfiles to overwrite existing files (if used, this must be the first flag equivalent to usage with >! instead of just > -l : use bilinear interpolation instead of default bicubic interpolation -x float : x component of center of aberration -y float : y component of center of aberration -a float : first (r^1) polynomial coeff -b float : second (r^3) polynomial coeff -c float : third (r^5) polynomial coeff -d : assume image has been de-interlaced and is FAT -o outfile : Save the file after lines have been removed in specified directory infile : Name of original file */ #include #include #include #include #include #include extern int optind; extern char *optarg; #define OPTIONS "wldx:y:a:b:c:o:" #define NIF 1 /* maximal number of input files. only allow one */ char *cmd; /* used to store the name of the program */ char *cmt; /* used to store the entire command line */ char *usage= "[-w] [-l] [-x float] [-y float] [-a float] [-b float] [-c float]"; /*** globals ***/ /*---------------------------------------------------------------------------*/ get_input_file_name(ifile, ifile_desc, argv) char **ifile; char **ifile_desc; char *argv[]; { *ifile = (char *) malloc (sizeof(char) * (strlen(argv[optind]) + 10)); *ifile_desc = (char *) malloc (sizeof(char) * (strlen(argv[optind]) + 12)); strcpy(*ifile, argv[optind]); strcpy(*ifile_desc, argv[optind]); strcat(*ifile, "/data"); strcat(*ifile_desc, "/descriptor"); } /*-----------------------------------------------------------*/ get_output_file_name(ofile, ofile_desc, overwrite_files) char **ofile; char **ofile_desc; int overwrite_files; { FILE *fp; struct stat stbuf; /* used to store result of stat operation */ /* used to check if files already exist */ char command[256]; /* stores unix command */ *ofile = (char *) malloc (sizeof(char) * (strlen(optarg) + 11)); *ofile_desc = (char *) malloc (sizeof(char) * (strlen(optarg) + 12)); strcpy(*ofile, optarg); if (stat(*ofile, &stbuf) == -1) { fprintf(stderr,"Couldn't access %s. Creating directory.\n", *ofile); sprintf(command,"mkdir %s", *ofile); if ((fp = popen(command,"r")) == NULL) fprintf(stderr,"Couldn't run %s.", command); close(fp); } else if ((stbuf.st_mode & S_IFMT) != S_IFDIR) { fprintf(stderr, "File %s exists. Aborted. No dir created.\n", *ofile); exit(1); } /* $$$ */ strcpy(*ofile_desc, *ofile); strcat(*ofile_desc, "/descriptor"); strcat(*ofile, "/data"); if (!overwrite_files) { if (stat(*ofile, &stbuf) != -1) { fprintf(stderr,"File %s exists. Aborted.\n",*ofile); exit(1); } if (stat(*ofile_desc, &stbuf) != -1) { fprintf(stderr,"File %s exists. Aborted.\n",*ofile_desc); exit(1); } } } /*-----------------------------------------------------------*/ get_frame_params(ifile_desc, nframe, sets, bytes_pixel, xdim, ydim) /* Read input file descriptor to find size and file type */ char *ifile_desc; int *nframe, *sets, *bytes_pixel; int *xdim, *ydim; { char label[20], junk[20], junk2[20]; char line[80]; FILE *ifs_desc = stdin; if ((ifs_desc = fopen(ifile_desc, "r")) == NULL) {fprintf(stderr, "%s: Cannot open x descriptor input file\n", cmd); exit(1);} while (fgets(line,80,ifs_desc) != NULL) { fprintf(stderr,"ppmswarp is in ""while (fgets""\n"); sscanf(line,"%s",label); if (!strcmp (label, "(_data_files")) sscanf(line,"%s%d%s", junk, nframe, junk2); else if (!strcmp (label, "(_dimensions")) sscanf(line,"%s%d%d%s", junk, ydim, xdim, junk2); else if (!strcmp (label, "(_data_sets")) sscanf(line,"%s%d%s", junk, sets, junk2); else if (!strcmp (label, "(_data_type")) { sscanf(line,"%s%s", junk, junk2); if ((!strcmp (junk2, "\"unsigned_1\")")) || (!strcmp (junk2, "unsigned_1"))) *bytes_pixel = 1; if ((!strcmp (junk2, "\"float\")")) || (!strcmp (junk2, "float"))) *bytes_pixel = 4; } } fclose (ifs_desc); } /*-----------------------------------------------------------*/ read_frame_data(ifile, frame, nframe, xdim, ydim, bytes_pixel, data) /* get the image */ char *ifile; int frame, nframe; int *data; { char *current_ifile; char junk[20]; int bytes; FILE *ifs; struct stat stbuf; /* used to store result of stat operation */ /* used to check if files already exist */ current_ifile = (char *) malloc (sizeof(char) * (strlen(ifile) + 6)); strcpy(current_ifile, ifile); fprintf(stderr,"ppmswarp steve: current_ifile=%s\n",current_ifile); if (nframe != 1) { /* if only one frame, don't tag number on end */ strcat(current_ifile, sprintf(junk, "%d",frame - 1)); /*steve*/ /*strcat(current_ifile, "steve was here");*/ /*steve*/ strcat(current_ifile, junk); fprintf(stderr,"ppmswarp steve: junk=%s, frame-1 =%d\n",junk,frame-1); fprintf(stderr,"ppmswarp steve: read_frame_data: nframe != 1; nframe=%d\n",nframe); fprintf(stderr,"ppmswarp steve: current_ifile=%s\n",current_ifile); } fprintf(stderr,"Working on %s", current_ifile); fprintf(stderr,".\n"); /* open input file and read data */ if (stat(current_ifile, &stbuf) == -1) { fprintf(stderr,"Error: Couldn't access %s. \n", current_ifile); exit(1); } if ((ifs = fopen(current_ifile, "r")) == NULL) { fprintf(stderr, "%s: Cannot open input file\n", cmd); exit(1); } bytes = fread(data, bytes_pixel, xdim * ydim, ifs); fprintf(stderr,"Read %d.\n",bytes); fclose(ifs); free(current_ifile); } /*-----------------------------------------------------------*/ /* ndf: output needs to be of same form as input. if input was P6 640 480 255 so should output. later there may be desire to have output size be just a little bigger or just a little smaller than input, like analogous to pchirp2nocrop, but don't worry that for now */ write_frame_data(ofile, frame, nframe, xdim, ydim, bytes_pixel, vimage) char *ofile; int frame, nframe; int xdim, ydim; int bytes_pixel; int *vimage; { char *current_ofile; char junk[20]; int countout; FILE *ofs = stdout; current_ofile = (char *) malloc (sizeof(char) * (strlen(ofile) + 6)); strcpy(current_ofile, ofile); if (nframe != 1) { /* if only one frame, don't tag number on end */ fprintf(stderr,"ppmswarp steve: nframe !=1; nframe = %d\n",nframe); strcat(current_ofile, sprintf(junk, "%d",frame - 1)); /*steve*/strcat(current_ofile, junk);/*need to do it again*/ } fprintf(stderr,"assigned file to ofile %s\n", current_ofile); if ((ofs = fopen(current_ofile, "w")) == NULL) { fprintf(stderr, "%s: Cannot open output file\n", cmd); exit(1); } countout = fwrite(vimage, bytes_pixel, xdim*ydim, ofs); if (countout != xdim*ydim) fprintf(stderr,"output err: wrote %d pixels; should be %d \n", countout, xdim*ydim); fclose(ofs); free(current_ofile); } /*-----------------------------------------------------------*/ write_output_params(ofile_desc, nframe, xdim, ydim, bytes_pixel) char *ofile_desc; int nframe; int xdim, ydim; int bytes_pixel; { char *dat_type; FILE *ofs_desc = stdout; if ((ofs_desc = fopen(ofile_desc, "w")) == NULL) {fprintf(stderr, "%s: Cannot open x descriptor output file\n", cmd); exit(1);} /* Write descriptor files */ dat_type = (char *) malloc(sizeof(char) * 30); if (bytes_pixel == 4) strcpy(dat_type, "float"); else strcpy(dat_type, "unsigned_1"); fprintf(ofs_desc, "(_data_files %d)\n(_channels 1)\n(_dimensions %d %d)\n(_data_type %s)", nframe, ydim, xdim, dat_type); fclose(ofs_desc); } /*-----------------------------------------------------------*/ main(argc, argv) /* process command line args */ int argc; char *argv[]; { FILE *fp; int errflag = 0; /* set to 1 on error */ int oflag = 0; /* infiles must be entered */ int overwrite_files = 0; /* set to 1 (allow) if user specifies -w */ int bilinear = 0; /* set to 1 (allow) if user specifies -l */ int deinterl = 0; /* set to 1 (allow) if user specifies -d */ /* otherwise default is bicubic */ struct stat stbuf; /* used to store result of stat operation */ /* used to check if files already exist */ char command[256]; /* stores unix command */ char c; /* c is a temporary character used for breaking the */ /* command line into its individual flags, names, etc*/ char *ofile = NULL; /* set default output file name to NULL */ char *ofile_desc = NULL; char *ifile = NULL; /* set default input file name to NULL */ char *ifile_desc = NULL; char **datafile; char **descfile; float x0, y0, a1, a2, a3; /* aberration parameters */ cmd = argv[0]; /* save the name of program */ while ((c = getopt(argc, argv, OPTIONS)) != EOF) switch (c) { case 'w': overwrite_files = 1; break; case 'l': bilinear = 1; break; case 'x': x0 = atof(optarg); break; case 'y': y0 = atof(optarg); break; case 'a': a1 = atof(optarg); break; case 'b': a2 = atof(optarg); break; case 'c': a3 = atof(optarg); break; case 'd': deinterl = 1; break; case 'o': if (!oflag) { datafile = &ofile; descfile = &ofile_desc; get_output_file_name(datafile, descfile, overwrite_files); oflag = 1; } break; case '?': errflag = 1; break; } /* check if error was found while parsing command line */ if (errflag /* error in options */ || !oflag || argc > optind + NIF) /* we desire only NIF input files. At this */ /* point in the execution, getopt should */ /* have parsed all the command line prompts*/ /* except the input file names. Therefore, */ /* if this relationship is not true, extra */ /* flags, arguments, or names were in the */ /* command prompt */ {fprintf(stderr, "\nusage: %s %s -o porks.pgm pork.pgm\n\n", cmd, usage); fprintf(stderr, "cat pork.pgm | %s %s > porks.pgm\n",cmd, usage); fprintf(stderr, "cat pork.ppm | %s %s > porks.ppm\n",cmd, usage); fprintf(stderr, "cat pork.ppm | %s > porks.ppm\n",cmd); fprintf(stderr, "cat pork.ppm | %s -o porks.ppm\n",cmd); exit(1);} /* get input file ... ndf: change to read P5 or P6 instead */ /* fscanf for "P5" or "P6";if not found, give appropriate message to stderr */ /* then search for width and height, and 255, while ignoring lines */ /* beginning with # which are comments. flag error if no 255, indicating */ /* support of other datatypes such as 65535 are forthcoming */ datafile = &ifile; descfile = &ifile_desc; get_input_file_name(datafile, descfile, argv); /*do the processing*/ do_it(ifile, ifile_desc, ofile, ofile_desc, x0, y0, a1, a2, a3, bilinear, deinterl); free(ifile); free(ifile_desc); free(ofile); free(ofile_desc); } /*-----------------------------------------------------------*/ /* ndf: this part of the code loops through frames in a sequence and calls the subroutines that actually do the real work; future coding efficiency can be better by doing images ina batch processing, but needs to be of form pic000.ppm, pic001.ppm, pic002.ppm and the warp masks, etc., could be saved in memory. dont worry this for now, just single pictures. later we'll support multiple pictgures (sequence of images) */ do_it(ifile, ifile_desc, ofile, ofile_desc, x0, y0, a1, a2, a3, bilinear, deinterl) char *ifile, *ifile_desc, *ofile, *ofile_desc; float x0, y0, a1, a2, a3; int bilinear, deinterl; { FILE *ifs; FILE *ifs_desc; FILE *ofs = stdout; FILE *ofs_desc = stdout; FILE *fp; struct stat stbuf; /* used to store result of stat operation */ /* used to check if files already exist */ int i, x, y; char label[20], junk[20], junk2[20]; char line[80]; char command[500]; unsigned char *indata, *outdata; float *vx, *vy; int xdim, ydim; int bytes, countout; int type; int nframe; int frame; int sets; int bytes_pixel; char *current_ifile; char *current_ofile; char *dat_type; nframe = 1; bytes_pixel = 1; /* default to picture of chars */ sets = 0; get_frame_params(ifile_desc, &nframe, &sets, &bytes_pixel, &xdim, &ydim); fprintf(stderr,"Input image size: (%d (y), %d (x)).",ydim, xdim); fprintf(stderr," Frames: %d.",nframe); fprintf(stderr," Bytes/pixel : %d.\n",bytes_pixel); if (bilinear == 1) fprintf(stderr," bilinear interpolation method \n"); else fprintf(stderr," bicubic interpolation method \n"); indata = (unsigned char *) malloc(sizeof(char) * bytes_pixel * (xdim*ydim)); outdata = (unsigned char *) malloc(sizeof(char) * bytes_pixel * (xdim*ydim)); vx = (float *) malloc(sizeof(float) * (xdim*ydim)); vy = (float *) malloc(sizeof(float) * (xdim*ydim)); /* x0 = 156.5; y0 = 113.0; /* typical warp params */ /* a1 = .9666354; a2 = 9.393309e-5; a3 = 2.213069e-6; /* */ /* make_warp_map_approx(x0, y0, a1, a2, a3, xdim, ydim, vx, vy); */ make_warp_map(x0, y0, a1, a2, a3, xdim, ydim, vx, vy, deinterl); fprintf(stderr,"Hold on, working...\n"); for (frame = 1; frame <= nframe; frame++) { fprintf(stderr,"swarp steve: frame = %d of %d\n",frame,nframe); read_frame_data(ifile, frame, nframe, xdim, ydim, bytes_pixel, indata); /* get the image, store it in indata */ /****** THIS IS WHERE THE ACTION IS *******/ if (bilinear == 1) bilinear_warp(indata, outdata, vx, vy, xdim, ydim); else bicube_warp(indata, outdata, vx, vy, xdim, ydim); /* makedisplay(xdim, ydim, outdata); */ /* write output file image data */ write_frame_data(ofile, frame, nframe, xdim, ydim, bytes_pixel, outdata); } /* for frame */ /* Write descriptor files */ write_output_params(ofile_desc, nframe, xdim, ydim, bytes_pixel); free(indata); /* "Freedom's just another word for nothin left to lose;" */ free(outdata); /* "Nothin ain't worth nothin but its free..." */ } /*-----------------------------------------------------------*/ make_warp_map_approx(x0, y0, a1, a2, a3, xdim, ydim, xresult, yresult, deinterl) /* A warp map is a velocity map. It says: for the center of each pixel in the output picture, where did that point come from in the input picture? This approximate version does the reverse: for the center of each pixel in the input picture, where is that point going in the output picture? The approximation is to reverse that value. It is a pretty good approximation for small, slowly varying warps, but I want to be exact. This is kept around for historical and educational purposes only. */ float x0, y0, a1, a2, a3; int xdim, ydim; float *xresult, *yresult; int deinterl; { int x, y, cc; float xx, yy, r, r2, rnew, frac; cc = 0; for (y = 0; y < ydim; y++) for (x = 0; x < xdim; x++) { xx = ((float) x) - x0; yy = ((float) y) - y0; r2 = (xx*xx + yy*yy); r = ((float) sqrt((double) r2)); /* rnew = a1*r + a2*r2 + a3*r*r2; */ /* frac = rnew/r; */ /* frac = a1 + a2*r + a3*r2; replaced by odd-powers poly */ frac = a1 + a2*r2 + a3*r2*r2; /* actually, never tested */ xresult[y * xdim + x] = xx*frac - xx; yresult[y * xdim + x] = yy*frac - yy; /* if (x == y) fprintf(stderr," %4d %9.5f %9.5f ", x, xx*frac - xx, yy*frac - yy); /* */ /* cc++; */ } /* fprintf(stderr," warp map count %d \n", cc); */ } make_warp_map(x0, y0, a1, a2, a3, xdim, ydim, xresult, yresult, deinterl) /* For the center of each pixel in the output picture, where did that point come from in the input picture? rguess is how far along a radial line (from the center of aberr.) pixel p[x,y] in the output picture moved relative to the input picture. This code uses a neighboring pixel for a first approximation, and Newton's method to improve the approx. */ /* 11/29/93 changed rguess, f, fprime for odd-powers poly */ float x0, y0, a1, a2, a3; int xdim, ydim; float *xresult, *yresult; int deinterl; { int x, y, i, cc, pf; float xx, yy, r, r2; float radius, rguess, rgold; float f, fprime; cc = 0; for (y = 0; y < ydim; y++) { rguess = 0.0; /* force first guess approx */ for (x = 0; x < xdim; x++) { xx = ((float) x) - x0; yy = ((float) y) - y0; if (deinterl == 1) yy = ((float) y+y) - y0; radius = ((float) sqrt((double) (xx*xx + yy*yy))); if (fabs((double) rguess) < 1e-4) { /* Use the warp_map_approx() method for first guess if rguess is small. For rest of row, prev sol'n is first guess. */ xx = ((float) x) - x0; yy = ((float) y) - y0; if (deinterl == 1) yy = ((float) y+y) - y0; r2 = (xx*xx + yy*yy); r = ((float) sqrt((double) r2)); /* rguess = r - (a1*r + a2*r2 + a3*r*r2); */ rguess = r - (a1 + (a2 + a3*r2)*r2)*r; } rgold = 1.25*rguess; /* force newton loop to try */ /* if (x == y) pf = 1; else pf = 0; */ /* if (pf == 1) fprintf(stderr," guess %10.6f",rguess); */ for (i=1; (i<30)&&(fabs(((double)(rgold-rguess)))>1e-5); i++) { /* newton loop until error is small */ r = radius + rguess; r2 = r*r; /* f = radius - (a1*r + a2*r2 + a3*r*r2); */ /* fprime = -(a1 + 2*a2*r + 3*a3*r2); */ f = radius - (a1 + (a2 + a3*r2)*r2)*r; fprime = -(a1 + (3*a2 + 5*a3*r2)*r2); rgold = rguess; rguess = .9*(rguess - f/fprime); } /* if (pf == 1) fprintf(stderr," %10.6f %10.6f %2d %4d %4d \n", rgold, rguess, i, x, y); /* */ i = (y * xdim + x); xresult[i] = -(xx/radius)*rguess; if (deinterl != 1) yresult[i] = -(yy/radius)*rguess; else yresult[i] = -(yy/(radius*2))*rguess; cc++; } } /* fprintf(stderr," warp map count %d \n", cc); */ } /* bilinear interpolation code from sourabh */ bilinear_warp(im, out, vx, vy, xdim, ydim) unsigned char *im, *out; float *vx; float *vy; int xdim; int ydim; { int x, y; float xpf, ypf; int xpi, ypi; int xbound, ybound; float dx, dy, ans; xbound = xdim-1; ybound = ydim-1; for (y=0; y xbound) || ( (ypi+1) > ybound) ) *(out+y*xdim+x) = *(im+y*xdim+x); else { dx = xpf-(float)xpi; dy = ypf-(float)ypi; ans = (1-dx) * (1-dy) * (*(im + ypi * xdim + xpi)) + dx * (1-dy) * (*(im + ypi * xdim + xpi + 1)) + (1-dx) * dy * (*(im+(ypi+1) * xdim + xpi)) + dx * dy * (*(im+(ypi+1) * xdim + xpi + 1)); if (ans > 255) ans = 255; if (ans < 0) ans = 0; *(out+y*xdim+x) = ((unsigned char) (ans + 0.5)); } /* */ } } /* John/Eero's warper, for control expt. */ bicube_warp(im, res, wx, wy, xdim, ydim) unsigned char *im, *res; float *wx, *wy; int xdim, ydim; { register unsigned char *im_ptr; /* accessed 38x in loop */ register int base; /* accessed 30x in loop */ register float frac, temp; /* accessed 20x in loop */ register float frac2, frac3; /* accessed 10x in loop */ int x_inbounds_1, x_inbounds0, x_inbounds1, x_inbounds2; /* 6x in loop */ register float x_coeff_1, x_coeff0, x_coeff1, x_coeff2; /* 5x in loop */ register float res_val; register int i,j,trunc; register float xfrac,xbase,yfrac,ybase; float x_off, y_off; x_off = (float)((xdim - 1) / 2.0); y_off = (float)((ydim - 1) / 2.0); for(j=0; j= 0) && ((xbase + 0) < xdim)) && (((ybase) >= 0) && ((ybase + 0) < ydim))) { res_val = 0.0; frac = xfrac - xbase; frac2 = frac*frac; frac3 = frac*frac2; base = xbase - 1; im_ptr = im + base; x_inbounds_1 = ((base >= 0) && (base < xdim))?1:0; if (x_inbounds_1) x_coeff_1 = (frac2/2.0 - frac/3.0 - frac3/6.0); base++; x_inbounds0 = ((base >= 0) && (base < xdim))?1:0; if (x_inbounds0) x_coeff0 = (1.0 - frac/2.0 - frac2 + frac3/2.0); base++; x_inbounds1 = ((base >= 0) && (base < xdim))?1:0; if (x_inbounds1) x_coeff1 = (frac + frac2/2.0 - frac3/2.0); base++; x_inbounds2 = ((base >= 0) && (base < xdim))?1:0; if (x_inbounds2) x_coeff2 = (frac3/6.0 - frac/6.0); frac = yfrac - ybase; frac2 = frac*frac; frac3 = frac*frac2; base = ybase - 1 ; im_ptr += base*xdim; if ((base >= 0) && (base < ydim)) { temp = 0.0; if (x_inbounds_1) temp += x_coeff_1 * *im_ptr; im_ptr++; if (x_inbounds0) temp += x_coeff0 * *im_ptr; im_ptr++; if (x_inbounds1) temp += x_coeff1 * *im_ptr; im_ptr++; if (x_inbounds2) temp += x_coeff2 * *im_ptr; im_ptr -= 3; res_val += temp * (frac2/2.0 - frac/3.0 - frac3/6.0); } base++; im_ptr += xdim; if ((base >= 0) && (base < ydim)) { temp = 0.0; if (x_inbounds_1) temp += x_coeff_1 * *im_ptr; im_ptr++; if (x_inbounds0) temp += x_coeff0 * *im_ptr; im_ptr++; if (x_inbounds1) temp += x_coeff1 * *im_ptr; im_ptr++; if (x_inbounds2) temp += x_coeff2 * *im_ptr; im_ptr -= 3; res_val += temp * (1 - frac/2.0 - frac2 + frac3/2.0); } base++; im_ptr += xdim; if ((base >= 0) && (base < ydim)) { temp = 0.0; if (x_inbounds_1) temp += x_coeff_1 * *im_ptr; im_ptr++; if (x_inbounds0) temp += x_coeff0 * *im_ptr; im_ptr++; if (x_inbounds1) temp += x_coeff1 * *im_ptr; im_ptr++; if (x_inbounds2) temp += x_coeff2 * *im_ptr; im_ptr -= 3; res_val += temp * (frac + frac2/2.0 - frac3/2.0); } base++; im_ptr += xdim; if ((base >= 0) && (base < ydim)) { temp = 0.0; if (x_inbounds_1) temp += x_coeff_1 * *im_ptr; im_ptr++; if (x_inbounds0) temp += x_coeff0 * *im_ptr; im_ptr++; if (x_inbounds1) temp += x_coeff1 * *im_ptr; im_ptr++; if (x_inbounds2) temp += x_coeff2 * *im_ptr; im_ptr -= 3; res_val += temp * (frac3/6.0 - frac/6.0); } trunc = ((int) (res_val + 0.5)); if (trunc > 255) trunc = 255; if (trunc < 0) trunc = 0; *res = trunc; } else { *res = im[j*xdim + i]; } res++; } } /* */ /* This is the end. */