$(o)/images/image-tool: $(o)/images/image-tool.o $(LIBIMAGES) $(LIBUCW)
$(o)/images/image-tool: LIBS+=$(LIBIMAGES_LIBS)
+TESTS+=$(o)/images/image-test.test
+$(o)/images/image-test: $(o)/images/image-test.o $(LIBIMAGES) $(LIBUCW)
+$(o)/images/image-test: LIBS+=$(LIBIMAGES_LIBS)
+$(o)/images/image-test.test: $(o)/images/image-test
+
TESTS+=$(o)/images/hilbert-test.test
$(o)/images/hilbert-test: LIBS+=-lm $(LIBSH)
$(o)/images/hilbert-test.test: $(o)/images/hilbert-test
#undef LOCAL_DEBUG
-#include "sherlock/sherlock.h"
-#include "lib/fastbuf.h"
-#include "images/images.h"
-//#include "images/image-sig.h"
-//#include "images/kd-tree.h"
-#include "sherlock/index.h"
+#include "lib/lib.h"
#include "lib/mempool.h"
-#include "sherlock/object.h"
-#include "sherlock/lizard-fb.h"
-#include <fcntl.h>
-#include <stdio.h>
+#include "images/images.h"
+#include <string.h>
-#include "sherlock/sherlock.h"
-#include "lib/fastbuf.h"
-//#include "images/images.h"
-#include "sherlock/index.h"
+static uns want_image_iface;
-#include <stdio.h>
-#include <fcntl.h>
-#include <alloca.h>
+static void
+test_image_iface(void)
+{
+ struct mempool *pool;
+ struct image_thread it;
+ struct image *i1, *i2;
+ struct image s1;
+ int i;
-#define BEST_CNT 30
+ pool = mp_new(1024);
+ image_thread_init(&it);
-struct image_tree image_tree;
+ i1 = image_new(&it, 731, 327, COLOR_SPACE_RGB, NULL);
+ ASSERT(i1);
+ ASSERT(i1->pixel_size == 3);
+ image_destroy(i1);
-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);
-}
+ i1 = image_new(&it, 2214, 0, COLOR_SPACE_RGB, NULL);
+ ASSERT(!i1);
-static void
-image_tree_done(void)
-{
- DBG("Freeing image search structures");
- xfree(image_tree.nodes);
- xfree(image_tree.leaves);
+ i1 = image_new(&it, 0xffffff, 0xffffff, COLOR_SPACE_RGB, NULL);
+ ASSERT(!i1);
+
+ i1 = image_new(&it, 370, 100, COLOR_SPACE_GRAYSCALE, pool);
+ ASSERT(i1);
+ ASSERT(i1->pixel_size == 1);
+ image_destroy(i1);
+ mp_flush(pool);
+
+ i1 = image_new(&it, 373, 101, COLOR_SPACE_RGB | IMAGE_ALIGNED, NULL);
+ ASSERT(i1);
+ ASSERT(i1->pixel_size == 4);
+ ASSERT(IMAGE_SSE_ALIGN_SIZE >= 16);
+ ASSERT(!(i1->row_size & (IMAGE_SSE_ALIGN_SIZE - 1)));
+ ASSERT(!((addr_int_t)i1->pixels & (IMAGE_SSE_ALIGN_SIZE - 1)));
+ image_destroy(i1);
+
+ i1 = image_new(&it, 283, 329, COLOR_SPACE_RGB, NULL);
+ ASSERT(i1);
+ ASSERT(i1->pixel_size == 3);
+
+ i2 = image_clone(&it, i1, COLOR_SPACE_RGB, NULL);
+ ASSERT(i2);
+ ASSERT(i2->pixel_size == 3);
+ image_destroy(i2);
+
+ i2 = image_clone(&it, i1, COLOR_SPACE_RGB | IMAGE_PIXELS_ALIGNED, NULL);
+ ASSERT(i2);
+ ASSERT(i2->pixel_size == 4);
+ image_destroy(i2);
+
+ i = image_init_subimage(&it, &s1, i1, 29, 39, 283 - 29, 100);
+ ASSERT(i);
+ image_destroy(&s1);
+
+ image_destroy(i1);
+
+ image_thread_cleanup(&it);
+ mp_delete(pool);
}
-
+
int
main(int argc, char **argv)
{
- struct image_vector query;
- if (argc != IMAGE_VEC_K + 1)
- die("Invalid number of arguments");
-
- for (uns i = 0; i < IMAGE_VEC_K; i++)
- {
- uns v;
- if (sscanf(argv[i + 1], "%d", &v) != 1)
- die("Invalid numeric format");
- query.f[i] = v;
- }
-
-
- struct image_search is;
- oid_t best[BEST_CNT];
- uns dist[BEST_CNT];
- uns cardpos[BEST_CNT];
- uns best_n = 0;
-
- image_tree_init();
- log(L_INFO, "Executing query (%s)", stk_print_image_vector(&query));
- image_search_init(&is, &image_tree, &query, IMAGE_SEARCH_DIST_UNLIMITED);
- for (uns i = 0; i < BEST_CNT; i++)
- {
- if (!image_search_next(&is, best + i, dist + i))
- {
- log(L_INFO, "No more images");
- break;
- }
- DBG("*** Found %d. best image with oid=%d", i + 1, best[i]);
- best_n++;
- }
- image_search_done(&is);
- image_tree_done();
-
- log(L_INFO, "Resolving URLs");
- struct mempool *pool = mp_new(1 << 16);
- struct buck2obj_buf *bob = buck2obj_alloc();
- struct fastbuf *fb = bopen("index/card-attrs", O_RDONLY, 1 << 10);
- for (uns i = 0; i < best_n; i++)
- {
- bsetpos(fb, best[i] * sizeof(struct card_attr));
- struct card_attr ca;
- bread(fb, &ca, sizeof(ca));
- cardpos[i] = ca.card;
- }
- bclose(fb);
- fb = bopen("index/cards", O_RDONLY, 1 << 14);
- for (uns i = 0; i < best_n; i++)
- {
- bsetpos(fb, (sh_off_t)cardpos[i] << CARD_POS_SHIFT);
- uns buck_len = bgetl(fb) - (LIZARD_COMPRESS_HEADER - 1);
- uns buck_type = bgetc(fb) + BUCKET_TYPE_PLAIN;
- mp_flush(pool);
- struct odes *obj = obj_read_bucket(bob, pool, buck_type, buck_len, fb, NULL);
-
- printf("%2d. match: dist=%-8d oid=%-8d url=%s\n", i + 1, dist[i], best[i],
- obj_find_aval(obj_find_attr(obj, 'U' + OBJ_ATTR_SON)->son, 'U'));
- }
- bclose(fb);
- buck2obj_free(bob);
- mp_delete(pool);
-
+ for (int i = 0; i < argc; i++)
+ if (!strcmp(argv[i], "image-iface"))
+ want_image_iface = 1;
+ if (want_image_iface)
+ test_image_iface();
return 0;
}
image_new(struct image_thread *it, uns cols, uns rows, uns flags, struct mempool *pool)
{
DBG("image_new(cols=%u rows=%u flags=0x%x pool=%p)", cols, rows, flags, pool);
- if (cols > IMAGE_MAX_SIZE || rows > IMAGE_MAX_SIZE)
+ flags &= IMAGE_PIXEL_FORMAT | IMAGE_SSE_ALIGNED;
+ if (unlikely(!image_dimensions_valid(cols, rows)))
{
- image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image dimension(s) too large");
+ image_thread_err_format(it, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows);
return NULL;
}
struct image *img;
- uns pixel_size, row_size, image_size, align;
+ uns pixel_size, row_size, align;
switch (flags & IMAGE_COLOR_SPACE)
{
case COLOR_SPACE_GRAYSCALE:
row_size = ALIGN(row_size, align);
u64 image_size_64 = (u64)row_size * rows;
u64 bytes_64 = image_size_64 + (sizeof(struct image) + IMAGE_SSE_ALIGN_SIZE - 1 + sizeof(uns));
- if (bytes_64 > MAX_IMAGE_BYTES)
+ if (unlikely(bytes_64 > MAX_IMAGE_BYTES))
{
image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Image does not fit in memory");
return NULL;
}
- if (!(image_size = image_size_64))
+ if (pool)
+ img = mp_alloc(pool, bytes_64);
+ else
{
- image_thread_err(it, IMAGE_ERR_INVALID_DIMENSIONS, "Zero dimension(s)");
- return NULL;
+ img = xmalloc(bytes_64);
+ flags |= IMAGE_NEED_DESTROY;
}
- img = pool ? mp_alloc(pool, (uns)bytes_64) : xmalloc((uns)bytes_64);
bzero(img, sizeof(struct image));
byte *p = (byte *)img + sizeof(struct image);
img->pixels = ALIGN_PTR(p, IMAGE_SSE_ALIGN_SIZE);
img->cols = cols;
img->rows = rows;
img->row_size = row_size;
- img->image_size = image_size;
+ img->image_size = bytes_64;
DBG("img=%p flags=0x%x pixel_size=%u row_size=%u image_size=%u pixels=%p",
img, img->flags, img->pixel_size, img->row_size, img->image_size, img->pixels);
return img;
image_destroy(struct image *img)
{
DBG("image_destroy(img=%p)", img);
- xfree(img);
+ if (img->flags & IMAGE_NEED_DESTROY)
+ xfree(img);
}
void
bzero(img->pixels, img->image_size);
}
+int
+image_init_matrix(struct image_thread *it, struct image *img, byte *pixels, uns cols, uns rows, uns row_size, uns flags)
+{
+ DBG("image_init_matrix(img=%p cols=%u rows=%u row_size=%u flags=0x%x)", img, cols, rows, row_size, uns flags);
+ if (unlikely(!image_dimensions_valid(cols, rows)))
+ {
+ image_thread_err_format(it, IMAGE_ERR_INVALID_DIMENSIONS, "Invalid image dimensions (%ux%u)", cols, rows);
+ return 0;
+ }
+ img->pixels = pixels;
+ img->cols = cols;
+ img->rows = rows;
+ img->row_size = row_size;
+ img->image_size = rows * row_size;
+ img->flags = flags & (IMAGE_PIXEL_FORMAT | IMAGE_SSE_ALIGNED);
+ return 1;
+}
+
+int
+image_init_subimage(struct image_thread *it UNUSED, struct image *img, struct image *src, uns left, uns top, uns cols, uns rows)
+{
+ DBG("image_init_subimage(img=%p src=%p left=%u top=%u cols=%u rows=%u)");
+ ASSERT(left + cols <= src->cols && top + rows <= src->rows);
+ img->pixels = src->pixels + left * src->pixel_size + top * src->row_size;
+ img->cols = cols;
+ img->rows = rows;
+ img->row_size = src->row_size;
+ img->image_size = src->row_size * rows;
+ img->flags = src->flags & ~IMAGE_NEED_DESTROY;
+ return 1;
+}
+
byte *
color_space_to_name(enum color_space cs)
{
IMAGE_ALPHA = 0x8, /* alpha channel */
IMAGE_PIXELS_ALIGNED = 0x10, /* align pixel size to the nearest power of two */
IMAGE_SSE_ALIGNED = 0x20, /* align scanlines to multiples of 16 bytes (both start and size) */
+ IMAGE_NEED_DESTROY = 0x40, /* image is allocated with xmalloc */
IMAGE_CHANNELS_FORMAT = IMAGE_COLOR_SPACE | IMAGE_ALPHA,
IMAGE_PIXEL_FORMAT = IMAGE_CHANNELS_FORMAT | IMAGE_PIXELS_ALIGNED,
IMAGE_ALIGNED = IMAGE_PIXELS_ALIGNED | IMAGE_SSE_ALIGNED,
u32 rows; /* number of rows */
u32 pixel_size; /* size of pixel (1, 2, 3 or 4) */
u32 row_size; /* scanline size in bytes */
- u32 image_size; /* size of pixels buffer (rows * rows_size) */
+ u32 image_size; /* rows * rows_size */
u32 flags; /* enum image_flag */
};
struct image *image_clone(struct image_thread *it, struct image *src, uns flags, struct mempool *pool);
void image_destroy(struct image *img); /* only with NULL mempool */
void image_clear(struct image_thread *it, struct image *img);
+int image_init_matrix(struct image_thread *it, struct image *img, byte *pixels, uns cols, uns rows, uns row_size, uns flags);
+int image_init_subimage(struct image_thread *it, struct image *img, struct image *src, uns left, uns top, uns cols, uns rows);
+
+static inline int
+image_dimensions_valid(uns cols, uns rows)
+{
+ return cols && rows && cols <= IMAGE_MAX_SIZE && rows <= IMAGE_MAX_SIZE;
+}
byte *color_space_to_name(enum color_space cs);
byte *image_channels_format_to_name(uns format);
libungif_read_func(GifFileType *gif, GifByteType *ptr, int len)
{
DBG("libungif_read_func(len=%d)", len);
- uns readed, total = 0;
- while (len && (readed = bread((struct fastbuf *)gif->UserData, (byte *)ptr, len)))
- {
- len -= readed;
- ptr += readed;
- total += readed;
- }
- return total;
+ return bread((struct fastbuf *)gif->UserData, (byte *)ptr, len);
}
static void