mirror of https://github.com/GNOME/gimp.git
applied patch from Gerald Friedland and Tobias Lenz. Introduces a hash
2005-09-25 Sven Neumann <sven@gimp.org> * app/base/siox.[ch]: applied patch from Gerald Friedland and Tobias Lenz. Introduces a hash table to speed up the algorithm and changes the post-processing so that multiple disjunct objects can be selected. * app/tools/gimpforegroundselecttool.c: changed tooltip, the tool doesn't any longer extract single objects only.
This commit is contained in:
parent
3d0adbf117
commit
d43c5c0774
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2005-09-25 Sven Neumann <sven@gimp.org>
|
||||
|
||||
* app/base/siox.[ch]: applied patch from Gerald Friedland and
|
||||
Tobias Lenz. Introduces a hash table to speed up the algorithm
|
||||
and changes the post-processing so that multiple disjunct objects
|
||||
can be selected.
|
||||
|
||||
* app/tools/gimpforegroundselecttool.c: changed tooltip, the tool
|
||||
doesn't any longer extract single objects only.
|
||||
|
||||
2005-09-25 Sven Neumann <sven@gimp.org>
|
||||
|
||||
* libgimpwidgets/gimpwidgetsenums.[ch]: removed GimpZoomWidgetType.
|
||||
|
|
557
app/base/siox.c
557
app/base/siox.c
|
@ -14,7 +14,8 @@
|
|||
* Algorithm idea by Gerald Friedland.
|
||||
* This implementation is Copyright (C) 2005
|
||||
* by Gerald Friedland <fland@inf.fu-berlin.de>
|
||||
* and Kristian Jantz <jantz@inf.fu-berlin.de>.
|
||||
* and Kristian Jantz <jantz@inf.fu-berlin.de>
|
||||
* and Tobias Lenz <tlenz@inf.fu-berlin.de>.
|
||||
*
|
||||
* Adapted for GIMP by Sven Neumann <sven@gimp.org>
|
||||
*
|
||||
|
@ -62,9 +63,23 @@
|
|||
#define SIOX_HIGH 254
|
||||
|
||||
|
||||
/* FIXME: turn this into an enum */
|
||||
#define SIOX_DRB_ADD 0
|
||||
#define SIOX_DRB_SUBTRACT 1
|
||||
|
||||
|
||||
/* #define SIOX_DEBUG */
|
||||
|
||||
|
||||
/* A struct that holds the classification result */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gfloat bgdist;
|
||||
gfloat fgdist;
|
||||
} classresult;
|
||||
|
||||
|
||||
/* Simulate a java.util.ArrayList */
|
||||
|
||||
/* Could be improved. At the moment we are wasting a node per list and
|
||||
|
@ -308,7 +323,7 @@ stageone (lab *points,
|
|||
|
||||
/* Stage two of modified KD-Tree algorithm */
|
||||
|
||||
/* This is very similar to stageone... but in future there will be more
|
||||
/* This is very similar to stageone... but in future there may be more
|
||||
* differences => not integrated into method stageone()
|
||||
*/
|
||||
static void
|
||||
|
@ -356,7 +371,7 @@ stagetwo (lab *points,
|
|||
gfloat pivot = (min + max) / 2.0;
|
||||
|
||||
#ifdef SIOX_DEBUG
|
||||
g_printerr ("max=%f min=%f pivot=%f\n", max, min, pivot);
|
||||
g_printerr ("siox.c: max=%f min=%f pivot=%f\n", max, min, pivot);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
|
@ -418,8 +433,8 @@ stagetwo (lab *points,
|
|||
point->b /= length;
|
||||
|
||||
#ifdef SIOX_DEBUG
|
||||
g_printerr ("cluster=%f, %f, %f sum=%d\n",
|
||||
point->l, point->a, point->b, sum);
|
||||
g_printerr ("siox.c: cluster=%f, %f, %f sum=%d\n",
|
||||
point->l, point->a, point->b, sum);
|
||||
#endif
|
||||
|
||||
add_to_list (clusters, point, 1, TRUE);
|
||||
|
@ -500,7 +515,7 @@ create_signature (lab *input,
|
|||
}
|
||||
|
||||
#ifdef SIOX_DEBUG
|
||||
g_printerr ("step #1 -> %d clusters\n", size);
|
||||
g_printerr ("siox.c: step #1 -> %d clusters\n", size);
|
||||
#endif
|
||||
|
||||
free_list (clusters);
|
||||
|
@ -516,7 +531,7 @@ create_signature (lab *input,
|
|||
free_list (clusters);
|
||||
|
||||
#ifdef SIOX_DEBUG
|
||||
g_printerr ("step #2 -> %d clusters\n", returnlength[0]);
|
||||
g_printerr ("siox.c: step #2 -> %d clusters\n", returnlength[0]);
|
||||
#endif
|
||||
|
||||
return rval;
|
||||
|
@ -565,10 +580,16 @@ dilate_mask (TileManager *mask,
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
/* Do not change these defines! They contain some magic!
|
||||
* Must all be non-zero and FINAL must be 0xFF!
|
||||
*/
|
||||
#define FIND_BLOB_SELECTED 0x1
|
||||
#define FIND_BLOB_FORCEFG 0x3
|
||||
#define FIND_BLOB_VISITED 0x7
|
||||
#define FIND_BLOB_FINAL 0xFF
|
||||
|
||||
static inline void
|
||||
threshold_mask (TileManager *mask,
|
||||
guchar maskval,
|
||||
guchar newval,
|
||||
gint x,
|
||||
gint y,
|
||||
gint width,
|
||||
|
@ -581,24 +602,37 @@ threshold_mask (TileManager *mask,
|
|||
pixel_region_init (®ion, mask, x, y, width, height, TRUE);
|
||||
|
||||
for (pr = pixel_regions_register (1, ®ion);
|
||||
pr != NULL;
|
||||
pr = pixel_regions_process (pr))
|
||||
pr != NULL; pr = pixel_regions_process (pr))
|
||||
{
|
||||
guchar *data = region.data;
|
||||
|
||||
for (row = 0; row < region.h; row++)
|
||||
{
|
||||
guchar *d = data;
|
||||
guchar *d = data;
|
||||
|
||||
/* everything that fits the mask is in the image*/
|
||||
/* everything that fits the mask is in the image */
|
||||
for (col = 0; col < region.w; col++, d++)
|
||||
*d = (*d & maskval) ? newval: 0;
|
||||
{
|
||||
if (*d > SIOX_HIGH)
|
||||
*d = FIND_BLOB_FORCEFG;
|
||||
else if (*d >= 0x80)
|
||||
*d = FIND_BLOB_SELECTED;
|
||||
else
|
||||
*d = 0;
|
||||
}
|
||||
|
||||
data += region.rowstride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct blob
|
||||
{
|
||||
gint seedx, seedy;
|
||||
gint size;
|
||||
gboolean mustkeep;
|
||||
};
|
||||
|
||||
/* This method checks out the neighbourhood of the pixel at position
|
||||
* (pos_x,pos_y) in the TileManager mask, it adds the sourrounding
|
||||
* pixels to the queue to allow further processing it uses maskVal to
|
||||
|
@ -606,103 +640,60 @@ threshold_mask (TileManager *mask,
|
|||
* are passed from above.
|
||||
*/
|
||||
static void
|
||||
process_neighbours (GQueue *q,
|
||||
gint pos_x,
|
||||
gint pos_y,
|
||||
TileManager *mask,
|
||||
guchar maskval,
|
||||
gint x,
|
||||
gint y,
|
||||
gint width,
|
||||
gint height)
|
||||
depth_first_search (TileManager *mask,
|
||||
gint x,
|
||||
gint y,
|
||||
gint xwidth,
|
||||
gint yheight,
|
||||
struct blob *b,
|
||||
guchar mark)
|
||||
{
|
||||
gint xx, yy;
|
||||
guchar val;
|
||||
|
||||
if (pos_x + 1 < x + width)
|
||||
GSList *stack =
|
||||
g_slist_prepend (g_slist_prepend (NULL, GINT_TO_POINTER (b->seedy)),
|
||||
GINT_TO_POINTER (b->seedx));
|
||||
|
||||
while (stack != NULL)
|
||||
{
|
||||
read_pixel_data_1 (mask, pos_x + 1, pos_y, &val);
|
||||
xx = GPOINTER_TO_INT (stack->data);
|
||||
stack = g_slist_delete_link (stack, stack);
|
||||
yy = GPOINTER_TO_INT (stack->data);
|
||||
stack = g_slist_delete_link (stack, stack);
|
||||
|
||||
if (!(val & maskval))
|
||||
{
|
||||
g_queue_push_tail (q, GINT_TO_POINTER (pos_x + 1));
|
||||
g_queue_push_tail (q, GINT_TO_POINTER (pos_y));
|
||||
}
|
||||
}
|
||||
|
||||
if (pos_x > x)
|
||||
{
|
||||
read_pixel_data_1 (mask, pos_x - 1, pos_y, &val);
|
||||
|
||||
if (!(val & maskval))
|
||||
read_pixel_data_1 (mask, xx, yy, &val);
|
||||
if (val && (val != mark))
|
||||
{
|
||||
g_queue_push_tail (q, GINT_TO_POINTER (pos_x - 1));
|
||||
g_queue_push_tail (q, GINT_TO_POINTER (pos_y));
|
||||
if (mark == FIND_BLOB_VISITED)
|
||||
{
|
||||
++(b->size);
|
||||
if (val == FIND_BLOB_FORCEFG)
|
||||
b->mustkeep = TRUE;
|
||||
}
|
||||
|
||||
write_pixel_data_1 (mask, xx, yy, &mark);
|
||||
|
||||
if (xx > x)
|
||||
stack =
|
||||
g_slist_prepend (g_slist_prepend (stack, GINT_TO_POINTER (yy)),
|
||||
GINT_TO_POINTER (xx - 1));
|
||||
if (xx + 1 < xwidth)
|
||||
stack =
|
||||
g_slist_prepend (g_slist_prepend (stack, GINT_TO_POINTER (yy)),
|
||||
GINT_TO_POINTER (xx + 1));
|
||||
if (yy > y)
|
||||
stack =
|
||||
g_slist_prepend (g_slist_prepend
|
||||
(stack, GINT_TO_POINTER (yy - 1)),
|
||||
GINT_TO_POINTER (xx));
|
||||
if (yy + 1 < yheight)
|
||||
stack =
|
||||
g_slist_prepend (g_slist_prepend
|
||||
(stack, GINT_TO_POINTER (yy + 1)),
|
||||
GINT_TO_POINTER (xx));
|
||||
}
|
||||
}
|
||||
|
||||
if (pos_y + 1 < y + height)
|
||||
{
|
||||
read_pixel_data_1 (mask, pos_x, pos_y + 1, &val);
|
||||
|
||||
if (!(val & maskval))
|
||||
{
|
||||
g_queue_push_tail (q, GINT_TO_POINTER (pos_x));
|
||||
g_queue_push_tail (q, GINT_TO_POINTER (pos_y + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (pos_y > y)
|
||||
{
|
||||
read_pixel_data_1 (mask, pos_x , pos_y - 1, &val);
|
||||
|
||||
if (!(val & maskval))
|
||||
{
|
||||
g_queue_push_tail (q, GINT_TO_POINTER (pos_x));
|
||||
g_queue_push_tail (q, GINT_TO_POINTER (pos_y - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* This method processes every position in the queue, it finishes when
|
||||
* the queeue is empty and no further pixels kann be visited.
|
||||
*/
|
||||
static gint
|
||||
process_queue (GQueue *q,
|
||||
TileManager *mask,
|
||||
guchar maskval,
|
||||
gint x,
|
||||
gint y,
|
||||
gint width,
|
||||
gint height)
|
||||
{
|
||||
gint regioncount = 0;
|
||||
|
||||
while (! g_queue_is_empty (q))
|
||||
{
|
||||
gint pos_x = GPOINTER_TO_INT (g_queue_pop_head (q));
|
||||
gint pos_y = GPOINTER_TO_INT (g_queue_pop_head (q));
|
||||
guchar val;
|
||||
|
||||
read_pixel_data_1 (mask, pos_x, pos_y, &val);
|
||||
|
||||
if (val & maskval)
|
||||
continue;
|
||||
|
||||
/* pixel is set in original selection */
|
||||
if (val & 0x1)
|
||||
{
|
||||
val |= maskval;
|
||||
write_pixel_data_1 (mask, pos_x, pos_y, &val);
|
||||
|
||||
regioncount++;
|
||||
|
||||
process_neighbours (q, pos_x, pos_y,
|
||||
mask, maskval, x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
return regioncount;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -719,6 +710,7 @@ process_queue (GQueue *q,
|
|||
* into mask, all pixels that belong to the biggest component, are set
|
||||
* to 255 any other to 0.
|
||||
*/
|
||||
|
||||
static void
|
||||
find_max_blob (TileManager *mask,
|
||||
gint x,
|
||||
|
@ -726,95 +718,92 @@ find_max_blob (TileManager *mask,
|
|||
gint width,
|
||||
gint height)
|
||||
{
|
||||
GQueue *q;
|
||||
PixelRegion region;
|
||||
gpointer pr;
|
||||
gint row, col;
|
||||
gint half = (width * height) / 2;
|
||||
gint maxblob_x = 0;
|
||||
gint maxblob_y = 0;
|
||||
gint maxregion = 0;
|
||||
guchar val;
|
||||
GSList *list = NULL;
|
||||
PixelRegion region;
|
||||
gpointer pr;
|
||||
gint row, col;
|
||||
gint maxsize = 0;
|
||||
guchar val;
|
||||
|
||||
/* this mask is used to flag a pixel as visited in the first pass */
|
||||
const guchar visited = 0x40;
|
||||
/* this mask is used to mark a pixel in the second pass */
|
||||
const guchar contained = 0x80;
|
||||
|
||||
threshold_mask (mask, 0x80, 0x1, x, y, width, height);
|
||||
|
||||
q = g_queue_new ();
|
||||
threshold_mask (mask, x, y, width, height);
|
||||
|
||||
pixel_region_init (®ion, mask, x, y, width, height, TRUE);
|
||||
|
||||
for (pr = pixel_regions_register (1, ®ion);
|
||||
pr != NULL && maxregion < half;
|
||||
pr = pixel_regions_process (pr))
|
||||
pr != NULL; pr = pixel_regions_process (pr))
|
||||
{
|
||||
const guchar *data = region.data;
|
||||
gint pos_y = region.y;
|
||||
gint pos_y = region.y;
|
||||
|
||||
for (row = 0; row < region.h; row++, pos_y++)
|
||||
{
|
||||
gint pos_x = region.x;
|
||||
const guchar *d = data;
|
||||
gint pos_x = region.x;
|
||||
|
||||
for (col = 0; col < region.w; col++, d++, pos_x++)
|
||||
for (col = 0; col < region.w; col++, pos_x++)
|
||||
{
|
||||
gint regioncount;
|
||||
read_pixel_data_1 (mask, pos_x, pos_y, &val);
|
||||
|
||||
read_pixel_data_1 (mask, pos_x, pos_y, &val);
|
||||
|
||||
if (val & visited)
|
||||
continue;
|
||||
|
||||
/* mark current pixel as visited */
|
||||
val |= visited;
|
||||
write_pixel_data_1 (mask, pos_x, pos_y, &val);
|
||||
|
||||
/* if this pixel is not marked as selection in original image,
|
||||
* skip it
|
||||
*/
|
||||
if (!(val & 0x1))
|
||||
continue;
|
||||
|
||||
/* check out neighbourhood */
|
||||
process_neighbours (q, pos_x, pos_y,
|
||||
mask, visited, x, y, width, height);
|
||||
|
||||
regioncount = 1 + process_queue (q, mask, visited,
|
||||
x, y, width, height);
|
||||
|
||||
/* remember bigest regions size and coords of an element */
|
||||
if (regioncount > maxregion)
|
||||
if (val && (val != FIND_BLOB_VISITED))
|
||||
{
|
||||
maxregion = regioncount;
|
||||
maxblob_x = pos_x;
|
||||
maxblob_y = pos_y;
|
||||
struct blob *b = g_new (struct blob, 1);
|
||||
|
||||
b->seedx = pos_x;
|
||||
b->seedy = pos_y;
|
||||
b->size = 0;
|
||||
b->mustkeep = FALSE;
|
||||
|
||||
depth_first_search (mask,
|
||||
x, y, x + width, y + height,
|
||||
b, FIND_BLOB_VISITED);
|
||||
|
||||
list = g_slist_prepend (list, b);
|
||||
|
||||
if (b->size > maxsize)
|
||||
maxsize = b->size;
|
||||
}
|
||||
}
|
||||
|
||||
data += region.rowstride;
|
||||
}
|
||||
}
|
||||
|
||||
/* now push maxblob coords to the queue and find maxblob again,
|
||||
* so that it can be set as the resulting mask
|
||||
*/
|
||||
while (list != NULL)
|
||||
{
|
||||
struct blob *b = list->data;
|
||||
|
||||
/* mark startpixel as visited */
|
||||
read_pixel_data_1 (mask, maxblob_x, maxblob_y, &val);
|
||||
val |= contained;
|
||||
write_pixel_data_1 (mask, maxblob_x, maxblob_y, &val);
|
||||
list = g_slist_delete_link (list, list);
|
||||
|
||||
process_neighbours (q, maxblob_x, maxblob_y,
|
||||
mask, contained, x, y, width, height);
|
||||
maxregion = process_queue (q, mask, contained, x, y, width, height);
|
||||
depth_first_search (mask, x, y, x + width, y + height, b,
|
||||
(b->mustkeep
|
||||
|| (b->size * 4 > maxsize)) ? FIND_BLOB_FINAL : 0);
|
||||
g_free (b);
|
||||
}
|
||||
}
|
||||
|
||||
g_queue_free (q);
|
||||
|
||||
/* set found pixel to 255 in the mask */
|
||||
threshold_mask (mask, contained, 0xFF, x, y, width, height);
|
||||
/* Creates a key for the hashtable from a given pixel color value */
|
||||
static inline gint
|
||||
create_key (const guchar *src,
|
||||
gint bpp,
|
||||
const guchar *colormap)
|
||||
{
|
||||
switch (bpp)
|
||||
{
|
||||
case 3: /* RGB */
|
||||
case 4: /* RGBA */
|
||||
return (src[RED_PIX] << 16 | src[GREEN_PIX] << 8 | src[BLUE_PIX]);
|
||||
case 2:
|
||||
case 1:
|
||||
if (colormap) /* INDEXED(A) */
|
||||
{
|
||||
gint i = *src * 3;
|
||||
return (colormap[i + RED_PIX] << 16 |
|
||||
colormap[i + GREEN_PIX] << 8 |
|
||||
colormap[i + BLUE_PIX]);
|
||||
}
|
||||
else /* GRAY(A) */
|
||||
{
|
||||
return *src;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -826,6 +815,7 @@ siox_progress_update (SioxProgressFunc progress_callback,
|
|||
progress_callback (progress_data, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* siox_foreground_extract:
|
||||
* @pixels: the tiles to extract the foreground from
|
||||
|
@ -839,7 +829,7 @@ siox_progress_update (SioxProgressFunc progress_callback,
|
|||
* @width: width of working area on mask
|
||||
* @height: height of working area on mask
|
||||
* @sensitivity: a double array with three entries specifing the accuracy,
|
||||
* a good value is: { 0.66, 1.25, 2.5 }
|
||||
* a good value is: { 0.64, 1.28, 2.56 }
|
||||
* @smoothness: boundary smoothness (a good value is 3)
|
||||
*
|
||||
* Writes the resulting segmentation into @mask.
|
||||
|
@ -874,6 +864,7 @@ siox_foreground_extract (TileManager *pixels,
|
|||
lab *bgsig;
|
||||
lab *fgsig;
|
||||
gfloat limits[3];
|
||||
GHashTable *pixtoclassresult;
|
||||
|
||||
g_return_if_fail (pixels != NULL);
|
||||
g_return_if_fail (mask != NULL && tile_manager_bpp (mask) == 1);
|
||||
|
@ -886,8 +877,12 @@ siox_foreground_extract (TileManager *pixels,
|
|||
|
||||
cpercep_init ();
|
||||
|
||||
pixtoclassresult = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
||||
NULL, g_free);
|
||||
|
||||
siox_progress_update (progress_callback, progress_data, 0.0);
|
||||
|
||||
|
||||
limits[0] = sensitivity[0];
|
||||
limits[1] = sensitivity[1];
|
||||
limits[2] = sensitivity[2];
|
||||
|
@ -898,8 +893,7 @@ siox_foreground_extract (TileManager *pixels,
|
|||
pixel_region_init (&mapPR, mask, x, y, width, height, FALSE);
|
||||
|
||||
for (pr = pixel_regions_register (1, &mapPR);
|
||||
pr != NULL;
|
||||
pr = pixel_regions_process (pr))
|
||||
pr != NULL; pr = pixel_regions_process (pr))
|
||||
{
|
||||
const guchar *map = mapPR.data;
|
||||
|
||||
|
@ -935,8 +929,7 @@ siox_foreground_extract (TileManager *pixels,
|
|||
pixel_region_init (&mapPR, mask, x, y, width, height, FALSE);
|
||||
|
||||
for (pr = pixel_regions_register (2, &srcPR, &mapPR);
|
||||
pr != NULL;
|
||||
pr = pixel_regions_process (pr))
|
||||
pr != NULL; pr = pixel_regions_process (pr))
|
||||
{
|
||||
const guchar *src = srcPR.data;
|
||||
const guchar *map = mapPR.data;
|
||||
|
@ -985,14 +978,19 @@ siox_foreground_extract (TileManager *pixels,
|
|||
|
||||
siox_progress_update (progress_callback, progress_data, 0.4);
|
||||
|
||||
/* Classify - the slow way....Better: Tree traversation */
|
||||
/* Classify - the cached way....Better: Tree traversation? */
|
||||
|
||||
#ifdef SIOX_DEBUG
|
||||
gint hits = 0;
|
||||
gint miss = 0;
|
||||
#endif
|
||||
|
||||
pixel_region_init (&srcPR, pixels,
|
||||
x - offset_x, y - offset_y, width, height, FALSE);
|
||||
pixel_region_init (&mapPR, mask, x, y, width, height, TRUE);
|
||||
|
||||
for (pr = pixel_regions_register (2, &srcPR, &mapPR);
|
||||
pr != NULL;
|
||||
pr = pixel_regions_process (pr))
|
||||
pr != NULL; pr = pixel_regions_process (pr))
|
||||
{
|
||||
const guchar *src = srcPR.data;
|
||||
guchar *map = mapPR.data;
|
||||
|
@ -1004,57 +1002,100 @@ siox_foreground_extract (TileManager *pixels,
|
|||
|
||||
for (col = 0; col < srcPR.w; col++, m++, s += bpp)
|
||||
{
|
||||
lab labpixel;
|
||||
gboolean background;
|
||||
gfloat min, d;
|
||||
lab labpixel;
|
||||
gfloat minbg, minfg, d;
|
||||
classresult *cr;
|
||||
gint key;
|
||||
|
||||
if (*m < SIOX_LOW || *m > SIOX_HIGH)
|
||||
continue;
|
||||
|
||||
key = create_key (s, bpp, colormap);
|
||||
|
||||
/* FIXME: Do not create HashTable in here, do it globally */
|
||||
cr = g_hash_table_lookup (pixtoclassresult,
|
||||
GINT_TO_POINTER (key));
|
||||
|
||||
if (cr)
|
||||
{
|
||||
*m = (cr->bgdist >= cr->fgdist) ? 254 : 0;
|
||||
|
||||
#ifdef SIOX_DEBUG
|
||||
++hits;
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef SIOX_DEBUG
|
||||
++miss;
|
||||
#endif
|
||||
cr = g_new0 (classresult, 1);
|
||||
calc_lab (s, bpp, colormap, &labpixel);
|
||||
|
||||
min = euklid (&labpixel, bgsig + 0);
|
||||
minbg = euklid (&labpixel, bgsig + 0);
|
||||
|
||||
for (i = 1; i < bgsiglen; i++)
|
||||
{
|
||||
d = euklid (&labpixel, bgsig + i);
|
||||
|
||||
if (d < min)
|
||||
min = d;
|
||||
if (d < minbg)
|
||||
minbg = d;
|
||||
}
|
||||
|
||||
cr->bgdist = minbg;
|
||||
|
||||
if (fgsiglen == 0)
|
||||
{
|
||||
background = (min < clustersize);
|
||||
if (minbg < clustersize)
|
||||
minfg = minbg + clustersize;
|
||||
else
|
||||
minfg = 0.00001; /* This is a guess -
|
||||
now we actually require a foreground
|
||||
signature, !=0 to avoid div by zero
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
background = TRUE;
|
||||
minfg = euklid (&labpixel, fgsig + 0);
|
||||
|
||||
for (i = 0; i < fgsiglen; i++)
|
||||
for (i = 1; i < fgsiglen; i++)
|
||||
{
|
||||
d = euklid (&labpixel, fgsig + i);
|
||||
|
||||
if (d < min)
|
||||
if (d < minfg)
|
||||
{
|
||||
min = d;
|
||||
background = FALSE;
|
||||
break;
|
||||
minfg = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*m = background ? 0 : 255;
|
||||
}
|
||||
cr->bgdist = minbg;
|
||||
cr->fgdist = minfg;
|
||||
|
||||
g_hash_table_insert (pixtoclassresult, GINT_TO_POINTER (key), cr);
|
||||
|
||||
*m = minbg >= minfg ? 254 : 0;
|
||||
}
|
||||
|
||||
src += srcPR.rowstride;
|
||||
map += mapPR.rowstride;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SIOX_DEBUG
|
||||
g_printerr ("siox.c: Hashtable size %d, misses=%d, hits=%d, ratio=%f\n",
|
||||
g_hash_table_size (pixtoclassresult),
|
||||
miss,
|
||||
hits,
|
||||
((gfloat) hits) / miss);
|
||||
#endif
|
||||
|
||||
g_free (fgsig);
|
||||
g_free (bgsig);
|
||||
|
||||
/* FIXME: Do not free memory in here - do it globally */
|
||||
g_hash_table_destroy (pixtoclassresult);
|
||||
|
||||
siox_progress_update (progress_callback, progress_data, 0.8);
|
||||
|
||||
/* smooth a bit for error killing */
|
||||
|
@ -1082,3 +1123,127 @@ siox_foreground_extract (TileManager *pixels,
|
|||
|
||||
siox_progress_update (progress_callback, progress_data, 1.0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* siox_drb:
|
||||
* @pixels: the rgb tiles to work on (read)
|
||||
* @colormap: colormap in case @pixels are indexed, %NULL otherwise
|
||||
* @mask: the alpha tiles to work on (write)
|
||||
* @x: horizontal offset into the mask
|
||||
* @y: vertical offset into the mask
|
||||
* @pixtoclassresult: the hashtable as generated by siox_foreground_extract
|
||||
* @brushmode: at this time either SIOX_DRB_ADD or SIOX_DRB_SUBTRACT
|
||||
* @brushradius: the radius of the brush
|
||||
* @threshold: a threshold to be defined by the user.
|
||||
* Range for SIOX_DRB_ADD: ]0..1] default: 1.0,
|
||||
* range for for SIOX_DRB_SUBTRACT: [0..1[, default: 0.0
|
||||
*
|
||||
* drb - detail refinement brush, a brush mask for subpixel classification.
|
||||
*
|
||||
* FIXME: Now it is assumed that the brush is a square. Should be able
|
||||
* to be whatever GIMP offers... TODO: This is still an experimental
|
||||
* method. There are more tests needed to evaluate performance of
|
||||
* this!
|
||||
*/
|
||||
void
|
||||
siox_drb (TileManager *pixels,
|
||||
const guchar *colormap,
|
||||
TileManager *mask,
|
||||
gint x,
|
||||
gint y,
|
||||
GHashTable *pixtoclassresult,
|
||||
gint brushmode,
|
||||
gint brushradius,
|
||||
gfloat threshold)
|
||||
{
|
||||
PixelRegion srcPR;
|
||||
PixelRegion mapPR;
|
||||
gpointer pr;
|
||||
gint bpp;
|
||||
gint row, col;
|
||||
|
||||
bpp = tile_manager_bpp (pixels);
|
||||
|
||||
pixel_region_init (&srcPR, pixels,
|
||||
x - brushradius, y - brushradius, brushradius * 2,
|
||||
brushradius * 2, FALSE);
|
||||
pixel_region_init (&mapPR, mask, x - brushradius, y - brushradius,
|
||||
brushradius * 2, brushradius * 2, TRUE);
|
||||
|
||||
for (pr = pixel_regions_register (2, &srcPR, &mapPR);
|
||||
pr != NULL; pr = pixel_regions_process (pr))
|
||||
{
|
||||
const guchar *src = srcPR.data;
|
||||
guchar *map = mapPR.data;
|
||||
|
||||
for (row = 0; row < srcPR.h; row++)
|
||||
{
|
||||
const guchar *s = src;
|
||||
guchar *m = map;
|
||||
|
||||
for (col = 0; col < srcPR.w; col++, m++, s += bpp)
|
||||
{
|
||||
gint key;
|
||||
classresult *cr;
|
||||
|
||||
key = create_key (s, bpp, colormap);
|
||||
cr = g_hash_table_lookup (pixtoclassresult,
|
||||
GINT_TO_POINTER (key));
|
||||
|
||||
if (! cr)
|
||||
continue; /* Unknown color -
|
||||
can only be sure background or sure forground */
|
||||
|
||||
gfloat mindistbg = (gfloat) sqrt (cr->bgdist);
|
||||
gfloat mindistfg = (gfloat) sqrt (cr->fgdist);
|
||||
|
||||
if (brushmode == SIOX_DRB_ADD)
|
||||
{
|
||||
gfloat alpha;
|
||||
|
||||
if (*m > SIOX_HIGH)
|
||||
continue;
|
||||
|
||||
if (mindistfg == 0.0)
|
||||
alpha = 1.0; /* avoid div by zero */
|
||||
else
|
||||
alpha = MIN (mindistbg / mindistfg, 1.0);
|
||||
|
||||
if (alpha < threshold)
|
||||
{
|
||||
/* background with a certain confidence
|
||||
* to be decided by user.
|
||||
*/
|
||||
alpha = 0.0;
|
||||
}
|
||||
|
||||
*m = (gint) 255 *alpha;
|
||||
}
|
||||
else if (brushmode == SIOX_DRB_SUBTRACT)
|
||||
{
|
||||
gfloat alpha;
|
||||
|
||||
if (*m < SIOX_HIGH)
|
||||
continue;
|
||||
|
||||
if (mindistbg == 0.0)
|
||||
alpha = 0.0; /* avoid div by zero */
|
||||
else
|
||||
alpha = 1.0 - MIN (mindistfg / mindistbg, 1.0);
|
||||
|
||||
if (alpha < threshold)
|
||||
{
|
||||
alpha = 0.0;
|
||||
}
|
||||
|
||||
*m = (gint) 255 *alpha;
|
||||
}
|
||||
}
|
||||
|
||||
src += srcPR.rowstride;
|
||||
map += mapPR.rowstride;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,9 +39,9 @@
|
|||
|
||||
#define SIOX_DEFAULT_SMOOTHNESS 3
|
||||
|
||||
#define SIOX_DEFAULT_SENSITIVITY_L 0.66
|
||||
#define SIOX_DEFAULT_SENSITIVITY_A 1.25
|
||||
#define SIOX_DEFAULT_SENSITIVITY_B 2.5
|
||||
#define SIOX_DEFAULT_SENSITIVITY_L 0.64
|
||||
#define SIOX_DEFAULT_SENSITIVITY_A 1.28
|
||||
#define SIOX_DEFAULT_SENSITIVITY_B 2.56
|
||||
|
||||
|
||||
typedef void (* SioxProgressFunc) (gpointer progress_data,
|
||||
|
|
|
@ -137,7 +137,7 @@ gimp_foreground_select_tool_register (GimpToolRegisterCallback callback,
|
|||
GIMP_CONTEXT_FOREGROUND_MASK | GIMP_CONTEXT_BACKGROUND_MASK,
|
||||
"gimp-foreground-select-tool",
|
||||
_("Foreground Select"),
|
||||
_("Extract a single foreground object"),
|
||||
_("Extract foreground objects"),
|
||||
N_("F_oreground Select"), NULL,
|
||||
NULL, GIMP_HELP_TOOL_FOREGROUND_SELECT,
|
||||
GIMP_STOCK_TOOL_FOREGROUND_SELECT,
|
||||
|
|
Loading…
Reference in New Issue