]> mj.ucw.cz Git - libucw.git/commitdiff
- removed alpha channel
authorPavel Charvat <pavel.charvat@netcentrum.cz>
Sun, 23 Apr 2006 14:17:12 +0000 (16:17 +0200)
committerPavel Charvat <pavel.charvat@netcentrum.cz>
Sun, 23 Apr 2006 14:17:12 +0000 (16:17 +0200)
- added a simple algorithm for fast repeated image comparisions (will be
  used in duplicates finder)
- some small fixes

images/Makefile
images/image-dup.c [new file with mode: 0644]
images/image-dup.h [new file with mode: 0644]
images/image-idx.c
images/image-obj.c
images/image-obj.h
images/image-sig.c
images/images.h

index 34196a3d0eea3d14859980758e3d10d4d4466d0e..71e4c79b19810b22e8c0ee386e3e5d612565ead8 100644 (file)
@@ -7,7 +7,7 @@ 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-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: $(o)/images/image-idx.o $(o)/images/image-obj.o $(o)/images/image-dup.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)
 
diff --git a/images/image-dup.c b/images/image-dup.c
new file mode 100644 (file)
index 0000000..688ee50
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+ *      Image Library -- Duplicates Comparison
+ *
+ *      (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:
+ *      - many possible optimization
+ *      - compare normalized pictures (brightness, ...)
+ *      - better image scale... now it can completely miss some rows/cols of pixels
+ *      - maybe better/slower last step
+ *      - different thresholds for various transformations
+ *      - do not test all transformations for symetric pictures
+ *      - ... secret ideas :-)
+ */
+
+#undef LOCAL_DEBUG
+
+#include "sherlock/sherlock.h"
+#include "lib/mempool.h"
+#include "images/images.h"
+#include "images/image-dup.h"
+
+static uns image_dup_scale_min_size = 8;
+static uns image_dup_ratio_threshold = 140;
+static uns image_dup_error_threshold = 2000;
+
+static inline byte *
+image_dup_block(struct image_dup *dup, uns col, uns row)
+{
+  ASSERT(col <= dup->cols && row <= dup->rows);
+  return dup->buf + (dup->line << row) + (3 << (row + col));
+}
+
+static inline void
+pixels_average(byte *dest, byte *src1, byte *src2)
+{
+  dest[0] = ((uns)src1[0] + (uns)src2[0]) >> 1;
+  dest[1] = ((uns)src1[1] + (uns)src2[1]) >> 1;
+  dest[2] = ((uns)src1[2] + (uns)src2[2]) >> 1;
+}
+
+void
+image_dup_init(struct image_dup *dup, struct image *image, struct mempool *pool)
+{
+  ASSERT(image->width && image->height);
+  
+  dup->image = image;
+  dup->width = image->width;
+  dup->height = image->height;
+  for (dup->cols = 0; (uns)(1 << dup->cols) < image->width; dup->cols++);
+  for (dup->rows = 0; (uns)(1 << dup->rows) < image->height; dup->rows++);
+  dup->buf = mp_alloc(pool, 12 << (dup->cols + dup->rows));
+  dup->line = 6 << dup->cols;
+  dup->flags = 0;
+  if (image->width >= image_dup_scale_min_size && image->height >= image_dup_scale_min_size)
+    dup->flags |= IMAGE_DUP_FLAG_SCALE;
+  
+  /* Scale original image to right bottom block */
+  {
+    byte *d = image_dup_block(dup, dup->cols, dup->rows);
+    uns width = 1 << dup->cols;
+    uns height = 1 << dup->rows;
+    uns line_size = 3 * image->width;
+    uns src_y = 0;
+    for (uns y = 0; y < height; y++)
+      {
+       byte *line = image->pixels + line_size * (src_y >> dup->rows);
+        uns src_x = 0;
+        for (uns x = 0; x < width; x++)
+          {
+           byte *s = line + 3 * (src_x >> dup->cols);
+           d[0] = s[0];
+           d[1] = s[1];
+           d[2] = s[2];
+           d += 3;
+           src_x += image->width;
+         }
+       src_y += image->height;
+      }
+  }
+
+  /* Complete bottom row */
+  for (uns i = dup->cols; i--; )
+    {
+      byte *d = image_dup_block(dup, i, dup->rows);
+      byte *s = image_dup_block(dup, i + 1, dup->rows);
+      for (uns y = 0; y < (uns)(1 << dup->rows); y++)
+       for (uns x = 0; x < (uns)(1 << i); x++)
+         {
+           pixels_average(d, s, s + 3);
+           d += 3;
+           s += 6;
+         }
+    }
+  /* Complete remaining blocks */
+  for (uns i = 0; i <= dup->cols; i++)
+    {
+      uns line_size = (3 << i);
+      for (uns j = dup->rows; j--; )
+        {
+          byte *d = image_dup_block(dup, i, j);
+          byte *s = image_dup_block(dup, i, j + 1);
+          for (uns y = 0; y < (uns)(1 << j); y++)
+            {
+              for (uns x = 0; x < (uns)(1 << i); x++)
+                {
+                 pixels_average(d, s, s + line_size);
+                 d += 3;
+                 s += 3;
+               }
+             s += line_size;
+           }
+        }
+    }
+}
+
+static inline uns
+err (int a, int b)
+{
+  a -= b;
+  return a * a;
+}
+
+static inline uns
+err_sum(byte *pos1, byte *end1, byte *pos2)
+{
+  uns e = 0;
+  while (pos1 != end1)
+    e += err(*pos1++, *pos2++);
+  return e;
+}
+
+static inline uns
+err_sum_transformed(byte *pos1, byte *end1, byte *pos2, uns width, int add1, int add2)
+{
+  DBG("err_sum_transformed(): %p %p %p %d %d %d", pos1, end1, pos2, width, add1, add2);
+  uns e = 0;
+  while (pos1 != end1)
+    {
+      for (uns i = 0; i < width; i++, pos2 += add1)
+      {
+       e += err(pos1[0], pos2[0]);
+       e += err(pos1[1], pos2[1]);
+       e += err(pos1[2], pos2[2]);
+       pos1 += 3;
+      }
+      pos2 += add2;
+    }
+  return e;
+}
+
+static inline int
+aspect_ratio_test(uns width1, uns height1, uns width2, uns height2)
+{
+  uns r1 = width1 * height2;
+  uns r2 = height1 * width2;
+  return
+    r1 <= ((r2 * image_dup_ratio_threshold) >> 5) && 
+    r2 <= ((r1 * image_dup_ratio_threshold) >> 5);
+}
+
+static inline int
+average_compare(struct image_dup *dup1, struct image_dup *dup2)
+{
+  byte *block1 = image_dup_block(dup1, 0, 0);
+  byte *block2 = image_dup_block(dup2, 0, 0);
+  uns e =
+    err(block1[0], block2[0]) +
+    err(block1[1], block2[1]) +
+    err(block1[2], block2[2]);
+  return e <= image_dup_error_threshold;
+}
+
+static int
+blocks_compare(struct image_dup *dup1, struct image_dup *dup2, uns col, uns row, uns trans)
+{
+  DBG("blocks_compare(): col=%d row=%d trans=%d", col, row, trans);
+  byte *block1 = image_dup_block(dup1, col, row);
+  byte *block2 = (trans < 4) ? image_dup_block(dup2, col, row) : image_dup_block(dup2, row, col);
+  int add1, add2;
+  switch (trans)
+    {
+      case 0: ;
+       uns err = (err_sum(block1, block1 + (3 << (col + row)), block2) >> (col + row));
+       DBG("average error=%d", err);
+       return err <= image_dup_error_threshold;
+      case 1:
+       add1 = -3;
+       add2 = 6 << col;
+       block2 += (3 << col) - 3;
+       break;
+      case 2:
+       add1 = 1;
+       add2 = -(6 << col);
+       block2 += (3 << (col + row)) - (3 << col);
+       break;
+      case 3:
+       add1 = -3;
+       add2 = 0;
+       block2 += (3 << (col + row)) - 3;
+       break;
+      case 4:
+       add1 = (3 << col);
+       add2 = -(3 << (col + row)) + 3;
+       break;
+      case 5:
+       add1 = -(3 << col);
+       add2 = (3 << (col + row)) + 3;
+       block2 += (3 << (col + row)) - (3 << col);
+       break;
+      case 6:
+       add1 = (3 << col);
+       add2 = -(3 << (col + row)) - 3;
+       block2 += (3 << col) - 3;
+       break;
+      case 7:
+       add1 = -(3 << col);
+       add2 = (3 << (col + row)) - 3;
+       block2 += (3 << (col + row)) - 3;
+       break;
+      default:
+       ASSERT(0);
+    }
+  uns err = (err_sum_transformed(block1, block1 + (3 << (col + row)), block2, (1 << col), add1, add2) >> (col + row));
+  DBG("average error=%d", err);
+  return err <= image_dup_error_threshold;
+}
+
+static int
+same_size_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans)
+{
+  byte *block1 = dup1->image->pixels;
+  byte *block2 = dup2->image->pixels;
+  DBG("same_size_compare(): trans=%d",  trans);
+  int add1, add2;
+  switch (trans)
+    {
+      case 0: ;
+        uns err = (err_sum(block1, block1 + 3 * dup1->width * dup1->height, block2) / (dup1->width * dup1->height));
+       DBG("average error=%d", err);
+       return err <= image_dup_error_threshold;
+      case 1:
+       add1 = -3;
+       add2 = 6 * dup1->width;
+       block2 += 3 * (dup1->width - 1);
+       break;
+      case 2:
+       add1 = 1;
+       add2 = -6 * dup1->width;
+       block2 += 3 * dup1->width * (dup1->height - 1);
+       break;
+      case 3:
+       add1 = -3;
+       add2 = 0;
+       block2 += 3 * (dup1->width * dup1->height - 1);
+       break;
+      case 4:
+       add1 = 3 * dup1->width;
+       add2 = -3 * (dup1->width * dup1->height - 1);
+       break;
+      case 5:
+       add1 = -3 * dup1->width;
+       add2 = 3 * (dup1->width * dup1->height + 1);
+       block2 += 3 * dup1->width * (dup1->height - 1);
+       break;
+      case 6:
+       add1 = 3 * dup1->width;
+       add2 = -3 * (dup1->width * dup1->height + 1);
+       block2 += 3 * (dup1->width - 1);
+       break;
+      case 7:
+       add1 = -3 * dup1->width;
+       add2 = 3 * (dup1->width * dup1->height - 1);
+       block2 += 3 * (dup1->width * dup1->height - 1);
+       break;
+      default:
+       ASSERT(0);
+    }
+  uns err = (err_sum_transformed(block1, block1 + 3 * dup1->width * dup1->height, block2, dup1->width, add1, add2) / (dup1->width * dup1->height));
+  DBG("average error=%d", err);
+  return err <= image_dup_error_threshold;
+}
+
+int
+image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans)
+{
+  if (!average_compare(dup1, dup2))
+    return 0;
+  if ((dup1->flags & dup2->flags) & IMAGE_DUP_FLAG_SCALE)
+    {
+      DBG("Scale support");
+      if (!aspect_ratio_test(dup1->width, dup1->height, dup2->width, dup2->height))
+       trans &= 0xf0;
+      if (!aspect_ratio_test(dup1->width, dup1->height, dup2->height, dup2->width))
+       trans &= 0x0f;
+    }
+  else
+    {
+      DBG("No scale support");
+      if (!(dup1->width == dup2->width && dup1->height == dup2->height))
+       trans &= 0xf0;
+      if (!(dup1->width == dup2->height && dup1->height == dup2->width))
+       trans &= 0x0f;
+    }
+  if (!trans)
+    return 0;
+  if (trans & 0x0f)
+    {
+      uns cols = MIN(dup1->cols, dup2->cols);
+      uns rows = MIN(dup1->rows, dup2->rows);
+      for (uns t = 0; t < 4; t++)
+       if (trans & (1 << t))
+         {
+           DBG("Testing trans %d", t);
+            for (uns i = MAX(cols, rows); i--; )
+              {
+               uns col = MAX(0, (int)(cols - i));
+               uns row = MAX(0, (int)(rows - i));
+               if (!blocks_compare(dup1, dup2, col, row, t))
+                 break;
+               if (!i &&
+                   (dup1->width != dup2->width || dup1->height != dup2->height ||
+                   same_size_compare(dup1, dup2, t)))
+                 return 1;
+             }
+         }
+    }
+  if (trans & 0xf0)
+    {
+      uns cols = MIN(dup1->cols, dup2->rows);
+      uns rows = MIN(dup1->rows, dup2->cols);
+      for (uns t = 4; t < 8; t++)
+       if (trans & (1 << t))
+         {
+           DBG("Testing trans %d", t);
+            for (uns i = MAX(cols, rows); i--; )
+              {
+               uns col = MAX(0, (int)(cols - i));
+               uns row = MAX(0, (int)(rows - i));
+               if (!blocks_compare(dup1, dup2, col, row, t))
+                 break;
+               if (!i &&
+                   (dup1->width != dup2->height || dup1->height != dup2->width ||
+                   same_size_compare(dup1, dup2, t)) )
+                 return 1;
+             }
+         }
+    }
+  return 0;
+}
diff --git a/images/image-dup.h b/images/image-dup.h
new file mode 100644 (file)
index 0000000..524db4e
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _IMAGES_IMAGE_DUP_H
+#define _IMAGES_IMAGE_DUP_H
+
+struct image;
+
+struct image_dup {
+  struct image *image;
+  byte *buf;
+  uns flags;
+  uns cols;
+  uns rows;
+  uns line;
+  uns width;
+  uns height;
+};
+
+#define IMAGE_DUP_FLAG_SCALE   0x1
+
+#define IMAGE_DUP_TRANS_ID     0x01
+#define IMAGE_DUP_TRANS_ALL    0xff
+
+void image_dup_init(struct image_dup *dup, struct image *image, struct mempool *pool);
+int image_dup_compare(struct image_dup *dup1, struct image_dup *dup2, uns trans);
+
+#endif
index 0600fc2cc6a18904afabb6a9a98ce999e23d9718..8868dfe311cd2c3c732d3139ae5fb444d7ca9235 100644 (file)
 #include "images/images.h"
 #include "images/image-obj.h"
 #include "images/image-sig.h"
