From: Pavel Charvat Date: Fri, 6 Dec 2013 22:11:57 +0000 (+0100) Subject: Libucw-images: Added `ucw-' prefix to utils. X-Git-Tag: v5.99~60 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=dc08bdc559870143ba385f172f9777bc37d1e4c1;p=libucw.git Libucw-images: Added `ucw-' prefix to utils. --- diff --git a/images/Makefile b/images/Makefile index 35cfbd10..f475127e 100644 --- a/images/Makefile +++ b/images/Makefile @@ -2,7 +2,7 @@ DIRS+=images -LIBIMAGES_PROGS=$(o)/images/image-tool $(o)/images/color-tool +LIBIMAGES_PROGS=$(o)/images/ucw-image-tool $(o)/images/ucw-color-tool LIBIMAGES_CONFIGS+=images LIBIMAGES_MODS=math config context image scale color io-main LIBIMAGES_INCLUDES=images.h error.h color.h math.h @@ -23,12 +23,12 @@ LIBIMAGES_DEPS+=$(o)/images/libucw-images-pic.a endif ifdef CONFIG_IMAGES_DUP -LIBIMAGES_PROGS+=$(o)/images/image-dup-test +LIBIMAGES_PROGS+=$(o)/images/ucw-image-dup-test LIBIMAGES_MODS+=dup-init dup-cmp LIBIMAGES_INCLUDES+=duplicates.h endif ifdef CONFIG_IMAGES_SIM -LIBIMAGES_PROGS+=$(o)/images/image-sim-test +LIBIMAGES_PROGS+=$(o)/images/ucw-image-sim-test LIBIMAGES_MODS+=sig-cmp endif ifneq ($(CONFIG_IMAGES_DUP)$(CONFIG_IMAGES_SIM),) @@ -73,10 +73,10 @@ $(o)/images/libucw-images.so: $(addsuffix .oo,$(addprefix $(o)/images/,$(LIBIMAG $(o)/images/libucw-images.so: SONAME_SUFFIX=.$(UCW_ABI_VERSION) $(o)/images/libucw-images.pc: $(LIBIMAGES_DEPS) -$(o)/images/image-tool: $(o)/images/image-tool.o $(LIBIMAGES) -$(o)/images/color-tool: $(o)/images/color-tool.o $(LIBIMAGES) -$(o)/images/image-dup-test: $(o)/images/image-dup-test.o $(LIBIMAGES) -$(o)/images/image-sim-test: $(o)/images/image-sim-test.o $(LIBIMAGES) +$(o)/images/ucw-image-tool: $(o)/images/ucw-image-tool.o $(LIBIMAGES) +$(o)/images/ucw-color-tool: $(o)/images/ucw-color-tool.o $(LIBIMAGES) +$(o)/images/ucw-image-dup-test: $(o)/images/ucw-image-dup-test.o $(LIBIMAGES) +$(o)/images/ucw-image-sim-test: $(o)/images/ucw-image-sim-test.o $(LIBIMAGES) TESTS+=$(o)/images/image-test.test $(o)/images/image-test: $(o)/images/image-test.o $(LIBIMAGES) diff --git a/images/color-tool.c b/images/color-tool.c deleted file mode 100644 index 73d97e4c..00000000 --- a/images/color-tool.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Color spaces tool - * - * (c) 2006 Pavel Charvat - * - * This software may be freely distributed and used according to the terms - * of the GNU General Public License. - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -static void NONRET -usage(void) -{ - fputs("\ -Usage: color-tool input-color-space output-color-space\n\ -", stderr); - exit(1); -} - -static char *shortopts = ""; -static struct option longopts[] = -{ - { NULL, 0, 0, 0 } -}; - -static const struct color_space_info * -parse_color_space(byte *s) -{ - if (!strcasecmp(s, "sRGB")) - return &color_srgb_info; - else if (!strcasecmp(s, "AdobeRGB") || !strcasecmp(s, "Adobe RGB")) - return &color_adobe_rgb_info; - else if (!strcasecmp(s, "CIERGB") || strcasecmp(s, "CIE RGB")) - return &color_cie_rgb_info; - else - die("Unknown color space"); -} - -static void -print_matrix(double m[9]) -{ - for (uns j = 0; j < 3; j++) - { - for (uns i = 0; i < 3; i++) - printf(" %12.8f", m[i + j * 3]); - printf("\n"); - } -} - -int -main(int argc, char **argv) -{ - log_init(argv[0]); - int opt; - while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) >= 0) - switch (opt) - { - default: - usage(); - } - - if (argc == optind + 1) - { - const struct color_space_info *a = parse_color_space(argv[optind]); - double a_to_xyz[9], xyz_to_a[9]; - color_compute_color_space_to_xyz_matrix(a_to_xyz, &a->chromacity); - color_invert_matrix(xyz_to_a, a_to_xyz); - printf("linear %s -> XYZ:\n", a->name); - print_matrix(a_to_xyz); - printf("XYZ -> linear %s:\n", a->name); - print_matrix(xyz_to_a); - printf("Simple gamma: %.8f\n", a->gamma.simple_gamma); - printf("Detailed gamma: g=%.8f o=%.8f t=%.8f s=%.8f\n", a->gamma.detailed_gamma, a->gamma.offset, a->gamma.transition, a->gamma.slope); - } - else if (argc == optind + 2) - { - const struct color_space_info *a = parse_color_space(argv[optind++]); - const struct color_space_info *b = parse_color_space(argv[optind]); - double a_to_b[9]; - color_compute_color_spaces_conversion_matrix(a_to_b, &a->chromacity, &b->chromacity); - printf("linear %s -> linear %s:\n", a->name, b->name); - print_matrix(a_to_b); - } - else - usage(); - - return 0; -} diff --git a/images/image-dup-test.c b/images/image-dup-test.c deleted file mode 100644 index feece3d1..00000000 --- a/images/image-dup-test.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Image duplicates testing - * - * (c) 2006 Pavel Charvat - * - * This software may be freely distributed and used according to the terms - * of the GNU General Public License. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static void NONRET -usage(void) -{ - fputs("\ -Usage: image-dup-test [options] image1 image2 \n\ -\n\ --q --quiet no progress messages\n\ --f --format-1 image1 format (jpeg, gif, png)\n\ --F --format-2 image2 format\n\ --g --background background color (hexadecimal RRGGBB)\n\ --t --transformations hexadecimal value of allowed transformtion (1=identity, FF=all)\n\ -", stderr); - exit(1); -} - -static char *shortopts = "qf:F:g:t:" CF_SHORT_OPTS; -static struct option longopts[] = -{ - CF_LONG_OPTS - { "quiet", 0, 0, 'q' }, - { "format-1", 0, 0, 'f' }, - { "format-2", 0, 0, 'F' }, - { "background", 0, 0, 'g' }, - { "transormations", 0, 0, 't' }, - { NULL, 0, 0, 0 } -}; - -static uns verbose = 1; -static byte *file_name_1; -static byte *file_name_2; -static enum image_format format_1; -static enum image_format format_2; -static struct color background_color; -static uns transformations = IMAGE_DUP_TRANS_ALL; - -#define MSG(x...) do{ if (verbose) msg(L_INFO, ##x); }while(0) - -int -main(int argc, char **argv) -{ - log_init(argv[0]); - int opt; - while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0) - switch (opt) - { - case 'q': - verbose = 0; - break; - case 'f': - if (!(format_1 = image_extension_to_format(optarg))) - usage(); - break; - case 'F': - if (!(format_2 = image_extension_to_format(optarg))) - usage(); - break; - case 'g': - { - if (strlen(optarg) != 6) - usage(); - errno = 0; - char *end; - long int v = strtol(optarg, &end, 16); - if (errno || *end || v < 0) - usage(); - color_make_rgb(&background_color, (v >> 16) & 255, (v >> 8) & 255, v & 255); - } - break; - case 't': - { - errno = 0; - char *end; - long int v = strtol(optarg, &end, 16); - if (errno || *end || v < 0 || v > 0xff) - usage(); - transformations = v; - } - break; - default: - usage(); - } - - if (argc != optind + 2) - usage(); - file_name_1 = argv[optind++]; - file_name_2 = argv[optind]; - -#define TRY(x) do{ if (!(x)) exit(1); }while(0) - MSG("Initializing image library"); - struct image_context ctx; - struct image_dup_context idc; - struct image_io io; - image_context_init(&ctx); - image_dup_context_init(&ctx, &idc); - - struct image *img1, *img2; - - TRY(image_io_init(&ctx, &io)); - MSG("Reading %s", file_name_1); - io.fastbuf = bopen(file_name_1, O_RDONLY, 1 << 18); - io.format = format_1 ? : image_file_name_to_format(file_name_1); - TRY(image_io_read_header(&io)); - io.flags = COLOR_SPACE_RGB | IMAGE_IO_USE_BACKGROUND; - if (background_color.color_space) - io.background_color = background_color; - else if (!io.background_color.color_space) - io.background_color = color_black; - TRY(image_io_read_data(&io, 1)); - bclose(io.fastbuf); - img1 = io.image; - MSG("Image size=%ux%u", img1->cols, img1->rows); - - image_io_reset(&io); - MSG("Reading %s", file_name_2); - io.fastbuf = bopen(file_name_2, O_RDONLY, 1 << 18); - io.format = format_2 ? : image_file_name_to_format(file_name_2); - TRY(image_io_read_header(&io)); - io.flags = COLOR_SPACE_RGB | IMAGE_IO_USE_BACKGROUND; - if (background_color.color_space) - io.background_color = background_color; - else if (!io.background_color.color_space) - io.background_color = color_black; - TRY(image_io_read_data(&io, 1)); - bclose(io.fastbuf); - img2 = io.image; - image_io_cleanup(&io); - MSG("Image size=%ux%u", img2->cols, img2->rows); - - struct image_dup *dup1, *dup2; - struct mempool *pool = mp_new(1 << 18); - MSG("Creating internal structures"); - dup1 = mp_start(pool, image_dup_estimate_size(img1->cols, img1->rows, 1, idc.qtree_limit)); - uns size = image_dup_new(&idc, img1, dup1, 1); - TRY(size); - mp_end(pool, (void *)dup1 + size); - dup2 = mp_start(pool, image_dup_estimate_size(img2->cols, img2->rows, 1, idc.qtree_limit)); - size = image_dup_new(&idc, img2, dup2, 1); - TRY(size); - mp_end(pool, (void *)dup2 + size); - - idc.flags = transformations | IMAGE_DUP_SCALE | IMAGE_DUP_WANT_ALL; - MSG("Similarity bitmap %02x", image_dup_compare(&idc, dup1, dup2)); - - mp_delete(pool); - - image_destroy(img1); - image_destroy(img2); - image_dup_context_cleanup(&idc); - image_context_cleanup(&ctx); - MSG("Done."); - return 0; -} diff --git a/images/image-sim-test.c b/images/image-sim-test.c deleted file mode 100644 index 52cf6771..00000000 --- a/images/image-sim-test.c +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Image similarity testing - * - * (c) 2006 Pavel Charvat - * - * This software may be freely distributed and used according to the terms - * of the GNU General Public License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -static void NONRET -usage(void) -{ - fputs("\ -Usage: image-sim-test [options] image1 [image2] \n\ -\n\ --q --quiet no progress messages\n\ --f --format-1 image1 format (jpeg, gif, png)\n\ --F --format-2 image2 format\n\ --g --background background color (hexadecimal RRGGBB)\n\ --r --segmentation-1 writes image1 segmentation to given file\n\ --R --segmentation-2 writes image2 segmentation to given file\n\ --6 --base64 display base64 encoded signature(s)\n\ --2 --base224 display base224 encoded signature(s)\n\ -", stderr); - exit(1); -} - -static char *shortopts = "qf:F:g:t:r:R:62" CF_SHORT_OPTS; -static struct option longopts[] = -{ - CF_LONG_OPTS - { "quiet", 0, 0, 'q' }, - { "format-1", 0, 0, 'f' }, - { "format-2", 0, 0, 'F' }, - { "background", 0, 0, 'g' }, - { "segmentation-1", 0, 0, 'r' }, - { "segmentation-2", 0, 0, 'R' }, - { "base64", 0, 0, '6' }, - { "base224", 0, 0, '2' }, - { NULL, 0, 0, 0 } -}; - -static uns verbose = 1; -static byte *file_name_1; -static byte *file_name_2; -static enum image_format format_1; -static enum image_format format_2; -static struct color background_color; -static byte *segmentation_name_1; -static byte *segmentation_name_2; -static uns display_base64; -static uns display_base224; - -#define MSG(x...) do{ if (verbose) msg(L_INFO, ##x); }while(0) -#define TRY(x) do{ if (!(x)) exit(1); }while(0) - -static void -msg_str(byte *s, void *param UNUSED) -{ - MSG("%s", s); -} - -static void -dump_signature(struct image_signature *sig) -{ - byte buf[MAX(IMAGE_VECTOR_DUMP_MAX, IMAGE_REGION_DUMP_MAX)]; - image_vector_dump(buf, &sig->vec); - MSG("vector: %s", buf); - for (uns i = 0; i < sig->len; i++) - { - image_region_dump(buf, sig->reg + i); - MSG("region %u: %s", i, buf); - } - uns sig_size = image_signature_size(sig->len); - if (display_base64) - { - byte buf[BASE64_ENC_LENGTH(sig_size) + 1]; - uns enc_size = base64_encode(buf, (byte *)sig, sig_size); - buf[enc_size] = 0; - MSG("base64 encoded: %s", buf); - } - if (display_base224) - { - byte buf[BASE224_ENC_LENGTH(sig_size) + 1]; - uns enc_size = base224_encode(buf, (byte *)sig, sig_size); - buf[enc_size] = 0; - MSG("base224 encoded: %s", buf); - } -} - -static struct image_context ctx; -static struct image_io io; - -static void -write_segmentation(struct image_sig_data *data, byte *fn) -{ - MSG("Writing segmentation to %s", fn); - - struct fastbuf *fb = bopen(fn, O_WRONLY | O_CREAT | O_TRUNC, 4096); - struct image *img; - TRY(img = image_new(&ctx, data->image->cols, data->image->rows, COLOR_SPACE_RGB, NULL)); - image_clear(&ctx, img); - - for (uns i = 0; i < data->regions_count; i++) - { - byte c[3]; - double luv[3], xyz[3], srgb[3]; - luv[0] = data->regions[i].a[0] * (4 / 2.55); - luv[1] = ((int)data->regions[i].a[1] - 128) * (4 / 2.55); - luv[2] = ((int)data->regions[i].a[2] - 128) * (4 / 2.55); - luv_to_xyz_exact(xyz, luv); - xyz_to_srgb_exact(srgb, xyz); - c[0] = CLAMP(srgb[0] * 255, 0, 255); - c[1] = CLAMP(srgb[1] * 255, 0, 255); - c[2] = CLAMP(srgb[2] * 255, 0, 255); - for (struct image_sig_block *block = data->regions[i].blocks; block; block = block->next) - { - uns x1 = block->x * 4; - uns y1 = block->y * 4; - uns x2 = MIN(x1 + 4, img->cols); - uns y2 = MIN(y1 + 4, img->rows); - byte *p = img->pixels + x1 * 3 + y1 * img->row_size; - for (uns y = y1; y < y2; y++, p += img->row_size) - { - byte *p2 = p; - for (uns x = x1; x < x2; x++, p2 += 3) - { - p2[0] = c[0]; - p2[1] = c[1]; - p2[2] = c[2]; - } - } - } - } - - io.fastbuf = fb; - io.image = img; - io.format = image_file_name_to_format(fn); - TRY(image_io_write(&io)); - image_io_reset(&io); - - image_destroy(img); - bclose(fb); -} - -int -main(int argc, char **argv) -{ - log_init(argv[0]); - int opt; - while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0) - switch (opt) - { - case 'q': - verbose = 0; - break; - case 'f': - if (!(format_1 = image_extension_to_format(optarg))) - usage(); - break; - case 'F': - if (!(format_2 = image_extension_to_format(optarg))) - usage(); - break; - case 'g': - { - if (strlen(optarg) != 6) - usage(); - errno = 0; - char *end; - long int v = strtol(optarg, &end, 16); - if (errno || *end || v < 0) - usage(); - color_make_rgb(&background_color, (v >> 16) & 255, (v >> 8) & 255, v & 255); - } - break; - case 'r': - segmentation_name_1 = optarg; - break; - case 'R': - segmentation_name_2 = optarg; - break; - case '6': - display_base64++; - break; - case '2': - display_base224++; - break; - default: - usage(); - } - - if (argc != optind + 2 && argc != optind + 1) - usage(); - file_name_1 = argv[optind++]; - if (argc > optind) - file_name_2 = argv[optind++]; - - MSG("Initializing image library"); - srandom(time(NULL) ^ getpid()); - srgb_to_luv_init(); - image_context_init(&ctx); - - struct image *img1, *img2; - - TRY(image_io_init(&ctx, &io)); - - if (file_name_1) - { - MSG("Reading %s", file_name_1); - io.fastbuf = bopen(file_name_1, O_RDONLY, 1 << 18); - io.format = format_1 ? : image_file_name_to_format(file_name_1); - TRY(image_io_read_header(&io)); - io.flags = COLOR_SPACE_RGB | IMAGE_IO_USE_BACKGROUND; - if (background_color.color_space) - io.background_color = background_color; - else if (!io.background_color.color_space) - io.background_color = color_black; - TRY(image_io_read_data(&io, 1)); - bclose(io.fastbuf); - img1 = io.image; - MSG("Image size=%ux%u", img1->cols, img1->rows); - image_io_reset(&io); - } - else - img1 = NULL; - - if (file_name_2) - { - MSG("Reading %s", file_name_2); - io.fastbuf = bopen(file_name_2, O_RDONLY, 1 << 18); - io.format = format_2 ? : image_file_name_to_format(file_name_2); - TRY(image_io_read_header(&io)); - io.flags = COLOR_SPACE_RGB | IMAGE_IO_USE_BACKGROUND; - if (background_color.color_space) - io.background_color = background_color; - else if (!io.background_color.color_space) - io.background_color = color_black; - TRY(image_io_read_data(&io, 1)); - bclose(io.fastbuf); - img2 = io.image; - MSG("Image size=%ux%u", img2->cols, img2->rows); - image_io_reset(&io); - } - else - img2 = NULL; - - struct image_signature sig1, sig2; - MSG("Computing signatures"); - if (img1) - { - struct image_sig_data data; - TRY(image_sig_init(&ctx, &data, img1)); - image_sig_preprocess(&data); - if (data.valid) - { - image_sig_segmentation(&data); - image_sig_detect_textured(&data); - } - if (segmentation_name_1) - write_segmentation(&data, segmentation_name_1); - image_sig_finish(&data, &sig1); - image_sig_cleanup(&data); - dump_signature(&sig1); - } - if (img2) - { - struct image_sig_data data; - TRY(image_sig_init(&ctx, &data, img2)); - image_sig_preprocess(&data); - if (data.valid) - { - image_sig_segmentation(&data); - image_sig_detect_textured(&data); - } - if (segmentation_name_2) - write_segmentation(&data, segmentation_name_2); - image_sig_finish(&data, &sig2); - image_sig_cleanup(&data); - dump_signature(&sig2); - } - - if (img1 && img2) - { - uns dist; - if (verbose) - { - struct fastbuf *fb = bfdopen(0, 4096); - dist = image_signatures_dist_explain(&sig1, &sig2, msg_str, NULL); - bclose(fb); - } - else - dist = image_signatures_dist(&sig1, &sig2); - MSG("dist=%u", dist); - } - - if (img1) - image_destroy(img1); - if (img2) - image_destroy(img2); - - image_io_cleanup(&io); - image_context_cleanup(&ctx); - MSG("Done."); - return 0; -} diff --git a/images/image-tool.c b/images/image-tool.c deleted file mode 100644 index 3f24d029..00000000 --- a/images/image-tool.c +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Image Library -- Simple image manipulation utility - * - * (c) 2006 Pavel Charvat - * - * This software may be freely distributed and used according to the terms - * of the GNU General Public License. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -static void NONRET -usage(void) -{ - fputs("\ -Usage: image-tool [options] infile [outfile]\n\ -\n\ --q --quiet no progress messages\n\ --f --input-format input image format (jpeg, gif, png)\n\ --F --output-format output image format\n\ --s --size force output dimensions (100x200)\n\ --b --fit-to-box scale to fit the box (100x200)\n\ --c --colorspace force output colorspace (Grayscale, Grayscale+Alpha, RGB, RGB+Alpha, ...)\n\ --Q --jpeg-quality JPEG quality (1..100)\n\ --g --background background color (hexadecimal RRGGBB)\n\ --G --default-background background applied only if the image contains no background info (RRGGBB, default=FFFFFF)\n\ --a --remove-alpha remove alpha channel\n\ --e --exif reads Exif data\n" -, stderr); - exit(1); -} - -static char *shortopts = "qf:F:s:b:c:Q:g:G:ae"; -static struct option longopts[] = -{ - { "quiet", 0, 0, 'q' }, - { "input-format", 0, 0, 'f' }, - { "output-format", 0, 0, 'F' }, - { "size", 0, 0, 's' }, - { "fit-to-box", 0, 0, 'b' }, - { "colorspace", 0, 0, 'c' }, - { "jpeg-quality", 0, 0, 'Q' }, - { "background", 0, 0, 'g' }, - { "default-background", 0, 0, 'G' }, - { "remove-alpha", 0, 0, 'a' }, - { "exif", 0, 0, 'e' }, - { NULL, 0, 0, 0 } -}; - -static uns verbose = 1; -static byte *input_file_name; -static enum image_format input_format; -static byte *output_file_name; -static enum image_format output_format; -static uns cols; -static uns rows; -static uns fit_to_box; -static uns channels_format; -static uns jpeg_quality; -static struct color background_color; -static struct color default_background_color; -static uns remove_alpha; -static uns exif; - -static void -parse_color(struct color *color, byte *s) -{ - if (strlen(s) != 6) - usage(); - errno = 0; - char *end; - long int v = strtol(s, &end, 16); - if (errno || *end || v < 0) - usage(); - color_make_rgb(color, (v >> 16) & 255, (v >> 8) & 255, v & 255); -} - -#define MSG(x...) do{ if (verbose) msg(L_INFO, ##x); }while(0) - -int -main(int argc, char **argv) -{ - log_init(argv[0]); - int opt; - default_background_color = color_white; - while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) >= 0) - switch (opt) - { - case 'q': - verbose = 0; - break; - case 'f': - if (!(input_format = image_extension_to_format(optarg))) - usage(); - break; - case 'F': - if (!(output_format = image_extension_to_format(optarg))) - usage(); - break; - case 's': - { - byte *r = strchr(optarg, 'x'); - if (!r) - usage(); - *r++ = 0; - if (!(cols = atoi(optarg)) || !(rows = atoi(r))) - usage(); - fit_to_box = 0; - break; - } - case 'b': - { - byte *r = strchr(optarg, 'x'); - if (!r) - usage(); - *r++ = 0; - if (!(cols = atoi(optarg)) || !(rows = atoi(r))) - usage(); - fit_to_box = 1; - break; - } - case 'c': - if (!(channels_format = image_name_to_channels_format(optarg))) - usage(); - break; - case 'Q': - if (!(jpeg_quality = atoi(optarg))) - usage(); - break; - case 'g': - parse_color(&background_color, optarg); - break; - case 'G': - parse_color(&default_background_color, optarg); - break; - case 'a': - remove_alpha++; - break; - case 'e': - exif++; - break; - default: - usage(); - } - - if (argc != optind + 1 && argc != optind + 2) - usage(); - input_file_name = argv[optind++]; - if (argc > optind) - output_file_name = argv[optind]; - -#define TRY(x) do{ if (!(x)) exit(1); }while(0) - MSG("Initializing image library"); - struct image_context ctx; - struct image_io io; - image_context_init(&ctx); - ctx.tracing_level = ~0U; - if (!image_io_init(&ctx, &io)) - die("Cannot initialize image I/O"); - - MSG("Reading %s", input_file_name); - byte cs_buf[IMAGE_CHANNELS_FORMAT_MAX_SIZE]; - io.fastbuf = bopen(input_file_name, O_RDONLY, 1 << 18); - io.format = input_format ? : image_file_name_to_format(input_file_name); - if (exif) - io.flags |= IMAGE_IO_WANT_EXIF; - TRY(image_io_read_header(&io)); - if (!output_file_name) - { - bclose(io.fastbuf); - printf("Format: %s\n", image_format_to_extension(io.format) ? : (byte *)"?"); - printf("Dimensions: %dx%d\n", io.cols, io.rows); - printf("Colorspace: %s\n", (io.flags & IMAGE_IO_HAS_PALETTE) ? (byte *)"Palette" : image_channels_format_to_name(io.flags, cs_buf)); - printf("NumColors: %u\n", io.number_of_colors); - if (io.background_color.color_space) - { - byte rgb[3]; - TRY(color_put(&ctx, &io.background_color, rgb, COLOR_SPACE_RGB)); - printf("Background: %02x%02x%02x\n", rgb[0], rgb[1], rgb[2]); - } - if (io.exif_size) - printf("ExifSize: %u\n", io.exif_size); - } - else - { - MSG("%s %dx%d %s", image_format_to_extension(io.format) ? : (byte *)"?", io.cols, io.rows, - (io.flags & IMAGE_IO_HAS_PALETTE) ? (byte *)"Palette" : image_channels_format_to_name(io.flags, cs_buf)); - if (cols) - if (fit_to_box) - { - image_dimensions_fit_to_box(&io.cols, &io.rows, MIN(cols, 0xffff), MIN(rows, 0xffff), 0); - } - else - { - io.cols = cols; - io.rows = rows; - } - if (background_color.color_space) - io.background_color = background_color; - else if (!io.background_color.color_space) - io.background_color = default_background_color; - if (remove_alpha) - io.flags &= ~IMAGE_ALPHA; - if (channels_format) - io.flags = io.flags & ~IMAGE_PIXEL_FORMAT | channels_format; - if (!(io.flags & IMAGE_ALPHA)) - io.flags |= IMAGE_IO_USE_BACKGROUND; - if (jpeg_quality) - io.jpeg_quality = jpeg_quality; - uns output_fmt = output_format ? : image_file_name_to_format(output_file_name); - uns output_cs = io.flags & IMAGE_COLOR_SPACE; - if (output_fmt != IMAGE_FORMAT_JPEG && - output_cs != COLOR_SPACE_GRAYSCALE && - output_cs != COLOR_SPACE_RGB) - { - MSG("Forcing RGB color space"); - io.flags = (io.flags & ~IMAGE_COLOR_SPACE) | COLOR_SPACE_RGB; - } - TRY(image_io_read_data(&io, 0)); - bclose(io.fastbuf); - MSG("Writing %s", output_file_name); - io.fastbuf = bopen(output_file_name, O_WRONLY | O_CREAT | O_TRUNC, 1 << 18); - io.format = output_format ? : image_file_name_to_format(output_file_name); - MSG("%s %dx%d %s", image_format_to_extension(io.format) ? : (byte *)"?", io.cols, io.rows, - image_channels_format_to_name(io.flags, cs_buf)); - TRY(image_io_write(&io)); - bclose(io.fastbuf); - } - - image_io_cleanup(&io); - image_context_cleanup(&ctx); - MSG("Done."); - return 0; -} diff --git a/images/ucw-color-tool.c b/images/ucw-color-tool.c new file mode 100644 index 00000000..091f982b --- /dev/null +++ b/images/ucw-color-tool.c @@ -0,0 +1,97 @@ +/* + * Color spaces tool + * + * (c) 2006 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU General Public License. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +static void NONRET +usage(void) +{ + fputs("\ +Usage: ucw-color-tool input-color-space output-color-space\n\ +", stderr); + exit(1); +} + +static char *shortopts = ""; +static struct option longopts[] = +{ + { NULL, 0, 0, 0 } +}; + +static const struct color_space_info * +parse_color_space(byte *s) +{ + if (!strcasecmp(s, "sRGB")) + return &color_srgb_info; + else if (!strcasecmp(s, "AdobeRGB") || !strcasecmp(s, "Adobe RGB")) + return &color_adobe_rgb_info; + else if (!strcasecmp(s, "CIERGB") || strcasecmp(s, "CIE RGB")) + return &color_cie_rgb_info; + else + die("Unknown color space"); +} + +static void +print_matrix(double m[9]) +{ + for (uns j = 0; j < 3; j++) + { + for (uns i = 0; i < 3; i++) + printf(" %12.8f", m[i + j * 3]); + printf("\n"); + } +} + +int +main(int argc, char **argv) +{ + log_init(argv[0]); + int opt; + while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) >= 0) + switch (opt) + { + default: + usage(); + } + + if (argc == optind + 1) + { + const struct color_space_info *a = parse_color_space(argv[optind]); + double a_to_xyz[9], xyz_to_a[9]; + color_compute_color_space_to_xyz_matrix(a_to_xyz, &a->chromacity); + color_invert_matrix(xyz_to_a, a_to_xyz); + printf("linear %s -> XYZ:\n", a->name); + print_matrix(a_to_xyz); + printf("XYZ -> linear %s:\n", a->name); + print_matrix(xyz_to_a); + printf("Simple gamma: %.8f\n", a->gamma.simple_gamma); + printf("Detailed gamma: g=%.8f o=%.8f t=%.8f s=%.8f\n", a->gamma.detailed_gamma, a->gamma.offset, a->gamma.transition, a->gamma.slope); + } + else if (argc == optind + 2) + { + const struct color_space_info *a = parse_color_space(argv[optind++]); + const struct color_space_info *b = parse_color_space(argv[optind]); + double a_to_b[9]; + color_compute_color_spaces_conversion_matrix(a_to_b, &a->chromacity, &b->chromacity); + printf("linear %s -> linear %s:\n", a->name, b->name); + print_matrix(a_to_b); + } + else + usage(); + + return 0; +} diff --git a/images/ucw-image-dup-test.c b/images/ucw-image-dup-test.c new file mode 100644 index 00000000..d5a9794f --- /dev/null +++ b/images/ucw-image-dup-test.c @@ -0,0 +1,174 @@ +/* + * Image duplicates testing + * + * (c) 2006 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU General Public License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static void NONRET +usage(void) +{ + fputs("\ +Usage: ucw-image-dup-test [options] image1 image2 \n\ +\n\ +-q --quiet no progress messages\n\ +-f --format-1 image1 format (jpeg, gif, png)\n\ +-F --format-2 image2 format\n\ +-g --background background color (hexadecimal RRGGBB)\n\ +-t --transformations hexadecimal value of allowed transformtion (1=identity, FF=all)\n\ +", stderr); + exit(1); +} + +static char *shortopts = "qf:F:g:t:" CF_SHORT_OPTS; +static struct option longopts[] = +{ + CF_LONG_OPTS + { "quiet", 0, 0, 'q' }, + { "format-1", 0, 0, 'f' }, + { "format-2", 0, 0, 'F' }, + { "background", 0, 0, 'g' }, + { "transormations", 0, 0, 't' }, + { NULL, 0, 0, 0 } +}; + +static uns verbose = 1; +static byte *file_name_1; +static byte *file_name_2; +static enum image_format format_1; +static enum image_format format_2; +static struct color background_color; +static uns transformations = IMAGE_DUP_TRANS_ALL; + +#define MSG(x...) do{ if (verbose) msg(L_INFO, ##x); }while(0) + +int +main(int argc, char **argv) +{ + log_init(argv[0]); + int opt; + while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0) + switch (opt) + { + case 'q': + verbose = 0; + break; + case 'f': + if (!(format_1 = image_extension_to_format(optarg))) + usage(); + break; + case 'F': + if (!(format_2 = image_extension_to_format(optarg))) + usage(); + break; + case 'g': + { + if (strlen(optarg) != 6) + usage(); + errno = 0; + char *end; + long int v = strtol(optarg, &end, 16); + if (errno || *end || v < 0) + usage(); + color_make_rgb(&background_color, (v >> 16) & 255, (v >> 8) & 255, v & 255); + } + break; + case 't': + { + errno = 0; + char *end; + long int v = strtol(optarg, &end, 16); + if (errno || *end || v < 0 || v > 0xff) + usage(); + transformations = v; + } + break; + default: + usage(); + } + + if (argc != optind + 2) + usage(); + file_name_1 = argv[optind++]; + file_name_2 = argv[optind]; + +#define TRY(x) do{ if (!(x)) exit(1); }while(0) + MSG("Initializing image library"); + struct image_context ctx; + struct image_dup_context idc; + struct image_io io; + image_context_init(&ctx); + image_dup_context_init(&ctx, &idc); + + struct image *img1, *img2; + + TRY(image_io_init(&ctx, &io)); + MSG("Reading %s", file_name_1); + io.fastbuf = bopen(file_name_1, O_RDONLY, 1 << 18); + io.format = format_1 ? : image_file_name_to_format(file_name_1); + TRY(image_io_read_header(&io)); + io.flags = COLOR_SPACE_RGB | IMAGE_IO_USE_BACKGROUND; + if (background_color.color_space) + io.background_color = background_color; + else if (!io.background_color.color_space) + io.background_color = color_black; + TRY(image_io_read_data(&io, 1)); + bclose(io.fastbuf); + img1 = io.image; + MSG("Image size=%ux%u", img1->cols, img1->rows); + + image_io_reset(&io); + MSG("Reading %s", file_name_2); + io.fastbuf = bopen(file_name_2, O_RDONLY, 1 << 18); + io.format = format_2 ? : image_file_name_to_format(file_name_2); + TRY(image_io_read_header(&io)); + io.flags = COLOR_SPACE_RGB | IMAGE_IO_USE_BACKGROUND; + if (background_color.color_space) + io.background_color = background_color; + else if (!io.background_color.color_space) + io.background_color = color_black; + TRY(image_io_read_data(&io, 1)); + bclose(io.fastbuf); + img2 = io.image; + image_io_cleanup(&io); + MSG("Image size=%ux%u", img2->cols, img2->rows); + + struct image_dup *dup1, *dup2; + struct mempool *pool = mp_new(1 << 18); + MSG("Creating internal structures"); + dup1 = mp_start(pool, image_dup_estimate_size(img1->cols, img1->rows, 1, idc.qtree_limit)); + uns size = image_dup_new(&idc, img1, dup1, 1); + TRY(size); + mp_end(pool, (void *)dup1 + size); + dup2 = mp_start(pool, image_dup_estimate_size(img2->cols, img2->rows, 1, idc.qtree_limit)); + size = image_dup_new(&idc, img2, dup2, 1); + TRY(size); + mp_end(pool, (void *)dup2 + size); + + idc.flags = transformations | IMAGE_DUP_SCALE | IMAGE_DUP_WANT_ALL; + MSG("Similarity bitmap %02x", image_dup_compare(&idc, dup1, dup2)); + + mp_delete(pool); + + image_destroy(img1); + image_destroy(img2); + image_dup_context_cleanup(&idc); + image_context_cleanup(&ctx); + MSG("Done."); + return 0; +} diff --git a/images/ucw-image-sim-test.c b/images/ucw-image-sim-test.c new file mode 100644 index 00000000..873621fc --- /dev/null +++ b/images/ucw-image-sim-test.c @@ -0,0 +1,323 @@ +/* + * Image similarity testing + * + * (c) 2006 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU General Public License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static void NONRET +usage(void) +{ + fputs("\ +Usage: ucw-image-sim-test [options] image1 [image2] \n\ +\n\ +-q --quiet no progress messages\n\ +-f --format-1 image1 format (jpeg, gif, png)\n\ +-F --format-2 image2 format\n\ +-g --background background color (hexadecimal RRGGBB)\n\ +-r --segmentation-1 writes image1 segmentation to given file\n\ +-R --segmentation-2 writes image2 segmentation to given file\n\ +-6 --base64 display base64 encoded signature(s)\n\ +-2 --base224 display base224 encoded signature(s)\n\ +", stderr); + exit(1); +} + +static char *shortopts = "qf:F:g:t:r:R:62" CF_SHORT_OPTS; +static struct option longopts[] = +{ + CF_LONG_OPTS + { "quiet", 0, 0, 'q' }, + { "format-1", 0, 0, 'f' }, + { "format-2", 0, 0, 'F' }, + { "background", 0, 0, 'g' }, + { "segmentation-1", 0, 0, 'r' }, + { "segmentation-2", 0, 0, 'R' }, + { "base64", 0, 0, '6' }, + { "base224", 0, 0, '2' }, + { NULL, 0, 0, 0 } +}; + +static uns verbose = 1; +static byte *file_name_1; +static byte *file_name_2; +static enum image_format format_1; +static enum image_format format_2; +static struct color background_color; +static byte *segmentation_name_1; +static byte *segmentation_name_2; +static uns display_base64; +static uns display_base224; + +#define MSG(x...) do{ if (verbose) msg(L_INFO, ##x); }while(0) +#define TRY(x) do{ if (!(x)) exit(1); }while(0) + +static void +msg_str(byte *s, void *param UNUSED) +{ + MSG("%s", s); +} + +static void +dump_signature(struct image_signature *sig) +{ + byte buf[MAX(IMAGE_VECTOR_DUMP_MAX, IMAGE_REGION_DUMP_MAX)]; + image_vector_dump(buf, &sig->vec); + MSG("vector: %s", buf); + for (uns i = 0; i < sig->len; i++) + { + image_region_dump(buf, sig->reg + i); + MSG("region %u: %s", i, buf); + } + uns sig_size = image_signature_size(sig->len); + if (display_base64) + { + byte buf[BASE64_ENC_LENGTH(sig_size) + 1]; + uns enc_size = base64_encode(buf, (byte *)sig, sig_size); + buf[enc_size] = 0; + MSG("base64 encoded: %s", buf); + } + if (display_base224) + { + byte buf[BASE224_ENC_LENGTH(sig_size) + 1]; + uns enc_size = base224_encode(buf, (byte *)sig, sig_size); + buf[enc_size] = 0; + MSG("base224 encoded: %s", buf); + } +} + +static struct image_context ctx; +static struct image_io io; + +static void +write_segmentation(struct image_sig_data *data, byte *fn) +{ + MSG("Writing segmentation to %s", fn); + + struct fastbuf *fb = bopen(fn, O_WRONLY | O_CREAT | O_TRUNC, 4096); + struct image *img; + TRY(img = image_new(&ctx, data->image->cols, data->image->rows, COLOR_SPACE_RGB, NULL)); + image_clear(&ctx, img); + + for (uns i = 0; i < data->regions_count; i++) + { + byte c[3]; + double luv[3], xyz[3], srgb[3]; + luv[0] = data->regions[i].a[0] * (4 / 2.55); + luv[1] = ((int)data->regions[i].a[1] - 128) * (4 / 2.55); + luv[2] = ((int)data->regions[i].a[2] - 128) * (4 / 2.55); + luv_to_xyz_exact(xyz, luv); + xyz_to_srgb_exact(srgb, xyz); + c[0] = CLAMP(srgb[0] * 255, 0, 255); + c[1] = CLAMP(srgb[1] * 255, 0, 255); + c[2] = CLAMP(srgb[2] * 255, 0, 255); + for (struct image_sig_block *block = data->regions[i].blocks; block; block = block->next) + { + uns x1 = block->x * 4; + uns y1 = block->y * 4; + uns x2 = MIN(x1 + 4, img->cols); + uns y2 = MIN(y1 + 4, img->rows); + byte *p = img->pixels + x1 * 3 + y1 * img->row_size; + for (uns y = y1; y < y2; y++, p += img->row_size) + { + byte *p2 = p; + for (uns x = x1; x < x2; x++, p2 += 3) + { + p2[0] = c[0]; + p2[1] = c[1]; + p2[2] = c[2]; + } + } + } + } + + io.fastbuf = fb; + io.image = img; + io.format = image_file_name_to_format(fn); + TRY(image_io_write(&io)); + image_io_reset(&io); + + image_destroy(img); + bclose(fb); +} + +int +main(int argc, char **argv) +{ + log_init(argv[0]); + int opt; + while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0) + switch (opt) + { + case 'q': + verbose = 0; + break; + case 'f': + if (!(format_1 = image_extension_to_format(optarg))) + usage(); + break; + case 'F': + if (!(format_2 = image_extension_to_format(optarg))) + usage(); + break; + case 'g': + { + if (strlen(optarg) != 6) + usage(); + errno = 0; + char *end; + long int v = strtol(optarg, &end, 16); + if (errno || *end || v < 0) + usage(); + color_make_rgb(&background_color, (v >> 16) & 255, (v >> 8) & 255, v & 255); + } + break; + case 'r': + segmentation_name_1 = optarg; + break; + case 'R': + segmentation_name_2 = optarg; + break; + case '6': + display_base64++; + break; + case '2': + display_base224++; + break; + default: + usage(); + } + + if (argc != optind + 2 && argc != optind + 1) + usage(); + file_name_1 = argv[optind++]; + if (argc > optind) + file_name_2 = argv[optind++]; + + MSG("Initializing image library"); + srandom(time(NULL) ^ getpid()); + srgb_to_luv_init(); + image_context_init(&ctx); + + struct image *img1, *img2; + + TRY(image_io_init(&ctx, &io)); + + if (file_name_1) + { + MSG("Reading %s", file_name_1); + io.fastbuf = bopen(file_name_1, O_RDONLY, 1 << 18); + io.format = format_1 ? : image_file_name_to_format(file_name_1); + TRY(image_io_read_header(&io)); + io.flags = COLOR_SPACE_RGB | IMAGE_IO_USE_BACKGROUND; + if (background_color.color_space) + io.background_color = background_color; + else if (!io.background_color.color_space) + io.background_color = color_black; + TRY(image_io_read_data(&io, 1)); + bclose(io.fastbuf); + img1 = io.image; + MSG("Image size=%ux%u", img1->cols, img1->rows); + image_io_reset(&io); + } + else + img1 = NULL; + + if (file_name_2) + { + MSG("Reading %s", file_name_2); + io.fastbuf = bopen(file_name_2, O_RDONLY, 1 << 18); + io.format = format_2 ? : image_file_name_to_format(file_name_2); + TRY(image_io_read_header(&io)); + io.flags = COLOR_SPACE_RGB | IMAGE_IO_USE_BACKGROUND; + if (background_color.color_space) + io.background_color = background_color; + else if (!io.background_color.color_space) + io.background_color = color_black; + TRY(image_io_read_data(&io, 1)); + bclose(io.fastbuf); + img2 = io.image; + MSG("Image size=%ux%u", img2->cols, img2->rows); + image_io_reset(&io); + } + else + img2 = NULL; + + struct image_signature sig1, sig2; + MSG("Computing signatures"); + if (img1) + { + struct image_sig_data data; + TRY(image_sig_init(&ctx, &data, img1)); + image_sig_preprocess(&data); + if (data.valid) + { + image_sig_segmentation(&data); + image_sig_detect_textured(&data); + } + if (segmentation_name_1) + write_segmentation(&data, segmentation_name_1); + image_sig_finish(&data, &sig1); + image_sig_cleanup(&data); + dump_signature(&sig1); + } + if (img2) + { + struct image_sig_data data; + TRY(image_sig_init(&ctx, &data, img2)); + image_sig_preprocess(&data); + if (data.valid) + { + image_sig_segmentation(&data); + image_sig_detect_textured(&data); + } + if (segmentation_name_2) + write_segmentation(&data, segmentation_name_2); + image_sig_finish(&data, &sig2); + image_sig_cleanup(&data); + dump_signature(&sig2); + } + + if (img1 && img2) + { + uns dist; + if (verbose) + { + struct fastbuf *fb = bfdopen(0, 4096); + dist = image_signatures_dist_explain(&sig1, &sig2, msg_str, NULL); + bclose(fb); + } + else + dist = image_signatures_dist(&sig1, &sig2); + MSG("dist=%u", dist); + } + + if (img1) + image_destroy(img1); + if (img2) + image_destroy(img2); + + image_io_cleanup(&io); + image_context_cleanup(&ctx); + MSG("Done."); + return 0; +} diff --git a/images/ucw-image-tool.c b/images/ucw-image-tool.c new file mode 100644 index 00000000..4b7f3df8 --- /dev/null +++ b/images/ucw-image-tool.c @@ -0,0 +1,243 @@ +/* + * Image Library -- Simple image manipulation utility + * + * (c) 2006 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU General Public License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static void NONRET +usage(void) +{ + fputs("\ +Usage: ucw-image-tool [options] infile [outfile]\n\ +\n\ +-q --quiet no progress messages\n\ +-f --input-format input image format (jpeg, gif, png)\n\ +-F --output-format output image format\n\ +-s --size force output dimensions (100x200)\n\ +-b --fit-to-box scale to fit the box (100x200)\n\ +-c --colorspace force output colorspace (Grayscale, Grayscale+Alpha, RGB, RGB+Alpha, ...)\n\ +-Q --jpeg-quality JPEG quality (1..100)\n\ +-g --background background color (hexadecimal RRGGBB)\n\ +-G --default-background background applied only if the image contains no background info (RRGGBB, default=FFFFFF)\n\ +-a --remove-alpha remove alpha channel\n\ +-e --exif reads Exif data\n" +, stderr); + exit(1); +} + +static char *shortopts = "qf:F:s:b:c:Q:g:G:ae"; +static struct option longopts[] = +{ + { "quiet", 0, 0, 'q' }, + { "input-format", 0, 0, 'f' }, + { "output-format", 0, 0, 'F' }, + { "size", 0, 0, 's' }, + { "fit-to-box", 0, 0, 'b' }, + { "colorspace", 0, 0, 'c' }, + { "jpeg-quality", 0, 0, 'Q' }, + { "background", 0, 0, 'g' }, + { "default-background", 0, 0, 'G' }, + { "remove-alpha", 0, 0, 'a' }, + { "exif", 0, 0, 'e' }, + { NULL, 0, 0, 0 } +}; + +static uns verbose = 1; +static byte *input_file_name; +static enum image_format input_format; +static byte *output_file_name; +static enum image_format output_format; +static uns cols; +static uns rows; +static uns fit_to_box; +static uns channels_format; +static uns jpeg_quality; +static struct color background_color; +static struct color default_background_color; +static uns remove_alpha; +static uns exif; + +static void +parse_color(struct color *color, byte *s) +{ + if (strlen(s) != 6) + usage(); + errno = 0; + char *end; + long int v = strtol(s, &end, 16); + if (errno || *end || v < 0) + usage(); + color_make_rgb(color, (v >> 16) & 255, (v >> 8) & 255, v & 255); +} + +#define MSG(x...) do{ if (verbose) msg(L_INFO, ##x); }while(0) + +int +main(int argc, char **argv) +{ + log_init(argv[0]); + int opt; + default_background_color = color_white; + while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) >= 0) + switch (opt) + { + case 'q': + verbose = 0; + break; + case 'f': + if (!(input_format = image_extension_to_format(optarg))) + usage(); + break; + case 'F': + if (!(output_format = image_extension_to_format(optarg))) + usage(); + break; + case 's': + { + byte *r = strchr(optarg, 'x'); + if (!r) + usage(); + *r++ = 0; + if (!(cols = atoi(optarg)) || !(rows = atoi(r))) + usage(); + fit_to_box = 0; + break; + } + case 'b': + { + byte *r = strchr(optarg, 'x'); + if (!r) + usage(); + *r++ = 0; + if (!(cols = atoi(optarg)) || !(rows = atoi(r))) + usage(); + fit_to_box = 1; + break; + } + case 'c': + if (!(channels_format = image_name_to_channels_format(optarg))) + usage(); + break; + case 'Q': + if (!(jpeg_quality = atoi(optarg))) + usage(); + break; + case 'g': + parse_color(&background_color, optarg); + break; + case 'G': + parse_color(&default_background_color, optarg); + break; + case 'a': + remove_alpha++; + break; + case 'e': + exif++; + break; + default: + usage(); + } + + if (argc != optind + 1 && argc != optind + 2) + usage(); + input_file_name = argv[optind++]; + if (argc > optind) + output_file_name = argv[optind]; + +#define TRY(x) do{ if (!(x)) exit(1); }while(0) + MSG("Initializing image library"); + struct image_context ctx; + struct image_io io; + image_context_init(&ctx); + ctx.tracing_level = ~0U; + if (!image_io_init(&ctx, &io)) + die("Cannot initialize image I/O"); + + MSG("Reading %s", input_file_name); + byte cs_buf[IMAGE_CHANNELS_FORMAT_MAX_SIZE]; + io.fastbuf = bopen(input_file_name, O_RDONLY, 1 << 18); + io.format = input_format ? : image_file_name_to_format(input_file_name); + if (exif) + io.flags |= IMAGE_IO_WANT_EXIF; + TRY(image_io_read_header(&io)); + if (!output_file_name) + { + bclose(io.fastbuf); + printf("Format: %s\n", image_format_to_extension(io.format) ? : (byte *)"?"); + printf("Dimensions: %dx%d\n", io.cols, io.rows); + printf("Colorspace: %s\n", (io.flags & IMAGE_IO_HAS_PALETTE) ? (byte *)"Palette" : image_channels_format_to_name(io.flags, cs_buf)); + printf("NumColors: %u\n", io.number_of_colors); + if (io.background_color.color_space) + { + byte rgb[3]; + TRY(color_put(&ctx, &io.background_color, rgb, COLOR_SPACE_RGB)); + printf("Background: %02x%02x%02x\n", rgb[0], rgb[1], rgb[2]); + } + if (io.exif_size) + printf("ExifSize: %u\n", io.exif_size); + } + else + { + MSG("%s %dx%d %s", image_format_to_extension(io.format) ? : (byte *)"?", io.cols, io.rows, + (io.flags & IMAGE_IO_HAS_PALETTE) ? (byte *)"Palette" : image_channels_format_to_name(io.flags, cs_buf)); + if (cols) + if (fit_to_box) + { + image_dimensions_fit_to_box(&io.cols, &io.rows, MIN(cols, 0xffff), MIN(rows, 0xffff), 0); + } + else + { + io.cols = cols; + io.rows = rows; + } + if (background_color.color_space) + io.background_color = background_color; + else if (!io.background_color.color_space) + io.background_color = default_background_color; + if (remove_alpha) + io.flags &= ~IMAGE_ALPHA; + if (channels_format) + io.flags = io.flags & ~IMAGE_PIXEL_FORMAT | channels_format; + if (!(io.flags & IMAGE_ALPHA)) + io.flags |= IMAGE_IO_USE_BACKGROUND; + if (jpeg_quality) + io.jpeg_quality = jpeg_quality; + uns output_fmt = output_format ? : image_file_name_to_format(output_file_name); + uns output_cs = io.flags & IMAGE_COLOR_SPACE; + if (output_fmt != IMAGE_FORMAT_JPEG && + output_cs != COLOR_SPACE_GRAYSCALE && + output_cs != COLOR_SPACE_RGB) + { + MSG("Forcing RGB color space"); + io.flags = (io.flags & ~IMAGE_COLOR_SPACE) | COLOR_SPACE_RGB; + } + TRY(image_io_read_data(&io, 0)); + bclose(io.fastbuf); + MSG("Writing %s", output_file_name); + io.fastbuf = bopen(output_file_name, O_WRONLY | O_CREAT | O_TRUNC, 1 << 18); + io.format = output_format ? : image_file_name_to_format(output_file_name); + MSG("%s %dx%d %s", image_format_to_extension(io.format) ? : (byte *)"?", io.cols, io.rows, + image_channels_format_to_name(io.flags, cs_buf)); + TRY(image_io_write(&io)); + bclose(io.fastbuf); + } + + image_io_cleanup(&io); + image_context_cleanup(&ctx); + MSG("Done."); + return 0; +}