]> mj.ucw.cz Git - libucw.git/commitdiff
First try of libjpeg... much faster than graphicsmagick
authorPavel Charvat <pavel.charvat@netcentrum.cz>
Wed, 12 Apr 2006 20:34:57 +0000 (22:34 +0200)
committerPavel Charvat <pavel.charvat@netcentrum.cz>
Wed, 12 Apr 2006 20:34:57 +0000 (22:34 +0200)
images/Makefile
images/image-idx.c
images/image-sig.c
images/image-thumb.c [new file with mode: 0644]
images/image-thumb.h [new file with mode: 0644]
images/images.h

index e19e8343a4aafcbace484e823de861e92c2e808d..de77b74514fc63cd0a8172a995bb9ba1eb0af661 100644 (file)
@@ -6,7 +6,7 @@ PROGS+=$(addprefix $(o)/images/,image-idx image-test)
 
 $(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)/indexer/iconfig.o $(o)/images/image-sig.o $(LIBSH) $(LIBLANG) $(LIBCHARSET)
-$(o)/images/image-idx: LIBS+=-lGraphicsMagick
+$(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)
 
index 074b60bceecf2bfebe12ee5f5350781fab2329ab..058c7774e92c4d0c4b7db44a4fcb8ac9a2db78d0 100644 (file)
@@ -22,6 +22,7 @@
 #include "lib/bbuf.h"
 
 #include "images/images.h"
+#include "images/image-thumb.h"
 
 #include <stdlib.h>
 #include <fcntl.h>
@@ -58,7 +59,7 @@ 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;
+           /*bb_t buf;
            uns buf_len = 0;
            bb_init(&buf);
            for (; attr; attr = attr->same)
@@ -69,24 +70,27 @@ generate_signatures(uns limit)
                buf_len += len;
              }
            byte thumb[buf_len];
-           uns thumb_len = base224_decode(thumb, buf.ptr, buf_len);
-          
-           int err = compute_image_signature(thumb, thumb_len, &sig);
-           if (!err)
+           uns thumb_len = base224_decode(thumb, buf.ptr, buf_len);*/
+           struct image thumb;
+           int err;
+           if (!(err = decompress_thumbnail(obj, pool, &thumb)))
              {
-               bwrite(fb_signatures, &oid, sizeof(oid));
-               bwrite(fb_signatures, &sig.vec, sizeof(struct image_vector));
-               bputc(fb_signatures, sig.len);
-               if (sig.len)
-                 bwrite(fb_signatures, sig.reg, sig.len * sizeof(struct image_region));
-               count++;
-               if (count >= limit)
-                 break;
+               if (!(err = compute_image_signature(&thumb, &sig)))
+                 {
+                   bwrite(fb_signatures, &oid, sizeof(oid));
+                   bwrite(fb_signatures, &sig.vec, sizeof(struct image_vector));
+                   bputc(fb_signatures, sig.len);
+                   if (sig.len)
+                     bwrite(fb_signatures, sig.reg, sig.len * sizeof(struct image_region));
+                   count++;
+                   if (count >= limit)
+                     break;
+                 }
+               else
+                 DBG("Cannot create signature, error=%d", err);
              }
            else
-             DBG("Cannot create signature, error=%d", err);
-
-           bb_done(&buf);
+             DBG("Cannot decompress thumbnail, error=%d", err);
          }
       }
   brewind(fb_signatures);
index a32b8016f2ed770a50c623e4959e84d15f8ae4ad..c240bd3ab81b21f55a1094247a911f8cececa5d2 100644 (file)
@@ -60,8 +60,11 @@ struct block {
 };
 
 static void
-compute_image_area_signature(PixelPacket *pixels, uns width, uns height, struct image_signature *sig)
+compute_image_area_signature(struct image *image, struct image_signature *sig)
 {
+  uns width = image->width;
+  uns height = image->height;
+  
   ASSERT(width >= 4 && height >= 4);
 
   uns w = width >> 2;
@@ -71,9 +74,9 @@ compute_image_area_signature(PixelPacket *pixels, uns width, uns height, struct
   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) */
-  PixelPacket *p = 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++)
+  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++)
       {
         int t[16], s[16], *tp = t;
 
@@ -84,13 +87,13 @@ compute_image_area_signature(PixelPacket *pixels, uns width, uns height, struct
        uns l_sum = 0;
        uns u_sum = 0;
        uns v_sum = 0;
-       for (uns y = 0; y < 4; y++, p += width - 4)
-         for (uns x = 0; x < 4; x++, p++)
+       for (uns y = 0; y < 4; y++, p += 3 * (width - 4))
+         for (uns x = 0; x < 4; x++, p += 3)
            {
              double rgb[3], luv[3], xyz[3];
-             rgb[0] = (p->red >> (QuantumDepth - 8)) / 255.;
-             rgb[1] = (p->green >> (QuantumDepth - 8)) / 255.;
-             rgb[2] = (p->blue >> (QuantumDepth - 8)) / 255.;
+             rgb[0] = p[0] / 255.;
+             rgb[1] = p[1] / 255.;
+             rgb[2] = p[2] / 255.;
              srgb_to_xyz_slow(rgb, xyz);
              xyz_to_luv_slow(xyz, luv);
              l_sum += *tp++ = luv[0];
@@ -199,8 +202,13 @@ compute_image_signature_finish(void)
 }
 
 int
-compute_image_signature(void *data, uns len, struct image_signature *sig)
+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");
@@ -215,5 +223,6 @@ compute_image_signature(void *data, uns len, struct image_signature *sig)
   compute_image_area_signature(pixels, image->columns, image->rows, sig);
   DestroyImage(image);
   return 0;
+#endif  
 }
 
diff --git a/images/image-thumb.c b/images/image-thumb.c
new file mode 100644 (file)
index 0000000..6c64c0e
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ *     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
new file mode 100644 (file)
index 0000000..e4b7990
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ *     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
index 763006d9d4cc200158d192a6810f82bfda412480..64c273d15b4b5a0a9fb832a6be379a9ed215174c 100644 (file)
@@ -4,6 +4,17 @@
 #include <alloca.h>
 #include <stdio.h>
 
+enum image_flag {
+  IMAGE_GRAYSCALE = 0x1,       /* RGB otherwise */
+};
+
+struct image {
+  uns flags;                   /* enum thumbnail_flags */
+  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
@@ -67,7 +78,7 @@ void image_tree_done(void);
 
 void compute_image_signature_prepare(void);
 void compute_image_signature_finish(void);
-int compute_image_signature(void *data, uns len, struct image_signature *sig);
+int compute_image_signature(struct image *image, struct image_signature *sig);
 
 #endif