From 51675360e87297cbf4423a1b12ae0c6a06a92158 Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Fri, 27 Sep 2019 23:18:00 +0200 Subject: Now camicro, the microscope camera viewer --- moticam.c | 595 ++++++++++++++------------------------------------------------ 1 file changed, 135 insertions(+), 460 deletions(-) (limited to 'moticam.c') diff --git a/moticam.c b/moticam.c index 431d72a..2e46144 100644 --- a/moticam.c +++ b/moticam.c @@ -1,4 +1,6 @@ -/* Moticam 3+ viewer. +/* Camicro - Microscope camera viewer. + * + * This files add support for direct connexion with USB Moticam 3+. * * Copyright (C) 2019 Nicolas Schodet * @@ -20,217 +22,78 @@ * Web: http://ni.fr.eu.org/ * Email: */ -#define _GNU_SOURCE #include -#include -#include #include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include "device.h" +#include "image.h" +#include "utils.h" #define ID_VENDOR 0x232f #define ID_PRODUCT 0x0100 -struct options { +struct moticam_device { + struct device device; + libusb_device_handle *handle; int width; int height; - double exposure; - double gain; - int count; bool raw; - const char *out; + size_t rawbuffer_size; + uint8_t *rawbuffer; + uint32_t *buffer; }; -void -usage(int status, const char *msg) -{ - if (msg) - error(0, 0, "%s", msg); - fprintf(status == EXIT_SUCCESS ? stdout : stderr, - "usage: %s [options] [FILE]\n" - "\n" - "Moticam 3+ viewer.\n" - "\n" - "positional arguments:\n" - " FILE output file pattern (default: out%%02d.png" - " or out for raw output)\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" - " -n, --count N number of image to take" - " (default: live video)\n" - " -r, --raw save raw images\n" - , program_invocation_name); - exit(status); -} - -void -parse_options(int argc, char **argv, struct options *options) -{ - options->width = 1024; - options->height = 768; - options->exposure = 100.0; - options->gain = 1.0; - options->count = 0; - options->raw = false; - options->out = NULL; - char *tail; - while (1) { - static struct option long_options[] = { - { "help", no_argument, 0, 'h' }, - { "width", required_argument, 0, 'w' }, - { "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, - &option_index); - if (c == -1) - break; - switch (c) { - case 'h': - 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 - usage(EXIT_FAILURE, "bad width value"); - break; - case 'e': - errno = 0; - options->exposure = strtod(optarg, &tail); - if (*tail != '\0' || errno || options->exposure < 1 - || options->exposure > 5000) - 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) - usage(EXIT_FAILURE, "bad gain value"); - break; - case 'n': - errno = 0; - options->count = strtoul(optarg, &tail, 10); - if (*tail != '\0' || errno) - usage(EXIT_FAILURE, "bad count value"); - break; - case 'r': - options->raw = true; - break; - case '?': - usage(EXIT_FAILURE, NULL); - break; - default: - abort(); - } - } - if (optind < argc) - options->out = argv[optind++]; - 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"); - } -} +static char * +moticam_usb_poll(struct libusb_device_descriptor *desc); +static struct device * +moticam_usb_open(libusb_device *usb_device, struct options *options); -libusb_device_handle * -device_open() -{ - libusb_device **list; - libusb_device *found = NULL; - int nfound = 0; - ssize_t cnt = libusb_get_device_list(NULL, &list); - ssize_t i = 0; - if (cnt < 0) - error(EXIT_FAILURE, 0, "can not list devices: %s", - libusb_strerror(cnt)); - for (i = 0; i < cnt; i++) { - libusb_device *device = list[i]; - struct libusb_device_descriptor desc; - int r = libusb_get_device_descriptor(device, &desc); - if (r) - error(EXIT_FAILURE, 0, "can not get device descriptor: %s", - libusb_strerror(r)); - if (desc.idVendor == ID_VENDOR && desc.idProduct == ID_PRODUCT) { - found = device; - nfound++; - break; - } - } - if (nfound > 1) - error(EXIT_FAILURE, 0, - "more than one matching device, not supported"); - libusb_device_handle *handle = NULL; - if (found) { - int r = libusb_open(found, &handle); - if (r) - error(EXIT_FAILURE, 0, "can not open device: %s", - libusb_strerror(r)); - } - libusb_free_device_list(list, 1); - return handle; -} +const struct device_driver moticam_device_driver = { + "Moticam USB cameras", + &moticam_usb_poll, + &moticam_usb_open, +}; -void -device_control_vendor(libusb_device_handle *handle, uint16_t wValue, +static void +moticam_control_vendor(struct device *device, uint16_t wValue, const uint8_t *data, int data_cnt) { - int r = libusb_control_transfer(handle, LIBUSB_REQUEST_TYPE_VENDOR, 240, - wValue, 0, (uint8_t *) data, data_cnt, 0); + struct moticam_device *mdev = (struct moticam_device *) device; + int r = libusb_control_transfer(mdev->handle, LIBUSB_REQUEST_TYPE_VENDOR, + 240, wValue, 0, (uint8_t *) data, data_cnt, 0); if (r < 0) - error(EXIT_FAILURE, 0, "can not send vendor: %s", libusb_strerror(r)); + utils_fatal("can not send vendor control: %s", libusb_strerror(r)); } -void -device_control_vendor_w(libusb_device_handle *handle, uint16_t wValue, +static void +moticam_control_vendor_w(struct device *device, uint16_t wValue, const uint16_t data0) { uint8_t data[] = { (uint8_t) (data0 >> 8), (uint8_t) data0 }; - device_control_vendor(handle, wValue, data, sizeof(data)); + moticam_control_vendor(device, wValue, data, sizeof(data)); } -void -device_reset(libusb_device_handle *handle) +static void +moticam_reset(struct device *device) { - device_control_vendor_w(handle, 0xba00, 0x0000); - device_control_vendor_w(handle, 0xba00, 0x0001); + moticam_control_vendor_w(device, 0xba00, 0x0000); + moticam_control_vendor_w(device, 0xba00, 0x0001); } -void -device_set_gain(libusb_device_handle *handle, double gain) +static void +moticam_set_exposure(struct device *device, double exposure_ms) +{ + int exposure_w = exposure_ms * 12.82; + if (exposure_w < 0x000c) + exposure_w = 0x000c; + else if (exposure_w > 0xffff) + exposure_w = 0xffff; + moticam_control_vendor_w(device, 0xba09, exposure_w); +} + +static void +moticam_set_gain(struct device *device, double gain) { double gmin, gmax; int xmin, xmax; @@ -259,29 +122,18 @@ device_set_gain(libusb_device_handle *handle, double gain) x = xmax; if (up) x = (x << 8) | 0x60; - device_control_vendor_w(handle, 0xba2d, x); - device_control_vendor_w(handle, 0xba2b, x); - device_control_vendor_w(handle, 0xba2e, x); - device_control_vendor_w(handle, 0xba2c, x); -} - -void -device_set_exposure(libusb_device_handle *handle, double exposure) -{ - int exposure_w = exposure * 12.82; - if (exposure_w < 0x000c) - exposure_w = 0x000c; - else if (exposure_w > 0xffff) - exposure_w = 0xffff; - device_control_vendor_w(handle, 0xba09, exposure_w); + moticam_control_vendor_w(device, 0xba2d, x); + moticam_control_vendor_w(device, 0xba2b, x); + moticam_control_vendor_w(device, 0xba2e, x); + moticam_control_vendor_w(device, 0xba2c, x); } -void -device_set_resolution(libusb_device_handle *handle, int width, int height) +static void +moticam_set_resolution(struct device *device, int width, int height) { static const uint8_t control_init[] = { 0x00, 0x14, 0x00, 0x20, 0x05, 0xff, 0x07, 0xff }; - device_control_vendor(handle, 0xba01, control_init, + moticam_control_vendor(device, 0xba01, control_init, sizeof(control_init)); static const uint8_t control_512x384[] = { 0x00, 0x03, 0x00, 0x03 }; static const uint8_t control_1024x768[] = { 0x00, 0x11, 0x00, 0x11 }; @@ -290,17 +142,17 @@ device_set_resolution(libusb_device_handle *handle, int width, int height) { case 512: assert(height == 384); - device_control_vendor(handle, 0xba22, control_512x384, + moticam_control_vendor(device, 0xba22, control_512x384, sizeof(control_512x384)); break; case 1024: assert(height == 768); - device_control_vendor(handle, 0xba22, control_1024x768, + moticam_control_vendor(device, 0xba22, control_1024x768, sizeof(control_1024x768)); break; case 2048: assert(height == 1536); - device_control_vendor(handle, 0xba22, control_2048x1536, + moticam_control_vendor(device, 0xba22, control_2048x1536, sizeof(control_2048x1536)); break; default: @@ -308,273 +160,96 @@ device_set_resolution(libusb_device_handle *handle, int width, int height) } } -void -delay_us(int us) +static const uint8_t * +moticam_read_raw(struct device *device, size_t *size) { - struct timespec delay, remaining; - delay.tv_sec = us / 1000000; - delay.tv_nsec = us % 1000000 * 1000; - while (nanosleep(&delay, &remaining) == -1) { - if (errno == EINTR) - delay = remaining; - else - error(EXIT_FAILURE, errno, "can not sleep"); + struct moticam_device *mdev = (struct moticam_device *) device; + int image_size = mdev->width * mdev->height; + int transfered = 0; + int r = libusb_bulk_transfer(mdev->handle, 0x83, mdev->rawbuffer, + mdev->rawbuffer_size, &transfered, 0); + if (r) + utils_fatal("can not read data: %s", libusb_strerror(r)); + if (transfered != image_size) { + utils_warning("bad image size (%d), drop", transfered); + return NULL; + } else { + *size = mdev->rawbuffer_size; + return mdev->rawbuffer; } } -void -device_init(libusb_device_handle *handle, struct options *options) -{ - device_reset(handle); - device_set_gain(handle, options->gain); - device_set_exposure(handle, 30.0); - device_set_resolution(handle, options->width, options->height); - device_set_exposure(handle, options->exposure); - delay_us(100000); -} - -void -device_uninit(libusb_device_handle *handle) +static const uint32_t * +moticam_read(struct device *device) { - device_set_exposure(handle, 0.0); - device_set_exposure(handle, 0.0); - device_set_exposure(handle, 0.0); -} - -void -bayer2argb(uint8_t *bayer, uint8_t *rgb, int width, int height) -{ - /* - * Compute missing value using an average of its neighbours. - * Input pattern: - * G R G R G R - * B G B G B G - * G R G R G R - * B G B G B G - */ - const int in_stride = width; - const int out_stride = width * 4; - /* Skip first line and column. */ - uint8_t *in = bayer + in_stride + 1; - uint8_t *out = rgb + out_stride + 4; - /* Loop over lines. */ - for (int i = 1; i < height - 1; i += 2) { - /* Even lines. */ - uint8_t *in_stop = in + (width - 2); - while (in != in_stop) { - *out++ = (in[-1] + in[+1] + 1) >> 1; /* B */ - *out++ = in[0]; /* G */ - *out++ = (in[-in_stride] + in[+in_stride] + 1) >> 1; /* R */ - *out++ = 255; /* A */ - in++; - *out++ = in[0]; /* B */ - *out++ = (in[-in_stride] + in[+in_stride] /* G */ - + in[-1] + in[+1] + 2) >> 2; - *out++ = (in[-in_stride - 1] + in[-in_stride + 1] /* R */ - + in[+in_stride - 1] + in[+in_stride + 1] + 2) >> 2; - *out++ = 255; /* A */ - in++; - } - /* Fill first and last pixels. */ - out[-(width - 1) * 4 + 0] = out[-(width - 2) * 4 + 0]; - out[-(width - 1) * 4 + 1] = out[-(width - 2) * 4 + 1]; - out[-(width - 1) * 4 + 2] = out[-(width - 2) * 4 + 2]; - out[-(width - 1) * 4 + 3] = out[-(width - 2) * 4 + 3]; - out[0] = out[-4]; - out[1] = out[-3]; - out[2] = out[-2]; - out[3] = out[-1]; - out += out_stride - (width - 2) * 4; - in += in_stride - (width - 2); - /* Odd lines. */ - in_stop = in + (width - 2); - while (in != in_stop) { - *out++ = (in[-in_stride - 1] + in[-in_stride + 1] /* B */ - + in[+in_stride - 1] + in[+in_stride + 1] + 2) >> 2; - *out++ = (in[-in_stride] + in[+in_stride] /* G */ - + in[-1] + in[+1] + 2) >> 2; - *out++ = in[0]; /* R */ - *out++ = 255; /* A */ - in++; - *out++ = (in[-in_stride] + in[+in_stride] + 1) >> 1; /* B */ - *out++ = in[0]; /* G */ - *out++ = (in[-1] + in[+1] + 1) >> 1; /* R */ - *out++ = 255; /* A */ - in++; - } - /* Fill first and last pixels. */ - out[-(width - 1) * 4 + 0] = out[-(width - 2) * 4 + 0]; - out[-(width - 1) * 4 + 1] = out[-(width - 2) * 4 + 1]; - out[-(width - 1) * 4 + 2] = out[-(width - 2) * 4 + 2]; - out[-(width - 1) * 4 + 3] = out[-(width - 2) * 4 + 3]; - out[0] = out[-4]; - out[1] = out[-3]; - out[2] = out[-2]; - out[3] = out[-1]; - out += out_stride - (width - 2) * 4; - in += in_stride - (width - 2); + 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; + } else { + return NULL; } - /* Last line. */ - out -= 4; - memcpy (out, out - out_stride, width * 4); - /* First line. */ - out -= (height - 1) * out_stride; - memcpy (out, out + out_stride, width * 4); } -void -run(libusb_device_handle *handle, struct options *options) +static void +moticam_close(struct device *device) { - int image_size = options->width * options->height; - int frame_size = 16384; - // Request an extra frame to read the zero length packet. - int data_size = (image_size + frame_size) / frame_size * frame_size; - uint8_t *data = malloc(data_size); - if (!data) - error(EXIT_FAILURE, 0, "memory exhausted"); - FILE *out = NULL; - uint8_t *rgb = NULL; - if (options->raw) { - out = fopen(options->out, "wb"); - if (!out) - error(EXIT_FAILURE, errno, "can not open output file `%s'", - options->out); - } - for (int i = 0; i < options->count;) { - int transfered = 0; - int r = libusb_bulk_transfer(handle, 0x83, data, data_size, - &transfered, 0); - if (r) - error(EXIT_FAILURE, 0, "can not read data: %s", - libusb_strerror(r)); - if (transfered != image_size) - fprintf(stderr, "bad image size (%d), drop\n", transfered); - else { - if (options->raw) { - fprintf(stderr, "write %d (%d)\n", i, transfered); - r = fwrite(data, transfered, 1, out); - if (r < 0) - error(EXIT_FAILURE, errno, "can not write"); - } else { - char *name = NULL; - if (asprintf(&name, options->out, i) < 0) - error(EXIT_FAILURE, 0, "can not prepare file name"); - fprintf(stderr, "write %s\n", name); - if (!rgb) { - rgb = malloc(image_size * 4); - if (!rgb) - error(EXIT_FAILURE, 0, "memory exhausted"); - } - bayer2argb(data, rgb, options->width, options->height); - 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; - r = png_image_write_to_file(&image, name, 0, rgb, 0, NULL); - if (r == 0) - error(EXIT_FAILURE, 0, "can not write image: %s", - image.message); - } - i++; - } - } - free(data); - if (out) - fclose(out); - if (rgb) - free(rgb); + 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); + libusb_close(mdev->handle); + free(mdev->rawbuffer); + if (mdev->buffer) + free(mdev->buffer); + free(device); } -void -run_video(libusb_device_handle *handle, struct options *options) +static char * +moticam_usb_poll(struct libusb_device_descriptor *desc) { - if (SDL_Init(SDL_INIT_VIDEO)) - error(EXIT_FAILURE, 0, "unable to initialize SDL: %s", - SDL_GetError()); - atexit(SDL_Quit); - SDL_DisableScreenSaver(); - SDL_Window *window; - SDL_Renderer *renderer; - if (SDL_CreateWindowAndRenderer(options->width, options->height, - SDL_WINDOW_RESIZABLE, &window, &renderer)) - error(EXIT_FAILURE, 0, "unable to create window: %s", SDL_GetError()); - SDL_SetWindowTitle(window, "Moticam"); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - if (SDL_RenderSetLogicalSize(renderer, options->width, options->height)) - error(EXIT_FAILURE, 0, "can not set logical size: %s", SDL_GetError()); - SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, - SDL_TEXTUREACCESS_STREAMING, options->width, options->height); - if (!texture) - error(EXIT_FAILURE, 0, "can not create texture: %s", SDL_GetError()); - int image_size = options->width * options->height; + if (desc->idVendor == ID_VENDOR && desc->idProduct == ID_PRODUCT) + return strdup("Moticam 3+"); + else + return NULL; +} + +static struct device * +moticam_usb_open(libusb_device *usb_device, struct options *options) +{ + /* 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.set_exposure = &moticam_set_exposure; + mdev->device.set_gain = &moticam_set_gain; + 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; - int data_size = (image_size + frame_size) / frame_size * frame_size; - uint8_t *data = malloc(data_size); - if (!data) - error(EXIT_FAILURE, 0, "memory exhausted"); - uint8_t *rgb = malloc(image_size * 4); - if (!rgb) - error(EXIT_FAILURE, 0, "memory exhausted"); - bool exit = false; - while (1) { - SDL_Event event; - while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) - exit = true; - if (event.type == SDL_KEYDOWN - && (event.key.keysym.sym == SDLK_q - || event.key.keysym.sym == SDLK_ESCAPE)) - exit = true; - } - if (exit) - break; - int transfered = 0; - int r = libusb_bulk_transfer(handle, 0x83, data, data_size, - &transfered, 0); - if (r) - error(EXIT_FAILURE, 0, "can not read data: %s", - libusb_strerror(r)); - if (transfered != image_size) - fprintf(stderr, "bad image size (%d), drop\n", transfered); - else { - bayer2argb(data, rgb, options->width, options->height); - SDL_UpdateTexture(texture, NULL, rgb, options->width * 4); - SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); - SDL_RenderClear(renderer); - SDL_RenderCopyEx(renderer, texture, NULL, NULL, 180.0, NULL, - SDL_FLIP_NONE); - SDL_RenderPresent(renderer); - } - } - free(rgb); - free(data); - SDL_DestroyTexture(texture); - SDL_DestroyRenderer(renderer); - SDL_DestroyWindow(window); -} - -int -main(int argc, char **argv) -{ - struct options options; - parse_options(argc, argv, &options); - libusb_context *usb; - int r = libusb_init(&usb); + /* 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)); + /* Open USB device. */ + int r = libusb_open(usb_device, &mdev->handle); if (r) - error(EXIT_FAILURE, 0, "unable to initialize libusb: %s", - libusb_strerror(r)); - libusb_device_handle *handle = device_open(); - if (!handle) - error(EXIT_FAILURE, 0, "unable to find device"); - device_init(handle, &options); - if (options.count) - run(handle, &options); - else - run_video(handle, &options); - device_uninit(handle); - libusb_close(handle); - libusb_exit(usb); - return EXIT_SUCCESS; + 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); + /* Done. */ + return &mdev->device; } -- cgit v1.2.3