home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: OtherApp / OtherApp.zip / wincam.zip / winc_src.zip / winc.c < prev    next >
C/C++ Source or Header  |  1997-02-28  |  17KB  |  643 lines

  1.  
  2. /*
  3.  *
  4.  * winc -- a command line program for accessing the WinCam.One digital camera.
  5.  *
  6.  * Copyright (C) 1996, Paul G. Fox
  7.  * This program is free software; you can redistribute it and/or modify it
  8.  * under the terms of the GNU General Public License as published by the
  9.  * Free Software Foundation; either version 2 of the License, or (at your
  10.  * option) any later version.
  11.  * 
  12.  * This program is distributed in the hope that it will be useful, but
  13.  * WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.  * General Public License for more details.
  16.  * 
  17.  * You should have received a copy of the GNU General Public License along
  18.  * with this program; if not, write to the Free Software Foundation, Inc.,
  19.  * 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  * This software was created with the help of proprietary information
  22.  * belonging to StarDot Technologies.
  23.  *
  24.  * $Header: E:/winc/RCS/winc.c 1.1 1997/03/01 03:44:14 Derek Exp Derek $
  25.  */
  26.  
  27. #include <unistd.h>
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <signal.h>
  32. #include <time.h>
  33. #include <utime.h>
  34. #include <sys/types.h>
  35. #include <sys/stat.h>
  36. #include "wincam.h"
  37. #include "pnm.h"
  38. #include "trace.h"
  39.  
  40. /*
  41.  * from mergeenv.c -- there's no convenient header file in which
  42.  * this appears.
  43.  */
  44. int    safeputenv(char *name, char *value);
  45.  
  46. /*
  47.  * forward declarations
  48.  */
  49. void emit_wcd_format(unsigned char *, int, unsigned char *, FILE *);
  50. void inhale_wcd_format(unsigned char *, int *, FILE *);
  51. void snapper_func(void *timep);
  52. void audible_snapper_func(void *timep);
  53. void reset_mod_time(char *filename, time_t mtime);
  54. void quitcatcher(int sig);
  55.  
  56. /*
  57.  * global camera handle, so it can be closed/flushed from a signal handler
  58.  */
  59. cam_t Cam = 0;        /* camera "handle" */
  60.  
  61. /*
  62.  * for getopt()
  63.  */
  64. extern char *optarg;
  65. extern int optind, opterr, optopt;
  66.  
  67. void
  68. usage(char *progname, int full)
  69. {
  70.     fprintf(stderr, 
  71.     "usage: %s -{flags}  where:\n%s%s", progname,
  72.     "   -h gives the usage message\n"
  73.     "   -H gives a more complete usage message\n"
  74.     "   -c grabs a color image\n"
  75.     "   -g grabs a greyscale image\n"
  76.     "   -v grabs a viewfinder image\n"
  77.     "     (if none of -v, -g, or -c is used, no image will be grabbed,\n"
  78.     "      but camera will still be discovered and initialized)\n"
  79.     "   -2 to double-scan an image\n"
  80.     "   -z N sets image reduction factor, or viewfinder zoom. (1, 2, 4, or 8)\n"
  81.     "   -b NN - brightness (1-100, 0 for default) [50]\n"
  82.     "   -l color-adjustment\n"
  83.     "   -a causes an audible alert (beep) when image is exposed\n"
  84.     "   -s SPEED sets the preferred baud rate\n"
  85.     "   -i IDSTRING assigns a prefix for all config info looked up\n"
  86.     "   -o FILE sets the output file to use in place of stdout\n"
  87.     "", !full ? "" :
  88.     "   -S NN saves a starfield image, using NN as the \"bad\" threshold\n"
  89.     "   -e NN sets exposure in milliseconds\n"
  90.     "   -E NN sets exposure in microseconds\n"
  91.     "   -f don't do faster grab\n"
  92.     "   -n leaves \"narrow\" image, rather than widening\n"
  93.     "   -r leaves a dark image, rather than brightening corners\n"
  94.     "    -G VAL processes with gamma function . (100 normal, 110 default)\n"
  95.     "   -d runs camera diagnostics\n"
  96.     "   -t STR sets tracing; arg is a string of traceflags:\n"
  97.     "        v just shows program and camera version information\n"
  98.     "        V will turn on all possible messages\n"
  99.     "        e to get exposure information\n"
  100.     "        a for simple API tracing\n"
  101.     "        A for full API tracing\n"
  102.     "        c for camera commands\n"
  103.     "        s for serial protocol\n"
  104.     "        S for all bytes of serial protocol\n"
  105.     "        g for graphics output routine tracing\n"
  106.     "        r for winc.rc file tracing\n"
  107.     "   -R writes .wcd format data\n"
  108.     "   -D gets .wcd format data from stdin\n"
  109.     "\n"
  110.     );
  111. }
  112.  
  113. int
  114. main(int argc, char *argv[])
  115. {
  116.     char *progname;
  117.     int c;
  118.  
  119.  
  120.     int do_viewfinder = 0;    /* flags three kinds of grabs */
  121.     int do_color = 0;
  122.     int do_greyscale = 0;
  123.     int do_starfield = 0;
  124.     int do_raw_output = 0, do_raw_input = 0;
  125.     int scantype = 1;        /* images can be single- or double- scanned */
  126.     int fraction = 1;        /* full image? what fraction? */
  127.     int widen = 1;        /* should we widen to fix aspect ratio? */
  128.     int vignette = 1;        /* should we fix corner darkening? */
  129.     int gamma = 0;        /* should we do darkness lightening? */
  130.     int faster = 1;
  131.     int audible_alert = 0;
  132.     int starfield_cutoff = 1;
  133.  
  134.     int goal = 0;        /* brightness goal */
  135.     int exp = 0;        /* default exposure is 1/30th of a second */
  136.     int zoom = 1;        /* no viewfinder zoom by default */
  137.     int diagnostics = 0;
  138.     struct winc_image_adjust image_adjust;
  139.     char *color_adj_string = 0;
  140.  
  141.     char *filename = 0;        /* where to put the image if not stdout */
  142.     FILE *outf;            /* file handle for output */
  143.     time_t snaptime;        /* for resetting image grab time */
  144.  
  145.     int rows, cols;
  146.     int blackadj;        /* average black value */
  147.     unsigned char blackvals[2*512 + 2]; /* space for black values, if needed  */
  148.     unsigned char image[640*492];    /* the biggest imagemap we'll need */
  149.     unsigned char convimage[3*640*492]; /* the biggest converted imagemap */
  150.  
  151.  
  152.     /* 
  153.      * what program is this?
  154.      */
  155.     progname = strrchr(argv[0], '/');
  156.     if (progname)
  157.     progname++;
  158.     else
  159.     progname = argv[0];
  160.  
  161.     _fsetmode(stdout,"b");
  162.  
  163.     /* 
  164.      * set up the trace and error facilities
  165.      * register the output callbacks, and initialize the tracing class
  166.      */
  167.     register_error_func((vprintffunc)vfprintf, (void *)stderr);
  168.     register_trace_func((vprintffunc)vfprintf, (void *)stderr);
  169.     set_trace_class("");
  170.  
  171.     /* 
  172.      * process arguments
  173.      */
  174.     while ((c = getopt(argc, argv, 
  175.             "nr2hHvfgcdmaRDo:l:G:z:b:e:E:t:f:i:s:S:"
  176.             )) != EOF) {
  177.     switch (c) {
  178.     case 'H':
  179.     case 'h':
  180.         usage(progname, (c == 'H'));
  181.         exit(0);
  182.     case 'e':   /* exposure (or initial exposure guess), in milliseconds */
  183.     case 'E':   /*    or microseconds */
  184.         exp = atoi(optarg);
  185.         if (c == 'e')
  186.         exp *= 1000;        /* convert to microseconds */
  187.         break;
  188.     case 'n':
  189.         widen = 0;
  190.         break;
  191.     case 'b':
  192.         goal = atoi(optarg);
  193.         break;
  194.     case 'S':
  195.         do_starfield = 1;
  196.         scantype = 2;
  197.         goal = 50;
  198.         starfield_cutoff = atoi(optarg);
  199.         if (safeputenv("NoStarfield", "none_at_all") == -1) {
  200.         fprintf(stderr, "%s: couldn't set NoStarfield\n",
  201.             progname);
  202.         exit(1);
  203.         }
  204.         break;
  205.     case 'r':
  206.         vignette = 0;
  207.         gamma = 100;    /* force winc_gamma() to be transparent */
  208.         break;
  209.     case 'G':
  210.         gamma = atoi(optarg);
  211.         break;
  212.     case 'z':
  213.         zoom = atoi(optarg);
  214.         fraction = zoom;
  215.         break;
  216.     case 'd':
  217.         diagnostics = 1;
  218.         break;
  219.     case 't':
  220.         set_trace_class(optarg);
  221.         break;
  222.     case 'a':
  223.         audible_alert  = 1;
  224.         break;
  225.     case 'v':
  226.         do_viewfinder = 1;
  227.         break;
  228.     case 'f':
  229.         faster = 0;
  230.         break;
  231.     case 'g':
  232.         do_greyscale = 1;
  233.         break;
  234.     case '2':
  235.         scantype = 2;
  236.         break;
  237.     case 'c':
  238.         do_color = 1;
  239.         break;
  240.     case 'l':
  241.         color_adj_string = optarg;
  242.         break;
  243.     case 'i':
  244.         if (safeputenv("WinCamId", optarg) == -1) {
  245.         fprintf(stderr, "%s: couldn't set WinCamId\n", progname);
  246.         exit(1);
  247.         }
  248.         break;
  249.     case 'o':
  250.         filename = optarg;
  251.         break;
  252.     case 's':
  253.         if (safeputenv("WinCamBaudRate", optarg) == -1) {
  254.         fprintf(stderr, "%s: couldn't set WinCamBaudRate\n", progname);
  255.         exit(1);
  256.         }
  257.         break;
  258.     case 'R':
  259.         /* option given once, output stardot .wcd format,
  260.         if twice, output raw image array */
  261.         do_raw_output++;
  262.         faster = 0;
  263.         break;
  264.     case 'D':
  265.         /* if option is given once, read stardot .wcd format,
  266.         else raw image array */
  267.         do_raw_input++;
  268.         faster = 0;
  269.         break;
  270.     default:
  271.         fprintf(stderr, "%s: unknown option '%c'\n", progname, c);
  272.         usage(progname, 0);
  273.         exit(1);
  274.     }
  275.     }
  276.  
  277.     if (optind != argc) {
  278.     fprintf(stderr, "%s: extra arguments\n", progname);
  279.     usage(progname, 0);
  280.     exit(1);
  281.     }
  282.  
  283.     if (do_viewfinder +
  284.     do_greyscale +
  285.     do_color +
  286.     do_starfield +
  287.     (do_raw_output != 0) > 1) {
  288.     fprintf(stderr, 
  289.         "%s: only one of -v, -g, -c, -S, or -R should be given\n",
  290.                         progname);
  291.     usage(progname, 0);
  292.     exit(1);
  293.     }
  294.  
  295.     if (filename) {
  296.     outf = fopen(filename, "wb");
  297.     if (!outf) {
  298.         errormsg(__FILE__ ": can't open file \"%s\"\n", filename);
  299.         exit(1);
  300.     }
  301.     } else {
  302.     outf = stdout;
  303.     }
  304.  
  305.     if (audible_alert != 0)
  306.     winc_register_alerter(audible_snapper_func, (void *)&snaptime);
  307.     else
  308.     winc_register_alerter(snapper_func, (void *)&snaptime);
  309.  
  310.     winc_image_adjustments(&image_adjust, color_adj_string, widen, vignette);
  311.  
  312.     if (exp)
  313.     goal = 0;    /* goal is meaningless if manual exposure */
  314.  
  315.     /* don't actually touch the camera if we're just debugging conversions */
  316.     if (!do_raw_input) {
  317.     /*
  318.      * finally, find a camera.  the argument (1) currently doesn't
  319.      * do anything -- eventually we should support more than one camera,
  320.      * on separate serial ports.
  321.      */
  322.     Cam = winc_find(1);
  323.     if (!Cam) {
  324.         fprintf(stderr, "%s: couldn't find camera\n", progname);
  325.         exit(1);
  326.     }
  327.  
  328.     (void) signal(SIGHUP, quitcatcher);
  329.     (void) signal(SIGINT, quitcatcher);
  330.     (void) signal(SIGTERM, quitcatcher);
  331.  
  332.  
  333.     if (diagnostics)
  334.         winc_diagnose(Cam);
  335.  
  336.     if (do_viewfinder) {
  337.  
  338.         if (winc_viewfinder_snap(Cam, zoom, -1, -1, image,
  339.             (exp == 0) ? 15 : 1, &exp, 0, goal, 0) != OK) {
  340.         errormsg(__FILE__ ": failed to snap initial viewfinder image\n");
  341.         exit(0);
  342.         }
  343.         /*
  344.          * viewfinder images are all 48 high and 64 across, and
  345.          * no massaging is necessary, even aspect ratio.
  346.          */
  347.         rows = 48;
  348.         cols = 64;
  349.         (void)winc_pgm_output(image, rows, cols, 15, outf);
  350.         winc_unlock(Cam);
  351.         winc_close(Cam);
  352.         exit(0);
  353.     }
  354.     }
  355.  
  356.     /* goal is passed as negative exposure, unless we're exposing manually */
  357.     if (!exp && goal)
  358.     exp = -goal;
  359.  
  360.     if (faster && (do_greyscale || do_color)) {
  361.     winc_snap_and_process(Cam, exp, image, convimage, &rows, &cols,
  362.                 do_color, scantype, fraction, &image_adjust);
  363.     winc_gamma(gamma, convimage, rows, cols, do_color);
  364.     if (do_color)
  365.         (void)winc_pnm_output(convimage, rows, cols, 255, outf);
  366.     else
  367.         (void)winc_pgm_output(convimage, rows, cols, 255, outf);
  368.     if (filename && snaptime != 0) {
  369.         fclose(outf);
  370.         reset_mod_time(filename, snaptime);
  371.     }
  372.     winc_unlock(Cam);
  373.     winc_close(Cam);
  374.     return 0;
  375.     }
  376.  
  377.     if (do_greyscale || do_color || do_starfield || do_raw_output) {
  378.     if (!do_raw_input) {
  379.         if (winc_image_snap(Cam, exp, scantype, 
  380.             fraction, !do_greyscale, image, &rows, &cols) != OK) {
  381.         errormsg(__FILE__ ": failed to snap image\n");
  382.         winc_close(Cam);
  383.         exit(0);
  384.         }
  385.  
  386.         if (winc_black_offset(Cam, &blackadj, do_raw_output ? blackvals : 0 )
  387.                                     != OK) {
  388.         errormsg(__FILE__ ": failed to get black adjustment\n");
  389.         winc_close(Cam);
  390.         exit(0);
  391.         }
  392.     } else {
  393.         int ch;
  394.         unsigned char *ip = image;
  395.  
  396.         if (do_raw_input > 1) {
  397.         /* load up the data */
  398.         while((ch = getc(stdin)) != EOF)
  399.             *ip++ = ch;
  400.  
  401.         if (scantype == 2) {
  402.             rows = 492; cols = 512;
  403.         } else {
  404.             switch(fraction) {
  405.             case 1: rows = 246; cols = 512; break;
  406.             case 2: rows = 246; cols = 256; break;
  407.             case 4: rows = 123; cols = 128; break;
  408.             default:
  409.             case 8: rows = 60;    cols = 64;  break;
  410.             }
  411.         }
  412.  
  413.         blackadj = 0; /* can't get this back easily */
  414.         } else {
  415.         inhale_wcd_format(image, &blackadj, stdin);
  416.         rows = 492;
  417.         cols = 512;
  418.         scantype = 2;
  419.         }
  420.     }
  421.  
  422. #if USE_MY_GREY
  423.     if (do_greyscale) {
  424.         winc_grey_convert(image, convimage, &rows, &cols, blackadj,
  425.                 scantype, fraction, &image_adjust);
  426.         if (vignette)
  427.         winc_vignette_fix(convimage, rows, cols, 0);
  428.         if (widen)
  429.         cols = winc_aspect_fix(convimage, rows, cols, 0);
  430.         (void)winc_pgm_output(convimage, rows, cols, 255, outf);
  431.     } else if (do_color) {
  432.         winc_color_convert(image, convimage, &rows, &cols, blackadj,
  433.                 do_color, scantype, fraction, &image_adjust);
  434.         if (vignette)
  435.         winc_vignette_fix(convimage, rows, cols, do_color);
  436.         if (widen)
  437.         cols = winc_aspect_fix(convimage, rows, cols, do_color);
  438.         if (do_color)
  439.         (void)winc_pnm_output(convimage, rows, cols, 255, outf);
  440.         else
  441.         (void)winc_pgm_output(convimage, rows, cols, 255, outf);
  442.     } 
  443. #else
  444.     if (do_color || do_greyscale) {
  445.         winc_color_convert(image, convimage, &rows, &cols, blackadj,
  446.                 do_color, scantype, fraction, &image_adjust);
  447.         winc_gamma(gamma, convimage, rows, cols, do_color);
  448.         if (do_color)
  449.         (void)winc_pnm_output(convimage, rows, cols, 255, outf);
  450.         else
  451.         (void)winc_pgm_output(convimage, rows, cols, 255, outf);
  452.     } 
  453. #endif
  454.     else if (do_raw_output) {
  455.         if (do_raw_output > 1) {
  456.         /* simple write of the image array, suitable for sucking
  457.             in with -D */
  458.         (void)fwrite(image, 1, rows*cols, outf);
  459.         } else {
  460.         emit_wcd_format(image, scantype, blackvals, outf);
  461.         }
  462.     } else if (do_starfield) {
  463.         (void)winc_pbm_output(image, convimage, rows, cols,
  464.                 starfield_cutoff, outf);
  465.     }
  466.     if (filename && snaptime != 0) {
  467.         fclose(outf);
  468.         reset_mod_time(filename, snaptime);
  469.     }
  470.     }
  471.  
  472.     if (Cam) {
  473.         winc_unlock(Cam);
  474.     winc_close(Cam);
  475.     }
  476.  
  477.     return 0;
  478. }
  479.  
  480.  
  481. /* 
  482.  * two routines to read and write  StarDot's .wcd format:
  483.  *  246 even rows (they call them odd; historical)
  484.  *  10    rows of zero
  485.  *  246 odd rows (again, they call these even)
  486.  *  8    rows of zero
  487.  *  2    rows of black values
  488.  * all rows are 512 bytes across
  489.  */
  490.  
  491. void
  492. emit_wcd_format(
  493.     unsigned char *image,
  494.     int scantype,
  495.     unsigned char *blackvals,
  496.     FILE *outf)
  497. {
  498.     /* this won't contain interesting data if the scan
  499.      *    wasn't a full height scan
  500.      */
  501.     unsigned char empty[512];
  502.     unsigned char *p;
  503.     int i;
  504.     memset(empty, 0, sizeof(empty));
  505.  
  506.     /* write the even rows */
  507.     p = image;
  508.     for (i = 0; i < 246; i++) {
  509.     (void)fwrite(p, 1, 512, outf);
  510.     p += 512;
  511.     if (scantype == 2)
  512.         p += 512;
  513.     }
  514.  
  515.     /* ten empty rows */
  516.     for (i = 0; i < 10; i++)
  517.     (void)fwrite(empty, 1, 512, outf);
  518.  
  519.     /* write the odd rows, or write the even rows again */
  520.     p = (scantype == 2) ? &image[512] : image;
  521.     for (i = 0; i < 246; i++) {
  522.     (void)fwrite(p, 1, 512, outf);
  523.     p += 512;
  524.     if (scantype == 2)
  525.         p += 512;
  526.     }
  527.  
  528.     /* eight empty rows */
  529.     for (i = 0; i < 8; i++)
  530.     (void)fwrite(empty, 1, 512, outf);
  531.  
  532.     /* two black rows */
  533.     (void)fwrite(blackvals, 1, 2*512, outf);
  534. }
  535.  
  536. void
  537. inhale_wcd_format(
  538.     unsigned char *image,
  539.     int *blackadjp,
  540.     FILE *inf)
  541. {
  542.     int i, sum;
  543.     unsigned char *p;
  544.     unsigned char scratch[512];
  545.  
  546.     /* read the even rows */
  547.     p = image;
  548.     for (i = 0; i < 246; i++) {
  549.     if (fread(p, 1, 512, inf) != 512)
  550.         goto errout;
  551.     p += 2*512;
  552.     }
  553.  
  554.     /* ten empty rows */
  555.     for (i = 0; i < 10; i++) {
  556.     if (fread(scratch, 1, 512, inf) != 512)
  557.         goto errout;
  558.     }
  559.  
  560.     /* read the odd rows */
  561.     p = &image[512];
  562.     for (i = 0; i < 246; i++) {
  563.     if (fread(p, 1, 512, inf) != 512)
  564.         goto errout;
  565.     p += 2*512;
  566.     }
  567.  
  568.     /* eight empty rows */
  569.     for (i = 0; i < 8; i++) {
  570.     if (fread(scratch, 1, 512, inf) != 512)
  571.         goto errout;
  572.     }
  573.  
  574.  
  575.     /* two black rows to average */
  576.     sum = 0;
  577.     if (fread(scratch, 1, 512, inf) != 512)
  578.         goto errout;
  579.     for (i = 0; i < 246; i++)
  580.     sum += scratch[i];
  581.  
  582.     if (fread(scratch, 1, 512, inf) != 512)
  583.         goto errout;
  584.     for (i = 0; i < 246; i++)
  585.     sum += scratch[i];
  586.  
  587.     sum /= (2 * 246);
  588.     *blackadjp = sum;
  589.  
  590.     return;
  591.  
  592.  errout:
  593.     fprintf(stderr, "Error reading .wcd format input\n");
  594.     exit(1);
  595. }
  596.  
  597.  
  598. /*
  599.  * the snapper funcs are "called back" by the winc library when the
  600.  * picture is actually snapped.  this lets us a) make a noise, and b)
  601.  * record the time
  602.  */
  603. void
  604. snapper_func(void *timep)
  605. {
  606.     *(time_t *)timep = time(0);
  607. }
  608.  
  609. void
  610. audible_snapper_func(void *timep)
  611. {
  612.     putc('\a', stderr);
  613.     *(time_t *)timep = time(0);
  614. }
  615.  
  616. /*
  617.  * attempt to reset the modification time of the given file to mtime.
  618.  * this is used to set the time on the output file to the time at which
  619.  * the image was snapped, rather than when it was written, since processing
  620.  * can take some time.
  621.  */
  622. void 
  623. reset_mod_time(char *filename, time_t mtime)
  624. {
  625.     struct stat stb;
  626.     struct utimbuf utb;
  627.     if (stat(filename, &stb) == -1)
  628.         return;
  629.  
  630.     utb.actime = stb.st_atime;
  631.     utb.modtime = mtime;
  632.     (void)utime(filename, &utb);
  633. }
  634.  
  635. void
  636. quitcatcher(int sig)
  637. {
  638.     if (Cam)
  639.         winc_close(Cam);
  640.     exit(1);
  641. }
  642.  
  643.