$(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
#include "lib/bbuf.h"
#include "images/images.h"
-#include "images/image-thumb.h"
+#include "images/image-obj.h"
+#include "images/image-sig.h"
#include <stdlib.h>
#include <fcntl.h>
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)
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));
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);
--- /dev/null
+/*
+ * Image Library -- Image Cards Manipulations
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * 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 <stdio.h>
+#include <alloca.h>
+
+/* 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 <png.h>
+#include <setjmp.h>
+
+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 <jpeglib.h>
+#include <setjmp.h>
+
+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 <magick/api.h>
+
+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);
+}
+
--- /dev/null
+/*
+ * Image Library -- Image Cards Manipulations
+ *
+ * (c) 2006 Pavel Charvat <pchar@ucw.cz>
+ *
+ * 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
#include "lib/heap.h"
+#include <alloca.h>
#define IMAGE_SEARCH_DIST_UNLIMITED (~0U)
#include "lib/math.h"
#include "lib/fastbuf.h"
#include "images/images.h"
+#include "images/image-obj.h"
+#include "images/image-sig.h"
+#include <alloca.h>
#include <magick/api.h>
/*
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);
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;
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];
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;
}
--- /dev/null
+#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
+
-#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"
#include <fcntl.h>
#include <stdio.h>
-#define BEST_CNT 20
+#include "sherlock/sherlock.h"
+#include "lib/fastbuf.h"
+#include "images/images.h"
+#include "sherlock/index.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#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)
{
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);
+++ /dev/null
-/*
- * Image Library -- Thumbnails
- *
- * (c) 2006 Pavel Charvat <pchar@ucw.cz>
- *
- * 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 <stdio.h>
-#include <jpeglib.h>
-#include <setjmp.h>
-
-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;
-}
+++ /dev/null
-/*
- * Image Library -- Thumbnails
- *
- * (c) 2006 Pavel Charvat <pchar@ucw.cz>
- *
- * 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
+++ /dev/null
-#define LOCAL_DEBUG
-
-#include "sherlock/sherlock.h"
-#include "lib/fastbuf.h"
-#include "images/images.h"
-#include "sherlock/index.h"
-
-#include <stdio.h>
-#include <fcntl.h>
-
-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);
-}
-
#ifndef _IMAGES_IMAGES_H
#define _IMAGES_IMAGES_H
-#include <alloca.h>
-#include <stdio.h>
-
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