Surface
detect surface points in an image stack
surface.c
Go to the documentation of this file.
00001 /*
00002  * This work is licensed under a Creative Commons 
00003  * Attribution-Noncommercial-Share Alike 3.0 United States License.
00004  * 
00005  *    http://creativecommons.org/licenses/by-nc-sa/3.0/us/
00006  * 
00007  * You are free:
00008  * 
00009  *    to Share - to copy, distribute, display, and perform the work
00010  *    to Remix - to make derivative works
00011  * 
00012  * Under the following conditions:
00013  * 
00014  *    Attribution. You must attribute the work in the manner specified by the
00015  *    author or licensor (but not in any way that suggests that they endorse you
00016  *    or your use of the work).
00017  * 
00018  *    Noncommercial. You may not use this work for commercial purposes.
00019  * 
00020  *    Share Alike. If you alter, transform, or build upon this work, you may
00021  *    distribute the resulting work only under the same or similar license to
00022  *    this one.
00023  * 
00024  * For any reuse or distribution, you must make clear to others the license
00025  * terms of this work. The best way to do this is by including this header.
00026  * 
00027  * Any of the above conditions can be waived if you get permission from the
00028  * copyright holder.
00029  * 
00030  * Apart from the remix rights granted under this license, nothing in this
00031  * license impairs or restricts the author's moral rights.
00032  */
00033 
00034 
00105 #include <config.h>
00106 
00107 #include <stdlib.h>
00108 #include <stdio.h>
00109 #include <assert.h>
00110 #include <inttypes.h>
00111 #include <string.h>
00112 
00113 #ifdef SURFACE_HAVE_DMALLOC
00114 #include <dmalloc.h>
00115 #endif
00116 
00117 #include <jwsc/base/error.h>
00118 #include <jwsc/base/option.h>
00119 #include <jwsc/matblock/matblock.h>
00120 #include <jwsc/imgblock/imgblock.h>
00121 #include <jwsc/imgblock/imgblock_io.h>
00122 #include <jwsc/imgblock/imgblock_util.h>
00123 #include <jwsc/imgblock/surface.h>
00124 
00125 
00126 #define DEFAULT_BEGIN_THRESH    0.06f
00127 #define DEFAULT_END_THRESH      0.025f
00128 #define DEFAULT_SIGMA           1.0f
00129 #define DEFAULT_SAMPLE_RATE     1.0f
00130 #define DEFAULT_GRAD_IMG_WEIGHT 1.0f
00131 #define DEFAULT_GRAD_ROW_WEIGHT 1.0f
00132 #define DEFAULT_GRAD_COL_WEIGHT 1.0f
00133 #define DEFAULT_RES_IMG         1.0f
00134 #define DEFAULT_RES_ROW         1.0f
00135 #define DEFAULT_RES_COL         1.0f
00136 
00137 #define NUM_OPTS_NO_ARG   3
00138 #define NUM_OPTS_WITH_ARG 9
00139 
00140 
00142 static Option_no_arg opts_no_arg[NUM_OPTS_NO_ARG];
00143 
00145 static Option_with_arg opts_with_arg[NUM_OPTS_WITH_ARG];
00146 
00147 
00149 static const char* pts_fname;
00150 
00152 static const char* map_fname;
00153 
00155 static const char* colored_fname;
00156 
00161 static float begin_thresh;
00162 
00166 static float end_thresh;
00167 
00169 static float sigma;
00170 
00172 static float sample_rate;
00173 
00175 static float grad_img_weight;
00176 
00178 static float grad_row_weight;
00179 
00181 static float grad_col_weight;
00182 
00187 static uint8_t images_named;
00188 
00190 static float res_img;
00191 
00193 static float res_row;
00194 
00196 static float res_col;
00197 
00198 
00200 static void print_usage(void)
00201 {
00202     fprintf(stderr, "usage: surface OPTIONS [img-%%d num_imgs | img-1 ... img-n]\n");
00203     fprintf(stderr, "where OPTIONS is one or more of the following:\n");
00204     print_options(stderr, 25, NUM_OPTS_NO_ARG, opts_no_arg, NUM_OPTS_WITH_ARG,
00205             opts_with_arg);
00206 }
00207 
00208 
00210 static Error* process_help_opt(void)
00211 {
00212     print_usage();
00213     exit(EXIT_SUCCESS);
00214     return NULL;
00215 }
00216 
00217 
00219 static Error* process_version_opt(void)
00220 {
00221     fprintf(stderr, "%s\n", SURFACE_PACKAGE_STRING);
00222     exit(EXIT_SUCCESS);
00223     return NULL;
00224 }
00225 
00226 
00228 static Error* process_named_opt(void)
00229 {
00230     images_named = 1;
00231     return NULL;
00232 }
00233 
00234 
00236 static Error* process_colored_out_opt(Option_arg arg)
00237 {
00238     if (arg == NULL)
00239     {
00240         return JWSC_EARG("Option 'colored-out' requires an argument");
00241     }
00242     colored_fname = arg;
00243     return NULL;
00244 }
00245 
00246 
00248 static Error* process_map_out_opt(Option_arg arg)
00249 {
00250     if (arg == NULL)
00251     {
00252         return JWSC_EARG("Option 'map-out' requires an argument");
00253     }
00254     map_fname = arg;
00255     return NULL;
00256 }
00257 
00258 
00260 static Error* process_pts_out_opt(Option_arg arg)
00261 {
00262     if (arg == NULL)
00263     {
00264         return JWSC_EARG("Option 'pts-out' requires an argument");
00265     }
00266     pts_fname = arg;
00267     return NULL;
00268 }
00269 
00270 
00272 static Error* process_begin_thresh_opt(Option_arg arg)
00273 {
00274     if (arg == NULL)
00275     {
00276         return JWSC_EARG("Option 'begin-thresh' requires an argument");
00277     }
00278     if (sscanf(arg, "%f", &begin_thresh) != 1 || begin_thresh < 0)
00279     {
00280         return JWSC_EARG("Option 'begin-thresh' must be >= 0");
00281     }
00282     return NULL;
00283 }
00284 
00285 
00287 static Error* process_end_thresh_opt(Option_arg arg)
00288 {
00289     if (arg == NULL)
00290     {
00291         return JWSC_EARG("Option 'end-thresh' requires an argument");
00292     }
00293     if (sscanf(arg, "%f", &end_thresh) != 1 || end_thresh < 0)
00294     {
00295         return JWSC_EARG("Option 'end-thresh' must be >= 0");
00296     }
00297     return NULL;
00298 }
00299 
00300 
00302 static Error* process_sigma_opt(Option_arg arg)
00303 {
00304     if (arg == NULL)
00305     {
00306         return JWSC_EARG("Option 'sigma' requires an argument");
00307     }
00308     if (sscanf(arg, "%f", &sigma) != 1 || sigma <= 0)
00309     {
00310         return JWSC_EARG("Option 'sigma' must be > 0");
00311     }
00312     return NULL;
00313 }
00314 
00315 
00317 static Error* process_sample_rate_opt(Option_arg arg)
00318 {
00319     if (arg == NULL)
00320     {
00321         return JWSC_EARG("Option 'sample-rate' requires an argument");
00322     }
00323     if (sscanf(arg, "%f", &sample_rate) != 1 || sample_rate <= 0 ||
00324             sample_rate > 1.0)
00325     {
00326         return JWSC_EARG("Option 'sample-rate' must be in (0, 1]");
00327     }
00328     return NULL;
00329 }
00330 
00331 
00333 static Error* process_grad_weight_opt(Option_arg arg)
00334 {
00335     if (arg == NULL)
00336     {
00337         return JWSC_EARG("Option 'grad-weight' requires an argument");
00338     }
00339     if (sscanf(arg, "%f,%f,%f", &grad_img_weight, &grad_row_weight, 
00340                 &grad_col_weight) != 3)
00341     {
00342         return JWSC_EARG("Option 'grad-weight' has format img,row,col");
00343     }
00344     if (grad_img_weight < 0 || grad_row_weight < 0 || grad_col_weight < 0)
00345     {
00346         return JWSC_EARG("Option 'grad-weight' must have img,row,col >= 0");
00347     }
00348     if ((grad_img_weight + grad_row_weight + grad_col_weight) <= 1.0e-8)
00349     {
00350         return JWSC_EARG("Option 'grad-weight' must have img+row+col > 0");
00351     }
00352     return NULL;
00353 }
00354 
00355 
00357 static Error* process_resolution_opt(Option_arg arg)
00358 {
00359     if (arg == NULL)
00360     {
00361         return JWSC_EARG("Option 'resolution' requires an argument");
00362     }
00363     if (sscanf(arg, "%f,%f,%f", &res_img, &res_row, &res_col) != 3)
00364     {
00365         return JWSC_EARG("Option 'resolution' has format img,row,col");
00366     }
00367     if (res_img <= 0 || res_img > 1 || res_row <= 0 || res_row > 1 ||
00368             res_col <= 0 || res_col > 1)
00369     {
00370         return JWSC_EARG("Option 'resolution' must be in (0,1]");
00371     }
00372     return NULL;
00373 }
00374 
00375 
00376 
00377 
00378 
00380 int main(int argc, const char** argv)
00381 {
00382     uint32_t num_rows, num_cols;
00383     uint32_t num_pts;
00384     uint32_t i, p;
00385     int32_t  num_imgs;
00386     int      argi;
00387     int      arg;
00388 
00389     char*       chars;
00390     char**      names;
00391     const char* input_fname;
00392 
00393     Pixel_f pxl;
00394 
00395     Imgblock_f*      img_in  = NULL;
00396     Imgblock_f*      img_out = NULL;
00397     Matblock_f*      m       = NULL;
00398     Error*           e       = NULL;
00399     Surface_point_f* pts     = NULL;
00400     Surface_point_f* pt      = NULL;
00401 
00402     /* Init the options. */
00403     init_option_no_arg(&(opts_no_arg[0]), "help", 'h',
00404             "Prints program usage.", process_help_opt);
00405     init_option_no_arg(&(opts_no_arg[1]), "version", 'v',
00406             "Prints program version.", process_version_opt);
00407     init_option_no_arg(&(opts_no_arg[2]), "named", 'n',
00408             "Indicates that the images to read are fully named and listed in sequence on the command line. The default is to read the images as numbered by a printf-formatted name.", process_named_opt);
00409     init_option_with_arg(&(opts_with_arg[0]), "colored-out", 'c',
00410             "Original images with surface colored. Use a printf formatted name to output the images in a numbered sequence.", process_colored_out_opt);
00411     init_option_with_arg(&(opts_with_arg[1]), "map-out", 'm',
00412             "Bi-level image surface map. Use a printf formatted name to output the images in a numbered sequence.", process_map_out_opt);
00413     init_option_with_arg(&(opts_with_arg[2]), "pts-out", 'p',
00414             "ASCII formatted surface points.", process_pts_out_opt);
00415     init_option_with_arg(&(opts_with_arg[3]), "begin-thresh", 'b',
00416             "Hysterisis starting threshold.", process_begin_thresh_opt);
00417     init_option_with_arg(&(opts_with_arg[4]), "end-thresh", 'e',
00418             "Hysterisis stopping threshold.", process_end_thresh_opt);
00419     init_option_with_arg(&(opts_with_arg[5]), "sigma", 's',
00420             "Gaussian smoothing sigma.", process_sigma_opt);
00421     init_option_with_arg(&(opts_with_arg[6]), "sample-rate", 'r',
00422             "Surface point sampling rate.", process_sample_rate_opt);
00423     init_option_with_arg(&(opts_with_arg[7]), "grad-weight", 'w',
00424             "Weights for each direction of the gradient. Format for the argument is 'img,row,col'. They all must be non-negative and sum to > 0.", 
00425             process_grad_weight_opt);
00426     init_option_with_arg(&(opts_with_arg[8]), "resolution", 'w',
00427             "Down-sampling resolution for the input image stack. Format for the argument is 'img,row,col'. They all must be in (0,1].",
00428             process_resolution_opt);
00429 
00430     /* Init default option values. */
00431     map_fname       = NULL;
00432     colored_fname   = NULL;
00433     pts_fname       = NULL;
00434     begin_thresh    = DEFAULT_BEGIN_THRESH;
00435     end_thresh      = DEFAULT_END_THRESH;
00436     sigma           = DEFAULT_SIGMA;
00437     sample_rate     = DEFAULT_SAMPLE_RATE;
00438     grad_img_weight = DEFAULT_GRAD_IMG_WEIGHT;
00439     grad_row_weight = DEFAULT_GRAD_ROW_WEIGHT;
00440     grad_col_weight = DEFAULT_GRAD_COL_WEIGHT;
00441     res_img         = DEFAULT_RES_IMG;
00442     res_row         = DEFAULT_RES_ROW;
00443     res_col         = DEFAULT_RES_COL;
00444     images_named    = 0;
00445 
00446     /* Check for the help option first. */
00447     process_option_no_arg(argc, argv, &argi, &(opts_no_arg[0]));
00448 
00449     if ((e = process_options(argc, argv, &argi, NUM_OPTS_NO_ARG, opts_no_arg,
00450                     NUM_OPTS_WITH_ARG, opts_with_arg)) != NULL)
00451     {
00452         print_error_msg_exit("surface", e->msg);
00453     }
00454 
00455     /* Either read the image block as a printf numbered sequence, or a list of
00456      * names in argv. */
00457     if (images_named)
00458     {
00459         if ((argc - argi) < 1)
00460         {
00461             print_error_msg_exit("surface", "No input images");
00462         }
00463 
00464         num_imgs = argc - argi;
00465         chars = calloc(num_imgs*256, sizeof(char));
00466         names = malloc(num_imgs*sizeof(char*));
00467         arg = argi;
00468         for (i = 0; i < num_imgs; i++)
00469         {
00470             names[ i ] = &(chars[ i*256 ]);
00471             strncpy(names[ i ], argv[ arg++ ], 255);
00472         }
00473         if ((e = read_imgblock_named_f(&img_in, (const char**)names, num_imgs)) 
00474                 != NULL)
00475         {
00476             print_error_msg_exit("surface", e->msg);
00477         }
00478         free(chars);
00479         free(names);
00480     }
00481     else
00482     {
00483         if ((argc - argi) < 2)
00484         {
00485             print_error_msg_exit("surface", "No input image format and count");
00486         }
00487 
00488         input_fname = argv[ argi ];
00489         if (sscanf(argv[ argi+1 ], "%d", &num_imgs) != 1 || num_imgs <= 0)
00490         {
00491             print_error_msg_exit("surface", "No input image count");
00492         }
00493 
00494         if ((e = read_imgblock_numbered_f(&img_in, input_fname, num_imgs)) 
00495                 != NULL)
00496         {
00497             print_error_msg_exit("surface", e->msg);
00498         }
00499     }
00500 
00501 
00502     /* Down sample the image data if requested. */
00503     assert(down_sample_imgblock_f(&img_in, img_in, res_img, res_row, res_col) 
00504             == NULL);
00505     num_imgs = img_in->num_imgs;
00506     num_rows = img_in->num_rows;
00507     num_cols = img_in->num_cols;
00508 
00509 
00510     /* Detect surface and sample them if requested. */
00511     create_matblock_from_imgblock_f(&m, img_in, 1.0f, 1.0f, 1.0f);
00512     if ((e = detect_matblock_surface_points_f(&pts, &num_pts, m, sigma, 
00513                     grad_img_weight, grad_row_weight, grad_col_weight, 
00514                     begin_thresh, end_thresh)) != NULL)
00515     {
00516         print_error_msg_exit("surface", e->msg);
00517         return EXIT_FAILURE;
00518     }
00519 
00520     fprintf(stdout, "Number of detected surface points: %d\n", num_pts);
00521     sample_surface_points_f(&pts, &num_pts, pts, num_pts, sample_rate);
00522     fprintf(stdout, "Number of sampled surface points: %d\n", num_pts);
00523 
00524     /* Write a surface points if requested. */
00525     if (pts_fname)
00526     {
00527         if ((e = write_surface_points_f(pts, num_pts, pts_fname)) != NULL)
00528         {
00529             print_error_msg("surface", NULL);
00530             print_error_msg(pts_fname, e->msg);
00531             free_error(e);
00532         }
00533     }
00534 
00535     /* Write a surface map image block if requested. */
00536     if (map_fname)
00537     {
00538         create_zero_matblock_f(&m, num_imgs, num_rows, num_cols);
00539 
00540         for (p = 0; p < num_pts; p++)
00541         {
00542             pt = &(pts[ p ]);
00543             m->elts[ pt->mat ][ pt->row ][ pt->col ] = 1.0f;
00544         }
00545 
00546         create_imgblock_from_matblock_f(&img_out, m);
00547 
00548         if ((e = write_imgblock_numbered_f(img_out, map_fname)) != NULL)
00549         {
00550             print_error_msg("surface", e->msg);
00551             free_error(e);
00552         }
00553     }
00554 
00555     /* Write the image block with the surface colored on it if requested. */
00556     if (colored_fname)
00557     {
00558         pxl.r = 1.0f;
00559         pxl.g = 0;
00560         pxl.b = 0;
00561 
00562         color_surface_points_f(&img_out, img_in, pts, num_pts, &pxl);
00563 
00564         if ((e = write_imgblock_numbered_f(img_out, colored_fname)) != NULL)
00565         {
00566             print_error_msg("surface", e->msg);
00567             free_error(e);
00568         }
00569     }
00570 
00571     free(pts);
00572     free_imgblock_f(img_in);
00573     free_imgblock_f(img_out);
00574     free_matblock_f(m);
00575 
00576     return EXIT_SUCCESS;
00577 }