+#include "images/image-dup.h"
 
 #include <stdlib.h>
 #include <fcntl.h>
 #include <string.h>
 
 /* This should happen in gatherer or scanner */
-UNUSED static void
+static void
 generate_signatures(uns limit)
 {
   struct fastbuf *fb_cards = index_bopen("cards", O_RDONLY);
@@ -59,13 +60,13 @@ generate_signatures(uns limit)
           die("Failed to read card");
         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'));
+           byte *url = obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U');
+           DBG("Reading oid=%d url=%s", oid, url);
            struct image_obj imo;
            imo_init(&imo, pool, obj);
            if (imo_decompress_thumbnail(&imo))
              {
-               int err;
-               if (!(err = compute_image_signature(&imo.thumb, &sig)))
+               if (compute_image_signature(&imo.thumb, &sig))
                  {
                    bwrite(fb_signatures, &oid, sizeof(oid));
                    bwrite(fb_signatures, &sig.vec, sizeof(struct image_vector));
@@ -77,7 +78,7 @@ generate_signatures(uns limit)
                      break;
                  }
                else
-                 DBG("Cannot create signature, error=%d", err);
+                 DBG("Cannot create signature");
              }
            else
              DBG("Cannot decompress thumbnail");
@@ -95,25 +96,6 @@ generate_signatures(uns limit)
   bclose(fb_signatures);
 }
 
