From 1bdf71f47e5f9f75308c5fef664814c2c125f305 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Sun, 10 Nov 2019 13:53:08 +0100 Subject: Rework device API, drop raw output --- device.c | 33 ++++++++----- device.h | 97 ++++++++++++++++++++++++++---------- image.c | 5 +- image.h | 3 +- live.c | 6 +-- main.c | 85 +++++++++++++++++--------------- moticam.c | 165 ++++++++++++++++++++++++++++++++++++++++++++------------------ options.c | 62 ++++++++--------------- options.h | 14 +++--- 9 files changed, 289 insertions(+), 181 deletions(-) diff --git a/device.c b/device.c index 2168d01..620f132 100644 --- a/device.c +++ b/device.c @@ -33,7 +33,7 @@ static const struct device_driver *drivers[] = { }; struct device * -device_open(libusb_context *usb, struct options *options) +device_open(libusb_context *usb) { libusb_device **list; libusb_device *found_usb_device = NULL; @@ -67,38 +67,45 @@ device_open(libusb_context *usb, struct options *options) struct device *device = NULL; if (found_usb_device) { free(found_device_name); - device = found_device_driver->usb_open(found_usb_device, options); + device = found_device_driver->usb_open(found_usb_device); } libusb_free_device_list(list, 1); return device; } -const uint8_t * -device_read_raw(struct device *device, size_t *size) +const struct device_info * +device_get_info(struct device *device) { - return device->read_raw(device, size); + return device->get_info(device); } -const uint32_t * -device_read(struct device *device) +void +device_set_exposure(struct device *device, double exposure_ms) { - return device->read(device); + device->set_exposure(device, exposure_ms); } void -device_set_exposure(struct device *device, double exposure_ms) +device_set_gain(struct device *device, double gain) { - return device->set_exposure(device, exposure_ms); + device->set_gain(device, gain); } void -device_set_gain(struct device *device, double gain) +device_set_resolution(struct device *device, int width, int height, + int stride) +{ + device->set_resolution(device, width, height, stride); +} + +const struct device_image * +device_read(struct device *device) { - return device->set_gain(device, gain); + return device->read(device); } void device_close(struct device *device) { - return device->close(device); + device->close(device); } diff --git a/device.h b/device.h index 72b2b0a..9e154d8 100644 --- a/device.h +++ b/device.h @@ -27,8 +27,6 @@ #include #include -#include "options.h" - struct device; /* USB poll function. If the device is handled by this driver, it should @@ -39,12 +37,10 @@ typedef char *(*device_driver_usb_poll_f)( /* USB open function. The device driver previously declared that it handles * this device. */ typedef struct device *(*device_driver_usb_open_f)( - libusb_device *usb_device, - struct options *options); + libusb_device *usb_device); /* Information on a device driver. */ -struct device_driver -{ +struct device_driver { /* Driver name. */ const char *name; /* Poll this driver for support for an USB device. */ @@ -53,12 +49,36 @@ struct device_driver device_driver_usb_open_f usb_open; }; -/* Return raw data if available, else, return NULL. */ -typedef const uint8_t *(*device_read_raw_f)( - struct device *device, size_t *size); +/* Information on a supported resolution. */ +struct device_info_resolution { + /* Image width. */ + int width; + /* Image height. */ + int height; +}; -/* Return an image if one is available, else, return NULL. */ -typedef const uint32_t *(*device_read_f)( +/* Informations on an open device. */ +struct device_info { + /* Exposure minimum and maximum values. */ + double exposure_min_ms, exposure_max_ms; + /* Exposure default value. */ + double exposure_default_ms; + /* Exposure increment between two settable values. */ + double exposure_step_ms; + /* Gain minimum and maximum values. */ + double gain_min, gain_max; + /* Gain default value. */ + double gain_default; + /* Gain increment between two settable values. */ + double gain_step; + /* Number of supported resolutions. */ + int resolutions; + /* Table of supported resolutions. */ + const struct device_info_resolution *resolution; +}; + +/* Retrieve informations. */ +typedef const struct device_info *(*device_get_info_f)( struct device *device); /* Set new exposure value. */ @@ -69,36 +89,54 @@ typedef void (*device_set_exposure_f)( typedef void (*device_set_gain_f)( struct device *device, double gain); +/* Set resolution, must be called before first image read. If stride is zero, + * let the device decide. */ +typedef void (*device_set_resolution_f)( + struct device *device, int width, int height, int stride); + +/* Image with attached information. */ +struct device_image { + /* Image width. */ + int width; + /* Image height. */ + int height; + /* Byte offset between a line and the next line. */ + int stride; + /* Frame buffer, ARGB format. */ + uint32_t *pixels; +}; + +/* Return an image if one is available, else, return NULL. */ +typedef const struct device_image *(*device_read_f)( + struct device *device); + /* Close device. */ typedef void (*device_close_f)( struct device *device); /* Open device. */ -struct device -{ - /* Read raw data. */ - device_read_raw_f read_raw; - /* Read an image. */ - device_read_f read; +struct device { + /* Retrieve informations. */ + device_get_info_f get_info; /* Change exposure. */ device_set_exposure_f set_exposure; /* Change gain. */ device_set_gain_f set_gain; + /* Change resolution. */ + device_set_resolution_f set_resolution; + /* Read an image. */ + device_read_f read; /* Close device. */ device_close_f close; }; /* Find and open device. */ struct device * -device_open(libusb_context *usb, struct options *options); +device_open(libusb_context *usb); -/* Read raw data. */ -const uint8_t * -device_read_raw(struct device *device, size_t *size); - -/* Read an image. */ -const uint32_t * -device_read(struct device *device); +/* Retrieve informations. */ +const struct device_info * +device_get_info(struct device *device); /* Set new exposure value. */ void @@ -108,6 +146,15 @@ device_set_exposure(struct device *device, double exposure_ms); void device_set_gain(struct device *device, double gain); +/* Set resolution. */ +void +device_set_resolution(struct device *device, int width, int height, + int stride); + +/* Read an image. */ +const struct device_image * +device_read(struct device *device); + /* Close device. */ void device_close(struct device *device); diff --git a/image.c b/image.c index 0fb684d..373010b 100644 --- a/image.c +++ b/image.c @@ -25,7 +25,8 @@ #include void -image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height) +image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height, + int stride) { /* * Compute missing value using an average of its neighbours. @@ -36,7 +37,7 @@ image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height) * B G B G B G */ const int in_stride = width; - const int out_stride = width * 4; + const int out_stride = stride; /* Skip first line and column. */ const uint8_t *in = bayer + in_stride + 1; uint8_t *out = (uint8_t *) rgb + out_stride + 4; diff --git a/image.h b/image.h index 5ad222d..4d04413 100644 --- a/image.h +++ b/image.h @@ -25,6 +25,7 @@ #include void -image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height); +image_bayer2argb(const uint8_t *bayer, uint32_t *rgb, int width, int height, + int stride); #endif /* image_h */ diff --git a/live.c b/live.c index c473b58..9bb52be 100644 --- a/live.c +++ b/live.c @@ -312,9 +312,9 @@ live_run(struct device *device, struct options *options) } if (exit) break; - const uint32_t *pixels = device_read(device); - if (pixels) { - SDL_UpdateTexture(texture, NULL, pixels, options->width * 4); + const struct device_image *image = device_read(device); + if (image) { + SDL_UpdateTexture(texture, NULL, image->pixels, image->stride); SDL_SetRenderDrawColor(live.renderer, 0, 0, 0, 0); SDL_RenderClear(live.renderer); SDL_RenderCopyEx(live.renderer, texture, NULL, diff --git a/main.c b/main.c index ad3d1f0..7fd84ef 100644 --- a/main.c +++ b/main.c @@ -32,45 +32,27 @@ #include "utils.h" void -run(struct device *device, struct options *options) +run(struct device *device, int count, const char *out) { - if (options->raw) { - FILE *out = fopen(options->out, "wb"); - if (!out) - utils_fatal("can not open output file `%s': %m", options->out); - for (int i = 0; i < options->count;) { - size_t data_size; - const uint8_t *data = device_read_raw(device, &data_size); - if (data) { - utils_info("write %d (%zu)", i, data_size); - int r = fwrite(data, data_size, 1, out); - if (r < 0) - utils_fatal("can not write: %m"); - i++; - } - } - fclose(out); - } else { - for (int i = 0; i < options->count;) { - const uint32_t *pixels = device_read(device); - if (pixels) { - char *name = NULL; - if (asprintf(&name, options->out, i) < 0) - utils_fatal("can not prepare file name"); - utils_info("write %s", name); - png_image image; - memset(&image, 0, sizeof(image)); - image.version = PNG_IMAGE_VERSION; - image.width = options->width; - image.height = options->height; - image.format = PNG_FORMAT_BGRA; - int r = png_image_write_to_file(&image, name, 0, pixels, 0, - NULL); - if (r == 0) - utils_fatal("can not write image: %s", image.message); - free(name); - i++; - } + for (int i = 0; i < count;) { + const struct device_image *device_image = device_read(device); + if (device_image) { + char *name = NULL; + if (asprintf(&name, out, i) < 0) + utils_fatal("can not prepare file name"); + utils_info("write %s", name); + png_image image; + memset(&image, 0, sizeof(image)); + image.version = PNG_IMAGE_VERSION; + image.width = device_image->width; + image.height = device_image->height; + image.format = PNG_FORMAT_BGRA; + int r = png_image_write_to_file(&image, name, 0, + device_image->pixels, device_image->stride, NULL); + if (r == 0) + utils_fatal("can not write image: %s", image.message); + free(name); + i++; } } } @@ -84,11 +66,34 @@ main(int argc, char **argv) int r = libusb_init(&usb); if (r) utils_fatal("unable to initialize libusb: %s", libusb_strerror(r)); - struct device *device = device_open(usb, &options); + struct device *device = device_open(usb); if (!device) utils_fatal("unable to find device"); + const struct device_info *info = device_get_info(device); + if (options.exposure_ms > 0.0) + device_set_exposure(device, options.exposure_ms); + else + options.exposure_ms = info->exposure_default_ms; + if (options.gain > 0.0) + device_set_gain(device, options.gain); + else + options.gain = info->gain_default; + int width = -1, height = -1; + for (int i = 0; width == -1 && i < info->resolutions; i++) { + const struct device_info_resolution *ir = &info->resolution[i]; + if ((options.width == -1 || options.width == ir->width) + && (options.height == -1 || options.height == ir->height)) { + width = ir->width; + height = ir->height; + } + } + if (width == -1) + utils_fatal("no matching resolution"); + device_set_resolution(device, width, height, 0); + options.width = width; + options.height = height; if (options.count) - run(device, &options); + run(device, options.count, options.out); else live_run(device, &options); device_close(device); diff --git a/moticam.c b/moticam.c index 2e46144..03dccd6 100644 --- a/moticam.c +++ b/moticam.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "device.h" #include "image.h" @@ -36,18 +37,17 @@ struct moticam_device { struct device device; libusb_device_handle *handle; - int width; - int height; - bool raw; + double exposure_ms; + double gain; size_t rawbuffer_size; uint8_t *rawbuffer; - uint32_t *buffer; + struct device_image image; }; static char * moticam_usb_poll(struct libusb_device_descriptor *desc); static struct device * -moticam_usb_open(libusb_device *usb_device, struct options *options); +moticam_usb_open(libusb_device *usb_device); const struct device_driver moticam_device_driver = { "Moticam USB cameras", @@ -55,6 +55,25 @@ const struct device_driver moticam_device_driver = { &moticam_usb_open, }; +static const struct device_info_resolution moticam_device_info_resolution[] = { + { 2048, 1536 }, + { 1024, 768 }, + { 512, 384 }, +}; + +static const struct device_info moticam_device_info = { + .exposure_min_ms = 1, + .exposure_max_ms = 5000, + .exposure_default_ms = 100, + .exposure_step_ms = 1, + .gain_min = 0.33, + .gain_max = 42.66, + .gain_default = 1, + .gain_step = 1.0 / 24, + .resolutions = 3, + .resolution = moticam_device_info_resolution, +}; + static void moticam_control_vendor(struct device *device, uint16_t wValue, const uint8_t *data, int data_cnt) @@ -75,14 +94,14 @@ moticam_control_vendor_w(struct device *device, uint16_t wValue, } static void -moticam_reset(struct device *device) +moticam_control_reset(struct device *device) { moticam_control_vendor_w(device, 0xba00, 0x0000); moticam_control_vendor_w(device, 0xba00, 0x0001); } static void -moticam_set_exposure(struct device *device, double exposure_ms) +moticam_control_exposure(struct device *device, double exposure_ms) { int exposure_w = exposure_ms * 12.82; if (exposure_w < 0x000c) @@ -93,7 +112,7 @@ moticam_set_exposure(struct device *device, double exposure_ms) } static void -moticam_set_gain(struct device *device, double gain) +moticam_control_gain(struct device *device, double gain) { double gmin, gmax; int xmin, xmax; @@ -129,7 +148,7 @@ moticam_set_gain(struct device *device, double gain) } static void -moticam_set_resolution(struct device *device, int width, int height) +moticam_control_resolution(struct device *device, int width, int height) { static const uint8_t control_init[] = { 0x00, 0x14, 0x00, 0x20, 0x05, 0xff, 0x07, 0xff }; @@ -160,11 +179,69 @@ moticam_set_resolution(struct device *device, int width, int height) } } -static const uint8_t * -moticam_read_raw(struct device *device, size_t *size) +static const struct device_info * +moticam_get_info(struct device *device) +{ + return &moticam_device_info; +} + +static void +moticam_set_exposure(struct device *device, double exposure_ms) +{ + struct moticam_device *mdev = (struct moticam_device *) device; + mdev->exposure_ms = exposure_ms; + if (mdev->image.pixels) + moticam_control_exposure(device, exposure_ms); +} + +static void +moticam_set_gain(struct device *device, double gain) +{ + struct moticam_device *mdev = (struct moticam_device *) device; + mdev->gain = gain; + if (mdev->image.pixels) + moticam_control_gain(device, gain); +} + +static void +moticam_set_resolution(struct device *device, int width, int height, + int stride) +{ + struct moticam_device *mdev = (struct moticam_device *) device; + /* Release previous configuration. */ + if (mdev->image.pixels) { + free(mdev->rawbuffer); + free(mdev->image.pixels); + moticam_control_exposure(&mdev->device, 30.0); + } + /* Choose stride. */ + if (!stride) + stride = width * sizeof(uint32_t); + assert(stride >= width * sizeof(uint32_t)); + /* Store parameters. */ + mdev->image.width = width; + mdev->image.height = height; + mdev->image.stride = stride; + /* Prepare sizes and buffers, request an extra frame to read the zero + * length packet. */ + int image_size = width * height; + int frame_size = 16384; + mdev->rawbuffer_size = (image_size + frame_size) + / frame_size * frame_size; + mdev->rawbuffer = utils_malloc(mdev->rawbuffer_size); + mdev->image.pixels = utils_malloc(stride * height); + /* Configure camera. */ + moticam_control_gain(&mdev->device, mdev->gain); + moticam_control_resolution(&mdev->device, width, height); + moticam_control_exposure(&mdev->device, mdev->exposure_ms); + utils_delay_us(100000); +} + +static bool +moticam_read_raw(struct device *device) { struct moticam_device *mdev = (struct moticam_device *) device; - int image_size = mdev->width * mdev->height; + int image_size = mdev->image.width * mdev->image.height; int transfered = 0; int r = libusb_bulk_transfer(mdev->handle, 0x83, mdev->rawbuffer, mdev->rawbuffer_size, &transfered, 0); @@ -172,23 +249,21 @@ moticam_read_raw(struct device *device, size_t *size) utils_fatal("can not read data: %s", libusb_strerror(r)); if (transfered != image_size) { utils_warning("bad image size (%d), drop", transfered); - return NULL; + return false; } else { - *size = mdev->rawbuffer_size; - return mdev->rawbuffer; + return true; } } -static const uint32_t * +static const struct device_image * moticam_read(struct device *device) { struct moticam_device *mdev = (struct moticam_device *) device; - size_t rawbuffer_size; - const uint8_t *rawbuffer = moticam_read_raw(device, &rawbuffer_size); - if (rawbuffer) { - image_bayer2argb(rawbuffer, mdev->buffer, mdev->width, - mdev->height); - return mdev->buffer; + assert(mdev->image.pixels); + if (moticam_read_raw(device)) { + image_bayer2argb(mdev->rawbuffer, mdev->image.pixels, mdev->image.width, + mdev->image.height, mdev->image.stride); + return &mdev->image; } else { return NULL; } @@ -198,13 +273,14 @@ static void moticam_close(struct device *device) { struct moticam_device *mdev = (struct moticam_device *) device; - moticam_set_exposure(device, 0.0); - moticam_set_exposure(device, 0.0); - moticam_set_exposure(device, 0.0); + moticam_control_exposure(device, 0.0); + moticam_control_exposure(device, 0.0); + moticam_control_exposure(device, 0.0); libusb_close(mdev->handle); - free(mdev->rawbuffer); - if (mdev->buffer) - free(mdev->buffer); + if (mdev->image.pixels) { + free(mdev->rawbuffer); + free(mdev->image.pixels); + } free(device); } @@ -218,38 +294,33 @@ moticam_usb_poll(struct libusb_device_descriptor *desc) } static struct device * -moticam_usb_open(libusb_device *usb_device, struct options *options) +moticam_usb_open(libusb_device *usb_device) { /* Create context. */ struct moticam_device *mdev = utils_malloc(sizeof(struct moticam_device)); - mdev->device.read_raw = &moticam_read_raw; - mdev->device.read = &moticam_read; + mdev->device.get_info = &moticam_get_info; mdev->device.set_exposure = &moticam_set_exposure; mdev->device.set_gain = &moticam_set_gain; + mdev->device.set_resolution = &moticam_set_resolution; + mdev->device.read = &moticam_read; mdev->device.close = &moticam_close; mdev->handle = NULL; - mdev->width = options->width; - mdev->height = options->height; - mdev->raw = options->raw; - int image_size = mdev->width * mdev->height; - int frame_size = 16384; - /* Request an extra frame to read the zero length packet. */ - mdev->rawbuffer_size = (image_size + frame_size) / frame_size * frame_size; - mdev->rawbuffer = utils_malloc(mdev->rawbuffer_size); - mdev->buffer = mdev->raw ? NULL : - utils_malloc(mdev->width * mdev->height * sizeof(uint32_t)); + mdev->exposure_ms = moticam_device_info.exposure_default_ms; + mdev->gain = moticam_device_info.gain_default; + mdev->rawbuffer_size = 0; + mdev->rawbuffer = NULL; + mdev->image.width = 0; + mdev->image.height = 0; + mdev->image.stride = 0; + mdev->image.pixels = NULL; /* Open USB device. */ int r = libusb_open(usb_device, &mdev->handle); if (r) utils_fatal("can not open device: %s", libusb_strerror(r)); /* Initialize camera. */ - moticam_reset(&mdev->device); - moticam_set_gain(&mdev->device, options->gain); - moticam_set_exposure(&mdev->device, 30.0); - moticam_set_resolution(&mdev->device, options->width, options->height); - moticam_set_exposure(&mdev->device, options->exposure_ms); - utils_delay_us(100000); + moticam_control_reset(&mdev->device); + moticam_control_exposure(&mdev->device, 30.0); /* Done. */ return &mdev->device; } diff --git a/options.c b/options.c index 80c49ed..2bf1489 100644 --- a/options.c +++ b/options.c @@ -42,20 +42,16 @@ usage(int status, const char *msg) "Microscope camera viewer.\n" "\n" "positional arguments:\n" - " FILE output file pattern (default: out%%02d.png" - " or out for raw output)\n" + " FILE output file pattern (default:" + " out%%02d.png)\n" "\n" "optional arguments:\n" " -h, --help show this help message and exit\n" - " -w, --width VALUE image width (512, 1024 or 2048," - " default: 1024)\n" - " -e, --exposure MS exposure value (1 to 5000," - " default: 100)\n" - " -g, --gain VALUE gain value (0.33 to 42.66," - " default: 1)\n" + " -w, --width VALUE image width\n" + " -e, --exposure MS exposure value\n" + " -g, --gain VALUE gain value\n" " -n, --count N number of image to take" " (default: live video)\n" - " -r, --raw save raw images\n" , program_invocation_name); exit(status); } @@ -63,12 +59,11 @@ usage(int status, const char *msg) void options_parse(int argc, char **argv, struct options *options) { - options->width = 1024; - options->height = 768; - options->exposure_ms = 100.0; - options->gain = 1.0; + options->width = -1; + options->height = -1; + options->exposure_ms = -1.0; + options->gain = -1.0; options->count = 0; - options->raw = false; options->out = NULL; char *tail; while (1) { @@ -78,11 +73,10 @@ options_parse(int argc, char **argv, struct options *options) { "exposure", required_argument, 0, 'e' }, { "gain", required_argument, 0, 'g' }, { "count", required_argument, 0, 'n' }, - { "raw", required_argument, 0, 'r' }, { NULL }, }; int option_index = 0; - int c = getopt_long(argc, argv, "hw:e:g:n:r", long_options, + int c = getopt_long(argc, argv, "hw:e:g:n:", long_options, &option_index); if (c == -1) break; @@ -91,30 +85,21 @@ options_parse(int argc, char **argv, struct options *options) usage(EXIT_SUCCESS, NULL); break; case 'w': - if (strcmp(optarg, "512") == 0) { - options->width = 512; - options->height = 384; - } else if (strcmp(optarg, "1024") == 0) { - options->width = 1024; - options->height = 768; - } else if (strcmp(optarg, "2048") == 0) { - options->width = 2048; - options->height = 1536; - } else + errno = 0; + options->width = strtol(optarg, &tail, 10); + if (*tail != '\0' || errno || options->width < 1) usage(EXIT_FAILURE, "bad width value"); break; case 'e': errno = 0; options->exposure_ms = strtod(optarg, &tail); - if (*tail != '\0' || errno || options->exposure_ms < 1 - || options->exposure_ms > 5000) + if (*tail != '\0' || errno || options->exposure_ms < 1) usage(EXIT_FAILURE, "bad exposure value"); break; case 'g': errno = 0; options->gain = strtod(optarg, &tail); - if (*tail != '\0' || errno || options->gain <= 0.0 - || options->gain >= 43.0) + if (*tail != '\0' || errno || options->gain <= 0.0) usage(EXIT_FAILURE, "bad gain value"); break; case 'n': @@ -123,9 +108,6 @@ options_parse(int argc, char **argv, struct options *options) if (*tail != '\0' || errno) usage(EXIT_FAILURE, "bad count value"); break; - case 'r': - options->raw = true; - break; case '?': usage(EXIT_FAILURE, NULL); break; @@ -138,13 +120,9 @@ options_parse(int argc, char **argv, struct options *options) if (optind < argc) usage(EXIT_FAILURE, "too many arguments"); if (!options->out) - options->out = options->raw ? "out" : "out%02d.png"; - if (!options->raw) - { - int argtypes[1]; - int formats = parse_printf_format(options->out, 1, argtypes); - if (formats != 1 || argtypes[0] != PA_INT) - usage(EXIT_FAILURE, "bad file pattern, use one %d"); - } + options->out = "out%02d.png"; + int argtypes[1]; + int formats = parse_printf_format(options->out, 1, argtypes); + if (formats != 1 || argtypes[0] != PA_INT) + usage(EXIT_FAILURE, "bad file pattern, use one %d"); } - diff --git a/options.h b/options.h index 5b85561..3cc0681 100644 --- a/options.h +++ b/options.h @@ -26,21 +26,19 @@ /* Runtime options. */ struct options { - /* Image width. */ + /* Image width, -1 for default. */ int width; - /* Image height. */ + /* Image height, -1 for default. */ int height; - /* Exposure in ms. */ + /* Exposure in ms, -1.0 for default. */ double exposure_ms; - /* Digital gain after image acquisition. */ + /* Digital gain after image acquisition, -1.0 for default. */ double gain; /* When zero, run interactive mode, else number of frame to dump. */ int count; - /* Dump raw image from sensor. Only used in dump mode. */ - bool raw; /* Output file name or pattern, only used in dump mode. This should - * include a printf like pattern (%d) used to name the files, unless raw - * dump is requested. Only used in dump mode. */ + * include a printf like pattern (%d) used to name the files. Only used in + * dump mode. */ const char *out; }; -- cgit v1.2.3