#! /bin/gawk -f # Last edited on 2003-02-02 23:32:52 by stolfi BEGIN{ usage = ( ARGV[0] " < INFILE > OUTFILE" ); abort = -1; # Takes a file with raw data on object position and numbering, and # outputs a cleaned version, suitable for `source'ing into a script. # The input file may contain the following lines: # # batch = BATCHNAME NOBJECTS # Start of a new logical batch of images (which may not # correspond to any single batch directory). All images in the # batch are assumed to include the same set of objects. # `BATCHNAME' is the name of the batch, which is used only for # error messages. NOBJECTS is the expected number of objects in # the batch, for consistency checks. # img = IMGNAME CDROM/DSCNNNNN # Specifies that the image whose filename is IMGNAME.jpg (and # originally was CDROM/DSCNNNNN.JPG) is part of the current # logical batch. This line is optionally followed by object ID # and position lines (below). # mag = N # Sets the magnifying factor for all pixel coordinates that follow. # ! ID H,V = ..... # A object ID number, and the Image coordinates (in pixels) of its # nominal center projected onto the {Z=0} plane. The format of this line, # after the object ID, is that obtained with "xv"'s eyedropper. # The `V' axis points down (0 at top line of image). # # The output file will contain one shell-like command for each image: # # process_image IMGNAME \ # H1 V1 X1 Y1 Z1 ID1 \ # ... \ # Hn Vn Xn Yn Zn IDn \ # eoi # # where {IDi} is the ID of a object, {Hi,Vi} is its nominal center # in Image coordinates, and {Xi,Yi,Zi} the corresponding Scene # coordinates. # # The Scene coordinates are computed from {Hi,Vi} through the # image's grid perspective map, read from file {IMGNAME.grmap} # The coordinate {Zi} is always zero. # # The first image in a batch must be followed by ID/position lines # for all the objects of the batch. Subsequent images may specify # only some objects. When a object is omitted, its Scene # coordinates {Xi,Yi} default to those most recently given; and the # Image coordinates {Hi,Vi} are computed from them. # # In addition, after all images of each batch, there will be a # command for the batch: # # process_batch BATCHNAME \ # IMGNAME1 \ # ... \ # IMGNAMEm \ # eob cur_batch = ""; cur_mag = 1; cur_nobjs = 0; printf "# Created mechanically by cleanup-objcenters\n"; printf "\n"; } (abort >= 0){ exit abort; } /^ *([#]|$)/{ next; } /^mag *= *[0-9.]+ *$/{ sub(/[=]/, " = ", $0); if (NF != 3) { data_error(("bad mag line \"" $0 "\"")); } cur_mag = $3; next; } /^batch *= *[-a-z0-9A-Z\/]+ +[0-9]+ *$/{ sub(/[=]/, " = ", $0); if (NF != 4) { data_error(("bad batch line \"" $0 "\"")); } if (cur_batch != "") { finish_batch(); } init_batch(); cur_batch = $3; cur_batch_nominal_nobjs = $4; next; } /^img *= *[-a-z0-9A-Z\/]+ +[-a-z0-9A-Z\/]+ *$/{ sub(/[=]/, " = ", $0); if (NF != 4) { data_error(("bad img line \"" $0 "\"")); } if (cur_img != "") { finish_image(cur_img); } cur_img = $3; cur_orig_img = $4; printf "%s\n", $0 > "/dev/stderr"; if (cur_img in img_nobjs) { data_error(("repeated image \"" cur_img "\" in batch \"" cur_batch "\"")); } # Add image to list of images of this batch: batch_img[batch_nimgs] = cur_img; batch_nimgs++; img_nobjs[cur_img] = 0; # Read and save the grid projective matrix for this image: split("", cur_Md); split("", cur_Mi); read_img_grid_map(cur_img,cur_Md,cur_Mi); for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { img_Md[cur_img,i,j] = cur_Md[i,j]; img_Mi[cur_img,i,j] = cur_Mi[i,j]; } # Print image header command printf "\n"; printf "process_image %s \\\n", cur_img; next; } /^[!] [A-Za-z][-A-Za-z0-9]* *[-]?[0-9]+[,] *[-]?[0-9]+ *[=]/{ # Object {H,V} coordinates measured with the "xv" eyedropper tool. # Cleanup the "xv" data, leaving only the object ID and {H,V}: gsub(/[,]/, " ",$0); gsub(/[=].*$/, "",$0); if (NF != 4) { data_error(("bad ref point line \"" $0 "\"")); } obj = $2; obj_h = cur_mag*$3; obj_v = cur_mag*$4; # If the object is a new one for this batch, add it to the batch's list if (! (obj in obj_x)) { batch_obj[batch_nobjs] = obj; batch_nobjs++; } # Count number of objects in image: img_nobjs[cur_img]++; # if (img_nobjs[cur_img] == cur_batch_nominal_nobjs+1) # { data_warning(("image " cur_img " has too many objects")); } # Save the Image coordinates {H,V} of object: img_obj_h[cur_img,obj] = obj_h; img_obj_v[cur_img,obj] = obj_v; # Compute the Scene coords {X,Y,Z} of object (assumed to have {Z=0}): split("", a); split("", b); a[0] = 1; a[1] = obj_h; a[2] = obj_v; r3x3_mul_row(a, cur_Md, b); obj_x[obj] = b[1]/b[0]; obj_y[obj] = b[2]/b[0]; obj_z[obj] = 0.0; # Frint object line: print_object(cur_img,obj) next; } // { data_error(("unrecognized line \"" $0 "\"")); } END { if (cur_batch != "") { finish_batch(); } } function init_batch( ) { batch_nimgs = 0; # Number of images in current batch. cur_img = ""; # Current image name. split("", batch_img); # Names of images in current batch, [0..batch_nimgs-1]. batch_nobjs = 0; # Number of objects in current batch. split("", batch_obj); # Object IDs of current batch. # Data for each image: split("", img_nobjs); # `img_nobjs[img]' is num of objs given for image. split("", img_Md); # `img_Md[img,0..2,0..2]' maps grid from Image to Scene. split("", img_Mi); # `img_Mi[img,0..2,0..2]' maps grid from Scene to Image. # Data for each object: split("", obj_x); # `img_obj_x[id]' Scene coord X of obj `id'. split("", obj_y); # `img_obj_y[id]' Scene coord Y of obj `id'. split("", obj_z); # `img_obj_z[id]' Scene coord Z of obj `id'. # Data for each image-object pair: split("", img_obj_h); # `img_obj_h[img,id]' Image coord H of obj `id'. split("", img_obj_v); # `img_obj_v[img,id]' Image coord V of obj `id'. } function print_object(img,obj) { printf " %4d %4d %6.1f %6.1f %6.1f %s \\\n", \ img_obj_h[img,obj], img_obj_v[img,obj], \ obj_x[obj], obj_y[obj], obj_z[obj], obj; } function finish_image(img, ifr,i,j,obj,ndefs,show_defs,a,b) { # Provide current default positions for all objects already mentioned # but not specified for this image: ndefs = 0; # Number of defaulted objs for this image # show_defs = (img_nobjs[img] != 0) && (img_nobjs[img] > 0.667*batch_nobjs); show_defs = 1; for (ifr = 0; ifr < batch_nobjs; ifr++) { obj = batch_obj[ifr]; if (! ((img,obj) in img_obj_h)) { # Object wasn't specified for this image, must take default: if (show_defs) { if (ndefs == 0) { printf "defaulted objects:" > "/dev/stderr"; } printf " %s", obj > "/dev/stderr"; } x = obj_x[obj]; y = obj_y[obj]; split("", b); split("", a); split("", cur_Mi); for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { cur_Mi[i,j] = img_Mi[img,i,j]; } b[0] = 1; b[1] = x; b[2] = y; r3x3_mul_row(b, cur_Mi, a); img_obj_h[img,obj] = a[1]/a[0]; img_obj_v[img,obj] = a[2]/a[0]; ndefs++; print_object(img,obj); } } if (show_defs && (ndefs != 0)) { printf "\n" > "/dev/stderr"; } printf " eoi\n"; } function finish_batch( iim,img) { # Finish off the last image: if (cur_img != "") { finish_image(cur_img); } # Check actual number of objects in batch against the declared one: if (batch_nobjs != cur_batch_nominal_nobjs) { data_warning(("batch " cur_batch " expected " cur_batch_nominal_nobjs " objs, given " batch_nobjs)); } # Print batch trailer command: printf "\n"; printf "process_batch %s \\\n", cur_batch; for (iim = 0; iim < batch_nimgs; iim++) { img = batch_img[iim]; printf " %s \\\n", img; } printf " eob\n"; } function read_img_grid_map(img,Md,Mi, i,j,fname) { fname = (img ".grmap"); if (! r3x3_read(fname, Md)) { data_warning(("file " fname " not found")); # Return a default matrix that simply scales {H,V} by 1/10 for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { Md[i,j] = (i == j ? (i == 0 ? 10.0 : 1.0) : 0.0); Mi[i,j] = (i == j ? (i == 0 ? 1.0 : 10.0) : 0.0); } return; } if (! r3x3_read(fname, Mi)) { data_error(("problem reading " fname)); } close(fname); } function data_error(msg) { printf "line %d: ** %s\n", FNR, msg > "/dev/stderr"; abort = 1; exit abort; } function data_warning(msg) { printf "line %d: ** warning: %s\n", FNR, msg > "/dev/stderr"; }