-UNUSED static void
-generate_random_signatures(uns count)
-{
-  log(L_INFO, "Generating %d random signatures", count);
-  struct fastbuf *fb_signatures = index_bopen("image-sig", O_CREAT | O_WRONLY | O_TRUNC);
-  bputl(fb_signatures, count);
-  for (uns i = 0; i < count; i++)
-    {
-      oid_t oid = i;
-      struct image_vector vec;
-      for (uns j = 0; j < IMAGE_VEC_K; j++)
-       vec.f[j] = random_max(256); 
-      bwrite(fb_signatures, &oid, sizeof(oid));
-      bwrite(fb_signatures, &vec, sizeof(vec));
-      bputc(fb_signatures, 0);
-    }
-  bclose(fb_signatures); 
-}
-
 struct signature_record {
   oid_t oid;
   struct image_vector vec;
@@ -333,7 +315,6 @@ main(int argc UNUSED, char **argv)
     usage("Invalid usage");
 
   generate_signatures(~0U);
-  //generate_random_signatures(1000000);
   build_search_tree();
   
   return 0;
index f5171d55f14bd6eafef46b0246eeb81527b07331..06753ca6e65e90135bcbb6c932d2646dbf790531 100644 (file)
@@ -10,7 +10,7 @@
  *     - 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)
