From 4bc6b76423665d7bfdf27a372b031a0161876a5f Mon Sep 17 00:00:00 2001 From: Pavel Charvat Date: Fri, 25 Aug 2006 13:43:30 +0200 Subject: [PATCH] - reorganization of signatures code - image-sim-test can display segmentations --- images/image-sim-test.c | 197 ++++++++++++++++++++++++++++++---------- images/sig-init.c | 158 +++++++++++++++++--------------- images/sig-seg.c | 14 ++- images/signature.h | 26 +++++- 4 files changed, 262 insertions(+), 133 deletions(-) diff --git a/images/image-sim-test.c b/images/image-sim-test.c index f87298eb..c58c9d08 100644 --- a/images/image-sim-test.c +++ b/images/image-sim-test.c @@ -25,17 +25,19 @@ static void NONRET usage(void) { fputs("\ -Usage: image-sim-test [options] image1 image2 \n\ +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\ +-s --segmentation-1 writes image1 segmentation to given file\n\ +-S --segmentation-2 writes image2 segmentation to given file\n\ ", stderr); exit(1); } -static char *shortopts = "qf:F:g:t:" CF_SHORT_OPTS; +static char *shortopts = "qf:F:g:t:s:S:" CF_SHORT_OPTS; static struct option longopts[] = { CF_LONG_OPTS @@ -43,17 +45,22 @@ static struct option longopts[] = { "format-1", 0, 0, 'f' }, { "format-2", 0, 0, 'F' }, { "background", 0, 0, 'g' }, + { "segmentation-1", 0, 0, 's' }, + { "segmentation-2", 0, 0, 'S' }, { 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; #define MSG(x...) do{ if (verbose) log(L_INFO, ##x); }while(0) +#define TRY(x) do{ if (!(x)) die("Error: %s", it.err_msg); }while(0) static void dump_signature(struct image_signature *sig) @@ -68,6 +75,56 @@ dump_signature(struct image_signature *sig) } } +static struct image_thread it; +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(&it, data->image->cols, data->image->rows, COLOR_SPACE_RGB, NULL)); + image_clear(&it, img); + + for (uns i = 0; i < data->regions_count; i++) + { + byte c[3]; + // FIXME: convert from Luv to RGB + c[0] = data->regions[i].a[0]; + c[1] = data->regions[i].a[1]; + c[2] = data->regions[i].a[2]; + 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) { @@ -99,69 +156,113 @@ main(int argc, char **argv) color_make_rgb(&background_color, (v >> 16) & 255, (v >> 8) & 255, v & 255); } break; + case 's': + segmentation_name_1 = optarg; + break; + case 'S': + segmentation_name_2 = optarg; + break; default: usage(); } - if (argc != optind + 2) + if (argc != optind + 2 && argc != optind + 1) 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) + if (argc > optind) + file_name_2 = argv[optind++]; + MSG("Initializing image library"); srandom(time(NULL) ^ getpid()); srgb_to_luv_init(); - struct image_thread it; - struct image_io io; image_thread_init(&it); struct image *img1, *img2; if (!image_io_init(&it, &io)) die("Cannot initialize image I/O (%s)", it.err_msg); - 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); - MSG("Computing signatures"); + 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; - TRY(compute_image_signature(&it, &sig1, img1)); - TRY(compute_image_signature(&it, &sig2, img2)); - dump_signature(&sig1); - dump_signature(&sig2); + MSG("Computing signatures"); + if (img1) + { + struct image_sig_data data; + TRY(image_sig_init(&it, &data, img1)); + image_sig_preprocess(&data); + if (data.valid) + image_sig_segmentation(&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(&it, &data, img2)); + image_sig_preprocess(&data); + if (data.valid) + image_sig_segmentation(&data); + if (segmentation_name_2) + write_segmentation(&data, segmentation_name_2); + image_sig_finish(&data, &sig2); + image_sig_cleanup(&data); + dump_signature(&sig2); + } - uns dist = image_signatures_dist(&sig1, &sig2); - MSG("dist=%.6f", dist / (double)(1 << IMAGE_SIG_DIST_SCALE)); - - image_destroy(img1); - image_destroy(img2); + if (img1 && img2) + { + uns dist = image_signatures_dist(&sig1, &sig2); + MSG("dist=%.6f", dist / (double)(1 << IMAGE_SIG_DIST_SCALE)); + } + + if (img1) + image_destroy(img1); + if (img2) + image_destroy(img2); + + image_io_cleanup(&io); image_thread_cleanup(&it); MSG("Done."); return 0; diff --git a/images/sig-init.c b/images/sig-init.c index 6b99e343..2c6ca2d4 100644 --- a/images/sig-init.c +++ b/images/sig-init.c @@ -13,7 +13,6 @@ #include "lib/math.h" #include "lib/fastbuf.h" #include "lib/conf.h" -#include "lib/heap.h" #include "images/math.h" #include "images/images.h" #include "images/color.h" @@ -21,51 +20,48 @@ #include -static double image_sig_inertia_scale[3] = { 3, 1, 0.3 }; - -struct block { - u32 area; /* block area in pixels (usually 16) */ - u32 v[IMAGE_VEC_F]; - u32 x, y; /* block position */ - struct block *next; -}; - int -compute_image_signature(struct image_thread *thread UNUSED, struct image_signature *sig, struct image *image) +image_sig_init(struct image_thread *thread UNUSED, struct image_sig_data *data, struct image *image) { - bzero(sig, sizeof(*sig)); ASSERT((image->flags & IMAGE_PIXEL_FORMAT) == COLOR_SPACE_RGB); - uns cols = image->cols; - uns rows = image->rows; - uns row_size = image->row_size; - - uns w = (cols + 3) >> 2; - uns h = (rows + 3) >> 2; - - DBG("Computing signature for image of %ux%u pixels (%ux%u blocks)", cols, rows, w, h); + data->image = image; + data->cols = (image->cols + 3) >> 2; + data->rows = (image->rows + 3) >> 2; + data->full_cols = image->cols >> 2; + data->full_rows = image->rows >> 2; + data->blocks_count = data->cols * data->rows; + data->blocks = xmalloc(data->blocks_count * sizeof(struct image_sig_block)); + data->area = image->cols * image->rows; + DBG("Computing signature for image of %ux%u pixels (%ux%u blocks)", + image->cols, image->rows, data->cols, data->rows); + return 1; +} - uns blocks_count = w * h; - struct image_sig_block *blocks = xmalloc(blocks_count * sizeof(struct image_sig_block)), *block = blocks; +void +image_sig_preprocess(struct image_sig_data *data) +{ + struct image *image = data->image; + struct image_sig_block *block = data->blocks; + uns sum[IMAGE_VEC_F]; + bzero(sum, sizeof(sum)); /* Every block of 4x4 pixels */ byte *row_start = image->pixels; - for (uns block_y = 0; block_y < h; block_y++, row_start += row_size * 4) + for (uns block_y = 0; block_y < data->rows; block_y++, row_start += image->row_size * 4) { byte *p = row_start; - for (uns block_x = 0; block_x < w; block_x++, p += 12, block++) + for (uns block_x = 0; block_x < data->cols; block_x++, p += 12, block++) { int t[16], s[16], *tp = t; block->x = block_x; block->y = block_y; /* Convert pixels to Luv color space and compute average coefficients */ - uns l_sum = 0; - uns u_sum = 0; - uns v_sum = 0; + uns l_sum = 0, u_sum = 0, v_sum = 0; byte *p2 = p; - if ((!(cols & 3) || block_x < w - 1) && (!(rows & 3) || block_y < h - 1)) + if (block_x < data->full_cols && block_y < data->full_rows) { - for (uns y = 0; y < 4; y++, p2 += row_size - 12) + for (uns y = 0; y < 4; y++, p2 += image->row_size - 12) for (uns x = 0; x < 4; x++, p2 += 3) { byte luv[3]; @@ -75,6 +71,9 @@ compute_image_signature(struct image_thread *thread UNUSED, struct image_signatu v_sum += luv[2]; } block->area = 16; + sum[0] += l_sum; + sum[1] += u_sum; + sum[2] += v_sum; block->v[0] = (l_sum >> 4); block->v[1] = (u_sum >> 4); block->v[2] = (v_sum >> 4); @@ -83,9 +82,9 @@ compute_image_signature(struct image_thread *thread UNUSED, struct image_signatu else { uns x, y; - uns square_cols = (block_x < w - 1 || !(cols & 3)) ? 4 : cols & 3; - uns square_rows = (block_y < h - 1 || !(rows & 3)) ? 4 : rows & 3; - for (y = 0; y < square_rows; y++, p2 += row_size) + uns square_cols = (block_x < data->full_cols) ? 4 : image->cols & 3; + uns square_rows = (block_y < data->full_rows) ? 4 : image->rows & 3; + for (y = 0; y < square_rows; y++, p2 += image->row_size) { byte *p3 = p2; for (x = 0; x < square_cols; x++, p3 += 3) @@ -109,10 +108,13 @@ compute_image_signature(struct image_thread *thread UNUSED, struct image_signatu tp++; } block->area = square_cols * square_rows; - uns div = 0x10000 / block->area; - block->v[0] = (l_sum * div) >> 16; - block->v[1] = (u_sum * div) >> 16; - block->v[2] = (v_sum * div) >> 16; + uns inv = 0x10000 / block->area; + sum[0] += l_sum; + sum[1] += u_sum; + sum[2] += v_sum; + block->v[0] = (l_sum * inv) >> 16; + block->v[1] = (u_sum * inv) >> 16; + block->v[2] = (v_sum * inv) >> 16; } /* Apply Daubechies wavelet transformation */ @@ -150,50 +152,44 @@ compute_image_signature(struct image_thread *thread UNUSED, struct image_signatu block->v[3] = fast_sqrt_u16(isqr(t[8]) + isqr(t[9]) + isqr(t[12]) + isqr(t[13])); block->v[4] = fast_sqrt_u16(isqr(t[2]) + isqr(t[3]) + isqr(t[6]) + isqr(t[7])); block->v[5] = fast_sqrt_u16(isqr(t[10]) + isqr(t[11]) + isqr(t[14]) + isqr(t[15])); + sum[3] += block->v[3] * block->area; + sum[4] += block->v[4] * block->area; + sum[5] += block->v[5] * block->area; } } - /* FIXME: simple average is for testing pusposes only */ - uns l_sum = 0; - uns u_sum = 0; - uns v_sum = 0; - uns lh_sum = 0; - uns hl_sum = 0; - uns hh_sum = 0; - for (uns i = 0; i < blocks_count; i++) - { - l_sum += blocks[i].v[0]; - u_sum += blocks[i].v[1]; - v_sum += blocks[i].v[2]; - lh_sum += blocks[i].v[3]; - hl_sum += blocks[i].v[4]; - hh_sum += blocks[i].v[5]; - } - - sig->vec.f[0] = l_sum / blocks_count; - sig->vec.f[1] = u_sum / blocks_count; - sig->vec.f[2] = v_sum / blocks_count; - sig->vec.f[3] = lh_sum / blocks_count; - sig->vec.f[4] = hl_sum / blocks_count; - sig->vec.f[5] = hh_sum / blocks_count; + /* Compute featrures average */ + uns inv = 0xffffffffU / data->area; + for (uns i = 0; i < IMAGE_VEC_F; i++) + data->f[i] = ((u64)sum[i] * inv) >> 32; - if (cols < image_sig_min_width || rows < image_sig_min_height) + if (image->cols < image_sig_min_width || image->rows < image_sig_min_height) { - xfree(blocks); - return 1; + data->valid = 0; + data->regions_count = 0; } + else + data->valid = 1; +} - /* Quantize blocks to image regions */ - struct image_sig_region regions[IMAGE_REG_MAX]; - sig->len = image_sig_segmentation(blocks, blocks_count, regions); +static double image_sig_inertia_scale[3] = { 3, 1, 0.3 }; +void +image_sig_finish(struct image_sig_data *data, struct image_signature *sig) +{ + for (uns i = 0; i < IMAGE_VEC_F; i++) + sig->vec.f[i] = data->f[i]; + sig->len = data->regions_count; + if (!sig->len) + return; + /* For each region */ u64 w_total = 0; - uns w_border = (MIN(w, h) + 3) / 4; + uns w_border = (MIN(data->cols, data->rows) + 3) / 4; uns w_mul = 127 * 256 / w_border; for (uns i = 0; i < sig->len; i++) { - struct image_sig_region *r = regions + i; + struct image_sig_region *r = data->regions + i; DBG("Processing region %u: count=%u", i, r->count); ASSERT(r->count); @@ -213,8 +209,8 @@ compute_image_signature(struct image_thread *thread UNUSED, struct image_signatu y_avg += b->y; uns d = b->x; d = MIN(d, b->y); - d = MIN(d, w - b->x - 1); - d = MIN(d, h - b->y - 1); + d = MIN(d, data->cols - b->x - 1); + d = MIN(d, data->rows - b->y - 1); if (d >= w_border) w_sum += 128; else @@ -275,8 +271,8 @@ compute_image_signature(struct image_thread *thread UNUSED, struct image_signatu uns wa = 128, wb = 128; for (uns i = sig->len; --i > 0; ) { - struct image_sig_region *r = regions + i; - wa -= sig->reg[i].wa = CLAMP(r->count * 128 / blocks_count, 1, (int)(wa - i)); + struct image_sig_region *r = data->regions + i; + wa -= sig->reg[i].wa = CLAMP(r->count * 128 / data->blocks_count, 1, (int)(wa - i)); wb -= sig->reg[i].wb = CLAMP(r->w_sum * 128 / w_total, 1, (int)(wa - i)); } sig->reg[0].wa = wa; @@ -291,9 +287,23 @@ compute_image_signature(struct image_thread *thread UNUSED, struct image_signatu DBG("region %u: features=%s", i, buf); } #endif +} - xfree(blocks); - - return 1; +void +image_sig_cleanup(struct image_sig_data *data) +{ + xfree(data->blocks); } +int +compute_image_signature(struct image_thread *thread, struct image_signature *sig, struct image *image) +{ + struct image_sig_data data; + if (!image_sig_init(thread, &data, image)) + return 0; + image_sig_preprocess(&data); + if (data.valid) + image_sig_segmentation(&data); + image_sig_finish(&data, sig); + image_sig_cleanup(&data); +} diff --git a/images/sig-seg.c b/images/sig-seg.c index 7bd2db01..94830573 100644 --- a/images/sig-seg.c +++ b/images/sig-seg.c @@ -301,18 +301,16 @@ postquant(struct image_sig_block *blocks, uns blocks_count, struct image_sig_reg return regions_end - regions; } -uns -image_sig_segmentation(struct image_sig_block *blocks, uns blocks_count, struct image_sig_region *regions) +void +image_sig_segmentation(struct image_sig_data *data) { - uns regions_count; - regions_count = prequant(blocks, blocks_count, regions); + data->regions_count = prequant(data->blocks, data->blocks_count, data->regions); #ifdef LOCAL_DEBUG - dump_segmentation(regions, regions_count); + dump_segmentation(data->regions, data->regions_count); #endif - regions_count = postquant(blocks, blocks_count, regions, regions_count); + data->regions_count = postquant(data->blocks, data->blocks_count, data->regions, data->regions_count); #ifdef LOCAL_DEBUG - dump_segmentation(regions, regions_count); + dump_segmentation(data->regions, data->regions_count); #endif - return regions_count; } diff --git a/images/signature.h b/images/signature.h index 54452638..3511f914 100644 --- a/images/signature.h +++ b/images/signature.h @@ -64,14 +64,34 @@ struct image_sig_region { u64 w_sum; }; -/* sig-seg.c */ - -uns image_sig_segmentation(struct image_sig_block *blocks, uns blocks_count, struct image_sig_region *regions); +struct image_sig_data { + struct image *image; + struct image_sig_block *blocks; + struct image_sig_region regions[IMAGE_REG_MAX]; + u32 cols; + u32 rows; + u32 full_cols; + u32 full_rows; + u32 area; + u32 valid; + u32 blocks_count; + u32 regions_count; + u32 f[IMAGE_VEC_F]; +}; /* sig-init.c */ int compute_image_signature(struct image_thread *thread, struct image_signature *sig, struct image *image); +int image_sig_init(struct image_thread *thread, struct image_sig_data *data, struct image *image); +void image_sig_preprocess(struct image_sig_data *data); +void image_sig_finish(struct image_sig_data *data, struct image_signature *sig); +void image_sig_cleanup(struct image_sig_data *data); + +/* sig-seg.c */ + +void image_sig_segmentation(struct image_sig_data *data); + /* sig-cmp.c */ #define IMAGE_SIG_DIST_SCALE (3 + 3 + 8 + 16) -- 2.39.2