From cd8337db5e67bec5aaaceb84252b63b3a9e23eea Mon Sep 17 00:00:00 2001 From: Pavel Charvat Date: Wed, 2 Aug 2006 17:01:53 +0200 Subject: [PATCH] unfinished revision of image comparisions --- images/Makefile | 7 +- images/dup-cmp.c | 337 +++++++++++++++++++++------------------- images/dup-cmp.h | 31 ++-- images/image-dup-test.c | 162 +++++++++++++++++++ images/image.c | 46 +++--- images/images.h | 6 +- 6 files changed, 390 insertions(+), 199 deletions(-) create mode 100644 images/image-dup-test.c diff --git a/images/Makefile b/images/Makefile index b8b00220..692a89c8 100644 --- a/images/Makefile +++ b/images/Makefile @@ -2,9 +2,9 @@ DIRS+=images -PROGS+=$(addprefix $(o)/images/,image-tool) +PROGS+=$(addprefix $(o)/images/,image-tool image-dup-test) -LIBIMAGES_MODS=image scale color alpha io-main +LIBIMAGES_MODS=image scale color alpha io-main dup-cmp LIBIMAGES=$(o)/images/libimages.$(LS) LIBIMAGES_LIBS= @@ -42,6 +42,9 @@ $(o)/images/libimages.so: $(addsuffix .oo,$(addprefix $(o)/images/,$(LIBIMAGES_M $(o)/images/image-tool: $(o)/images/image-tool.o $(LIBIMAGES) $(LIBUCW) $(o)/images/image-tool: LIBS+=$(LIBIMAGES_LIBS) +$(o)/images/image-dup-test: $(o)/images/image-dup-test.o $(LIBIMAGES) $(LIBUCW) +$(o)/images/image-dup-test: LIBS+=$(LIBIMAGES_LIBS) + TESTS+=$(o)/images/image-test.test $(o)/images/image-test: $(o)/images/image-test.o $(LIBIMAGES) $(LIBUCW) $(o)/images/image-test: LIBS+=$(LIBIMAGES_LIBS) diff --git a/images/dup-cmp.c b/images/dup-cmp.c index fbb2de8b..6b8155ae 100644 --- a/images/dup-cmp.c +++ b/images/dup-cmp.c @@ -10,31 +10,40 @@ * FIXME: * - many possible optimization * - compare normalized pictures (brightness, ...) - * - better image scale... now it can completely miss some rows/cols of pixels + * - a blur should help to deal with scaling errors * - maybe better/slower last step * - different thresholds for various transformations * - do not test all transformations for symetric pictures - * - allocated memory could be easily decreased to about 1/3 + * - allocated memory could be easily decreased to about 1/3 * for aspect ratio threshold near one - * - ... secret ideas :-) */ -#undef LOCAL_DEBUG +#define LOCAL_DEBUG #include "sherlock/sherlock.h" #include "lib/mempool.h" #include "images/images.h" #include "images/dup-cmp.h" -static uns image_dup_scale_min_size = 16; +#include "lib/mempool.h" +#include "lib/fastbuf.h" +#include + static uns image_dup_ratio_threshold = 140; -static uns image_dup_error_threshold = 50; +static uns image_dup_error_threshold = 600; +static uns image_dup_tab_limit = 8; static inline byte * -image_dup_block(struct image_dup *dup, uns col, uns row) +image_dup_block(struct image_dup *dup, uns tab_col, uns tab_row) +{ + return dup->tab_pixels + (dup->tab_row_size << tab_row) + (3 << (tab_row + tab_col)); +} + +static inline struct image * +image_dup_subimage(struct image_thread *thread, struct image_dup *dup, struct image *block, uns tab_col, uns tab_row) { - ASSERT(col <= dup->cols && row <= dup->rows); - return dup->buf + (dup->line << row) + (3 << (row + col)); + return image_init_matrix(thread, block, image_dup_block(dup, tab_col, tab_row), + 1 << tab_col, 1 << tab_row, 3 << tab_col, COLOR_SPACE_RGB); } static inline void @@ -45,61 +54,34 @@ pixels_average(byte *dest, byte *src1, byte *src2) dest[2] = ((uns)src1[2] + (uns)src2[2]) >> 1; } -uns -image_dup_estimate_size(uns width, uns height) +int +image_dup_init(struct image_thread *thread, struct image_dup *dup, struct image *img, struct mempool *pool) { - uns cols, rows; - for (cols = 0; (uns)(2 << cols) < width; cols++); - for (rows = 0; (uns)(2 << rows) < height; rows++); - return sizeof(struct image_dup) + (12 << (cols + rows)); -} + DBG("image_dup_init()"); + + ASSERT((img->flags & IMAGE_PIXEL_FORMAT) == COLOR_SPACE_RGB); + + dup->image = img; + for (dup->tab_cols = 0; (uns)(2 << dup->tab_cols) < img->cols && dup->tab_cols < image_dup_tab_limit; dup->tab_cols++); + for (dup->tab_rows = 0; (uns)(2 << dup->tab_rows) < img->rows && dup->tab_rows < image_dup_tab_limit; dup->tab_rows++); + dup->tab_pixels = mp_alloc(pool, dup->tab_size = (12 << (dup->tab_cols + dup->tab_rows))); + dup->tab_row_size = 6 << dup->tab_cols; -void -image_dup_init(struct image_dup *dup, struct image_data *image, struct mempool *pool) -{ - ASSERT(image->width && image->height); - - dup->image = image; - dup->width = image->width; - dup->height = image->height; - for (dup->cols = 0; (uns)(2 << dup->cols) < image->width; dup->cols++); - for (dup->rows = 0; (uns)(2 << dup->rows) < image->height; dup->rows++); - dup->buf = mp_alloc(pool, dup->buf_size = (12 << (dup->cols + dup->rows))); - dup->line = 6 << dup->cols; - dup->flags = 0; - if (image->width >= image_dup_scale_min_size && image->height >= image_dup_scale_min_size) - dup->flags |= IMAGE_DUP_FLAG_SCALE; - /* Scale original image to right bottom block */ { - byte *d = image_dup_block(dup, dup->cols, dup->rows); - uns width = 1 << dup->cols; - uns height = 1 << dup->rows; - uns line_size = 3 * image->width; - uns src_y = 0; - for (uns y = 0; y < height; y++) - { - byte *line = image->pixels + line_size * (src_y >> dup->rows); - uns src_x = 0; - for (uns x = 0; x < width; x++) - { - byte *s = line + 3 * (src_x >> dup->cols); - d[0] = s[0]; - d[1] = s[1]; - d[2] = s[2]; - d += 3; - src_x += image->width; - } - src_y += image->height; - } + struct image block; + if (!image_dup_subimage(thread, dup, &block, dup->tab_cols, dup->tab_rows)) + return 0; + if (!image_scale(thread, &block, img)) + return 0; } /* Complete bottom row */ - for (uns i = dup->cols; i--; ) + for (uns i = dup->tab_cols; i--; ) { - byte *d = image_dup_block(dup, i, dup->rows); - byte *s = image_dup_block(dup, i + 1, dup->rows); - for (uns y = 0; y < (uns)(1 << dup->rows); y++) + byte *d = image_dup_block(dup, i, dup->tab_rows); + byte *s = image_dup_block(dup, i + 1, dup->tab_rows); + for (uns y = 0; y < (uns)(1 << dup->tab_rows); y++) for (uns x = 0; x < (uns)(1 << i); x++) { pixels_average(d, s, s + 3); @@ -107,12 +89,12 @@ image_dup_init(struct image_dup *dup, struct image_data *image, struct mempool * s += 6; } } - + /* Complete remaining blocks */ - for (uns i = 0; i <= dup->cols; i++) + for (uns i = 0; i <= dup->tab_cols; i++) { uns line_size = (3 << i); - for (uns j = dup->rows; j--; ) + for (uns j = dup->tab_rows; j--; ) { byte *d = image_dup_block(dup, i, j); byte *s = image_dup_block(dup, i, j + 1); @@ -128,6 +110,8 @@ image_dup_init(struct image_dup *dup, struct image_data *image, struct mempool * } } } + + return 1; } static inline uns @@ -137,42 +121,55 @@ err (int a, int b) return a * a; } -static inline uns -err_sum(byte *pos1, byte *end1, byte *pos2) +static inline u64 +err_sum(byte *pos1, byte *pos2, uns count) { - uns e = 0; - while (pos1 != end1) - e += err(*pos1++, *pos2++); - return e; + uns e64 = 0; + while (count--) + { + uns e = err(*pos1++, *pos2++); + e += err(*pos1++, *pos2++); + e += err(*pos1++, *pos2++); + e64 += e; + } + return e64; } -static inline uns -err_sum_transformed(byte *pos1, byte *end1, byte *pos2, uns width, int add1, int add2) +static inline u64 +err_sum_transformed(byte *pos1, byte *pos2, uns cols, uns rows, int row_step_1, int col_step_2, int row_step_2) { - DBG("err_sum_transformed(): %p %p %p %d %d %d", pos1, end1, pos2, width, add1, add2); - uns e = 0; - while (pos1 != end1) + DBG("err_sum_transformed(pos1=%p pos2=%p cols=%u rows=%u row_step_1=%d col_step_2=%d row_step_2=%d)", + pos1, pos2, cols, rows, row_step_1, col_step_2, row_step_2); + u64 e64 = 0; + for (uns j = rows; j--; ) { - for (uns i = 0; i < width; i++, pos2 += add1) + byte *p1 = pos1; + byte *p2 = pos2; + uns e = 0; + for (uns i = cols; i--; ) { - e += err(pos1[0], pos2[0]); - e += err(pos1[1], pos2[1]); - e += err(pos1[2], pos2[2]); - pos1 += 3; + e += err(p1[0], p2[0]); + e += err(p1[1], p2[1]); + e += err(p1[2], p2[2]); + p1 += 3; + p2 += col_step_2; } - pos2 += add2; + pos1 += row_step_1; + pos2 += row_step_2; + e64 += e; } - return e; + return e64; } static inline int -aspect_ratio_test(uns width1, uns height1, uns width2, uns height2) +aspect_ratio_test(uns cols1, uns rows1, uns cols2, uns rows2) { - uns r1 = width1 * height2; - uns r2 = height1 * width2; + DBG("aspect_ratio_test(cols1=%u rows1=%u cols2=%u rows2=%u)", cols1, rows1, cols2, rows2); + uns r1 = cols1 * rows2; + uns r2 = rows1 * cols2; return - r1 <= ((r2 * image_dup_ratio_threshold) >> 5) && - r2 <= ((r1 * image_dup_ratio_threshold) >> 5); + r1 <= ((r2 * image_dup_ratio_threshold) >> 7) && + r2 <= ((r1 * image_dup_ratio_threshold) >> 7); } static inline int @@ -188,56 +185,60 @@ average_compare(struct image_dup *dup1, struct image_dup *dup2) } static int -blocks_compare(struct image_dup *dup1, struct image_dup *dup2, uns col, uns row, uns trans) +blocks_compare(struct image_dup *dup1, struct image_dup *dup2, uns tab_col, uns tab_row, uns trans) { - DBG("blocks_compare(): col=%d row=%d trans=%d", col, row, trans); - byte *block1 = image_dup_block(dup1, col, row); - byte *block2 = (trans < 4) ? image_dup_block(dup2, col, row) : image_dup_block(dup2, row, col); - int add1, add2; + DBG("blocks_compare(tab_col=%d tab_row=%d trans=%d)", tab_col, tab_row, trans); + byte *block1 = image_dup_block(dup1, tab_col, tab_row); + byte *block2; + int col_step, row_step; + if (trans < 4) + block2 = image_dup_block(dup2, tab_col, tab_row); + else + block2 = image_dup_block(dup2, tab_row, tab_col); switch (trans) { case 0: ; - uns err = (err_sum(block1, block1 + (3 << (col + row)), block2) >> (col + row)); + uns err = (err_sum(block1, block2, 1 << (tab_col + tab_row)) >> (tab_col + tab_row)); DBG("average error=%d", err); return err <= image_dup_error_threshold; case 1: - add1 = -3; - add2 = 6 << col; - block2 += (3 << col) - 3; + col_step = -3; + row_step = (3 << tab_col); + block2 += row_step - 3; break; case 2: - add1 = 1; - add2 = -(6 << col); - block2 += (3 << (col + row)) - (3 << col); + col_step = 3; + row_step = -(3 << tab_col); + block2 += (3 << (tab_col + tab_row)) + row_step; break; case 3: - add1 = -3; - add2 = 0; - block2 += (3 << (col + row)) - 3; + col_step = -3; + row_step = -(3 << tab_col); + block2 += (3 << (tab_col + tab_row)) - 3; break; case 4: - add1 = (3 << col); - add2 = -(3 << (col + row)) + 3; + col_step = (3 << tab_row); + row_step = 3; break; case 5: - add1 = -(3 << col); - add2 = (3 << (col + row)) + 3; - block2 += (3 << (col + row)) - (3 << col); + col_step = -(3 << tab_row); + row_step = 3; + block2 += (3 << (tab_col + tab_row)) + col_step; break; case 6: - add1 = (3 << col); - add2 = -(3 << (col + row)) - 3; - block2 += (3 << col) - 3; + col_step = (3 << tab_row); + row_step = -3; + block2 += col_step - 3; break; case 7: - add1 = -(3 << col); - add2 = (3 << (col + row)) - 3; - block2 += (3 << (col + row)) - 3; + col_step = -(3 << tab_row); + row_step = -3; + block2 += (3 << (tab_col + tab_row)) - 3; break; default: ASSERT(0); } - uns err = (err_sum_transformed(block1, block1 + (3 << (col + row)), block2, (1 << col), add1, add2) >> (col + row)); + uns err = (err_sum_transformed(block1, block2, (1 << tab_col), (1 << tab_row), (3 << tab_col), col_step, row_step) >> (tab_col + tab_row)); DBG("average error=%d", err); return err <= image_dup_error_threshold; } @@ -245,87 +246,93 @@ blocks_compare(struct image_dup *dup1, struct image_dup *dup2, uns col, uns row, static int same_size_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans) { - byte *block1 = dup1->image->pixels; - byte *block2 = dup2->image->pixels; - DBG("same_size_compare(): trans=%d", trans); - int add1, add2; + struct image *img1 = dup1->image; + struct image *img2 = dup2->image; + byte *block1 = img1->pixels; + byte *block2 = img2->pixels; + int col_step, row_step; + DBG("same_size_compare(trans=%d)", trans); switch (trans) { case 0: ; - uns err = (err_sum(block1, block1 + 3 * dup1->width * dup1->height, block2) / (dup1->width * dup1->height)); - DBG("average error=%d", err); - return err <= image_dup_error_threshold; + col_step = 3; + row_step = img2->row_size; + break; case 1: - add1 = -3; - add2 = 6 * dup1->width; - block2 += 3 * (dup1->width - 1); + col_step = -3; + row_step = img2->row_size; + block2 += 3 * (img2->cols - 1); break; case 2: - add1 = 1; - add2 = -6 * dup1->width; - block2 += 3 * dup1->width * (dup1->height - 1); + col_step = 3; + row_step = -img2->row_size; + block2 += img2->row_size * (img2->rows - 1); break; case 3: - add1 = -3; - add2 = 0; - block2 += 3 * (dup1->width * dup1->height - 1); + col_step = -3; + row_step = -img2->row_size; + block2 += img2->row_size * (img2->rows - 1) + 3 * (img2->cols - 1); break; case 4: - add1 = 3 * dup1->width; - add2 = -3 * (dup1->width * dup1->height - 1); + col_step = img2->row_size; + row_step = 3; break; case 5: - add1 = -3 * dup1->width; - add2 = 3 * (dup1->width * dup1->height + 1); - block2 += 3 * dup1->width * (dup1->height - 1); + col_step = -img2->row_size; + row_step = 3; + block2 += img2->row_size * (img2->rows - 1); break; case 6: - add1 = 3 * dup1->width; - add2 = -3 * (dup1->width * dup1->height + 1); - block2 += 3 * (dup1->width - 1); + col_step = img2->row_size; + row_step = -3; + block2 += 3 * (img2->cols - 1); break; case 7: - add1 = -3 * dup1->width; - add2 = 3 * (dup1->width * dup1->height - 1); - block2 += 3 * (dup1->width * dup1->height - 1); + col_step = -img2->row_size; + row_step = -3; + block2 += img2->row_size * (img2->rows - 1) + 3 * (img2->cols - 1); break; default: ASSERT(0); } - uns err = (err_sum_transformed(block1, block1 + 3 * dup1->width * dup1->height, block2, dup1->width, add1, add2) / (dup1->width * dup1->height)); + uns err = (err_sum_transformed(block1, block2, img1->cols, img1->rows, img1->row_size, col_step, row_step) / ((u64)img1->cols * img1->rows)); DBG("average error=%d", err); return err <= image_dup_error_threshold; } int -image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans) +image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns flags) { + DBG("image_dup_compare()"); if (!average_compare(dup1, dup2)) return 0; - if ((dup1->flags & dup2->flags) & IMAGE_DUP_FLAG_SCALE) + struct image *img1 = dup1->image; + struct image *img2 = dup2->image; + if (flags & IMAGE_DUP_SCALE) { DBG("Scale support"); - if (!aspect_ratio_test(dup1->width, dup1->height, dup2->width, dup2->height)) - trans &= 0xf0; - if (!aspect_ratio_test(dup1->width, dup1->height, dup2->height, dup2->width)) - trans &= 0x0f; + if (!aspect_ratio_test(img1->cols, img1->rows, img2->cols, img2->rows)) + flags &= ~0x0f; + if (!aspect_ratio_test(img1->cols, img1->rows, img2->rows, img2->cols)) + flags &= ~0xf0; } else { DBG("No scale support"); - if (!(dup1->width == dup2->width && dup1->height == dup2->height)) - trans &= 0xf0; - if (!(dup1->width == dup2->height && dup1->height == dup2->width)) - trans &= 0x0f; + if (!(img1->cols == img2->cols && img1->rows == img2->rows)) + flags &= ~0x0f; + if (!(img1->cols == img2->rows && img1->rows == img2->cols)) + flags &= ~0xf0; } - if (!trans) + if (!(flags & 0xff)) return 0; - if (trans & 0x0f) + uns result = 0; + if (flags & 0x0f) { - uns cols = MIN(dup1->cols, dup2->cols); - uns rows = MIN(dup1->rows, dup2->rows); + uns cols = MIN(dup1->tab_cols, dup2->tab_cols); + uns rows = MIN(dup1->tab_rows, dup2->tab_rows); for (uns t = 0; t < 4; t++) - if (trans & (1 << t)) + if (flags & (1 << t)) { DBG("Testing trans %d", t); for (uns i = MAX(cols, rows); i--; ) @@ -335,18 +342,22 @@ image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans) if (!blocks_compare(dup1, dup2, col, row, t)) break; if (!i && - (dup1->width != dup2->width || dup1->height != dup2->height || + (img1->cols != img2->cols || img1->rows != img2->rows || same_size_compare(dup1, dup2, t))) - return 1; + { + result |= 1 << t; + if (!(flags & IMAGE_DUP_WANT_ALL)) + return result; + } } } } - if (trans & 0xf0) + if (flags & 0xf0) { - uns cols = MIN(dup1->cols, dup2->rows); - uns rows = MIN(dup1->rows, dup2->cols); + uns cols = MIN(dup1->tab_cols, dup2->tab_rows); + uns rows = MIN(dup1->tab_rows, dup2->tab_cols); for (uns t = 4; t < 8; t++) - if (trans & (1 << t)) + if (flags & (1 << t)) { DBG("Testing trans %d", t); for (uns i = MAX(cols, rows); i--; ) @@ -356,11 +367,15 @@ image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans) if (!blocks_compare(dup1, dup2, col, row, t)) break; if (!i && - (dup1->width != dup2->height || dup1->height != dup2->width || + (img1->cols != img2->rows || img1->rows != img2->cols || same_size_compare(dup1, dup2, t)) ) - return 1; + { + result |= 1 << t; + if (!(flags & IMAGE_DUP_WANT_ALL)) + return result; + } } } } - return 0; + return result; } diff --git a/images/dup-cmp.h b/images/dup-cmp.h index 03d477d3..3b24d3ef 100644 --- a/images/dup-cmp.h +++ b/images/dup-cmp.h @@ -1,27 +1,28 @@ #ifndef _IMAGES_DUP_CMP_H #define _IMAGES_DUP_CMP_H -struct image_data; - struct image_dup { struct image *image; - byte *buf; - uns buf_size; - uns flags; - uns cols; - uns rows; - uns line; - uns width; - uns height; + byte *tab_pixels; + u32 tab_cols; + u32 tab_rows; + u32 tab_row_size; + u32 tab_size; }; -#define IMAGE_DUP_FLAG_SCALE 0x1 - #define IMAGE_DUP_TRANS_ID 0x01 +#define IMAGE_DUP_FLIP_X 0x02 +#define IMAGE_DUP_FLIP_Y 0x04 +#define IMAGE_DUP_ROT_180 0x08 +#define IMAGE_DUP_FLIP_BACK 0x10 +#define IMAGE_DUP_ROT_CCW 0x20 +#define IMAGE_DUP_ROT_CW 0x40 +#define IMAGE_DUP_FLIP_SLASH 0x80 #define IMAGE_DUP_TRANS_ALL 0xff +#define IMAGE_DUP_SCALE 0x100 +#define IMAGE_DUP_WANT_ALL 0x200 -void image_dup_init(struct image_dup *dup, struct image *image, struct mempool *pool); -int image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans); -uns image_dup_estimate_size(uns width, uns height); +int image_dup_init(struct image_thread *thread, struct image_dup *dup, struct image *image, struct mempool *pool); +int image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns flags); #endif diff --git a/images/image-dup-test.c b/images/image-dup-test.c new file mode 100644 index 00000000..488ccfda --- /dev/null +++ b/images/image-dup-test.c @@ -0,0 +1,162 @@ +/* + * 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 "lib/lib.h" +#include "lib/getopt.h" +#include "lib/fastbuf.h" +#include "images/images.h" +#include "images/color.h" +#include "images/dup-cmp.h" +#include +#include +#include +#include + +static void NONRET +usage(void) +{ + fputs("\ +Usage: image-tool [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) log(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)) die("Error: %s", it.err_msg); }while(0) + MSG("Initializing image library"); + struct image_thread it; + struct image_io io; + image_thread_init(&it); + + struct image *img1, *img2; + + image_io_init(&it, &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"); + TRY(image_dup_init(&it, &dup1, img1, pool)); + TRY(image_dup_init(&it, &dup2, img2, pool)); + + MSG("Similarity bitmap %02x", image_dup_compare(&dup1, &dup2, transformations | IMAGE_DUP_SCALE | IMAGE_DUP_WANT_ALL)); + + mp_delete(pool); + + image_destroy(img1); + image_destroy(img2); + image_thread_cleanup(&it); + MSG("Done."); + return 0; +} diff --git a/images/image.c b/images/image.c index e030a947..58ec9ec1 100644 --- a/images/image.c +++ b/images/image.c @@ -41,18 +41,10 @@ image_thread_err_format(struct image_thread *it, uns code, char *msg, ...) va_end(args); } -struct image * -image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool *pool) +static inline uns +flags_to_pixel_size(uns flags) { - DBG("image_new(cols=%u rows=%u flags=0x%x pool=%p)", cols, rows, flags, pool); - flags &= IMAGE_NEW_FLAGS; - if (unlikely(!image_dimensions_valid(cols, rows))) - { - image_thread_err_format(it, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows); - return NULL; - } - struct image *img; - uns pixel_size, row_size, align; + uns pixel_size; switch (flags & IMAGE_COLOR_SPACE) { case COLOR_SPACE_GRAYSCALE: @@ -66,6 +58,22 @@ image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool } if (flags & IMAGE_ALPHA) pixel_size++; + return pixel_size; +} + +struct image * +image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool *pool) +{ + DBG("image_new(cols=%u rows=%u flags=0x%x pool=%p)", cols, rows, flags, pool); + flags &= IMAGE_NEW_FLAGS; + if (unlikely(!image_dimensions_valid(cols, rows))) + { + image_thread_err_format(it, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows); + return NULL; + } + struct image *img; + uns pixel_size, row_size, align; + pixel_size = flags_to_pixel_size(flags); switch (pixel_size) { case 1: @@ -171,37 +179,39 @@ image_clear(struct image_thread *it UNUSED, struct image *img) bzero(img->pixels, img->image_size); } -int +struct image * image_init_matrix(struct image_thread *it, struct image *img, byte *pixels, uns cols, uns rows, uns row_size, uns flags) { - DBG("image_init_matrix(img=%p cols=%u rows=%u row_size=%u flags=0x%x)", img, cols, rows, row_size, uns flags); + DBG("image_init_matrix(img=%p pixels=%p cols=%u rows=%u row_size=%u flags=0x%x)", img, pixels, cols, rows, row_size, flags); if (unlikely(!image_dimensions_valid(cols, rows))) { image_thread_err_format(it, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows); - return 0; + return NULL; } img->pixels = pixels; img->cols = cols; img->rows = rows; + img->pixel_size = flags_to_pixel_size(flags); img->row_size = row_size; img->image_size = rows * row_size; img->flags = flags & (IMAGE_NEW_FLAGS | IMAGE_GAPS_PROTECTED); - return 1; + return img; } -int +struct image * image_init_subimage(struct image_thread *it UNUSED, struct image *img, struct image *src, uns left, uns top, uns cols, uns rows) { - DBG("image_init_subimage(img=%p src=%p left=%u top=%u cols=%u rows=%u)"); + DBG("image_init_subimage(img=%p src=%p left=%u top=%u cols=%u rows=%u)", img, src, left, top, cols, rows); ASSERT(left + cols <= src->cols && top + rows <= src->rows); img->pixels = src->pixels + left * src->pixel_size + top * src->row_size; img->cols = cols; img->rows = rows; + img->pixel_size = src->pixel_size; img->row_size = src->row_size; img->image_size = src->row_size * rows; img->flags = src->flags & IMAGE_NEW_FLAGS; img->flags |= IMAGE_GAPS_PROTECTED; - return 1; + return img; } byte * diff --git a/images/images.h b/images/images.h index 4ac9da6e..f0072941 100644 --- a/images/images.h +++ b/images/images.h @@ -70,7 +70,7 @@ enum image_flag { IMAGE_PIXELS_ALIGNED = 0x10, /* align pixel size to the nearest power of two */ IMAGE_SSE_ALIGNED = 0x20, /* align scanlines to multiples of 16 bytes (both start and size) */ IMAGE_NEED_DESTROY = 0x40, /* image is allocated with xmalloc */ - IMAGE_GAPS_PROTECTED = 0x80, /* cannot write to gaps between rows */ + IMAGE_GAPS_PROTECTED = 0x80, /* cannot access gaps between rows */ IMAGE_CHANNELS_FORMAT = IMAGE_COLOR_SPACE | IMAGE_ALPHA, IMAGE_PIXEL_FORMAT = IMAGE_CHANNELS_FORMAT | IMAGE_PIXELS_ALIGNED, IMAGE_ALIGNED = IMAGE_PIXELS_ALIGNED | IMAGE_SSE_ALIGNED, @@ -93,8 +93,8 @@ struct image *image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct image *image_clone(struct image_thread *it, struct image *src, uns flags, struct mempool *pool); void image_destroy(struct image *img); void image_clear(struct image_thread *it, struct image *img); -int image_init_matrix(struct image_thread *it, struct image *img, byte *pixels, uns cols, uns rows, uns row_size, uns flags); -int image_init_subimage(struct image_thread *it, struct image *img, struct image *src, uns left, uns top, uns cols, uns rows); +struct image *image_init_matrix(struct image_thread *it, struct image *img, byte *pixels, uns cols, uns rows, uns row_size, uns flags); +struct image *image_init_subimage(struct image_thread *it, struct image *img, struct image *src, uns left, uns top, uns cols, uns rows); static inline int image_dimensions_valid(uns cols, uns rows) -- 2.39.2