+ *     - supply background color to transparent PNG images
  *     - optimize decompression parameters
  *     - create interface for thumbnail compression (for gatherer) and reading (MUX)
  *     - benchmatk libraries
@@ -145,41 +145,33 @@ libpng_decompress_thumbnail(struct image_obj *imo)
     {
       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);
+        png_set_strip_alpha(png_ptr);
        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);
+        png_set_strip_alpha(png_ptr);
        break;
       case PNG_COLOR_TYPE_GRAY_ALPHA:
-       imo->thumb.flags |= IMAGE_GRAYSCALE | IMAGE_ALPHA;
+       imo->thumb.flags |= IMAGE_GRAYSCALE;
         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;
+        png_set_strip_alpha(png_ptr);
        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);
+  ASSERT(png_get_channels(png_ptr, info_ptr) == 3);
 
   /* Read image data */
   DBG("Reading image data");
-  struct pixel *pixels = imo->thumb.pixels = mp_alloc(imo->pool, width * height * sizeof(struct pixel));
+  byte *pixels = imo->thumb.pixels = mp_alloc(imo->pool, width * height * 3);
   png_bytep rows[height];
-  for (uns i = 0; i < height; i++, pixels += width)
+  for (uns i = 0; i < height; i++, pixels += width * 3)
     rows[i] = (png_bytep)pixels;
   png_read_image(png_ptr, rows);
   png_read_end(png_ptr, end_ptr);
