Fiducials.cpp
Go to the documentation of this file.
00001 // Copyright (c) 2013-2014 by Wayne C. Gramlich.  All rights reserved.
00002 
00003 #include <assert.h>
00004 #include <sys/time.h>
00005 #include <angles/angles.h>
00006 
00007 #include <opencv2/highgui/highgui_c.h>
00008 
00009 #include "Camera_Tag.hpp"
00010 #include "CRC.hpp"
00011 #include "CV.hpp"
00012 #include "File.hpp"
00013 #include "FEC.hpp"
00014 #include "Fiducials.hpp"
00015 #include "Map.hpp"
00016 #include "String.hpp"
00017 #include "Tag.hpp"
00018 
00019 // Introduction:
00020 //
00021 // This code body takes a camera image that contains one or more fiducial
00022 // tag images and computes an estimated location and orientation for the
00023 // image camera.  This computation is done in conjunction with a map that
00024 // contains the best estimate of the locations of each of the fiducial tags.
00025 // In short:
00026 // 
00027 //    Image_Location = F(Image, Map)
00028 //
00029 // To further complicate things, in fact, the Map is updated as
00030 // side effect of computing the current location.  In the literature,
00031 // this is call SLAM, for Simultaneous Localization And Mapping.
00032 // Thus, what is really going on is:
00033 //
00034 //    (Image_Location, Map') = F(Image, Map)
00035 //
00036 // where Map' is the updated map.
00037 //
00038 // Despite the fact that the map is being updated, it best to treat the
00039 // localization computation different from the Map updating computation.
00040 // Thus, what is really going on is:
00041 //
00042 //    Image_Location = F(Image, Map)
00043 //
00044 //    Map' = G(Image, Map)
00045 //
00046 // where F is the localization function, and G is the map update function.
00047 //
00048 // The sections below are entitled:
00049 //
00050 // * Tag Geometry
00051 // * Coordinate Systems
00052 // * Map Organization
00053 // * Image Tags
00054 // * Localization Computation
00055 // * Fusing with Dead Reckoning
00056 // * Mapping into Robot Coordinates
00057 // * Coordinate Space Transforms
00058 //
00059 // Tag Geometry:
00060 //
00061 // A tag consists of a 12 x 12 matrix of bits that encodes 64-bits of
00062 // information in an 8 x 8 data matrix.  The outermost square of the matrix
00063 // is set to zeros (white squares) and the next level in is 1's
00064 // (black squares).  When viewed in an upright form printed on paper
00065 // it looks as follows (-- = zero, XX = one, ## = bit number):
00066 //
00067 // -- -- -- -- -- -- -- -- -- -- -- --
00068 // -- XX XX XX XX XX XX XX XX XX XX -- 
00069 // -- XX 56 57 58 59 60 61 62 63 XX --
00070 // -- XX 48 49 50 51 52 53 54 55 XX --
00071 // -- XX 40 41 42 43 44 45 45 47 XX --
00072 // -- XX 32 33 34 35 36 37 38 39 XX --
00073 // -- XX 24 25 26 27 28 29 30 31 XX --
00074 // -- XX 16 17 18 19 20 21 22 23 XX --
00075 // -- XX 08 09 10 11 12 13 14 15 XX --
00076 // -- XX 00 01 02 03 04 05 06 07 XX --
00077 // -- XX XX XX XX XX XX XX XX XX XX -- 
00078 // -- -- -- -- -- -- -- -- -- -- -- --
00079 //
00080 // Coordinate Systems:
00081 //
00082 // When it comes to performing the image analysis of a picture to
00083 // compute image camera location and twist there are three coordinate
00084 // systems to consider -- the floor, camera, and robot coordinate systems:
00085 //
00086 //    -        The floor coordinate system is a right handed cartesian
00087 //        coordinate system where one point on the floor is the
00088 //        origin.  The X and Y axes are separated by a 90 degree
00089 //        angle and X axis can be oriented to aligned with either a
00090 //        building feature or an east-west line of latitude or
00091 //        whatever the user chooses.  Conceptually, the floor is
00092 //        viewed from the top looking down.  As long as all length
00093 //        measurements are performed using the same units, the
00094 //        choice of length unit does not actually matter (e.g.
00095 //        meter, centimeter, millimeter, inch, foot, etc.)
00096 //
00097 //    -        The image coordinate system is another right handed cartesian
00098 //        coordinate system that is used to locate the individual
00099 //        pixels in the image.  The image is one of the common image
00100 //        format sizes (320 x 240, 640 x 480, etc.)  For this system,
00101 //        the origin is selected to be the lower left pixel of the
00102 //        image.  For 640 x 480 image, the lower left coordinate is
00103 //        (0, 0) and the upper right pixel coordinate is (639, 479).
00104 //
00105 //        While most computer imaging packages (e.g. OpenCV) typically
00106 //      use a left-handed coordinate system where the origin is in
00107 //      the upper left,        we will apply a transformation that causes
00108 //      the origin to be placed in the lower left.  All distances are
00109 //      measured in units of pixels.  The reason for using a right
00110 //      handed coordinate system is because trigonometric functions
00111 //      are implicitly right-handed.
00112 //
00113 //    -        The robot coordinate system is another right handed cartesian
00114 //        coordinate system where the origin is located in the center
00115 //        between the two drive wheels.  The X axis of the robot
00116 //        coordinate system goes through the front of the robot.
00117 //      Realistically, the robot coordinate system is not part
00118 //      of the algorithms, instead this coordinate system is called
00119 //      out just to point out that it is there.
00120 //
00121 // Map Organization:
00122 //
00123 // Conceptually there a bunch of square fiducial tags (hereafter called
00124 // just tags) scattered on the floor.  (Yes, we know they are actually
00125 // on the ceiling, we'll deal with that detail later.)  There is a map
00126 // that records for each tag, the following information in an ordered
00127 // quintuple:
00128 //
00129 //    (mtid, mtx, mty, mttw, mtdiag)
00130 //
00131 // where:
00132 //
00133 //    mtid        (Map Tag IDentifier) is the tag identifier (a number between
00134 //              0 and 2^16-1),
00135 //    mtx        (Map Tag X) is the x coordinate of the tag center in
00136 //              floor coordinates,
00137 //    mty        (Map Tag Y) is the y coordinate of the tag center in
00138 //              floor coordinates,
00139 //    mttw        (Map Tag TWist) is the angle (twist) of the bottom
00140 //              tag edge and the floor X axis, and
00141 //    mtdiag        (Map Tag DIAGonal) is the length of a tag diagonal
00142 //              (both diagonals of a square have the same length) in
00143 //              in floor coordinates.
00144 //
00145 // Eventually, the quintuple should probably be upgraded to 1) an
00146 // identifier, 2) an X/Y/Z location and 3) a W/X/Y/Z quaternion of
00147 // for the tag orientation.  That will come later.  For now we will
00148 // just use the quintuple.
00149 //
00150 // Conceptually, the floor is printed out on a large sheet of paper
00151 // with all tags present.  The camera image is printed out on a piece
00152 // of translucent material (e.g. acetate) with the same dimensions
00153 // as the floor sheet.  This translucent image is placed on the floor
00154 // sheet and moved around until the image tag(s) align with the
00155 // corresponding tag(s) on the floor sheet.  The center of translucent
00156 // image is now sitting directly on top of the image location on the
00157 // floor sheet.  The amount of "twist" on the image relate to the
00158 // floor coordinate system complete the location computation.
00159 // Hopefully, this mental image of what we are trying to accomplish
00160 // will help as you wade through the math below.
00161 //
00162 // Conceptually, the camera is going to be placed a fixed distance
00163 // above the floor and take an image of rectangular area of the floor.
00164 // For each tag in the image, the image processing algorithm
00165 // computes the following:
00166 //
00167 //        (camx, camy, camtw)
00168 //
00169 // where:
00170 //
00171 //    camx        (CAMera X) is the camera center X coordinate in the
00172 //                floor coordinate system,
00173 //    camy        (CAMera Y) is the camera center Y coordinate in the
00174 //              floor coordinate system, and
00175 //    camtw        (CAMera TWist) is the angle (twist) of the rectangle bottom
00176 //                tag edge with respect to the floor coordinate system X axis.
00177 //
00178 // If there are multiple tags present in the image, there will be
00179 // multiple (camx, camy, camtw) triples computed.  These triples
00180 // should all be fairly close to one another.  There is a final
00181 // fusion step that fuses them all together into a single result.
00182 //
00183 // Image Tags:
00184 //
00185 // In each image there may be zero, one or more visible tags.  If
00186 // there are zero tags present, there is nothing to work with and
00187 // the camera location can not be computed.  When there are one or
00188 // more tags, we tag detection algorithm computes the following
00189 // information:
00190 //
00191 //    (tid, tc0, tc1, tc2, tc3)
00192 //
00193 //    tid        (Tag IDentifier) is the tag identifier,
00194 //    tc0       (Tag Corner 0) is actually (tc0x, tc0y) the location
00195 //              if the tag corner 0 in image coordinated,
00196 //    tc1       (Tag Corner 1) is actually (tc0x, tc0y) the location
00197 //              if the tag corner 0 in image coordinated,
00198 //    tc2       (Tag Corner 2) is actually (tc0x, tc0y) the location
00199 //              if the tag corner 0 in image coordinated, and
00200 //    tc3       (Tag Corner 3) is actually (tc0x, tc0y) the location
00201 //              if the tag corner 0 in image coordinated.
00202 //
00203 // The 4 tag corners go in a clockwise direction around the tag
00204 // (when viewed from above, of course.)  The tag looks as follows
00205 // in ASCII art:
00206 //
00207 //      +Y
00208 //       ^
00209 //       |
00210 //       | tc2                         tc3
00211 //       |     ** ** ** ** ** ** ** **
00212 //       |     ** ** ** ** ** ** ** **
00213 //       |     ** ** ** ** ** ** ** **
00214 //       |     ** ** ** ** ** ** ** **
00215 //       |     ** ** ** ** ** ** ** **
00216 //       |     ** ** ** ** ** ** ** **
00217 //       |     15 14 13 12 11 10  9  8
00218 //       |      7  6  5  4  3  2  1  0
00219 //       | tc1                         tc0
00220 //       |
00221 //       +--------------------------------> +X
00222 //
00223 // Localization Computation:
00224 //
00225 // The localization algorithm operates as follows:
00226 //
00227 //    foreach tag in image:
00228 //        (tid, tc0, tc1, tc2, tc3) = tag_extract(image)
00229 //        (mtid, mtx, mty, mttw, mtdiag) = Map(tid)
00230 //        (camx, camy, camtw) = F(tc0, tc1, tc2, tc3, mtx, mty, mttw, mtdiag)
00231 //
00232 // Now let's get into the detail of the localization function F().
00233 //
00234 // Using (tc0, tc1, tc2, tc3), we compute the following:
00235 //
00236 //    tagctrx        (TAG CenTeR X) is the tag center X coordinate in
00237 //              image coordinates,
00238 //    tagctry        (TAG CenTeR y) is the tag center X coordinate in
00239 //              image coordinates,
00240 //    tagdiag        (TAG DIAGonal) is the average diagonal length in image
00241 //              coordinates,
00242 //    tagtw        (TAG TWist) is the angle of the lower tag edge with
00243 //              respect to the image coordinate X axis.
00244 //
00245 // These values are computed as follows:
00246 //
00247 //   tagctrx = (tc0x + tc1x + tc2x + tc3x) / 4         # The average of the X's
00248 //   tagctry = (tc0y + tc1y + tc2y + ct3y) / 4         # The average of the Y's
00249 //   tagdiag1 = sqrt( (tc0x-tc2x)^2 + (tc0y-tc2y)^2) ) # First diagonal
00250 //   tagdiag2 = sqrt( (tc1x-tc3x)^2 + (tc1y-tc3y)^2) ) # Second diagonal
00251 //   tagdiag = (tagdiag1 + tagdiag2) / 2               # Avg. of both diagonals
00252 //   tagtw = arctangent2(tc0y - tc1y, tc0x - tc1y)     # Bottom tag edge angle
00253 //
00254 // The image center is defined as:
00255 //
00256 //        (imgctrx, imgctry)
00257 //
00258 // where
00259 //
00260 //    imgctrx        (IMG CenTeR X) is the image center X coordinate in image
00261 //              coordinates, and
00262 //    imgctry        (IMG CenTeR Y) is the image center Y coordinate in image
00263 //              coordinates.
00264 //
00265 // for an image that is 640 x 480, the image center is (320, 240).
00266 // (Actually, it should be (319.5, 239.5).)
00267 //
00268 // Using the image center the following values are computed:
00269 //
00270 //    tagdist           (TAG DISTance) is the distance from the tag center to
00271 //                 image center in image coordinates, and
00272 //    tagbear      (TAG BEARing) is the angle from the tag center to the
00273 //                 image center in image coordinates.
00274 //
00275 // These two values are computed as:
00276 //
00277 //    tagdist = sqrt( (imgctrx - tagctrx)^2 + (imgctry - tagctry)^2) )
00278 //    tagbear = arctangent2( imgctry - tagctry, imgctrx - tagctry)
00279 //
00280 // tagdist is in image coordinate space and we will need the same distance
00281 // in floor coordinate space.  This is done using tagdiag and mtd (the
00282 // map tag diagonal length.)
00283 //
00284 //    flrtagdist = tagdist * (mtd / tagdiag )
00285 //
00286 // where flrtagdist stands for FLooR TAG DISTance.
00287 //
00288 // Now we need to compute to angles that are measured relative to the
00289 // floor X axis:
00290 //
00291 //    camtw        (CAMera TWist) is the direction that the camera X axis points
00292 //                relative to the floor X Axis, and
00293 //    cambear        (CAMera BEARing) is the direction to the camera center relative
00294 //                to the floor X Axis.
00295 //
00296 // camtw and cambear are computed as:
00297 //
00298 //    camtw = mttw - tagtw
00299 //
00300 //    cambear = camtw + tagbear
00301 //
00302 // Now camx, and camy are computed as follows:
00303 //
00304 //    camx = mtx + flrtagdist * cos(cambear)
00305 //    camy = mty + flrtagdist * sin(cambear)
00306 //
00307 // Thus, the final result of (camx, camy, ctw) has been determined.
00308 //
00309 // Mapping into Robot Coordinates:
00310 //
00311 // Once we know the camera location and orientation,
00312 // (camx, camy, camtw), we need to compute the ordered triple:
00313 //
00314 //    (rx, ry, rbear)
00315 //
00316 // where
00317 //
00318 //    rx        (Robot X) is the X coordinate of the robot center in
00319 //              floor coordinates,
00320 //    ry        (Robot Y) is the Y coordinate of the robot center in
00321 //              floor coordinates, and
00322 //    rbear        (Robot BEARing) is the bearing angle of the robot X axis
00323 //              to the floor coordinate X axis.
00324 //
00325 // These values are computed as a function:
00326 //
00327 //    (rx, ry, rbear) = F( (camx, camy, camtw) )
00328 //
00329 // There are two constants needed to do this computation:
00330 //
00331 //    robdist        (ROBot DISTance) is the distance from the camera
00332 //              center to the robot center in floor coordinates, and
00333 //    robcamtw        (ROBot CAMera TWist) is the angle from the angle from
00334 //              camera X axis to the robot X axis.
00335 //
00336 // Both robdist and robcamtw are constants that can be directly measured
00337 // from the camera placement relative to the robot origin.
00338 //
00339 // Now (rx, ry, rtw) can be computed as follows:
00340 //
00341 //    rtw = camtw + robcamtw
00342 //    rx = camx + robdist * cos(rcamtw)
00343 //    ry = camy + robdist * sin(rcamtw)
00344 //
00345 // That covers the localization processing portion of the algorithm.
00346 //
00347 // Fusing with Dead Reckoning:
00348 //
00349 // The robot dead reckoning system keeps track of the robot position
00350 // using wheel encoders.  These values are represented in the ordered
00351 // triple:
00352 //
00353 //    (ex, ey, etw)
00354 //
00355 // where
00356 //
00357 //    ex        (Encoder X) is the robot X coordinate in floor coordinates,
00358 //    ey        (Encoder Y) is the robot Y coordinate in floor coordinates, and
00359 //    etw        (Encoder TWist) is the robot X axis twist relative to the
00360 //              floor coordinate X axis.
00361 //
00362 // (rx, ry, rtw) and (ex, ey, etw) are supposed to be the same.  Over
00363 // time, small errors will accumulate in the computation of (ex, ey, etw).
00364 // (rx, ry, rtw) can be used to reset (ex, ey, etw).
00365 //
00366 // Coordinate Space Transforms:
00367 //
00368 // That pretty much summarizes what the basic algorithm does.
00369 //
00370 // What remains is to do the transformations that place the tags
00371 // on the ceiling.  There are three transformations.
00372 //
00373 //    tags lift                The tags are lifted straight up from the floor
00374 //                        to the ceiling.  The person looking on down
00375 //                        from above would see no changes in tag position
00376 //                        or orientation (ignoring parallax issues.)
00377 //
00378 //    camera flip        The camera is rotated 180 degrees around the
00379 //                        camera X axis.  The causes the Y axis to change
00380 //                        its sign.
00381 //
00382 //    image framing        For historical reasons, the camera image API
00383 //                        provides the camera image with the image origin
00384 //                        in upper left hand corner, whereas the all of
00385 //                        the math above assumes that image origin is
00386 //                        in the lower left corner.  It turns out this
00387 //                        is just changes the Y axis sign again.
00388 //
00389 // The bottom line is that the "camera flip" and the "image framing"
00390 // transformation cancel one another out.  Thus, the there is no
00391 // work needed to tweak the equations above.
00392 
00393 
00410 
00411 // FIXME: Why isn't the from_twist and to_twist included???!!!
00412 // FIXME: Why don't we just pass the *Arc* object???!!!
00413 
00414 void Fiducials__arc_announce(void *announce_object,
00415   int from_id, double from_x, double from_y, double from_z,
00416   int to_id, double to_x, double to_y, double to_z,
00417   double goodness, bool in_spanning_tree) {
00418     File__format(stderr,
00419       "Arc: from=(%d, %f, %f, %f,) to=(%d, %f, %f, %f) %f %d\n",
00420       from_id, from_x, from_y, from_z,
00421       to_id, to_x, to_y, to_z,
00422       goodness, in_spanning_tree);
00423 }
00424 
00436 
00437 //FIXME: Why don't we just pass in a *Location* object???!!!
00438 
00439 void Fiducials__location_announce(void *announce_object, int id,
00440   double x, double y, double z, double bearing) {
00441     File__format(stderr,
00442       "Location: id=%d x=%f y=%f bearing=%f\n", id, x, y, bearing);
00443 }
00444 
00463 
00464 void Fiducials__fiducial_announce(void *announce_object,
00465     int id, int direction, double world_diagonal,
00466     double x1, double y1, double x2, double y2,
00467     double x3, double y3, double x4, double y4) {
00468     File__format(stderr,
00469        "Fiducial: id=%d dir=%d diag=%.2f (%.2f,%.2f), " /* + */
00470        "(%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)",
00471        id, direction, world_diagonal, x1, y1, x2, y2, x3, y3, x4, y4);
00472 }
00473 
00480 
00481 void Fiducials__image_set(Fiducials fiducials, CV_Image image)
00482 {
00483     fiducials->original_image = image;
00484 }
00485 
00502 
00503 //FIXME: Is there any point to having *show* set to false???!!!
00504 
00505 void Fiducials__image_show(Fiducials fiducials, bool show) {
00506     // Grab some values out of *fiduicals*:
00507     CV_Image debug_image = fiducials->debug_image;
00508     CV_Image gray_image = fiducials->gray_image;
00509     CV_Image original_image = fiducials->original_image;
00510 
00511     // Create the window we need:
00512     String_Const window_name = "Example1";
00513     if (show) {
00514         cvNamedWindow(window_name, CV__window_auto_size);
00515     }
00516 
00517     // Processing *original_image* with different options
00518     // for each time through the loop:
00519     unsigned int debug_index = 0;
00520     unsigned int previous_debug_index = debug_index;
00521     bool done = (bool)0;
00522     while (!done) {
00523         // Process {gray_image}; a debug image lands in {debug_image}:
00524         Fiducials__process(fiducials);
00525 
00526         // Display either *original_image* or *debug_image*:
00527         if (show) {
00528             cvShowImage(window_name, debug_image);
00529         }
00530 
00531         // Get a *control_character* from the user:
00532         char control_character = '\0';
00533         if (show) {
00534             control_character = (char)(cvWaitKey(0) & 0xff);
00535         }
00536 
00537         // Dispatch on *control_character*:
00538         switch (control_character) {
00539           case '\33':
00540             //# Exit program:
00541             done = (bool)1;
00542             File__format(stderr, "done\n");
00543             break;
00544           case '+':
00545             //# Increment {debug_index}:
00546             debug_index += 1;
00547             break;
00548           case '-':
00549             // Decrement {debug_index}:
00550             if (debug_index > 0) {
00551                 debug_index -= 1;
00552             }
00553             break;
00554           case '<':
00555             // Set {debug_index} to beginning:
00556             debug_index = 0;
00557             break;
00558           case '>':
00559             // Set {debug_index} to end:
00560             debug_index = 100;
00561             break;
00562           case 'b':
00563             // Toggle image blur:
00564             fiducials->blur = !fiducials->blur;
00565             File__format(stderr, "blur = %d\n", fiducials->blur);
00566             break;
00567           case 'f':
00568             // Toggle image blur:
00569             fiducials->y_flip = !fiducials->y_flip;
00570             File__format(stderr, "y_flip = %d\n", fiducials->y_flip);
00571             break;
00572           default:
00573             // Deal with unknown {control_character}:
00574             if ((unsigned int)control_character <= 127) {
00575                 File__format(stderr,
00576                   "Unknown control character %d\n", control_character);
00577             }
00578             break;
00579         }
00580 
00581         // Update *debug_index* in *fiducials*:
00582         fiducials->debug_index = debug_index;
00583 
00584         // Show user *debug_index* if it has changed:
00585         if (debug_index != previous_debug_index) {
00586           File__format(stderr,
00587             "****************************debug_index = %d\n", debug_index);
00588           previous_debug_index = debug_index;
00589         }
00590     }
00591 
00592     // Release storage:
00593     CV__release_image(original_image);
00594     if (show) {
00595         cvDestroyWindow(window_name);
00596     }
00597 }
00598 
00606 
00607 //FIXME: Change this code so that the image size is determined from
00608 // the first image that is processed.  This allows the image size
00609 // to change in midstream!!!
00610 
00611 Fiducials Fiducials__create(
00612   CV_Image original_image, Fiducials_Create fiducials_create)
00613 {
00614     // Create *image_size*:
00615     unsigned int width = CV_Image__width_get(original_image);
00616     unsigned int height = CV_Image__height_get(original_image);
00617     CV_Size image_size = CV_Size__create(width, height);
00618     CV_Memory_Storage storage = CV_Memory_Storage__create(0);
00619 
00620     // Grab some values from *fiducials_create*:
00621     String_Const fiducials_path = fiducials_create->fiducials_path;
00622     String_Const lens_calibrate_file_name =
00623       fiducials_create->lens_calibrate_file_name;
00624     Memory announce_object = fiducials_create->announce_object;
00625     Fiducials_Arc_Announce_Routine arc_announce_routine =
00626       fiducials_create->arc_announce_routine;
00627     Fiducials_Location_Announce_Routine location_announce_routine =
00628       fiducials_create->location_announce_routine;
00629     Fiducials_Tag_Announce_Routine tag_announce_routine =
00630       fiducials_create->tag_announce_routine;
00631     Fiducials_Fiducial_Announce_Routine  fiducial_announce_routine = 
00632       fiducials_create->fiducial_announce_routine; 
00633     String_Const log_file_name = fiducials_create->log_file_name;
00634     String_Const map_base_name = fiducials_create->map_base_name;
00635     String_Const tag_heights_file_name =
00636       fiducials_create->tag_heights_file_name;
00637 
00638     // Get *log_file* open if *log_file_name* is not null:
00639     File log_file = stderr;
00640     if (log_file_name != (String_Const)0) {
00641         String full_log_file_name =
00642           String__format("%s/%s", fiducials_path, log_file_name);
00643         log_file = File__open(log_file_name, "w");
00644         String__free(full_log_file_name);
00645     }
00646     File__format(log_file, "CV width=%d CV height = %d\n", width, height);
00647 
00648     int term_criteria_type =
00649       CV__term_criteria_iterations | CV__term_criteria_eps;
00650 
00651     // The north/west/south/east mappings must reside in static
00652     // memory rather than on the stack:
00653 
00654     static int north_mapping[64] = {
00655         //corner1              corner0
00656          0,  1,  2,  3,  4,  5,  6,  7,
00657          8,  9, 10, 11, 12, 13, 14, 15,
00658         16, 17, 18, 19, 20, 21, 22, 23,
00659         24, 25, 26, 27, 28, 29, 30, 31,
00660         32, 33, 34, 35, 36, 37, 38, 39,
00661         40, 41, 42, 43, 44, 45, 46, 47,
00662         48, 49, 50, 51, 52, 53, 54, 55,
00663         56, 57, 58, 59, 60, 61, 62, 63,
00664         //corner2              corner3
00665     };
00666 
00667     static int west_mapping[64] = {
00668         //corner1              corner0
00669          7, 15, 23, 31, 39, 47, 55, 63,
00670          6, 14, 22, 30, 38, 46, 54, 62,
00671          5, 13, 21, 29, 37, 45, 53, 61,
00672          4, 12, 20, 28, 36, 44, 52, 60,
00673          3, 11, 19, 27, 35, 43, 51, 59,
00674          2, 10, 18, 26, 34, 42, 50, 58,
00675          1,  9, 17, 25, 33, 41, 49, 57,
00676          0,  8, 16, 24, 32, 40, 48, 56,
00677         //corner2              corner3
00678     };
00679 
00680     static int south_mapping[64] = {
00681         //corner1              corner0
00682         63, 62, 61, 60, 59, 58, 57, 56,
00683         55, 54, 53, 52, 51, 50, 49, 48,
00684         47, 46, 45, 44, 43, 42, 41, 40,
00685         39, 38, 37, 36, 35, 34, 33, 32,
00686         31, 30, 29, 28, 27, 26, 25, 24,
00687         23, 22, 21, 20, 19, 18, 17, 16,
00688         15, 14, 13, 12, 11, 10,  9,  8,
00689          7,  6,  5,  4,  3,  2,  1,  0,
00690         //corner2              corner3
00691     };
00692 
00693     static int east_mapping[64] = {
00694         //corner1              corner0
00695         56, 48, 40, 32, 24, 16,  8,  0,
00696         57, 49, 41, 33, 25, 17,  9,  1,
00697         58, 50, 42, 34, 26, 18, 10,  2,
00698         59, 51, 43, 35, 27, 19, 11,  3,
00699         60, 52, 44, 36, 28, 20, 12,  4,
00700         61, 53, 45, 37, 29, 21, 13,  5,
00701         62, 54, 46, 38, 30, 22, 14,  6,
00702         63, 55, 47, 39, 31, 23, 15,  7,
00703         //corner2              corner3
00704     };
00705 
00706     static int north_mapping_flipped[64] = {
00707         //corner1              corner0
00708          7,  6,  5,  4,  3,  2,  1,  0,
00709         15, 14, 13, 12, 11, 10,  9,  8,
00710         23, 22, 21, 20, 19, 18, 17, 16,
00711         31, 30, 29, 28, 27, 26, 25, 24,
00712         39, 38, 37, 36, 35, 34, 33, 32,
00713         47, 46, 45, 44, 43, 42, 41, 40,
00714         55, 54, 53, 52, 51, 50, 49, 48,
00715         63, 62, 61, 60, 59, 58, 57, 56,
00716          //corner2              corner3
00717     };
00718 
00719     static int west_mapping_flipped[64] = {
00720         //corner1              corner0
00721         63, 55, 47, 39, 31, 23, 15, 7,
00722         62, 54, 46, 38, 30, 22, 14, 6,
00723         61, 53, 45, 37, 29, 21, 13, 5,
00724         60, 52, 44, 36, 28, 20, 12, 4,
00725         59, 51, 43, 35, 27, 19, 11, 3,
00726         58, 50, 42, 34, 26, 18, 10, 2,
00727         57, 49, 41, 33, 25, 17,  9, 1,
00728         56, 48, 40, 32, 24, 16,  8, 0,
00729         //corner2              corner3
00730     };
00731 
00732     static int south_mapping_flipped[64] = {
00733         //corner1              corner0
00734         56, 57, 58, 59, 60, 61, 62, 63, 
00735         48, 49, 50, 51, 52, 53, 54, 55,
00736         40, 41, 42, 43, 44, 45, 46, 47,
00737         32, 33, 34, 35, 36, 37, 38, 39,
00738         24, 25, 26, 27, 28, 29, 30, 31,
00739         16, 17, 18, 19, 20, 21, 22, 23,
00740          8,  9, 10, 11, 12, 13, 14, 15, 
00741          0,  1,  2,  3,  4,  5,  6,  7,
00742         //corner2              corner3
00743     };
00744 
00745     static int east_mapping_flipped[64] = {
00746         //corner1              corner0
00747          0,  8, 16, 24, 32, 40, 48, 56,
00748          1,  9, 17, 25, 33, 41, 49, 57,
00749          2, 10, 18, 26, 34, 42, 50, 58,
00750          3, 11, 19, 27, 35, 43, 51, 59,
00751          4, 13, 20, 28, 36, 44, 52, 60,
00752          5, 13, 21, 29, 37, 45, 53, 61,
00753          6, 14, 22, 30, 38, 46, 54, 62,
00754          7, 15, 23, 31, 39, 47, 55, 63,
00755          //corner2              corner3
00756     };
00757 
00758     // The north/west/south/east mappings must reside in static
00759     // memory rather than on the stack:
00760 
00761     static int *mappings[4] = {
00762         &north_mapping_flipped[0],
00763         &west_mapping_flipped[0],
00764         &south_mapping_flipped[0],
00765         &east_mapping_flipped[0],
00766     };
00767 
00768     //for (unsigned int index = 0; index < 4; index++) {
00769     //        File__format(log_file, "mappings[%d]=0x%x\n", index, mappings[index]);
00770     //}
00771 
00772     CV_Image map_x = (CV_Image)0;
00773     CV_Image map_y = (CV_Image)0;
00774     if (lens_calibrate_file_name != (String)0) {
00775         String full_lens_calibrate_file_name =
00776           String__format("%s/%s", fiducials_path, lens_calibrate_file_name);
00777         assert (CV__undistortion_setup(
00778           full_lens_calibrate_file_name, width, height, &map_x, &map_y) == 0);
00779         String__free(full_lens_calibrate_file_name);
00780     }
00781 
00782     // Create the *map*:
00783     Map map = (Map)0;
00784     if (fiducials_create->do_2d_slam) {
00785         Map map = Map__create(fiducials_path, map_base_name, announce_object,
00786           arc_announce_routine, tag_announce_routine,
00787           tag_heights_file_name, "Fiducials__new:Map__create");
00788      }
00789 
00790     Fiducials_Results results =
00791       Memory__new(Fiducials_Results, "Fiducials__create");
00792     results->map_changed = (bool)0;
00793 
00794     // Create and load *fiducials*:
00795     Fiducials fiducials = Memory__new(Fiducials, "Fiducials__create");
00796     fiducials = new(fiducials) Fiducials__Struct();
00797     fiducials->arc_announce_routine = arc_announce_routine;
00798     if (fiducial_announce_routine != NULL) 
00799        fiducials->fiducial_announce_routine = fiducial_announce_routine;
00800     else
00801        fiducials->fiducial_announce_routine = Fiducials__fiducial_announce;
00802     fiducials->announce_object = announce_object;
00803     fiducials->blue = CV_Scalar__rgb(0.0, 0.0, 1.0);
00804     fiducials->blur = (bool)0;
00805     fiducials->corners = CV_Point2D32F_Vector__create(4);
00806     fiducials->cyan = CV_Scalar__rgb(0.0, 1.0, 1.0);
00807     fiducials->debug_image = CV_Image__create(image_size, CV__depth_8u, 3);
00808     fiducials->debug_index = 0;
00809     fiducials->edge_image = CV_Image__create(image_size, CV__depth_8u, 1);
00810     fiducials->fec = FEC__create(8, 4, 4);
00811     fiducials->gray_image = CV_Image__create(image_size, CV__depth_8u, 1);
00812     fiducials->green = CV_Scalar__rgb(0.0, 255.0, 0.0);
00813     fiducials->image_size = image_size;
00814     fiducials->last_x = 0.0;
00815     fiducials->last_y = 0.0;
00816     fiducials->location_announce_routine = location_announce_routine;
00817     fiducials->log_file = log_file;
00818     fiducials->map = map;
00819     fiducials->map_x = map_x;
00820     fiducials->map_y = map_y;
00821     fiducials->mappings = &mappings[0];
00822     fiducials->origin = CV_Point__create(0, 0);
00823     fiducials->original_image = original_image;
00824     fiducials->path = fiducials_path;
00825     fiducials->purple = CV_Scalar__rgb(255.0, 0.0, 255.0);
00826     fiducials->red = CV_Scalar__rgb(255.0, 0.0, 0.0);
00827     fiducials->references = CV_Point2D32F_Vector__create(8);
00828     fiducials->results = results;
00829     fiducials->sample_points = CV_Point2D32F_Vector__create(64);
00830     fiducials->size_5x5 = CV_Size__create(5, 5);
00831     fiducials->size_m1xm1 = CV_Size__create(-1, -1);
00832     fiducials->sequence_number = 0;
00833     fiducials->storage = storage;
00834     fiducials->temporary_gray_image =
00835       CV_Image__create(image_size, CV__depth_8u, 1);
00836     fiducials->weights_index = 0;
00837     fiducials->term_criteria = 
00838       CV_Term_Criteria__create(term_criteria_type, 5, 0.2);
00839     fiducials->y_flip = (bool)0;
00840     fiducials->black = CV_Scalar__rgb(0, 0, 0);
00841 
00842     return fiducials;
00843 }
00844 
00849 
00850 void Fiducials__free(Fiducials fiducials) {
00851     // Write the map out if it changed:
00852     if (fiducials->map)
00853       Map__save(fiducials->map);
00854 
00855     // Free up some *CV_Scalar* colors:
00856     CV_Scalar__free(fiducials->blue);
00857     CV_Scalar__free(fiducials->cyan);
00858     CV_Scalar__free(fiducials->green);
00859     CV_Scalar__free(fiducials->purple);
00860     CV_Scalar__free(fiducials->red);
00861     CV_Scalar__free(fiducials->black);
00862 
00863     // Free up some *SV_Size* objects:
00864     CV_Size__free(fiducials->image_size);
00865     CV_Size__free(fiducials->size_5x5);
00866     CV_Size__free(fiducials->size_m1xm1);
00867 
00868     // Free up the storage associated with *locations*:
00869     unsigned int locations_size = fiducials->locations.size();
00870     for (unsigned int index = 0; index < locations_size; index++) {
00871         Location * location = fiducials->locations[index];
00872         // Kludge: memory double free?!!!
00873         // delete location;
00874     }
00875 
00876     unsigned int locations_path_size = fiducials->locations_path.size();
00877     for (unsigned int index = 0; index < locations_path_size; index++) {
00878         Location * location = fiducials->locations_path[index];
00879         // Kludge: memory double free?!!!
00880         //Location__free(location);
00881     }
00882 
00883     // Relaase the *Map*:
00884     if (fiducials->map)
00885       Map__free(fiducials->map);
00886 
00887     // Finally release *fiducials*:
00888     Memory__free((Memory)fiducials);
00889 }
00890 
00896 
00897 void Fiducials__map_save(Fiducials fiducials) {
00898     if (fiducials->map)
00899       Map__save(fiducials->map);
00900 }
00901 
00909 
00910 Fiducials_Results Fiducials__process(Fiducials fiducials) {
00911     // Clear *storage*:
00912     CV_Memory_Storage storage = fiducials->storage;
00913     CV_Memory_Storage__clear(storage);
00914 
00915     // Grab some values from *fiducials*:
00916     CV_Image debug_image = fiducials->debug_image;
00917     unsigned int debug_index = fiducials->debug_index;
00918     CV_Image edge_image = fiducials->edge_image;
00919     CV_Image gray_image = fiducials->gray_image;
00920     File log_file = fiducials->log_file;
00921     CV_Image original_image = fiducials->original_image;
00922     Fiducials_Results results = fiducials->results;
00923     CV_Image temporary_gray_image = fiducials->temporary_gray_image;
00924     Fiducials_Location_Announce_Routine location_announce_routine =
00925       fiducials->location_announce_routine;
00926     unsigned int sequence_number = fiducials->sequence_number++;
00927 
00928     // For *debug_level* 0, we show the original image in color:
00929     if (debug_index == 0) {
00930         CV_Image__copy(original_image, debug_image, (CV_Image)0);
00931     }
00932 
00933     // Convert from color to gray scale:
00934     int channels = CV_Image__channels_get(original_image);
00935 
00936     // Deal with *debug_index* 0:
00937     if (debug_index == 0) {
00938         if (channels == 3) {
00939             // Original image is color, so a simple copy will work:
00940             CV_Image__copy(original_image, debug_image, (CV_Image)0);
00941         } else if (channels == 1) {
00942             // Original image is gray, so we have to convert back to "color":
00943             CV_Image__convert_color(original_image,
00944               debug_image, CV__gray_to_rgb);
00945         }
00946     }
00947 
00948     // Convert *original_image* to gray scale:
00949     if (channels == 3) {
00950         // Original image is color, so we need to convert to gray scale:
00951         CV_Image__convert_color(original_image, gray_image, CV__rgb_to_gray);
00952     } else if (channels == 1) {
00953         // Original image is gray, so a simple copy will work:
00954         CV_Image__copy(original_image, gray_image, (CV_Image)0);
00955     } else {
00956         assert(0);
00957     }
00958 
00959     // Show results of gray scale converion for *debug_index* 1:
00960     if (debug_index == 1) {
00961         CV_Image__convert_color(gray_image, debug_image, CV__gray_to_rgb);
00962     }
00963     
00964     // Preform undistort if available:
00965     if (fiducials->map_x != (CV_Image)0) {
00966         int flags = CV_INTER_NN | CV_WARP_FILL_OUTLIERS;
00967         CV_Image__copy(gray_image, temporary_gray_image, (CV_Image)0);
00968         CV_Image__remap(temporary_gray_image, gray_image,
00969           fiducials->map_x, fiducials->map_y, flags, fiducials->black);
00970     }
00971 
00972     // Show results of undistort:
00973     if (debug_index == 2) {
00974         CV_Image__convert_color(gray_image, debug_image, CV__gray_to_rgb);
00975     }
00976 
00977     // Perform Gaussian blur if requested:
00978     if (fiducials->blur) {
00979         CV_Image__smooth(gray_image, gray_image, CV__gaussian, 3, 0, 0.0, 0.0);
00980     }
00981 
00982     // Show results of Gaussian blur for *debug_index* 2:
00983     if (debug_index == 3) {
00984         CV_Image__convert_color(gray_image, debug_image, CV__gray_to_rgb);
00985     }
00986 
00987     // Perform adpative threshold:
00988     CV_Image__adaptive_threshold(gray_image, edge_image, 255.0,
00989       CV__adaptive_thresh_gaussian_c, CV__thresh_binary, 45, 5.0);
00990 
00991     // Show results of adaptive threshold for *debug_index* 3:
00992     if (debug_index == 4) {
00993         CV_Image__convert_color(edge_image, debug_image, CV__gray_to_rgb);
00994     }
00995 
00996     // Find the *edge_image* *contours*:
00997     CV_Point origin = fiducials->origin;
00998     int header_size = 128;
00999     CV_Sequence contours = CV_Image__find_contours(edge_image, storage,
01000       header_size, CV__retr_list, CV__chain_approx_simple, origin);
01001     if (contours == (CV_Sequence)0) {
01002         File__format(log_file, "no contours found\n");
01003     }
01004 
01005     // For *debug_index* 4, show the *edge_image* *contours*:
01006     if (debug_index == 5) {
01007         //File__format(log_file, "Draw red contours\n");
01008         CV_Scalar red = fiducials->red;
01009         CV_Image__convert_color(gray_image, debug_image, CV__gray_to_rgb);
01010         CV_Image__draw_contours(debug_image,
01011           contours, red, red, 2, 2, 8, origin);
01012     }
01013 
01014     // For the remaining debug steps, we use the original *gray_image*:
01015     if (debug_index >= 5) {
01016         CV_Image__convert_color(gray_image, debug_image, CV__gray_to_rgb);
01017     }
01018 
01019     // Iterate over all of the *contours*:
01020     Map map = fiducials->map;
01021     unsigned int contours_count = 0;
01022     for (CV_Sequence contour = contours; contour != (CV_Sequence)0;
01023       contour = CV_Sequence__next_get(contour)) {
01024         // Keep a count of total countours:
01025         contours_count += 1;
01026         //File__format(log_file, "contours_count=%d\n", contours_count);
01027 
01028         static CvSlice whole_sequence;
01029         CV_Slice CV__whole_seq = &whole_sequence;
01030         whole_sequence = CV_WHOLE_SEQ;
01031 
01032         // Perform a polygon approximation of {contour}:
01033         int arc_length =
01034           (int)(CV_Sequence__arc_length(contour, CV__whole_seq, 1) * 0.02);
01035         CV_Sequence polygon_contour =
01036           CV_Sequence__approximate_polygon(contour,
01037           header_size, storage, CV__poly_approx_dp, arc_length, 0.0);
01038         if (debug_index == 6) {
01039             //File__format(log_file, "Draw green contours\n");
01040             CV_Scalar green = fiducials->green;
01041             CV_Image__draw_contours(debug_image,
01042               polygon_contour, green, green, 2, 2, 1, origin);
01043         }
01044 
01045         // If we have a 4-sided polygon with an area greater than 500 square
01046         // pixels, we can explore to see if we have a tag:
01047         if (CV_Sequence__total_get(polygon_contour) == 4 &&
01048           fabs(CV_Sequence__contour_area(polygon_contour,
01049           CV__whole_seq, 0)) > 500.0 &&
01050           CV_Sequence__check_contour_convexity(polygon_contour)) {
01051             // For debugging, display the polygons in red:
01052             //File__format(log_file, "Have 4 sides > 500i\n");
01053 
01054             // Just show the fiducial outlines for *debug_index* of 6:
01055             if (debug_index == 7) {
01056                 CV_Scalar red = fiducials->red;
01057                 CV_Image__draw_contours(debug_image,
01058                   polygon_contour, red, red, 2, 2, 1, origin);
01059             }
01060 
01061             // Copy the 4 corners from {poly_contour} to {corners}:
01062             CV_Point2D32F_Vector corners = fiducials->corners;
01063             for (unsigned int index = 0; index < 4; index++) {
01064                 CV_Point2D32F corner =
01065                   CV_Point2D32F_Vector__fetch1(corners, index);
01066                 CV_Point point =
01067                   CV_Sequence__point_fetch1(polygon_contour, index);
01068                 CV_Point2D32F__point_set(corner, point);
01069 
01070                 if (debug_index == 7) {
01071                     //File__format(log_file,
01072                     //  "point[%d] x:%f y:%f\n", index, point->x, point->y);
01073                 }
01074             }
01075 
01076             // Now find the sub pixel corners of {corners}:
01077             CV_Image__find_corner_sub_pix(gray_image, corners, 4,
01078               fiducials->size_5x5, fiducials->size_m1xm1,
01079               fiducials->term_criteria);
01080 
01081             // Ensure that the corners are in a counter_clockwise direction:
01082             CV_Point2D32F_Vector__corners_normalize(corners);
01083 
01084             // For debugging show the 4 corners of the possible tag where
01085             //corner0=red, corner1=green, corner2=blue, corner3=purple:
01086             if (debug_index == 8) {
01087                 for (unsigned int index = 0; index < 4; index++) {
01088                     CV_Point point =
01089                       CV_Sequence__point_fetch1(polygon_contour, index);
01090                     int x = CV_Point__x_get(point);
01091                     int y = CV_Point__y_get(point);
01092                     CV_Scalar color = (CV_Scalar)0;
01093                     String_Const text = (String)0;
01094                     switch (index) {
01095                       case 0:
01096                         color = fiducials->red;
01097                         text = "red";
01098                         break;
01099                       case 1:
01100                         color = fiducials->green;
01101                         text = "green";
01102                         break;
01103                       case 2:
01104                         color = fiducials->blue;
01105                         text = "blue";
01106                         break;
01107                       case 3:
01108                         color = fiducials->purple;
01109                         text = "purple";
01110                         break;
01111                       default:
01112                         assert(0);
01113                     }
01114                     CV_Image__cross_draw(debug_image, x, y, color);
01115                     File__format(log_file,
01116                       "poly_point[%d]=(%d:%d) %s\n", index, x, y, text);
01117                 }
01118             }
01119 
01120             // Compute the 8 reference points for deciding whether the
01121             // polygon is "tag like" in its borders:
01122             CV_Point2D32F_Vector references =
01123               Fiducials__references_compute(fiducials, corners);
01124 
01125             // Now sample the periphery of the tag and looking for the
01126             // darkest white value (i.e. minimum) and the lightest black
01127             // value (i.e. maximum):
01128             //int white_darkest =
01129             //  CV_Image__points_minimum(gray_image, references, 0, 3);
01130             //int black_lightest =
01131             //  CV_Image__points_maximum(gray_image, references, 4, 7);
01132             int white_darkest =
01133               Fiducials__points_minimum(fiducials, references, 0, 3);
01134             int black_lightest =
01135               Fiducials__points_maximum(fiducials, references, 4, 7);
01136 
01137             // {threshold} should be smack between the two:
01138             int threshold = (white_darkest + black_lightest) / 2;
01139             
01140             // For debugging, show the 8 points that are sampled around the
01141             // the tag periphery to even decide whether to do further testing.
01142             // Show "black" as green crosses, and "white" as green crosses:
01143             if (debug_index == 9) {
01144                 CV_Scalar red = fiducials->red;
01145                 CV_Scalar green = fiducials->green;
01146                 for (unsigned int index = 0; index < 8; index++) {
01147                     CV_Point2D32F reference =
01148                       CV_Point2D32F_Vector__fetch1(references, index);
01149                     int x = CV__round(CV_Point2D32F__x_get(reference));
01150                     int y = CV__round(CV_Point2D32F__y_get(reference));
01151                     //int value =
01152                     //  CV_Image__point_sample(gray_image, reference);
01153                     int value =
01154                       Fiducials__point_sample(fiducials, reference);
01155                     CV_Scalar color = red;
01156                     if (value < threshold) {
01157                         color = green;
01158                     }
01159                     CV_Image__cross_draw(debug_image, x, y, color);
01160                     File__format(log_file, "ref[%d:%d]:%d\n", x, y, value);
01161                 }
01162             }
01163 
01164             // If we have enough contrast keep on trying for a tag match:
01165             if (black_lightest < white_darkest) {
01166                 // We have a tag to try:
01167 
01168                 // Now it is time to read all the bits of the tag out:
01169                 CV_Point2D32F_Vector sample_points = fiducials->sample_points;
01170 
01171                 // Now compute the locations to sample for tag bits:
01172                 Fiducials__sample_points_compute(corners, sample_points);
01173 
01174                 // Extract all 64 tag bit values:
01175                 bool *tag_bits = &fiducials->tag_bits[0];
01176                 for (unsigned int index = 0; index < 64; index++) {
01177                     // Grab the pixel value and convert into a {bit}:
01178                     CV_Point2D32F sample_point =
01179                       CV_Point2D32F_Vector__fetch1(sample_points, index);
01180                     //int value =
01181                     //  CV_Image__point_sample(gray_image, sample_point);
01182                     int value =
01183                       Fiducials__point_sample(fiducials, sample_point);
01184                     int bit = (value < threshold);
01185                     tag_bits[index] = bit;
01186 
01187                     // For debugging:
01188                     if (debug_index == 10) {
01189                         CV_Scalar red = fiducials->red;
01190                         CV_Scalar green = fiducials->green;
01191                         CV_Scalar cyan = fiducials->cyan;
01192                         CV_Scalar blue = fiducials->blue;
01193 
01194                         // Show white bits as {red} and black bits as {green}:
01195                         CV_Scalar color = red;
01196                         if (bit) {
01197                             color = green;
01198                         }
01199 
01200                         // Show where bit 0 and 7 are:
01201                         //if (index == 0) {
01202                         //    // Bit 0 is {cyan}:
01203                         //    color = cyan;
01204                         //}
01205                         //if (index == 7) {
01206                         //    // Bit 7 is {blue}:
01207                         //    color = blue;
01208                         //}
01209 
01210                         // Now splat a cross of {color} at ({x},{y}):
01211                         int x =
01212                           CV__round(CV_Point2D32F__x_get(sample_point));
01213                         int y =
01214                           CV__round(CV_Point2D32F__y_get(sample_point));
01215                         CV_Image__cross_draw(debug_image, x, y, color);
01216                     }
01217                 }
01218 
01219                 //tag_bits :@= extractor.tag_bits
01220                 //bit_field :@= extractor.bit_field
01221                 //tag_bytes :@= extractor.tag_bytes
01222 
01223                 // Now we iterate through the 4 different mapping
01224                 // orientations to see if any one of the 4 mappings match:
01225                 int **mappings = fiducials->mappings;
01226                 unsigned int mappings_size = 4;
01227                 for (unsigned int direction_index = 0;
01228                   direction_index < mappings_size; direction_index++) {
01229                     // Grab the mapping:
01230                     int *mapping = mappings[direction_index];
01231                     //File__format(log_file,
01232                     //  "mappings[%d]:0x%x\n", direction_index, mapping);
01233 
01234 
01235                     int mapped_bits[64];
01236                     for (unsigned int i = 0; i < 64; i++) {
01237                          mapped_bits[mapping[i]] = tag_bits[i];
01238                     }
01239 
01240                     // Fill in tag bytes;
01241                     unsigned int tag_bytes[8];
01242                     for (unsigned int i = 0; i < 8; i++) {
01243                         unsigned int byte = 0;
01244                         for (unsigned int j = 0; j < 8; j++) {
01245                             if (mapped_bits[(i<<3) + j]) {
01246                                 //byte |= 1 << j;
01247                                 byte |= 1 << (7 - j);
01248                             }
01249                         }
01250                         tag_bytes[i] = byte;
01251                     }
01252                     if (debug_index == 11) {
01253                         File__format(log_file,
01254                           "dir=%d Tag[0]=0x%x Tag[1]=0x%x\n",
01255                           direction_index, tag_bytes[0], tag_bytes[1]);
01256                     }
01257 
01258                     // Now we need to do some FEC (Forward Error Correction):
01259                     FEC fec = fiducials->fec;
01260                     if (FEC__correct(fec, tag_bytes, 8)) {
01261                         // We passed FEC:
01262                         if (debug_index == 11) {
01263                             File__format(log_file, "FEC correct\n");
01264                         }
01265 
01266                         // Now see if the two CRC's match:
01267                         unsigned int computed_crc = CRC__compute(tag_bytes, 2);
01268                         unsigned int tag_crc = (tag_bytes[3] << 8) | tag_bytes[2];
01269                         if (computed_crc == tag_crc) {
01270                             // Yippee!!! We have a tag:
01271                             // Compute {tag_id} from the the first two bytes
01272                             // of {tag_bytes}:
01273                             unsigned int tag_id =
01274                               (tag_bytes[1] << 8) | tag_bytes[0];
01275 
01276                             if (debug_index == 11) {
01277                                 File__format(log_file,
01278                                   "CRC correct, Tag=%d\n", tag_id);
01279                             }
01280 
01281                             // Allocate a *camera_tag*:
01282                             CameraTag * camera_tag = new CameraTag();
01283 
01284                             double vertices[4][2];
01285                             for (unsigned int index = 0; index < 4; index++) {
01286                               CV_Point2D32F pt = CV_Point2D32F_Vector__fetch1(corners, index);
01287                               vertices[index][0] = pt->x;
01288                               vertices[index][1] = pt->y;
01289                             }                            
01290                             fiducials->fiducial_announce_routine(
01291                                 fiducials->announce_object, tag_id,
01292                                 direction_index, 0.0,
01293                                 vertices[0][0], vertices[0][1],
01294                                 vertices[1][0], vertices[1][1],
01295                                 vertices[2][0], vertices[2][1],
01296                                 vertices[3][0], vertices[3][1]);
01297 
01298                             if (map) {
01299                                 // Load up *camera_tag* to get center, twist, etc.:
01300                                 Tag * tag = Map__tag_lookup(map, tag_id);
01301                                 if (debug_index == 11) {
01302                                     camera_tag->initialize(tag,
01303                                       direction_index, corners, debug_image);
01304                                 } else {
01305                                     camera_tag->initialize(tag,
01306                                       direction_index, corners, (CV_Image)0);
01307                                 }
01308                                 fiducials->current_visibles.push_back(tag);
01309                                 File__format(log_file, "Tag: %d x=%f y=%f\n",
01310                                   tag->id, tag->x, tag->y);
01311 
01312                                 // Record the maximum *camera_diagonal*:
01313                                 double camera_diagonal = camera_tag->diagonal;
01314                                 double diagonal =
01315                                   camera_diagonal;
01316                                 if (diagonal  > tag->diagonal) {
01317                                     tag->diagonal = diagonal;
01318                                     tag->updated = (bool)1;
01319                                 }
01320 
01321                                 // Append *camera_tag* to *camera_tags*:
01322                                 fiducials->camera_tags.push_back(camera_tag);
01323                                 //File__format(log_file,
01324                                 //  "Found %d\n", camera_tag->tag->id);
01325                             }
01326                         }
01327                     }
01328                 }
01329             }
01330         }
01331     }
01332 
01333     if (!map) {
01334         return results;
01335     }
01336  
01337     // Just for consistency sort *camera_tags*:
01338     std::sort(fiducials->camera_tags.begin(), fiducials->camera_tags.end(),
01339         CameraTag::less);
01340 
01341     // Sweep through all *camera_tag* pairs to generate associated *Arc*'s:
01342     unsigned int camera_tags_size = fiducials->camera_tags.size();
01343     if (camera_tags_size >= 2) {
01344         // Iterate through all pairs, using a "triangle" scan:
01345         for (unsigned int tag1_index = 0;
01346           tag1_index < camera_tags_size - 1; tag1_index++) {
01347             CameraTag * camera_tag1 = fiducials->camera_tags[tag1_index];
01348         
01349             for (unsigned int tag2_index = tag1_index + 1;
01350               tag2_index < camera_tags_size; tag2_index++) {
01351                 CameraTag * camera_tag2 = fiducials->camera_tags[tag2_index];
01352                 assert (camera_tag1->tag->id != camera_tag2->tag->id);
01353                 if (Map__arc_update(map,
01354                   camera_tag1, camera_tag2, gray_image, sequence_number) > 0) {
01355                     results->map_changed = (bool)1;
01356                 }
01357             }
01358         }
01359     }
01360 
01361     fiducials->locations.clear();
01362     results->image_interesting = (bool)0;
01363     if (camera_tags_size > 0) {
01364         double pi = 3.14159265358979323846264;
01365         unsigned int half_width = CV_Image__width_get(gray_image) >> 1;
01366         unsigned int half_height = CV_Image__height_get(gray_image) >> 1;
01367         //File__format(log_file,
01368         //  "half_width=%d half_height=%d\n", half_width, half_height);
01369         for (unsigned int index = 0; index < camera_tags_size; index++) {
01370             CameraTag * camera_tag = fiducials->camera_tags[index];
01371             Tag * tag = camera_tag->tag;
01372             //File__format(log_file,
01373             //  "[%d]:tag_id=%d tag_x=%f tag_y=%f tag_twist=%f\n",
01374             //  index, tag->id, tag->x, tag->y, tag->twist * 180.0 / pi);
01375             double camera_dx = camera_tag->x - half_width;
01376             double camera_dy = camera_tag->y - half_height;
01377             //File__format(log_file,
01378             //  "[%d]:camera_dx=%f camera_dy=%f camera_twist=%f\n",
01379             //  index, camera_dx, camera_dy, camera_tag->twist * 180.0 / pi);
01380             double polar_distance = hypot(camera_dx, camera_dy);
01381             double polar_angle = atan2(camera_dy, camera_dx);
01382             //File__format(log_file,
01383             //  "[%d]:polar_distance=%f polar_angle=%f\n", index,
01384             //  polar_distance, polar_angle * 180.0 / pi);
01385             double floor_distance = 
01386               polar_distance * tag->world_diagonal / tag->diagonal;
01387             double angle =
01388               angles::normalize_angle(polar_angle + pi - camera_tag->twist);
01389             //File__format(log_file,
01390             //  "[%d]:floor_distance=%f angle=%f\n",
01391             //  index, floor_distance, angle * 180.0 / pi);
01392             double x = tag->x + floor_distance * cos(angle);
01393             double y = tag->y + floor_distance * sin(angle);
01394             double bearing =
01395               angles::normalize_angle(camera_tag->twist + tag->twist);
01396 
01397             // FIXME: Kludge,  There is a sign error somewhere in the code
01398             // causes the "sign" on the X axis to be inverted.  We kludge
01399             // around the problem with the following disgusting code:
01400             bearing = angles::normalize_angle(bearing - pi / 2.0);
01401             bearing = -bearing;
01402             bearing = angles::normalize_angle(bearing + pi / 2.0);
01403 
01404             //File__format(log_file, "[%d]:x=%f:y=%f:bearing=%f\n",
01405             //  index, x, y, bearing * 180.0 / pi);
01406             unsigned int location_index = fiducials->locations.size();
01407             Location * location = new Location(tag->id,
01408               x, y, bearing, floor_distance, location_index);
01409             fiducials->locations.push_back(location);
01410         }
01411 
01412         // Compute closest location:
01413         Location * closest_location = NULL;
01414         unsigned int locations_size = fiducials->locations.size();
01415         for (unsigned int index = 0; index < locations_size; index++) {
01416           Location * location = fiducials->locations[index];
01417             if (closest_location == NULL) {
01418                 closest_location = location;
01419             } else {
01420                 if (location->goodness < closest_location->goodness) {
01421                     closest_location = location;
01422                 }
01423             }
01424         }
01425 
01426         if (closest_location != NULL) {
01427             fiducials->locations_path.push_back(closest_location);
01428             //File__format(log_file,
01429             //  "Location: x=%f y=%f bearing=%f goodness=%f index=%d\n",
01430             //  closest_location->x, closest_location->y,
01431             //  closest_location->bearing * 180.0 / pi,
01432             //  closest_location->goodness, closest_location->index);
01433 
01434             double change_dx = closest_location->x - fiducials->last_x;
01435             double change_dy = closest_location->y - fiducials->last_y;
01436             double change = hypot(change_dx, change_dy);
01437             if (change > 0.1) {
01438                 results->image_interesting = (bool)1;
01439             }
01440             fiducials->last_x = closest_location->x;
01441             fiducials->last_y = closest_location->y;
01442 
01443             // send rviz marker message here
01444             File__format(log_file,
01445               "Location: id=%d x=%f y=%f bearing=%f\n",
01446               closest_location->id, closest_location->x, closest_location->y,
01447               closest_location->bearing);
01448             location_announce_routine(fiducials->announce_object,
01449               closest_location->id, closest_location->x, closest_location->y,
01450               /* z */ 0.0, closest_location->bearing);
01451         }
01452     }
01453 
01454     // Visit each *current_tag* in *current_visibles*:
01455     unsigned int current_visibles_size = fiducials->current_visibles.size();
01456     for (unsigned int current_visibles_index = 0;
01457       current_visibles_index < current_visibles_size;
01458       current_visibles_index++) {
01459         Tag * current_visible =
01460           fiducials->current_visibles[current_visibles_index];
01461         //File__format(log_file, "Current[%d]:%d\n",
01462         //  current_visibles_index, current_visible->id);
01463 
01464         // Always announce *current_visible* as visible:
01465         current_visible->visible = (bool)1;
01466         if( current_visible->updated ) {
01467             Map__tag_announce(map, current_visible,
01468                 (bool)1, original_image, sequence_number);
01469             current_visible->updated = (bool)0;
01470         }
01471     }
01472 
01473     // Identifiy tags that are no longer visible:
01474     unsigned int previous_visibles_size = fiducials->previous_visibles.size();
01475     for (unsigned int previous_visibles_index = 0;
01476        previous_visibles_index < previous_visibles_size;
01477        previous_visibles_index++) {
01478         Tag * previous_visible =
01479           fiducials->previous_visibles[previous_visibles_index];
01480         //File__format(log_file, "Previous[%d]:%d\n",
01481         //  previous_visibles_index, previous_visible->id);
01482 
01483         // Now look to see if *previous_visible* is in *current_visibles*:
01484         Tag * current_visible = NULL;
01485         for (unsigned int current_visibles_index = 0;
01486           current_visibles_index < current_visibles_size;
01487           current_visibles_index++) {
01488             current_visible = 
01489               fiducials->current_visibles[current_visibles_index];
01490             if (current_visible == previous_visible) {
01491                 break;
01492             }
01493             current_visible = NULL;
01494         }        
01495 
01496         // *current_visible* is null if it was not found:
01497         if (current_visible == NULL) {
01498             // Not found => announce the tag as no longer visible:
01499             previous_visible->visible = (bool)0;
01500             Map__tag_announce(map,
01501               previous_visible, (bool)0, original_image, sequence_number);
01502         }
01503     }
01504     // Clear *previous_visibles* and swap *current_visible* with
01505     // *previous_visibles*:
01506     fiducials->current_visibles = fiducials->previous_visibles;
01507     fiducials->previous_visibles.clear();
01508     //File__format(log_file, "current_visibles=0x%x previous_visibles=0x%x\n",
01509     //  current_visibles, previous_visibles);
01510 
01511     // Clean out *camera_tags*:
01512     for( unsigned int i=0; i<fiducials->camera_tags.size(); i++ ) {
01513       delete fiducials->camera_tags[i];
01514     }
01515     fiducials->camera_tags.clear();
01516 
01517     // Flip the debug image:
01518     if (fiducials->y_flip) {
01519         CV_Image__flip(debug_image, debug_image, 0);
01520     }
01521 
01522     // Update the map:
01523     Map__update(map, original_image, sequence_number);
01524 
01525     File__format(log_file, "\n");
01526     File__flush(log_file);
01527 
01528     return results;
01529 }
01530 
01540 
01541 int Fiducials__point_sample(Fiducials fiducials, CV_Point2D32F point) {
01542     // This routine will return a sample *fiducials* at *point*.
01543 
01544     // Get the (*x*, *y*) coordinates of *point*:
01545     int x = CV__round(CV_Point2D32F__x_get(point));
01546     int y = CV__round(CV_Point2D32F__y_get(point));
01547     CV_Image image = fiducials->gray_image;
01548 
01549     static int weights0[9] = {
01550       0,   0,  0,
01551       0, 100,  0,
01552       0,  0,   0};
01553 
01554     static int weights1[9] = {
01555        0,  15,  0,
01556       15,  40,  15,
01557        0,  15,  0};
01558 
01559     static int weights2[9] = {
01560        5,  10,  5,
01561       10,  40, 10,
01562        5,  10,  5};
01563 
01564     // Sample *image*:
01565     static int x_offsets[9] = {
01566       -1,  0,  1,
01567       -1,  0,  1,
01568       -1,  0,  1};
01569     static int y_offsets[9] = {
01570       -1, -1, -1,
01571        0,  0,  0,
01572        1,  1,  1};
01573 
01574     // Select sample *weights*:
01575     int *weights = (int *)0;
01576     switch (fiducials->weights_index) {
01577       case 1:
01578         weights = weights1;
01579         break;
01580       case 2:
01581         weights = weights2;
01582         break;
01583       default:
01584         weights = weights0;
01585         break;
01586     }
01587 
01588     // Interate across sample point;
01589     int numerator = 0;
01590     int denominator = 0;
01591     for (int index = 0; index < 9; index++) {
01592         int sample = CV_Image__gray_fetch(image,
01593           x + x_offsets[index], y + y_offsets[index]);
01594         if (sample >= 0) {
01595             int weight = weights[index];
01596             numerator += sample * weight;
01597             denominator += weight;
01598         }
01599     }
01600 
01601     // Compute *result* checking for divide by zero:
01602     int result = 0;
01603     if (denominator > 0) {
01604         result = numerator / denominator;
01605     }
01606     return result;
01607 }
01608 
01615 
01616 //FIXME: Does this routine belong in CV.c???!!!  
01617 //FIXME: Should this be merged with *CV_Point2D32F_Vector__is_clockwise*???!!!
01618 
01619 void CV_Point2D32F_Vector__corners_normalize(CV_Point2D32F_Vector corners) {
01620     // This routine will ensure that {corners} are ordered
01621     // in the counter-clockwise direction.
01622 
01623     if (CV_Point2D32F_Vector__is_clockwise(corners)) {
01624         // Extract two corners to be swapped:
01625         CV_Point2D32F corner1 = CV_Point2D32F_Vector__fetch1(corners, 1);
01626         CV_Point2D32F corner3 = CV_Point2D32F_Vector__fetch1(corners, 3);
01627 
01628         // Extract X and Y for both corners:
01629         double x1 = CV_Point2D32F__x_get(corner1);
01630         double y1 = CV_Point2D32F__y_get(corner1);
01631         double x3 = CV_Point2D32F__x_get(corner3);
01632         double y3 = CV_Point2D32F__y_get(corner3);
01633 
01634         // Swap contents of {corner1} and {corner3}:
01635         CV_Point2D32F__x_set(corner1, x3);
01636         CV_Point2D32F__y_set(corner1, y3);
01637         CV_Point2D32F__x_set(corner3, x1);
01638         CV_Point2D32F__y_set(corner3, y1);
01639     }
01640 }
01641 
01648 
01649 bool CV_Point2D32F_Vector__is_clockwise(CV_Point2D32F_Vector corners) {
01650 
01651     // Extract the three corners:
01652     CV_Point2D32F corner0 = CV_Point2D32F_Vector__fetch1(corners, 0);
01653     CV_Point2D32F corner1 = CV_Point2D32F_Vector__fetch1(corners, 1);
01654     CV_Point2D32F corner2 = CV_Point2D32F_Vector__fetch1(corners, 2);
01655 
01656     // Extract X and Y for all four corners:
01657     double x0 = CV_Point2D32F__x_get(corner0);
01658     double y0 = CV_Point2D32F__y_get(corner0);
01659     double x1 = CV_Point2D32F__x_get(corner1);
01660     double y1 = CV_Point2D32F__y_get(corner1);
01661     double x2 = CV_Point2D32F__x_get(corner2);
01662     double y2 = CV_Point2D32F__y_get(corner2);
01663 
01664     // Create two vectors from the first two lines of the polygon:
01665     double v1x = x1 - x0;
01666     double v1y = y1 - y0;
01667     double v2x = x2 - x1;
01668     double v2y = y2 - y1;
01669 
01670     // Determine the sign of the Z coordinate of the cross product:
01671     double z = v1x * v2y - v2x * v1y;
01672 
01673     // If the Z coordinate is negative, to reverse the sequence of the corners:
01674     return z < 0.0;
01675  }
01676 
01690 
01691 CV_Point2D32F_Vector Fiducials__references_compute(
01692   Fiducials fiducials, CV_Point2D32F_Vector corners) {
01693 
01694     // Extract the 8 references from {references}:
01695     CV_Point2D32F_Vector references = fiducials->references;
01696     CV_Point2D32F reference0 = CV_Point2D32F_Vector__fetch1(references, 0);
01697     CV_Point2D32F reference1 = CV_Point2D32F_Vector__fetch1(references, 1);
01698     CV_Point2D32F reference2 = CV_Point2D32F_Vector__fetch1(references, 2);
01699     CV_Point2D32F reference3 = CV_Point2D32F_Vector__fetch1(references, 3);
01700     CV_Point2D32F reference4 = CV_Point2D32F_Vector__fetch1(references, 4);
01701     CV_Point2D32F reference5 = CV_Point2D32F_Vector__fetch1(references, 5);
01702     CV_Point2D32F reference6 = CV_Point2D32F_Vector__fetch1(references, 6);
01703     CV_Point2D32F reference7 = CV_Point2D32F_Vector__fetch1(references, 7);
01704 
01705     // Extract the 4 corners from {corners}:
01706     CV_Point2D32F corner0 = CV_Point2D32F_Vector__fetch1(corners, 0);
01707     CV_Point2D32F corner1 = CV_Point2D32F_Vector__fetch1(corners, 1);
01708     CV_Point2D32F corner2 = CV_Point2D32F_Vector__fetch1(corners, 2);
01709     CV_Point2D32F corner3 = CV_Point2D32F_Vector__fetch1(corners, 3);
01710 
01711     // Extract the x and y references from {corner0} through {corner3}:
01712     double x0 = CV_Point2D32F__x_get(corner0);
01713     double y0 = CV_Point2D32F__y_get(corner0);
01714     double x1 = CV_Point2D32F__x_get(corner1);
01715     double y1 = CV_Point2D32F__y_get(corner1);
01716     double x2 = CV_Point2D32F__x_get(corner2);
01717     double y2 = CV_Point2D32F__y_get(corner2);
01718     double x3 = CV_Point2D32F__x_get(corner3);
01719     double y3 = CV_Point2D32F__y_get(corner3);
01720 
01721     double dx21 = x2 - x1;
01722     double dy21 = y2 - y1;
01723     double dx30 = x3 - x0;
01724     double dy30 = y3 - y0;
01725 
01726     // Determine the points ({xx0, yy0}) and ({xx1, yy1}) that determine
01727     // a line parrallel to one side of the quadralatal:
01728     double xx0 = x1 + dx21 * 5.0 / 20.0;
01729     double yy0 = y1 + dy21 * 5.0 / 20.0;
01730     double xx1 = x0 + dx30 * 5.0 / 20.0;
01731     double yy1 = y0 + dy30 * 5.0 / 20.0;
01732 
01733     // Set the outside and inside reference points along the line
01734     // through points ({xx0, yy0}) and ({xx1, yy1}):
01735     double dxx10 = xx1 - xx0;
01736     double dyy10 = yy1 - yy0;
01737     CV_Point2D32F__x_set(reference0, xx0 + dxx10 * -1.0 / 20.0);
01738     CV_Point2D32F__y_set(reference0, yy0 + dyy10 * -1.0 / 20.0);
01739     CV_Point2D32F__x_set(reference4, xx0 + dxx10 * 1.0 / 20.0);
01740     CV_Point2D32F__y_set(reference4, yy0 + dyy10 * 1.0 / 20.0);
01741     CV_Point2D32F__x_set(reference1, xx0 + dxx10 * 21.0 / 20.0);
01742     CV_Point2D32F__y_set(reference1, yy0 + dyy10 * 21.0 / 20.0);
01743     CV_Point2D32F__x_set(reference5, xx0 + dxx10 * 19.0 / 20.0);
01744     CV_Point2D32F__y_set(reference5, yy0 + dyy10 * 19.0 / 20.0);
01745 
01746     // Determine the points ({xx2, yy2}) and ({xx3, yy3}) that determine
01747     // a line parrallel to the other side of the quadralatal:
01748     double xx2 = x1 + dx21 * 15.0 / 20.0;
01749     double yy2 = y1 + dy21 * 15.0 / 20.0;
01750     double xx3 = x0 + dx30 * 15.0 / 20.0;
01751     double yy3 = y0 + dy30 * 15.0 / 20.0;
01752 
01753     // Set the outside and inside reference points along the line
01754     // through points ({xx2, yy2}) and ({xx3, yy3}):
01755     double dxx32 = xx3 - xx2;
01756     double dyy32 = yy3 - yy2;
01757     CV_Point2D32F__x_set(reference2, xx2 + dxx32 * -1.0 / 20.0);
01758     CV_Point2D32F__y_set(reference2, yy2 + dyy32 * -1.0 / 20.0);
01759     CV_Point2D32F__x_set(reference6, xx2 + dxx32 * 1.0 / 20.0);
01760     CV_Point2D32F__y_set(reference6, yy2 + dyy32 * 1.0 / 20.0);
01761     CV_Point2D32F__x_set(reference3, xx2 + dxx32 * 21.0 / 20.0);
01762     CV_Point2D32F__y_set(reference3, yy2 + dyy32 * 21.0 / 20.0);
01763     CV_Point2D32F__x_set(reference7, xx2 + dxx32 * 19.0 / 20.0);
01764     CV_Point2D32F__y_set(reference7, yy2 + dyy32 * 19.0 / 20.0);
01765 
01766     return references;
01767 }
01768 
01781 
01782 int Fiducials__points_maximum(Fiducials fiducials,
01783   CV_Point2D32F_Vector points, unsigned int start_index, unsigned int end_index) {
01784 
01785     // Start with a big value move it down:
01786     int result = 0;
01787 
01788     // Iterate across the {points} from {start_index} to {end_index}:
01789     for (unsigned int index = start_index; index <= end_index; index++) {
01790         CV_Point2D32F point = CV_Point2D32F_Vector__fetch1(points, index);
01791         int value = Fiducials__point_sample(fiducials, point);
01792         //call d@(form@("max[%f%:%f%]:%d%\n\") %
01793         //  f@(point.x) % f@(point.y) / f@(value))
01794         if (value > result) {
01795         // New maximum value:
01796             result = value;
01797         }
01798     }
01799     return result;
01800 }
01801 
01813 
01814 int Fiducials__points_minimum(Fiducials fiducials,
01815   CV_Point2D32F_Vector points, unsigned int start_index, unsigned int end_index) {
01816 
01817     // Start with a big value move it down:
01818     int result = 0x7fffffff;
01819 
01820     // Iterate across the {points} from {start_index} to {end_index}:
01821     for (unsigned int index = start_index; index <= end_index; index++) {
01822         CV_Point2D32F point = CV_Point2D32F_Vector__fetch1(points, index);
01823         int value = Fiducials__point_sample(fiducials, point);
01824         if (value < result) {
01825             // New minimum value:
01826             result = value;
01827         }
01828     }
01829     return result;
01830 }
01831 
01844 
01845 //FIXME: sample points comes from *fiducials*, so shouldn't have
01846 // have *fiducials* as a argument instead of *sample_points*???!!!
01847 
01848 void Fiducials__sample_points_compute(
01849   CV_Point2D32F_Vector corners, CV_Point2D32F_Vector sample_points) {
01850 
01851     CV_Point2D32F corner0 = CV_Point2D32F_Vector__fetch1(corners, 0);
01852     CV_Point2D32F corner1 = CV_Point2D32F_Vector__fetch1(corners, 1);
01853     CV_Point2D32F corner2 = CV_Point2D32F_Vector__fetch1(corners, 2);
01854     CV_Point2D32F corner3 = CV_Point2D32F_Vector__fetch1(corners, 3);
01855 
01856     // Extract the x and y references from {corner0} through {corner3}:
01857     double x0 = CV_Point2D32F__x_get(corner0);
01858     double y0 = CV_Point2D32F__y_get(corner0);
01859     double x1 = CV_Point2D32F__x_get(corner1);
01860     double y1 = CV_Point2D32F__y_get(corner1);
01861     double x2 = CV_Point2D32F__x_get(corner2);
01862     double y2 = CV_Point2D32F__y_get(corner2);
01863     double x3 = CV_Point2D32F__x_get(corner3);
01864     double y3 = CV_Point2D32F__y_get(corner3);
01865 
01866     // Figure out the vector directions {corner1} to {corner2}, as well as,
01867     // the vector from {corner3} to {corner0}.  If {corners} specify a
01868     // quadralateral, these vectors should be approximately parallel:
01869     double dx21 = x2 - x1;
01870     double dy21 = y2 - y1;
01871     double dx30 = x3 - x0;
01872     double dy30 = y3 - y0;
01873 
01874     // {index} will cycle through the 64 sample points in {sample_points}:
01875     unsigned int index = 0;
01876 
01877     // There are ten rows (or columns) enclosed by the quadralateral.
01878     // (The outermost "white" rows and columns are not enclosed by the
01879     // quadralateral.)  Since we want to sample the middle 8 rows (or
01880     // columns), We want a fraction that goes from 3/20, 5/20, ..., 17/20.
01881     // The fractions 1/20 and 19/20 would correspond to a black border,
01882     // which we do not care about:
01883     double i_fraction = 3.0 / 20.0;
01884     double i_increment = 2.0 / 20.0;
01885 
01886     // Loop over the first axis of the grid:
01887     unsigned int i = 0;
01888     while (i < 8) {
01889 
01890         // Compute ({xx1},{yy1}) which is a point that is {i_fraction} between
01891         // ({x1},{y1}) and ({x2},{y2}), as well as, ({xx2},{yy2}) which is a
01892         // point that is {i_fraction} between ({x0},{y0}) and ({x3},{y3}).
01893         double xx1 = x1 + dx21 * i_fraction;
01894         double yy1 = y1 + dy21 * i_fraction;
01895         double xx2 = x0 + dx30 * i_fraction;
01896         double yy2 = y0 + dy30 * i_fraction;
01897 
01898         // Compute the vector from ({xx1},{yy1}) to ({xx2},{yy2}):
01899         double dxx21 = xx2 - xx1;
01900         double dyy21 = yy2 - yy1;
01901 
01902         // As with {i_fraction}, {j_fraction} needs to sample the
01903         // the data stripes through the quadralateral with values
01904         // that range from 3/20 through 17/20:
01905         double j_fraction = 3.0 / 20.0;
01906         double j_increment = 2.0 / 20.0;
01907 
01908         // Loop over the second axis of the grid:
01909         unsigned int j = 0;
01910         while (j < 8) {
01911             // Fetch next {sample_point}:
01912             CV_Point2D32F sample_point =
01913               CV_Point2D32F_Vector__fetch1(sample_points, index);
01914             index = index + 1;
01915 
01916             // Write the rvGrid position into the rvGrid array:
01917             CV_Point2D32F__x_set(sample_point, xx1 + dxx21 * j_fraction);
01918             CV_Point2D32F__y_set(sample_point, yy1 + dyy21 * j_fraction);
01919 
01920             // Increment {j_faction} to the sample point:
01921             j_fraction = j_fraction + j_increment;
01922             j = j + 1;
01923         }
01924 
01925         // Increment {i_fraction} to the next sample striple:
01926         i_fraction = i_fraction + i_increment;
01927         i = i + 1;
01928     }
01929 
01930     CV_Point2D32F sample_point0 =
01931       CV_Point2D32F_Vector__fetch1(sample_points, 0);
01932     CV_Point2D32F sample_point7 =
01933       CV_Point2D32F_Vector__fetch1(sample_points, 7);
01934     CV_Point2D32F sample_point56 =
01935       CV_Point2D32F_Vector__fetch1(sample_points, 56);
01936     CV_Point2D32F sample_point63 =
01937       CV_Point2D32F_Vector__fetch1(sample_points, 63);
01938 
01939     // clockwise direction.  Bit 0 will be closest to corners[1], bit 7
01940     // will be closest to corners[0], bit 56 closest to corners[2] and
01941     // bit 63 closest to corners[3].
01942 
01943     //Fiducials__sample_points_helper("0:7", corner0, sample_point7);
01944     //Fiducials__sample_points_helper("1:0", corner0, sample_point0);
01945     //Fiducials__sample_points_helper("2:56", corner0, sample_point56);
01946     //Fiducials__sample_points_helper("3:63", corner0, sample_point63);
01947 }
01948 
01949 // Used in *Fiducials__sample_points*() for debugging:
01950 //
01951 //void Fiducials__sample_points_helper(
01952 //  String_Const label, CV_Point2D32F corner, CV_Point2D32F sample_point) {
01953 //    double corner_x = CV_Point2D32F__x_get(corner);
01954 //    double corner_y = CV_Point2D32F__y_get(corner);
01955 //    double sample_point_x = CV_Point2D32F__x_get(sample_point);
01956 //    double sample_point_y = CV_Point2D32F__y_get(sample_point);
01957 //    File__format(stderr, "Label: %s corner: %f:%f sample_point %f:%f\n",
01958 //      label, (int)corner_x, (int)corner_y,
01959 //      (int)sample_point_x, (int)sample_point_y);
01960 //}
01961 
01978 
01979 void Fiducials__tag_announce(void *announce_object, int id,
01980   double x, double y, double z, double twist, double diagonal,
01981   double distance_per_pixel, bool visible, int hop_count) {
01982     String_Const visible_text = "";
01983     if (!visible) {
01984         visible_text = "*** No longer visible ***";
01985     }
01986     File__format(stderr, "id=%d x=%f y=%f twist=%f %s\n",
01987       id, x, y, twist, visible_text);
01988 }
01989 
01990 static struct Fiducials_Create__Struct fiducials_create_struct =
01991 {
01992     (String_Const)0,                                // fiducials_path
01993     (String_Const)0,                                // lens_calibrate_file_name
01994     (void *)0,                                        // announce_object
01995     (Fiducials_Arc_Announce_Routine)0,                // arc_announce_routine
01996     (Fiducials_Location_Announce_Routine)0,        // location_announce_routine
01997     (Fiducials_Tag_Announce_Routine)0,                // tag_announce_routine
01998     (Fiducials_Fiducial_Announce_Routine)0,            // fiducial_announce_routine
01999     (String_Const)0,                                // log_file_name
02000     (String_Const)0,                                // map_base_name
02001     (String_Const)0,                                // tag_heights_file_name
02002 };
02003 
02010 
02011 Fiducials_Create Fiducials_Create__one_and_only(void)
02012 {
02013     return &fiducials_create_struct;
02014 }


fiducial_lib
Author(s): Wayne Gramlich
autogenerated on Thu Jun 6 2019 18:08:04