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
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),)
$(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)
+++ /dev/null
-/*
- * Color spaces tool
- *
- * (c) 2006 Pavel Charvat <pchar@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU General Public License.
- */
-
-#include <ucw/lib.h>
-#include <images/images.h>
-#include <images/color.h>
-
-#include <getopt.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-
-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;
-}
+++ /dev/null
-/*
- * Image duplicates testing
- *
- * (c) 2006 Pavel Charvat <pchar@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU General Public License.
- */
-
-#include <ucw/lib.h>
-#include <ucw/getopt.h>
-#include <ucw/fastbuf.h>
-#include <ucw/mempool.h>
-#include <images/images.h>
-#include <images/color.h>
-#include <images/duplicates.h>
-
-#include <stdlib.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-
-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;
-}
+++ /dev/null
-/*
- * Image similarity testing
- *
- * (c) 2006 Pavel Charvat <pchar@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU General Public License.
- */
-
-#include <ucw/lib.h>
-#include <ucw/getopt.h>
-#include <ucw/fastbuf.h>
-#include <ucw/base64.h>
-#include <ucw/base224.h>
-#include <images/images.h>
-#include <images/color.h>
-#include <images/signature.h>
-
-#include <stdlib.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-#include <time.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-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;
-}
+++ /dev/null
-/*
- * Image Library -- Simple image manipulation utility
- *
- * (c) 2006 Pavel Charvat <pchar@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU General Public License.
- */
-
-#include <ucw/lib.h>
-#include <ucw/fastbuf.h>
-#include <images/images.h>
-#include <images/color.h>
-
-#include <getopt.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <stdio.h>
-
-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;
-}
--- /dev/null
+/*
+ * Color spaces tool
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU General Public License.
+ */
+
+#include <ucw/lib.h>
+#include <images/images.h>
+#include <images/color.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+
+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;
+}
--- /dev/null
+/*
+ * Image duplicates testing
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU General Public License.
+ */
+
+#include <ucw/lib.h>
+#include <ucw/getopt.h>
+#include <ucw/fastbuf.h>
+#include <ucw/mempool.h>
+#include <images/images.h>
+#include <images/color.h>
+#include <images/duplicates.h>
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+
+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;
+}
--- /dev/null
+/*
+ * Image similarity testing
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU General Public License.
+ */
+
+#include <ucw/lib.h>
+#include <ucw/getopt.h>
+#include <ucw/fastbuf.h>
+#include <ucw/base64.h>
+#include <ucw/base224.h>
+#include <images/images.h>
+#include <images/color.h>
+#include <images/signature.h>
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+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;
+}
--- /dev/null
+/*
+ * Image Library -- Simple image manipulation utility
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU General Public License.
+ */
+
+#include <ucw/lib.h>
+#include <ucw/fastbuf.h>
+#include <images/images.h>
+#include <images/color.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+
+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;
+}