@@ -296,23 +288,15 @@ libjpeg_decompress_thumbnail(struct image_obj *imo)
   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));
+  ASSERT(sizeof(JSAMPLE) == 1);
+  byte *pixels = imo->thumb.pixels = mp_alloc(imo->pool, cinfo.output_width * cinfo.output_height * 3);
   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++;
-           }
+          jpeg_read_scanlines(&cinfo, (JSAMPLE **)&pixels, 1);
+         pixels += size;
         }
     }
   else
@@ -324,9 +308,8 @@ libjpeg_decompress_thumbnail(struct image_obj *imo)
           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++;
+              pixels[0] = pixels[1] = pixels[2] = p[0];
+             pixels += 3;
            }
         }
     }
@@ -386,13 +369,14 @@ magick_decompress_thumbnail(struct image_obj *imo)
   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++)
+  byte *p = imo->thumb.pixels = mp_alloc(imo->pool, size * 3);
+  for (uns i = 0; i < size; i++)
     {
-      p->r = pixels->red >> (QuantumDepth - 8);
-      p->g = pixels->green >> (QuantumDepth - 8);
-      p->b = pixels->blue >> (QuantumDepth - 8);
-      p->a = 255;
+      p[0] = pixels->red >> (QuantumDepth - 8);
+      p[1] = pixels->green >> (QuantumDepth - 8);
+      p[2] = pixels->blue >> (QuantumDepth - 8);
+      p += 3;
+      pixels++;
     }
   DestroyImage(image);
   return 1;
