]> mj.ucw.cz Git - libucw.git/commitdiff
LIBPNG support for reading of PNG thumbnails (probably faster
authorPavel Charvat <pavel.charvat@netcentrum.cz>
Mon, 17 Apr 2006 22:12:04 +0000 (00:12 +0200)
committerPavel Charvat <pavel.charvat@netcentrum.cz>
Mon, 17 Apr 2006 22:12:04 +0000 (00:12 +0200)
than GraphicsMagick... going to make some benchmarks)
Some routines moved/renamed... and many cosmetic changes

12 files changed:
images/Makefile
images/image-idx.c
images/image-obj.c [new file with mode: 0644]
images/image-obj.h [new file with mode: 0644]
images/image-search.h
images/image-sig.c
images/image-sig.h [new file with mode: 0644]
images/image-test.c
images/image-thumb.c [deleted file]
images/image-thumb.h [deleted file]
images/images.c [deleted file]
images/images.h

index cd3af5ff51a4e56e63423dd595167d898011362d..34196a3d0eea3d14859980758e3d10d4d4466d0e 100644 (file)
@@ -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
index 058c7774e92c4d0c4b7db44a4fcb8ac9a2db78d0..0600fc2cc6a18904afabb6a9a98ce999e23d9718 100644 (file)
@@ -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 <stdlib.h>
 #include <fcntl.h>
@@ -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 (file)
index 0000000..f5171d5
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ *     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);
+}
+
diff --git a/images/image-obj.h b/images/image-obj.h
new file mode 100644 (file)
index 0000000..bce8359
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *     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
index 25d34171832af8017e05164e3d5f6461f0e6593a..4907545e8709ea5fb915c4297975e811514b4c63 100644 (file)
@@ -1,4 +1,5 @@
 #include "lib/heap.h"
+#include <alloca.h>
 
 #define IMAGE_SEARCH_DIST_UNLIMITED    (~0U)
 
index c240bd3ab81b21f55a1094247a911f8cececa5d2..cc159fce6a02fe3fe14a4c116c8050699b173245 100644 (file)
@@ -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 <alloca.h>
 #include <magick/api.h>
 
 /*
@@ -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 (file)
index 0000000..96e2e25
--- /dev/null
@@ -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
+
index 979a6a098ff966738f2347cd53520c6fbfe2bb68..69820fd4a60d106a299bc0be9175e023a644695a 100644 (file)
@@ -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"
 #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)
 {
@@ -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 (file)
index 6c64c0e..0000000
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- *     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;
-}
diff --git a/images/image-thumb.h b/images/image-thumb.h
deleted file mode 100644 (file)
index e4b7990..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- *     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
diff --git a/images/images.c b/images/images.c
deleted file mode 100644 (file)
index 28178c5..0000000
+++ /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 <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);
-}
-
index 64c273d15b4b5a0a9fb832a6be379a9ed215174c..711210bb2e8bfbc24270aff90a83f4f2887b8ffa 100644 (file)
@@ -1,84 +1,21 @@
 #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