From: Pavel Charvat Date: Mon, 17 Apr 2006 22:12:04 +0000 (+0200) Subject: LIBPNG support for reading of PNG thumbnails (probably faster X-Git-Tag: holmes-import~651 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=865c98d50c1be6edef8cc8c892637b8518d0672b;p=libucw.git LIBPNG support for reading of PNG thumbnails (probably faster than GraphicsMagick... going to make some benchmarks) Some routines moved/renamed... and many cosmetic changes --- diff --git a/images/Makefile b/images/Makefile index cd3af5ff..34196a3d 100644 --- a/images/Makefile +++ b/images/Makefile @@ -6,9 +6,10 @@ PROGS+=$(addprefix $(o)/images/,image-idx image-test decomp) $(o)/images/image-sig.o $(o)/images/image-sig.oo: CFLAGS+=-I/usr/include/GraphicsMagick $(o)/images/image-idx.o $(o)/images/image-idx.oo: CFLAGS+=-I/usr/include/GraphicsMagick -$(o)/images/image-idx: $(o)/images/images.o $(o)/images/image-idx.o $(o)/images/image-thumb.o $(o)/indexer/iconfig.o $(o)/images/image-sig.o $(LIBSH) $(LIBLANG) $(LIBCHARSET) -$(o)/images/image-idx: LIBS+=-lGraphicsMagick -ljpeg -$(o)/images/image-test: $(o)/images/images.o $(o)/images/image-test.o $(LIBSH) +$(o)/images/image-obj.o $(o)/images/image-obj.oo: CFLAGS+=-I/usr/include/GraphicsMagick +$(o)/images/image-idx: $(o)/images/image-idx.o $(o)/images/image-obj.o $(o)/indexer/iconfig.o $(o)/images/image-sig.o $(LIBSH) $(LIBLANG) $(LIBCHARSET) +$(o)/images/image-idx: LIBS+=-lGraphicsMagick -ljpeg -lpng +$(o)/images/image-test: $(o)/images/image-test.o $(LIBSH) # By :;DF diff --git a/images/image-idx.c b/images/image-idx.c index 058c7774..0600fc2c 100644 --- a/images/image-idx.c +++ b/images/image-idx.c @@ -22,7 +22,8 @@ #include "lib/bbuf.h" #include "images/images.h" -#include "images/image-thumb.h" +#include "images/image-obj.h" +#include "images/image-sig.h" #include #include @@ -43,7 +44,7 @@ generate_signatures(uns limit) log(L_INFO, "Generating image signatures"); bputl(fb_signatures, 0); - compute_image_signature_prepare(); + imo_decompress_thumbnails_init(); for (oid_t oid = 0; bread(fb_card_attrs, &ca, sizeof(ca)); oid++) if ((uns)((ca.type_flags >> 4) - 8) < 4) @@ -59,23 +60,12 @@ generate_signatures(uns limit) if (attr = obj_find_attr(obj, 'N')) { DBG("Reading oid=%d url=%s", oid, obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U')); - /*bb_t buf; - uns buf_len = 0; - bb_init(&buf); - for (; attr; attr = attr->same) + struct image_obj imo; + imo_init(&imo, pool, obj); + if (imo_decompress_thumbnail(&imo)) { - uns len = strlen(attr->val); - bb_grow(&buf, buf_len + len); - memcpy(buf.ptr + buf_len, attr->val, len); - buf_len += len; - } - byte thumb[buf_len]; - uns thumb_len = base224_decode(thumb, buf.ptr, buf_len);*/ - struct image thumb; - int err; - if (!(err = decompress_thumbnail(obj, pool, &thumb))) - { - if (!(err = compute_image_signature(&thumb, &sig))) + int err; + if (!(err = compute_image_signature(&imo.thumb, &sig))) { bwrite(fb_signatures, &oid, sizeof(oid)); bwrite(fb_signatures, &sig.vec, sizeof(struct image_vector)); @@ -90,14 +80,14 @@ generate_signatures(uns limit) DBG("Cannot create signature, error=%d", err); } else - DBG("Cannot decompress thumbnail, error=%d", err); + DBG("Cannot decompress thumbnail"); } } brewind(fb_signatures); bputl(fb_signatures, count); DBG("%d signatures written", count); - compute_image_signature_finish(); + imo_decompress_thumbnails_done(); buck2obj_free(bob); mp_delete(pool); bclose(fb_cards); diff --git a/images/image-obj.c b/images/image-obj.c new file mode 100644 index 00000000..f5171d55 --- /dev/null +++ b/images/image-obj.c @@ -0,0 +1,521 @@ +/* + * Image Library -- Image Cards Manipulations + * + * (c) 2006 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + * + * FIXME: + * - improve thumbnail creation in gatherer... faster compression, + * only grayscale/RGB colorspaces and maybe fixed headers (abbreviated datastreams in libjpeg) + * - hook memory allocation managers, get rid of multiple initializations + * - alpha channel is propably useless (we could assume white background) + * - optimize decompression parameters + * - create interface for thumbnail compression (for gatherer) and reading (MUX) + * - benchmatk libraries + */ + +#undef LOCAL_DEBUG + +#include "sherlock/sherlock.h" +#include "lib/base224.h" +#include "lib/mempool.h" +#include "sherlock/object.h" +#include "images/images.h" +#include "images/image-obj.h" + +#include +#include + +/* Selection of libraries to use */ +#define USE_LIBPNG +#define USE_LIBJPEG +#define USE_MAGICK + +#if defined(USE_LIBPNG) && defined(USE_LIBJPEG) +#undef USE_MAGICK +#endif + + +/********************************* LIBPNG Library ****************************************/ + +#ifdef USE_LIBPNG + +#include +#include + +static struct mempool *libpng_pool; +static byte *libpng_buf; +static uns libpng_len; + +static png_voidp +libpng_malloc(png_structp png_ptr UNUSED, png_size_t size) +{ + DBG("libpng_malloc(): size=%d", (uns)size); + return mp_alloc(libpng_pool, size); +} + +static void +libpng_free(png_structp png_ptr UNUSED, png_voidp ptr UNUSED) +{ + DBG("libpng_free()"); +} + +static void NONRET +libpng_error(png_structp png_ptr, png_const_charp msg UNUSED) +{ + DBG("libpng_error(): msg=%s", (byte *)msg); + longjmp(png_jmpbuf(png_ptr), 1); +} + +static void +libpng_warning(png_structp png_ptr UNUSED, png_const_charp msg UNUSED) +{ + DBG("libpng_warning(): msg=%s", (byte *)msg); +} + +static void +libpng_read_data(png_structp png_ptr UNUSED, png_bytep data, png_size_t length) +{ + DBG("libpng_read_data(): len=%d", (uns)length); + if (unlikely(libpng_len < length)) + png_error(png_ptr, "Incomplete data"); + memcpy(data, libpng_buf, length); + libpng_buf += length; + libpng_len -= length; +} + +static inline void +libpng_decompress_thumbnails_init(void) +{ +} + +static inline void +libpng_decompress_thumbnails_done(void) +{ +} + +static int +libpng_decompress_thumbnail(struct image_obj *imo) +{ + /* create libpng read structure */ + DBG("Creating libpng read structure"); + libpng_pool = imo->pool; + libpng_buf = imo->thumb_data; + libpng_len = imo->thumb_size; + png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, + NULL, libpng_error, libpng_warning, + NULL, libpng_malloc, libpng_free); + if (unlikely(!png_ptr)) + return 0; + png_infop info_ptr = png_create_info_struct(png_ptr); + if (unlikely(!info_ptr)) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return 0; + } + png_infop end_ptr = png_create_info_struct(png_ptr); + if (unlikely(!end_ptr)) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 0; + } + if (setjmp(png_jmpbuf(png_ptr))) + { + DBG("Libpng failed to read the image, longjump saved us"); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr); + return 0; + } + png_set_read_fn(png_ptr, NULL, libpng_read_data); + + /* Read image info */ + DBG("Reading image info"); + png_read_info(png_ptr, info_ptr); + png_uint_32 width, height; + int bit_depth, color_type; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); + ASSERT(width == imo->thumb.width && height == imo->thumb.height); + + /* Apply transformations */ + imo->thumb.flags = 0; + if (bit_depth == 16) + png_set_strip_16(png_ptr); + switch (color_type) + { + case PNG_COLOR_TYPE_PALETTE: + png_set_palette_to_rgb(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + { + png_set_tRNS_to_alpha(png_ptr); + imo->thumb.flags |= IMAGE_ALPHA; + } + else + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + break; + case PNG_COLOR_TYPE_GRAY: + imo->thumb.flags |= IMAGE_GRAYSCALE; + png_set_gray_to_rgb(png_ptr); + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + imo->thumb.flags |= IMAGE_GRAYSCALE | IMAGE_ALPHA; + png_set_gray_to_rgb(png_ptr); + break; + case PNG_COLOR_TYPE_RGB: + png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + imo->thumb.flags |= IMAGE_ALPHA; + break; + default: + ASSERT(0); + } + png_read_update_info(png_ptr, info_ptr); + ASSERT(png_get_channels(png_ptr, info_ptr) == sizeof(struct pixel)); + ASSERT(png_get_rowbytes(png_ptr, info_ptr) == sizeof(struct pixel) * width); + + /* Read image data */ + DBG("Reading image data"); + struct pixel *pixels = imo->thumb.pixels = mp_alloc(imo->pool, width * height * sizeof(struct pixel)); + png_bytep rows[height]; + for (uns i = 0; i < height; i++, pixels += width) + rows[i] = (png_bytep)pixels; + png_read_image(png_ptr, rows); + png_read_end(png_ptr, end_ptr); + + /* Destroy libpng read structure */ + png_destroy_read_struct(&png_ptr, &info_ptr, &end_ptr); + return 1; +} + +#endif /* USE_LIBPNG */ + + + +/******************************* LIBJPEG Library *************************************/ + +#ifdef USE_LIBJPEG + +#include +#include + +struct libjpeg_err { + struct jpeg_error_mgr pub; + jmp_buf setjmp_buf; +}; + +static void NONRET +libjpeg_error_exit(j_common_ptr cinfo) +{ + DBG("libjpeg_error_exit()"); + longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1); +} + +static void +libjpeg_emit_message(j_common_ptr cinfo UNUSED, int msg_level UNUSED) +{ + DBG("libjpeg_emit_message(): level=%d", msg_level); + /* if (unlikely(msg_level == -1)) + longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1); */ +} + +static void +libjpeg_init_source(j_decompress_ptr cinfo UNUSED) +{ + DBG("libjpeg_init_source()"); +} + +static boolean NONRET +libjpeg_fill_input_buffer(j_decompress_ptr cinfo) +{ + DBG("libjpeg_fill_input_buffer()"); + longjmp(((struct libjpeg_err *)(cinfo)->err)->setjmp_buf, 1); +} + +static void +libjpeg_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ + DBG("libjpeg_skip_input_data(): len=%d", (int)num_bytes); + if (num_bytes > 0) + { + cinfo->src->next_input_byte += num_bytes; + cinfo->src->bytes_in_buffer -= num_bytes; + } +} + +static inline void +libjpeg_decompress_thumbnails_init(void) +{ +} + +static inline void +libjpeg_decompress_thumbnails_done(void) +{ +} + +static int +libjpeg_decompress_thumbnail(struct image_obj *imo) +{ + /* Create libjpeg read structure */ + DBG("Creating libjpeg read structure"); + struct jpeg_decompress_struct cinfo; + struct libjpeg_err err; + cinfo.err = jpeg_std_error(&err.pub); + err.pub.error_exit = libjpeg_error_exit; + err.pub.emit_message = libjpeg_emit_message; + if (setjmp(err.setjmp_buf)) + { + DBG("Libjpeg failed to read the image, longjump saved us"); + jpeg_destroy_decompress(&cinfo); + return 0; + } + jpeg_create_decompress(&cinfo); + + /* Initialize source manager */ + struct jpeg_source_mgr src; + cinfo.src = &src; + src.next_input_byte = imo->thumb_data; + src.bytes_in_buffer = imo->thumb_size; + src.init_source = libjpeg_init_source; + src.fill_input_buffer = libjpeg_fill_input_buffer; + src.skip_input_data = libjpeg_skip_input_data; + src.resync_to_restart = jpeg_resync_to_restart; + src.term_source = libjpeg_init_source; + + /* Read JPEG header and setup decompression options */ + DBG("Reading image header"); + jpeg_read_header(&cinfo, TRUE); + imo->thumb.flags = 0; + if (cinfo.out_color_space == JCS_GRAYSCALE) + imo->thumb.flags |= IMAGE_GRAYSCALE; + else + cinfo.out_color_space = JCS_RGB; + + /* Decompress the image */ + DBG("Reading image data"); + jpeg_start_decompress(&cinfo); + ASSERT(imo->thumb.width == cinfo.output_width && imo->thumb.height == cinfo.output_height); + struct pixel *pixels = imo->thumb.pixels = mp_alloc(imo->pool, cinfo.output_width * cinfo.output_height * sizeof(struct pixel)); + if (cinfo.out_color_space == JCS_RGB) + { /* Read RGB pixels */ + uns size = cinfo.output_width * 3; + JSAMPLE buf[size], *buf_end = buf + size; + while (cinfo.output_scanline < cinfo.output_height) + { + JSAMPLE *p = buf; + jpeg_read_scanlines(&cinfo, &p, 1); + for (; p != buf_end; p += 3) + { + pixels->r = p[0] >> (sizeof(JSAMPLE) * 8 - 8); + pixels->g = p[1] >> (sizeof(JSAMPLE) * 8 - 8); + pixels->b = p[2] >> (sizeof(JSAMPLE) * 8 - 8); + pixels->a = 255; + pixels++; + } + } + } + else + { /* Read grayscale pixels */ + JSAMPLE buf[cinfo.output_width], *buf_end = buf + cinfo.output_width; + while (cinfo.output_scanline < cinfo.output_height) + { + JSAMPLE *p = buf; + jpeg_read_scanlines(&cinfo, &p, 1); + for (; p != buf_end; p++) + { + pixels->r = pixels->g = pixels->b = p[0] >> (sizeof(JSAMPLE) * 8 - 8); + pixels->a = 255; + pixels++; + } + } + } + jpeg_finish_decompress(&cinfo); + + /* Destroy libjpeg object and leave */ + jpeg_destroy_decompress(&cinfo); + return 1; +} + +#endif /* USE_LIBJPEG */ + + + +/****************************** GraphicsMagick Library ******************************/ + +#ifdef USE_MAGICK + +#include + +static ExceptionInfo magick_exception; +static QuantizeInfo magick_quantize; +static ImageInfo *magick_info; + +static void +magick_decompress_thumbnails_init(void) +{ + DBG("Initializing magick thumbnails decompression"); + InitializeMagick(NULL); + GetExceptionInfo(&magick_exception); + magick_info = CloneImageInfo(NULL); + magick_info->subrange = 1; + GetQuantizeInfo(&magick_quantize); + magick_quantize.colorspace = RGBColorspace; +} + +static void +magick_decompress_thumbnails_done(void) +{ + DBG("Finalizing magick thumbnails decompression"); + DestroyImageInfo(magick_info); + DestroyExceptionInfo(&magick_exception); + DestroyMagick(); +} + +static int +magick_decompress_thumbnail(struct image_obj *imo) +{ + DBG("Reading image data"); + Image *image = BlobToImage(magick_info, imo->thumb_data, imo->thumb_size, &magick_exception); + if (unlikely(!image)) + return 0; + ASSERT(image->columns == imo->thumb.width && image->rows == imo->thumb.height); + DBG("Quantizing image"); + QuantizeImage(&magick_quantize, image); + DBG("Converting pixels"); + PixelPacket *pixels = (PixelPacket *)AcquireImagePixels(image, 0, 0, image->columns, image->rows, &magick_exception); + ASSERT(pixels); + uns size = image->columns * image->rows; + struct pixel *p = imo->thumb.pixels = mp_alloc(imo->pool, size * sizeof(struct pixel)); + for (uns i = 0; i < size; i++, p++, pixels++) + { + p->r = pixels->red >> (QuantumDepth - 8); + p->g = pixels->green >> (QuantumDepth - 8); + p->b = pixels->blue >> (QuantumDepth - 8); + p->a = 255; + } + DestroyImage(image); + return 1; +} + +#endif /* USE_MAGICK */ + + + +/*************************************************************************************/ + +static int +extract_image_info(struct image_obj *imo) +{ + DBG("Parsing image info attribute"); + imo->flags |= IMAGE_OBJ_INFO; + byte *info = obj_find_aval(imo->obj, 'G'); + if (!info) + { + DBG("Attribute G not found"); + return 0; + } + uns colors; + byte color_space[MAX_ATTR_SIZE], thumb_format[MAX_ATTR_SIZE]; + UNUSED uns cnt = sscanf(info, "%d%d%s%d%d%d%s", &imo->width, &imo->height, color_space, &colors, &imo->thumb.width, &imo->thumb.height, thumb_format); + ASSERT(cnt == 7); + if (*thumb_format == 'j') + imo->flags |= IMAGE_OBJ_THUMB_JPEG; + return 1; +} + +static int +extract_thumb_data(struct image_obj *imo) +{ + DBG("Extracting thumbnail data"); + imo->flags |= IMAGE_OBJ_THUMB_DATA; + struct oattr *attr = obj_find_attr(imo->obj, 'N'); + if (!attr) + { + DBG("There is no thumbnail attribute N"); + return 0; + } + uns count = 0; + for (struct oattr *a = attr; a; a = a->same) + count++; + byte b224[count * MAX_ATTR_SIZE], *b = b224; + for (struct oattr *a = attr; a; a = a->same) + for (byte *s = a->val; *s; ) + *b++ = *s++; + ASSERT(b != b224); + uns size = b - b224; + imo->thumb_data = mp_alloc(imo->pool, size); + imo->thumb_size = base224_decode(imo->thumb_data, b224, size); + DBG("Thumbnail data size is %d", imo->thumb_size); + return 1; +} + +static int +extract_thumb_image(struct image_obj *imo) +{ + DBG("Decompressing thumbnail image"); + imo->flags |= IMAGE_OBJ_THUMB_IMAGE; + + /* JPEG format */ + if (imo->flags & IMAGE_OBJ_THUMB_JPEG) + { +#if defined(USE_LIBJPEG) + return libjpeg_decompress_thumbnail(imo); +#elif defined(USE_MAGICK) + return magick_decompress_thumbnail(imo); +#else + DBG("JPEG not supported"); + return 0; +#endif + } + /* PNG format */ + else + { +#if defined(USE_LIBPNG) + return libpng_decompress_thumbnail(imo); +#elif defined(USE_MAGICK) + return magick_decompress_thumbnail(imo); +#else + DBG("PNG not supported"); + return 0; +#endif + } +} + +void +imo_decompress_thumbnails_init(void) +{ +#ifdef USE_LIBPNG + libpng_decompress_thumbnails_init(); +#endif +#ifdef USE_LIBJPEG + libjpeg_decompress_thumbnails_init(); +#endif +#ifdef USE_MAGICK + magick_decompress_thumbnails_init(); +#endif +} + +void +imo_decompress_thumbnails_done(void) +{ +#ifdef USE_MAGICK + magick_decompress_thumbnails_done(); +#endif +#ifdef USE_LIBJPEG + libjpeg_decompress_thumbnails_done(); +#endif +#ifdef USE_LIBPNG + libpng_decompress_thumbnails_done(); +#endif +} + +int +imo_decompress_thumbnail(struct image_obj *imo) +{ + return + extract_image_info(imo) && + extract_thumb_data(imo) && + extract_thumb_image(imo); +} + diff --git a/images/image-obj.h b/images/image-obj.h new file mode 100644 index 00000000..bce83591 --- /dev/null +++ b/images/image-obj.h @@ -0,0 +1,48 @@ +/* + * Image Library -- Image Cards Manipulations + * + * (c) 2006 Pavel Charvat + * + * This software may be freely distributed and used according to the terms + * of the GNU Lesser General Public License. + */ + +#ifndef _IMAGES_IMAGE_OBJ_H +#define _IMAGES_IMAGE_OBJ_H + +#include "images/images.h" + +struct mempool; +struct odes; + +enum image_obj_flag { + IMAGE_OBJ_INFO = 0x1, + IMAGE_OBJ_THUMB_JPEG = 0x2, + IMAGE_OBJ_THUMB_DATA = 0x4, + IMAGE_OBJ_THUMB_IMAGE = 0x8 +}; + +struct image_obj { + struct odes *obj; + struct mempool *pool; + uns flags; + uns width; + uns height; + byte *thumb_data; + uns thumb_size; + struct image thumb; +}; + +static inline void +imo_init(struct image_obj *imo, struct mempool *pool, struct odes *obj) +{ + imo->obj = obj; + imo->pool = pool; + imo->flags = 0; +} + +void imo_decompress_thumbnails_init(void); +void imo_decompress_thumbnails_done(void); +int imo_decompress_thumbnail(struct image_obj *imo); + +#endif diff --git a/images/image-search.h b/images/image-search.h index 25d34171..4907545e 100644 --- a/images/image-search.h +++ b/images/image-search.h @@ -1,4 +1,5 @@ #include "lib/heap.h" +#include #define IMAGE_SEARCH_DIST_UNLIMITED (~0U) diff --git a/images/image-sig.c b/images/image-sig.c index c240bd3a..cc159fce 100644 --- a/images/image-sig.c +++ b/images/image-sig.c @@ -4,7 +4,10 @@ #include "lib/math.h" #include "lib/fastbuf.h" #include "images/images.h" +#include "images/image-obj.h" +#include "images/image-sig.h" +#include #include /* @@ -59,14 +62,18 @@ struct block { uns lh, hl, hh; /* energies in Daubechies wavelet bands */ }; -static void -compute_image_area_signature(struct image *image, struct image_signature *sig) +int +compute_image_signature(struct image *image, struct image_signature *sig) { uns width = image->width; uns height = image->height; - - ASSERT(width >= 4 && height >= 4); + if (width < 4 || height < 4) + { + DBG("Image too small... %dx%d", width, height); + return 0; + } + uns w = width >> 2; uns h = height >> 2; DBG("Computing signature for image %dx%d... %dx%d blocks", width, height, w, h); @@ -74,9 +81,9 @@ compute_image_area_signature(struct image *image, struct image_signature *sig) struct block *blocks = xmalloc(blocks_count * sizeof(struct block)), *block = blocks; /* FIXME: use mempool */ /* Every 4x4 block (FIXME: deal with smaller blocks near the edges) */ - byte *p = image->pixels; - for (uns block_y = 0; block_y < h; block_y++, p += 3 * ((width & 3) + width * 3)) - for (uns block_x = 0; block_x < w; block_x++, p -= 3 * (4 * width - 4), block++) + struct pixel *p = image->pixels; + for (uns block_y = 0; block_y < h; block_y++, p += (width & 3) + width * 3) + for (uns block_x = 0; block_x < w; block_x++, p -= 4 * width - 4, block++) { int t[16], s[16], *tp = t; @@ -87,13 +94,13 @@ compute_image_area_signature(struct image *image, struct image_signature *sig) uns l_sum = 0; uns u_sum = 0; uns v_sum = 0; - for (uns y = 0; y < 4; y++, p += 3 * (width - 4)) - for (uns x = 0; x < 4; x++, p += 3) + for (uns y = 0; y < 4; y++, p += width - 4) + for (uns x = 0; x < 4; x++, p += 1) { double rgb[3], luv[3], xyz[3]; - rgb[0] = p[0] / 255.; - rgb[1] = p[1] / 255.; - rgb[2] = p[2] / 255.; + rgb[0] = p->r / 255.; + rgb[1] = p->g / 255.; + rgb[2] = p->b / 255.; srgb_to_xyz_slow(rgb, xyz); xyz_to_luv_slow(xyz, luv); l_sum += *tp++ = luv[0]; @@ -176,53 +183,6 @@ compute_image_area_signature(struct image *image, struct image_signature *sig) xfree(blocks); DBG("Resulting signature is (%s)", stk_print_image_vector(&sig->vec)); -} - -static ExceptionInfo exception; -static QuantizeInfo quantize_info; -static ImageInfo *image_info; - -void -compute_image_signature_prepare(void) -{ - InitializeMagick(NULL); - GetExceptionInfo(&exception); - image_info = CloneImageInfo(NULL); - image_info->subrange = 1; - GetQuantizeInfo(&quantize_info); - quantize_info.colorspace = RGBColorspace; -} - -void -compute_image_signature_finish(void) -{ - DestroyImageInfo(image_info); - DestroyExceptionInfo(&exception); - DestroyMagick(); -} - -int -compute_image_signature(struct image *image, struct image_signature *sig) -{ - if ((image->flags & IMAGE_GRAYSCALE) || (image->width < 4) || (image->height < 4)) - return -1; - compute_image_area_signature(image, sig); - return 0; -#if 0 - Image *image = BlobToImage(image_info, data, len, &exception); /* Damn slow... takes most of CPU time :-/ */ - if (!image) - die("Invalid image format"); - if (image->columns < 4 || image->rows < 4) - { - DBG("Image too small (%dx%d)", (int)image->columns, (int)image->rows); - DestroyImage(image); - return -1; - } - QuantizeImage(&quantize_info, image); /* Also slow... and propably not necessary... */ - PixelPacket *pixels = (PixelPacket *) AcquireImagePixels(image, 0, 0, image->columns, image->rows, &exception); - compute_image_area_signature(pixels, image->columns, image->rows, sig); - DestroyImage(image); - return 0; -#endif + return 1; } diff --git a/images/image-sig.h b/images/image-sig.h new file mode 100644 index 00000000..96e2e25b --- /dev/null +++ b/images/image-sig.h @@ -0,0 +1,65 @@ +#ifndef _IMAGES_IMAGE_SIG_H +#define _IMAGES_IMAGE_SIG_H + +#include "images/images.h" + +#define IMAGE_VEC_K 6 +#define IMAGE_REG_K 9 +#define IMAGE_REG_MAX 4 + +typedef u16 image_feature_t; /* 8 or 16 bits precision */ + +/* K-dimensional feature vector */ +struct image_vector { + image_feature_t f[IMAGE_VEC_K]; +}; + +/* K-dimensional interval */ +struct image_bbox { + struct image_vector vec[2]; +}; + +/* Fetures for image regions */ +struct image_region { + image_feature_t f[IMAGE_REG_K]; +}; + +/* Image signature */ +struct image_signature { + struct image_vector vec; /* Combination of all regions... simplier signature */ + image_feature_t len; /* Number of regions */ + struct image_region reg[IMAGE_REG_MAX];/* Feature vector for every region */ +}; + +/* Similarity search tree... will be changed */ +struct image_tree { + uns count; /* Number of images in the tree */ + uns depth; /* Tree depth */ + struct image_bbox bbox; /* Bounding box containing all the */ + struct image_node *nodes; /* Internal nodes */ + struct image_leaf *leaves; /* Leaves */ +}; + +/* Internal node in the search tree */ +#define IMAGE_NODE_LEAF 0x80000000 /* Node contains pointer to leaves array */ +#define IMAGE_NODE_DIM 0xff /* Split dimension */ +struct image_node { + u32 val; +}; + +/* Leaves in the search tree */ +#define IMAGE_LEAF_LAST 0x80000000 /* Last entry in the list */ +#define IMAGE_LEAF_BITS(i) (31 / IMAGE_VEC_K) /* Number of bits for relative position in i-th dimension */ +struct image_leaf { + u32 flags; /* Relative position in bbox and last node flag */ + oid_t oid; +}; + +#define stk_print_image_vector(v) ({ struct image_vector *_v = v; \ + byte *_s = (byte *) alloca(IMAGE_VEC_K * 6), *_p = _s + sprintf(_s, "%d", _v->f[0]); \ + for (uns _i = 1; _i < IMAGE_VEC_K; _i++) _p += sprintf(_p, " %d", _v->f[_i]); _s; }) + +int compute_image_signature(struct image *image, struct image_signature *sig); + +#endif + diff --git a/images/image-test.c b/images/image-test.c index 979a6a09..69820fd4 100644 --- a/images/image-test.c +++ b/images/image-test.c @@ -1,8 +1,9 @@ -#define LOCAL_DEBUG +#undef LOCAL_DEBUG #include "sherlock/sherlock.h" #include "lib/fastbuf.h" #include "images/images.h" +#include "images/image-sig.h" #include "images/image-search.h" #include "sherlock/index.h" #include "lib/mempool.h" @@ -11,8 +12,43 @@ #include #include -#define BEST_CNT 20 +#include "sherlock/sherlock.h" +#include "lib/fastbuf.h" +#include "images/images.h" +#include "sherlock/index.h" + +#include +#include + +#define BEST_CNT 30 +struct image_tree image_tree; + +static void +image_tree_init(void) +{ + DBG("Initializing image search structures"); + struct fastbuf *fb = bopen("index/image-tree", O_RDONLY, 1 << 16); /* FIXME: filename hack */ + image_tree.count = bgetl(fb); + image_tree.depth = bgetl(fb); + ASSERT(image_tree.count < 0x80000000 && image_tree.depth > 0 && image_tree.depth < 30); + image_tree.nodes = xmalloc((1 << image_tree.depth) * sizeof(struct image_node)); + image_tree.leaves = xmalloc(image_tree.count * sizeof(struct image_leaf)); + bread(fb, &image_tree.bbox, sizeof(struct image_bbox)); + bread(fb, image_tree.nodes + 1, ((1 << image_tree.depth) - 1) * sizeof(struct image_node)); + bread(fb, image_tree.leaves, image_tree.count * sizeof(struct image_leaf)); + DBG("Search tree with depth %d and %d leaves loaded", image_tree.depth, image_tree.count); + bclose(fb); +} + +static void +image_tree_done(void) +{ + DBG("Freeing image search structures"); + xfree(image_tree.nodes); + xfree(image_tree.leaves); +} + int main(int argc, char **argv) { @@ -45,7 +81,7 @@ main(int argc, char **argv) log(L_INFO, "No more images"); break; } - log(L_INFO, "*** Found %d. best image with oid=%d", i + 1, best[i]); + DBG("*** Found %d. best image with oid=%d", i + 1, best[i]); best_n++; } image_search_done(&is); diff --git a/images/image-thumb.c b/images/image-thumb.c deleted file mode 100644 index 6c64c0e8..00000000 --- a/images/image-thumb.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Image Library -- Thumbnails - * - * (c) 2006 Pavel Charvat - * - * This software may be freely distributed and used according to the terms - * of the GNU Lesser General Public License. - * - * FIXME: - * - support for PNG (libpng seems to be fast) - * - improve thumbnail creation in gatherer... faster compression, - * only grayscale/RGB colorspaces and maybe fixed headers (abbreviated datastreams in libjpeg) - * - hook memory allocation managers, get rid of multiple initializations - * - optimize decompression parameters - */ - -#undef LOCAL_DEBUG - -#include "sherlock/sherlock.h" -#include "lib/base224.h" -#include "lib/mempool.h" -#include "sherlock/object.h" -#include "images/images.h" -#include "images/image-thumb.h" - -#include -#include -#include - -struct hook_error_mgr { - struct jpeg_error_mgr pub; - jmp_buf setjmp_buf; -}; - -#define LONGJMP(cinfo) do{ longjmp(((struct hook_error_mgr *)(cinfo)->err)->setjmp_buf, 1); }while(0) - -static void NONRET -hook_error_exit(j_common_ptr cinfo) -{ - LONGJMP(cinfo); -} - -static void -hook_init_source(j_decompress_ptr cinfo UNUSED) -{ -} - -static boolean NONRET -hook_fill_input_buffer(j_decompress_ptr cinfo) -{ - LONGJMP(cinfo); -} - -static void -hook_skip_input_data(j_decompress_ptr cinfo, long num_bytes) -{ - if (num_bytes > 0) - { - cinfo->src->next_input_byte += (size_t) num_bytes; - cinfo->src->bytes_in_buffer -= (size_t) num_bytes; - } -} - -int -decompress_thumbnail(struct odes *obj, struct mempool *pool, struct image *image) -{ - DBG("Decompressing thumbnail from URL %s", obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U')); - - /* Find the attribute */ - struct oattr *attr = obj_find_attr(obj, 'N'); - if (!attr) - { - DBG("There is no thumbnail"); - return -1; - } - - /* Merge all instances of the attribute to continuous buffer */ - uns attr_count = 0; - for (struct oattr *a = attr; a; a = a->same) - attr_count++; - byte b224[attr_count * MAX_ATTR_SIZE], *b = b224; - for (struct oattr *a = attr; a; a = a->same) - for (byte *s = a->val; *s; ) - *b++ = *s++; - ASSERT(b != b224); - - /* Decode base-224 data */ - byte buf[b - b224]; - uns len = base224_decode(buf, b224, b - b224); - DBG("Data length is %d", len); - - /* Initialize libjpeg decompression object */ - DBG("Creating libjpeg decompression object"); - struct jpeg_decompress_struct cinfo; - struct hook_error_mgr err; - cinfo.err = jpeg_std_error(&err.pub); // FIXME: we need to hide error messages - err.pub.error_exit = hook_error_exit; - if (setjmp(err.setjmp_buf)) - { - DBG("Error during the decompression, longjump saved us"); - jpeg_destroy_decompress(&cinfo); - return -2; - } - jpeg_create_decompress(&cinfo); - - /* Initialize source manager */ - struct jpeg_source_mgr src; - cinfo.src = &src; - src.next_input_byte = buf; - src.bytes_in_buffer = len; - src.init_source = hook_init_source; - src.fill_input_buffer = hook_fill_input_buffer; - src.skip_input_data = hook_skip_input_data; - src.resync_to_restart = jpeg_resync_to_restart; - src.term_source = hook_init_source; - - /* Read JPEG header and setup decompression options */ - DBG("Reading image header"); - jpeg_read_header(&cinfo, TRUE); - image->flags = 0; - if (cinfo.out_color_space == JCS_GRAYSCALE) - image->flags |= IMAGE_GRAYSCALE; - else - cinfo.out_color_space = JCS_RGB; - - /* Decompress the image */ - DBG("Reading image data"); - jpeg_start_decompress(&cinfo); - ASSERT(sizeof(JSAMPLE) == 1 && ( - (cinfo.out_color_space == JCS_GRAYSCALE && cinfo.output_components == 1) || - (cinfo.out_color_space == JCS_RGB && cinfo.output_components == 3))); - image->width = cinfo.output_width; - image->height = cinfo.output_height; - uns scanline = cinfo.output_width * cinfo.output_components; - uns size = scanline * cinfo.output_height; - ASSERT(size); - byte *pixels = image->pixels = mp_alloc(pool, size); - while (cinfo.output_scanline < cinfo.output_height) - { - jpeg_read_scanlines(&cinfo, (JSAMPLE **)&pixels, 1); - pixels += scanline; - } - jpeg_finish_decompress(&cinfo); - - /* Destroy libjpeg object and leave */ - jpeg_destroy_decompress(&cinfo); - DBG("Thumbnail decompressed successfully"); - return 0; -} diff --git a/images/image-thumb.h b/images/image-thumb.h deleted file mode 100644 index e4b79900..00000000 --- a/images/image-thumb.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Image Library -- Thumbnails - * - * (c) 2006 Pavel Charvat - * - * This software may be freely distributed and used according to the terms - * of the GNU Lesser General Public License. - */ - -#ifndef _IMAGE_THUMB_H -#define _IMAGE_THUMB_H - -struct odes; -struct mempool; -struct image; - -int decompress_thumbnail(struct odes *obj, struct mempool *pool, struct image *image); - -#endif diff --git a/images/images.c b/images/images.c deleted file mode 100644 index 28178c56..00000000 --- a/images/images.c +++ /dev/null @@ -1,37 +0,0 @@ -#define LOCAL_DEBUG - -#include "sherlock/sherlock.h" -#include "lib/fastbuf.h" -#include "images/images.h" -#include "sherlock/index.h" - -#include -#include - -struct image_tree image_tree; - -void -image_tree_init(void) -{ - DBG("Initializing image search structures"); - struct fastbuf *fb = bopen("index/image-tree", O_RDONLY, 1 << 16); /* FIXME: filename hack */ - image_tree.count = bgetl(fb); - image_tree.depth = bgetl(fb); - ASSERT(image_tree.count < 0x80000000 && image_tree.depth > 0 && image_tree.depth < 30); - image_tree.nodes = xmalloc((1 << image_tree.depth) * sizeof(struct image_node)); - image_tree.leaves = xmalloc(image_tree.count * sizeof(struct image_leaf)); - bread(fb, &image_tree.bbox, sizeof(struct image_bbox)); - bread(fb, image_tree.nodes + 1, ((1 << image_tree.depth) - 1) * sizeof(struct image_node)); - bread(fb, image_tree.leaves, image_tree.count * sizeof(struct image_leaf)); - DBG("Search tree with depth %d and %d leaves loaded", image_tree.depth, image_tree.count); - bclose(fb); -} - -void -image_tree_done(void) -{ - DBG("Freeing image search structures"); - xfree(image_tree.nodes); - xfree(image_tree.leaves); -} - diff --git a/images/images.h b/images/images.h index 64c273d1..711210bb 100644 --- a/images/images.h +++ b/images/images.h @@ -1,84 +1,21 @@ #ifndef _IMAGES_IMAGES_H #define _IMAGES_IMAGES_H -#include -#include - enum image_flag { - IMAGE_GRAYSCALE = 0x1, /* RGB otherwise */ + IMAGE_GRAYSCALE = 0x1, /* grayscale image */ + IMAGE_ALPHA = 0x2, /* alpha present */ }; +struct pixel { + byte r, g, b, a; +} PACKED; + struct image { - uns flags; /* enum thumbnail_flags */ + uns flags; /* enum image_flag */ uns width; /* number of columns */ uns height; /* number of rows */ - byte *pixels; /* 3 bytes per pixel for RGB, 1 byte for grayscale */ -}; - -#define IMAGE_VEC_K 6 -#define IMAGE_REG_K 9 -#define IMAGE_REG_MAX 4 - -typedef u16 image_feature_t; /* 8 or 16 bits precision */ - -/* K-dimensional feature vector */ -struct image_vector { - image_feature_t f[IMAGE_VEC_K]; -}; - -/* K-dimensional interval */ -struct image_bbox { - struct image_vector vec[2]; -}; - -/* Fetures for image regions */ -struct image_region { - image_feature_t f[IMAGE_REG_K]; -}; - -/* Image signature */ -struct image_signature { - struct image_vector vec; /* Combination of all regions... simplier signature */ - image_feature_t len; /* Number of regions */ - struct image_region reg[IMAGE_REG_MAX];/* Feature vector for every region */ -}; - -/* Similarity search tree... will be changed */ -struct image_tree { - uns count; /* Number of images in the tree */ - uns depth; /* Tree depth */ - struct image_bbox bbox; /* Bounding box containing all the */ - struct image_node *nodes; /* Internal nodes */ - struct image_leaf *leaves; /* Leaves */ + struct pixel *pixels; /* RGBA */ }; -/* Internal node in the search tree */ -#define IMAGE_NODE_LEAF 0x80000000 /* Node contains pointer to leaves array */ -#define IMAGE_NODE_DIM 0xff /* Split dimension */ -struct image_node { - u32 val; -}; - -/* Leaves in the search tree */ -#define IMAGE_LEAF_LAST 0x80000000 /* Last entry in the list */ -#define IMAGE_LEAF_BITS(i) (31 / IMAGE_VEC_K) /* Number of bits for relative position in i-th dimension */ -struct image_leaf { - u32 flags; /* Relative position in bbox and last node flag */ - oid_t oid; -}; - -#define stk_print_image_vector(v) ({ struct image_vector *_v = v; \ - byte *_s = (byte *) alloca(IMAGE_VEC_K * 6), *_p = _s + sprintf(_s, "%d", _v->f[0]); \ - for (uns _i = 1; _i < IMAGE_VEC_K; _i++) _p += sprintf(_p, " %d", _v->f[_i]); _s; }) - -extern struct image_tree image_tree; - -void image_tree_init(void); -void image_tree_done(void); - -void compute_image_signature_prepare(void); -void compute_image_signature_finish(void); -int compute_image_signature(struct image *image, struct image_signature *sig); - #endif