Commit e91e1ad5 authored by Sven Neumann's avatar Sven Neumann Committed by Sven Neumann

applied patch by Kristian Jantz. Reimplements find_max_blob() so that it

2005-08-17  Sven Neumann  <sven@gimp.org>

	* app/base/siox.c: applied patch by Kristian Jantz. Reimplements
	find_max_blob() so that it needs less memory. Slows things down a
	bit, but that can be improved later.
parent 9795fdeb
2005-08-17 Sven Neumann <sven@gimp.org>
* app/base/siox.c: applied patch by Kristian Jantz. Reimplements
find_max_blob() so that it needs less memory. Slows things down a
bit, but that can be improved later.
2005-08-17 Sven Neumann <sven@gimp.org>
* plug-ins/common/grid.c: update the progress less frequently.
......
......@@ -564,158 +564,304 @@ dilate_mask (TileManager *mask,
dilate_region (&region);
}
static void
find_max_blob (TileManager *mask,
gint x,
gint y,
gint width,
gint height)
threshold_mask (TileManager *mask,
guchar maskval,
guchar newval,
gint x,
gint y,
gint width,
gint height)
{
GQueue *q;
gint *labels;
glong size;
PixelRegion region;
gpointer pr;
gint row, col;
gint curlabel = 1;
gint maxblob = 1;
gint maxregion = 0;
/* FIXME: this function uses an insane amount of memory */
size = width * height * sizeof (gint);
labels = g_try_malloc (size);
if (! labels)
pixel_region_init (&region, mask, x, y, width, height, TRUE);
for (pr = pixel_regions_register (1, &region);
pr != NULL;
pr = pixel_regions_process (pr))
{
g_warning ("%s: couldn't allocate %ld bytes", G_STRFUNC, size);
return;
}
guchar *data = region.data;
for (row = 0; row < region.h; row++)
{
guchar *d = data;
memset (labels, 0, size);
/* everything that fits the mask is in the image*/
for (col = 0; col < region.w; col++, d++)
*d = (*d & maskval) ? newval: 0;
q = g_queue_new ();
data += region.rowstride;
}
}
}
static void
set_mask (TileManager *mask,
guchar maskval,
gint x,
gint y,
gint width,
gint height)
{
PixelRegion region;
gpointer pr;
gint row, col;
pixel_region_init (&region, mask, x, y, width, height, FALSE);
pixel_region_init (&region, mask, x, y, width, height, TRUE);
for (pr = pixel_regions_register (1, &region);
pr != NULL;
pr = pixel_regions_process (pr))
{
const guchar *data = region.data;
gint index = (region.x - x) + (region.y - y) * width;
guchar *data = region.data;
for (row = 0; row < region.h; row++)
{
const guchar *d = data;
gint i = index;
guchar *d = data;
for (col = 0; col < region.w; col++, d++, i++)
{
gint regioncount;
gint pos_x;
gint pos_y;
if (labels[i])
continue;
for (col = 0; col < region.w; col++, d++)
*d = *d & maskval;
if (! (*d & 0x80))
{
labels[i] = -1;
continue;
}
data += region.rowstride;
}
}
}
labels[i] = curlabel;
regioncount = 1;
/* 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
* determine if the sourounding pixels have already been visited x,y
* are passed from above.
*/
static void
process_neighbours (gint pos_x,
gint pos_y,
gint x,
gint y,
gint width,
gint height,
GQueue *q,
TileManager *mask,
guchar maskval)
{
guchar val;
pos_x = i % width;
pos_y = i / width;
if (pos_x + 1 < width)
{
read_pixel_data_1 (mask, x + pos_x + 1, y + pos_y, &val);
if (pos_x + 1 < width && ! labels[i + 1])
g_queue_push_tail (q, GINT_TO_POINTER (i + 1));
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 > 0 && ! labels[i - 1])
g_queue_push_tail (q, GINT_TO_POINTER (i - 1));
if (pos_x > 0)
{
read_pixel_data_1 (mask, x + pos_x - 1, y + pos_y, &val);
if (pos_y + 1 < height && ! labels[i + width])
g_queue_push_tail (q, GINT_TO_POINTER (i + width));
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_y > 0 && ! labels[i - width])
g_queue_push_tail (q, GINT_TO_POINTER (i - width));
if (pos_y + 1 < height)
{
read_pixel_data_1 (mask, x + pos_x, y + pos_y + 1, &val);
while (! g_queue_is_empty (q))
{
gint pos = GPOINTER_TO_INT (g_queue_pop_head (q));
guchar 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 (labels[pos])
continue;
if (pos_y > 0)
{
read_pixel_data_1 (mask, x + pos_x , y + pos_y - 1, &val);
pos_x = pos % width;
pos_y = pos / width;
if (!(val & maskval))
{
g_queue_push_tail (q, GINT_TO_POINTER (pos_x ));
g_queue_push_tail (q, GINT_TO_POINTER (pos_y - 1));
}
}
}
read_pixel_data_1 (mask, x + pos_x, y + pos_y, &val);
if (val & 0x80)
{
labels[pos] = curlabel;
regioncount++;
/* This method processes every position in the queue, it finishes when
* the queeue is empty and no further pixels kann be visited.
*/
static int
process_queue (GQueue *q,
gint x,
gint y,
gint width,
gint height,
TileManager *mask,
guchar maskval)
{
gint regioncount = 0;
if (pos_x + 1 < width && ! labels[pos + 1])
g_queue_push_tail (q, GINT_TO_POINTER (pos + 1));
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;
if (pos_x > 0 && ! labels[pos - 1])
g_queue_push_tail (q, GINT_TO_POINTER (pos - 1));
read_pixel_data_1 (mask, x + pos_x, y + pos_y, &val);
if (pos_y + 1 < height && ! labels[pos + width])
g_queue_push_tail (q, GINT_TO_POINTER (pos + width));
if (val & maskval)
continue;
if (pos_y > 0 && ! labels[pos - width])
g_queue_push_tail (q, GINT_TO_POINTER (pos - width));
}
else
{
labels[pos] = -1;
}
}
val |= maskval;
if (regioncount > maxregion)
{
maxregion = regioncount;
maxblob = curlabel;
}
/* pixel is set in original selection */
if (val & 0x1)
{
write_pixel_data_1 (mask, x + pos_x, y + pos_y, &val);
curlabel++;
}
regioncount++;
data += region.rowstride;
index += width;
}
process_neighbours (pos_x, pos_y,
x, y, width, height, q, mask, maskval);
}
}
g_queue_free (q);
return regioncount;
}
/*
* This method finds the biggest connected component in mask, it
* clears everything in mask except the biggest component Pixels that
* should be considererd set in incoming mask, must fullfil (pixel &
* 0x1) the method uses no further memory, except a queue, it finds
* the biggest component by a 2 phase algorithm 1. in the first phase
* the koordinates of an element of the biggest component are
* identified, during set phase all pixels are visited 2. in the
* second phase first visitation flags are reseted, and afterwards a
* connected component starting at the found coordinates is
* determined, this is the biggest component, the result is written
* 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,
gint y,
gint width,
gint height)
{
GQueue *q;
PixelRegion region;
gpointer pr;
gint row, col;
gint maxblobx = 0;
gint maxbloby = 0;
gint maxregion = 0;
gint pos_x;
gint pos_y;
guchar val;
/* this mask is used to check if a pixel has been visited */
const guchar visited = 0x80;
/* this mask is used to set a pixel to unvisited */
const guchar unvisited = 0x7F;
threshold_mask (mask, 0x80, 0x1, x, y, width, height);
q = g_queue_new ();
pixel_region_init (&region, mask, x, y, width, height, TRUE);
/* mark every pixel in the mask as unvisited */
set_mask (mask, unvisited, x, y, width, height);
for (pr = pixel_regions_register (1, &region);
pr != NULL;
pr = pixel_regions_process (pr))
{
guchar *data = region.data;
gint index = (region.x - x) + (region.y - y) * width;
const guchar *data = region.data;
gint index = (region.x - x) + (region.y - y) * width;
for (row = 0; row < region.h; row++)
{
guchar *d = data;
gint i = index;
const guchar *d = data;
gint i = index;
for (col = 0; col < region.w; col++, d++, i++)
*d = (labels[i] == maxblob) ? 255 : 0;
{
gint regioncount;
pos_x = i % width;
pos_y = i / width;
read_pixel_data_1 (mask, x + pos_x, y + pos_y, &val);
if (val & visited)
continue;
/* mark current pixel as visited */
val |= visited;
write_pixel_data_1 (mask, x + pos_x, y + 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 (pos_x, pos_y,
x, y, width, height, q, mask, visited);
regioncount = 1 + process_queue (q,
x, y, width, height, mask,
visited);
/* remember bigest regions size and coords of an element*/
if (regioncount > maxregion)
{
maxregion = regioncount;
maxblobx = pos_x;
maxbloby = pos_y;
}
}
data += region.rowstride;
index += width;
}
}
g_free (labels);
/* clear visited flag for all pixels */
set_mask (mask, unvisited, x, y, width, height);
/* now push maxblob coords onto stack and find maxblob again, so
* that it can be set as resulting mask
*/
/* mark startpixel als visited */
read_pixel_data_1 (mask, x + maxblobx, y + maxbloby, &val);
/* mark as visited */
val |= visited;
write_pixel_data_1 (mask, x + maxblobx, y + maxbloby, &val);
process_neighbours (maxblobx, maxbloby,
x, y, width, height, q, mask, visited);
maxregion = process_queue (q, x, y, width, height, mask, visited);
g_queue_free (q);
/* set found pixel to 255 in the mask */
threshold_mask (mask, visited, 0xFF, x, y, width, height);
}
static inline void
......@@ -733,7 +879,6 @@ siox_progress_update (SioxProgressFunc progress_callback,
* @colormap: colormap in case @pixels are indexed, %NULL otherwise
* @offset_x: horizontal offset of @pixels with respect to the @mask
* @offset_y: vertical offset of @pixels with respect to the @mask
* @mask: a mask indicating sure foreground (255), sure background (0)
* and undecided regions ([1..254]).
* @x: horizontal offset into the mask
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment