Workshop o mikrokontrolérech na SKSP 2024.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
9.5 KiB

3 months ago
/*
* Image Library -- Image segmentation
*
* (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.
*/
#undef LOCAL_DEBUG
#include <ucw/lib.h>
#include <ucw/conf.h>
#include <ucw/heap.h>
#include <images/images.h>
#include <images/signature.h>
#include <images/math.h>
#include <string.h>
#ifdef LOCAL_DEBUG
static void
dump_segmentation(struct image_sig_region *regions, uint regions_count)
{
uint cols = 0, rows = 0;
for (uint i = 0; i < regions_count; i++)
for (struct image_sig_block *b = regions[i].blocks; b; b = b->next)
{
cols = MAX(cols, b->x + 1);
rows = MAX(rows, b->y + 1);
}
uint size = (cols + 1) * rows;
byte buf[size];
bzero(buf, size);
for (uint i = 0; i < regions_count; i++)
{
byte c = (i < 10) ? '0' + i : 'A' - 10 + i;
for (struct image_sig_block *b = regions[i].blocks; b; b = b->next)
buf[b->x + b->y * (cols + 1)] = c;
}
for (uint i = 0; i < rows; i++)
log(L_DEBUG, "%s", &buf[i * (cols + 1)]);
}
#endif
/* Pre-quantization - recursively split groups of blocks with large error */
static inline void
prequant_init_region(struct image_sig_region *region)
{
bzero(region, sizeof(*region));
}
static inline void
prequant_add_block(struct image_sig_region *region, struct image_sig_block *block)
{
block->next = region->blocks;
region->blocks = block;
region->count++;
for (uint i = 0; i < IMAGE_VEC_F; i++)
{
region->b[i] += block->v[i];
region->c[i] += isqr(block->v[i]);
}
}
static void
prequant_finish_region(struct image_sig_region *region)
{
if (region->count < 2)
{
region->e = 0;
}
else
{
u64 a = 0;
region->e = 0;
for (uint i = 0; i < IMAGE_VEC_F; i++)
{
region->e += region->c[i];
a += (u64)region->b[i] * region->b[i];
}
region->e -= a / region->count;
DBG("Finished region %u", (uint)region->e / region->count);
}
}
static inline uint
prequant_heap_cmp(struct image_sig_region *a, struct image_sig_region *b)
{
return a->e > b->e;
}
#define ASORT_PREFIX(x) prequant_##x
#define ASORT_KEY_TYPE uint
#include <ucw/sorter/array-simple.h>
static uint
prequant(struct image_sig_block *blocks, uint blocks_count, struct image_sig_region *regions)
{
DBG("Starting pre-quantization");
uint regions_count, heap_count, axis;
struct image_sig_block *blocks_end = blocks + blocks_count, *block, *block2;
struct image_sig_region *heap[IMAGE_REG_MAX + 1], *region, *region2;
/* Initialize single region with all blocks */
regions_count = heap_count = 1;
heap[1] = regions;
prequant_init_region(regions);
for (block = blocks; block != blocks_end; block++)
prequant_add_block(regions, block);
prequant_finish_region(regions);
/* Main cycle */
while (regions_count < IMAGE_REG_MAX &&
regions_count <= DARY_LEN(image_sig_prequant_thresholds) && heap_count)
{
region = heap[1];
DBG("Step... regions_count=%u heap_count=%u region->count=%u, region->e=%u",
regions_count, heap_count, region->count, (uint)region->e);
if (region->count < 2 ||
region->e < image_sig_prequant_thresholds[regions_count - 1] * blocks_count)
{
HEAP_DELETE_MIN(struct image_sig_region *, heap, heap_count, prequant_heap_cmp, HEAP_SWAP);
continue;
}
/* Select axis to split - the one with maximum average quadratic error */
axis = 0;
u64 cov = (u64)region->count * region->c[0] - (u64)region->b[0] * region->b[0];
for (uint i = 1; i < 6; i++)
{
uint j = (u64)region->count * region->c[i] - (u64)region->b[i] * region->b[i];
if (j > cov)
{
axis = i;
cov = j;
}
}
DBG("Splitting axis %u with average quadratic error %u", axis, (uint)(cov / (region->count * region->count)));
/* Sort values on the split axis */
uint val[256], cnt[256], cval;
if (region->count > 64)
{
bzero(cnt, sizeof(cnt));
for (block = region->blocks; block; block = block->next)
cnt[block->v[axis]]++;
cval = 0;
for (uint i = 0; i < 256; i++)
if (cnt[i])
{
val[cval] = i;
cnt[cval] = cnt[i];
cval++;
}
}
else
{
block = region->blocks;
for (uint i = 0; i < region->count; i++, block = block->next)
val[i] = block->v[axis];
prequant_sort(val, region->count);
cval = 1;
cnt[0] = 1;
for (uint i = 1; i < region->count; i++)
if (val[i] == val[cval - 1])
cnt[cval - 1]++;
else
{
val[cval] = val[i];
cnt[cval] = 1;
cval++;
}
}
/* Select split value - to minimize error */
uint b1 = val[0] * cnt[0];
uint c1 = isqr(val[0]) * cnt[0];
uint b2 = region->b[axis] - b1;
uint c2 = region->c[axis] - c1;
uint i = cnt[0], j = region->count - cnt[0];
u64 best_err = c1 - (u64)b1 * b1 / i + c2 - (u64)b2 * b2 / j;
uint split_val = val[0];
for (uint k = 1; k < cval - 1; k++)
{
uint b0 = val[k] * cnt[k];
uint c0 = isqr(val[k]) * cnt[k];
b1 += b0;
b2 -= b0;
c1 += c0;
c2 -= c0;
i += cnt[k];
j -= cnt[k];
u64 err = (u64)c1 - (u64)b1 * b1 / i + (u64)c2 - (u64)b2 * b2 / j;
if (err < best_err)
{
best_err = err;
split_val = val[k];
}
}
DBG("split_val=%u best_err=%llu b[axis]=%u c[axis]=%u", split_val, (long long)best_err, region->b[axis], region->c[axis]);
/* Split region */
block = region->blocks;
region2 = regions + regions_count++;
prequant_init_region(region);
prequant_init_region(region2);
while (block)
{
block2 = block->next;
if (block->v[axis] <= split_val)
prequant_add_block(region, block);
else
prequant_add_block(region2, block);
block = block2;
}
prequant_finish_region(region);
prequant_finish_region(region2);
HEAP_INCREASE(struct image_sig_region *, heap, heap_count, prequant_heap_cmp, HEAP_SWAP, 1, region);
HEAP_INSERT(struct image_sig_region *, heap, heap_count, prequant_heap_cmp, HEAP_SWAP, region2);
}
DBG("Pre-quantized to %u regions", regions_count);
return regions_count;
}
/* Post-quantization - run a few K-mean iterations to improve pre-quantized regions */
static uint
postquant(struct image_sig_block *blocks, uint blocks_count, struct image_sig_region *regions, uint regions_count)
{
DBG("Starting post-quantization");
struct image_sig_block *blocks_end = blocks + blocks_count, *block;
struct image_sig_region *regions_end = regions + regions_count, *region;
uint error = 0, last_error;
/* Initialize regions and initial segmentation error */
for (region = regions; region != regions_end; )
{
uint inv = 0xffffffffU / region->count;
for (uint i = 0; i < IMAGE_VEC_F; i++)
{
region->a[i] = ((u64)region->b[i] * inv) >> 32;
error += region->c[i] - region->a[i] * region->b[i];
}
region++;
}
/* Convergation cycle */
for (uint step = 0; step < image_sig_postquant_max_steps; step++)
{
DBG("Step...");
/* Clear regions */
for (region = regions; region != regions_end; region++)
{
region->blocks = NULL;
region->count = 0;
bzero(region->b, sizeof(region->b));
bzero(region->c, sizeof(region->c));
}
/* Assign each block to its nearest pivot and accumulate region variables */
for (block = blocks; block != blocks_end; block++)
{
struct image_sig_region *best_region = NULL;
uint best_dist = ~0U;
for (region = regions; region != regions_end; region++)
{
uint dist =
isqr(block->v[0] - region->a[0]) +
isqr(block->v[1] - region->a[1]) +
isqr(block->v[2] - region->a[2]) +
isqr(block->v[3] - region->a[3]) +
isqr(block->v[4] - region->a[4]) +
isqr(block->v[5] - region->a[5]);
if (dist <= best_dist)
{
best_dist = dist;
best_region = region;
}
}
region = best_region;
region->count++;
block->next = region->blocks;
region->blocks = block;
for (uint i = 0; i < IMAGE_VEC_F; i++)
{
region->b[i] += block->v[i];
region->c[i] += isqr(block->v[i]);
}
}
/* Finish regions, delete empty ones (should appear rarely), compute segmentation error */
last_error = error;
error = 0;
for (region = regions; region != regions_end; )
if (region->count)
{
uint inv = 0xffffffffU / region->count;
for (uint i = 0; i < IMAGE_VEC_F; i++)
{
region->a[i] = ((u64)region->b[i] * inv) >> 32;
error += region->c[i] - region->a[i] * region->b[i];
}
region++;
}
else
{
regions_end--;
*region = *regions_end;
}
DBG("last_error=%u error=%u", last_error, error);
/* Convergation criteria */
if (step >= image_sig_postquant_min_steps)
{
if (error > last_error)
break;
u64 dif = last_error - error;
if (dif * image_sig_postquant_threshold < (u64)last_error * 100)
break;
}
}
DBG("Post-quantized to %u regions with average square error %u", regions_end - regions, error / blocks_count);
return regions_end - regions;
}
void
image_sig_segmentation(struct image_sig_data *data)
{
data->regions_count = prequant(data->blocks, data->blocks_count, data->regions);
#ifdef LOCAL_DEBUG
dump_segmentation(data->regions, data->regions_count);
#endif
data->regions_count = postquant(data->blocks, data->blocks_count, data->regions, data->regions_count);
#ifdef LOCAL_DEBUG
dump_segmentation(data->regions, data->regions_count);
#endif
}