@@ -408,7 +392,8 @@ static int
 extract_image_info(struct image_obj *imo)
 {
   DBG("Parsing image info attribute");
-  imo->flags |= IMAGE_OBJ_INFO;
+  ASSERT(!(imo->flags & IMAGE_OBJ_VALID_INFO));
+  imo->flags |= IMAGE_OBJ_VALID_INFO;
   byte *info = obj_find_aval(imo->obj, 'G');
   if (!info)
     {
@@ -419,8 +404,17 @@ extract_image_info(struct image_obj *imo)
   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;
+  switch (*thumb_format)
+    {
+      case 'j':
+       imo->thumb_format = IMAGE_OBJ_FORMAT_JPEG;
+       break;
+      case 'p':
+       imo->thumb_format = IMAGE_OBJ_FORMAT_PNG;
+       break;
+      default:
+       ASSERT(0);
+    }
   return 1;
 }
 
@@ -428,7 +422,9 @@ static int
 extract_thumb_data(struct image_obj *imo)
 {
   DBG("Extracting thumbnail data");
-  imo->flags |= IMAGE_OBJ_THUMB_DATA;
+  ASSERT(!(imo->flags & IMAGE_OBJ_VALID_DATA) && 
+      (imo->flags & IMAGE_OBJ_VALID_INFO));
+  imo->flags |= IMAGE_OBJ_VALID_DATA;
   struct oattr *attr = obj_find_attr(imo->obj, 'N');
   if (!attr)
     {
@@ -454,31 +450,32 @@ 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)
+  ASSERT(!(imo->flags & IMAGE_OBJ_VALID_IMAGE) &&
+      (imo->flags & IMAGE_OBJ_VALID_INFO) &&
+      (imo->flags & IMAGE_OBJ_VALID_DATA));
+  imo->flags |= IMAGE_OBJ_VALID_IMAGE;
+  switch (imo->thumb_format)
     {
+      case IMAGE_OBJ_FORMAT_JPEG:
 #if defined(USE_LIBJPEG)
-      return libjpeg_decompress_thumbnail(imo);
+        return libjpeg_decompress_thumbnail(imo);
 #elif defined(USE_MAGICK)
-      return magick_decompress_thumbnail(imo);
+        return magick_decompress_thumbnail(imo);
 #else
-      DBG("JPEG not supported");
-      return 0;
-#endif      
-    }
-  /* PNG format */
-  else
-    {
+        DBG("JPEG not supported");
+        return 0;
+#endif
+      case IMAGE_OBJ_FORMAT_PNG:
 #if defined(USE_LIBPNG)
-      return libpng_decompress_thumbnail(imo);
+        return libpng_decompress_thumbnail(imo);
 #elif defined(USE_MAGICK)
-      return magick_decompress_thumbnail(imo);
+        return magick_decompress_thumbnail(imo);
 #else
-      DBG("PNG not supported");
-      return 0;
+        DBG("PNG not supported");
+        return 0;
 #endif      
+      default:
+       ASSERT(0);
     }
 }
 
index bce83591e69f3eab4be45e83c76a9ac2b37ac8b1..95976bc8ce85412b3f2644486b1f093e3cfff81f 100644 (file)
 struct mempool;
 struct odes;
 
+enum image_obj_format {
+  IMAGE_OBJ_FORMAT_JPEG,
+  IMAGE_OBJ_FORMAT_PNG
+};
+
 enum image_obj_flag {
-  IMAGE_OBJ_INFO = 0x1,
-  IMAGE_OBJ_THUMB_JPEG = 0x2,
-  IMAGE_OBJ_THUMB_DATA = 0x4,
-  IMAGE_OBJ_THUMB_IMAGE = 0x8
+  IMAGE_OBJ_VALID_INFO = 0x1,
+  IMAGE_OBJ_VALID_DATA = 0x2,
+  IMAGE_OBJ_VALID_IMAGE = 0x4
 };
 
 struct image_obj {
@@ -28,6 +32,7 @@ struct image_obj {
   uns flags;
   uns width;
   uns height;
+  uns thumb_format;
   byte *thumb_data;
   uns thumb_size;
   struct image thumb;
index cc159fce6a02fe3fe14a4c116c8050699b173245..fab1de3970056c20490da634c5cc270678cc7940 100644 (file)
@@ -81,9 +81,9 @@ compute_image_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) */
-  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++)
+  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;
 
@@ -94,13 +94,13 @@ compute_image_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 += width - 4)
-         for (uns x = 0; x < 4; x++, p += 1)
+       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->r / 255.;
-             rgb[1] = p->g / 255.;
-             rgb[2] = p->b / 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];
index 711210bb2e8bfbc24270aff90a83f4f2887b8ffa..4f2bd2aa562dd35b2b7d403e781d38d29983cb51 100644 (file)
@@ -3,18 +3,13 @@
 
 enum image_flag {
   IMAGE_GRAYSCALE = 0x1,       /* grayscale image */
-  IMAGE_ALPHA = 0x2,           /* alpha present */
 };
 
-struct pixel {
-  byte r, g, b, a;
-} PACKED;
-
 struct image {
   uns flags;                   /* enum image_flag */
   uns width;                   /* number of columns */
   uns height;                  /* number of rows */
-  struct pixel *pixels;                /* RGBA */
+  byte *pixels;                        /* RGB */
 };
 
 #endif