2005-07-08 07:45:38 +08:00
|
|
|
/*
|
2005-07-14 03:37:27 +08:00
|
|
|
* SIOX: Simple Interactive Object Extraction
|
2005-07-08 07:45:38 +08:00
|
|
|
*
|
|
|
|
* For algorithm documentation refer to:
|
2005-07-08 07:48:58 +08:00
|
|
|
* G. Friedland, K. Jantz, L. Knipping, R. Rojas:
|
|
|
|
* "Image Segmentation by Uniform Color Clustering
|
|
|
|
* -- Approach and Benchmark Results",
|
|
|
|
* Technical Report B-05-07, Department of Computer Science,
|
|
|
|
* Freie Universitaet Berlin, June 2005.
|
2005-07-08 07:45:38 +08:00
|
|
|
* http://www.inf.fu-berlin.de/inst/pubs/tr-b-05-07.pdf
|
2005-07-08 07:48:58 +08:00
|
|
|
*
|
2005-07-14 03:30:25 +08:00
|
|
|
* See http://www.siox.org/ for more information.
|
|
|
|
*
|
2005-07-08 07:48:58 +08:00
|
|
|
* 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>.
|
|
|
|
*
|
2005-07-14 23:40:19 +08:00
|
|
|
* Adapted for GIMP by Sven Neumann <sven@gimp.org>
|
|
|
|
*
|
2005-07-08 07:45:38 +08:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2005-07-08 07:48:58 +08:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
* 02110-1301, USA.
|
2005-07-08 07:45:38 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
#include <glib-object.h>
|
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
#include "libgimpmath/gimpmath.h"
|
2005-07-08 07:45:38 +08:00
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
#include "base-types.h"
|
|
|
|
|
|
|
|
#include "paint-funcs/paint-funcs.h"
|
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
#include "cpercep.h"
|
2005-07-10 06:44:26 +08:00
|
|
|
#include "pixel-region.h"
|
2005-07-14 03:30:25 +08:00
|
|
|
#include "siox.h"
|
2005-07-10 06:44:26 +08:00
|
|
|
#include "tile-manager.h"
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
|
2005-08-07 03:08:43 +08:00
|
|
|
/* Amount of color dimensions in one point */
|
|
|
|
#define SIOX_DIMS 3
|
|
|
|
|
|
|
|
|
2005-07-29 04:12:16 +08:00
|
|
|
/* Thresholds in the mask:
|
2005-08-08 08:31:55 +08:00
|
|
|
* pixels < SIOX_LOW are known background
|
|
|
|
* pixels > SIOX_HIGH are known foreground
|
2005-07-28 06:52:34 +08:00
|
|
|
*/
|
2005-08-07 03:08:43 +08:00
|
|
|
#define SIOX_LOW 1
|
|
|
|
#define SIOX_HIGH 254
|
2005-07-28 06:52:34 +08:00
|
|
|
|
|
|
|
|
2005-08-08 08:31:55 +08:00
|
|
|
/* #define SIOX_DEBUG */
|
2005-07-28 08:18:39 +08:00
|
|
|
|
|
|
|
|
2005-07-08 07:45:38 +08:00
|
|
|
/* Simulate a java.util.ArrayList */
|
2005-07-29 23:25:20 +08:00
|
|
|
|
|
|
|
/* Could be improved. At the moment we are wasting a node per list and
|
|
|
|
* the tail pointer on each node is only used in the first node.
|
|
|
|
*/
|
2005-07-08 07:45:38 +08:00
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
typedef struct
|
|
|
|
{
|
2005-07-28 06:52:34 +08:00
|
|
|
gfloat l;
|
|
|
|
gfloat a;
|
|
|
|
gfloat b;
|
|
|
|
gint cardinality;
|
2005-07-08 07:45:38 +08:00
|
|
|
} lab;
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
typedef struct _ArrayList ArrayList;
|
2005-07-08 07:45:38 +08:00
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
struct _ArrayList
|
|
|
|
{
|
|
|
|
lab *array;
|
2005-07-09 06:33:18 +08:00
|
|
|
guint arraylength;
|
|
|
|
gboolean owned;
|
2005-07-08 07:48:58 +08:00
|
|
|
ArrayList *next;
|
2005-07-29 23:25:20 +08:00
|
|
|
ArrayList *tail; /* only valid in the root item */
|
2005-07-08 07:48:58 +08:00
|
|
|
};
|
2005-07-08 07:45:38 +08:00
|
|
|
|
2005-07-29 23:25:20 +08:00
|
|
|
static ArrayList *
|
|
|
|
list_new (void)
|
|
|
|
{
|
|
|
|
ArrayList *list = g_new0 (ArrayList, 1);
|
|
|
|
|
|
|
|
list->tail = list;
|
|
|
|
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
static void
|
2005-07-09 05:24:55 +08:00
|
|
|
add_to_list (ArrayList *list,
|
2005-07-09 06:33:18 +08:00
|
|
|
lab *array,
|
|
|
|
guint arraylength,
|
|
|
|
gboolean take)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-07-29 23:25:20 +08:00
|
|
|
ArrayList *tail = list->tail;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-29 23:25:20 +08:00
|
|
|
tail->array = array;
|
|
|
|
tail->arraylength = arraylength;
|
|
|
|
tail->owned = take;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-29 23:25:20 +08:00
|
|
|
list->tail = tail->next = g_new0 (ArrayList, 1);
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
2005-08-08 04:20:53 +08:00
|
|
|
static gint
|
2005-07-09 05:24:55 +08:00
|
|
|
list_size (ArrayList *list)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-07-08 07:48:58 +08:00
|
|
|
ArrayList *cur = list;
|
2005-08-08 00:07:32 +08:00
|
|
|
gint count = 0;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
while (cur->array)
|
|
|
|
{
|
|
|
|
count++;
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
static lab *
|
2005-07-09 05:24:55 +08:00
|
|
|
list_to_array (ArrayList *list,
|
2005-07-28 06:52:34 +08:00
|
|
|
gint *returnlength)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-07-10 06:44:26 +08:00
|
|
|
ArrayList *cur = list;
|
|
|
|
gint i = 0;
|
|
|
|
gint len = list_size (list);
|
|
|
|
lab *array = g_new (lab, len);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-09 05:24:55 +08:00
|
|
|
*returnlength = len;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
while (cur->array)
|
|
|
|
{
|
2005-07-10 06:44:26 +08:00
|
|
|
array[i++] = cur->array[0];
|
2005-07-09 05:24:55 +08:00
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
/* Every array in the list node has only one point
|
|
|
|
* when we call this method
|
|
|
|
*/
|
|
|
|
cur = cur->next;
|
|
|
|
}
|
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
return array;
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
static void
|
2005-07-09 06:33:18 +08:00
|
|
|
free_list (ArrayList *list)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-07-08 07:48:58 +08:00
|
|
|
ArrayList *cur = list;
|
|
|
|
|
2005-07-09 05:24:55 +08:00
|
|
|
while (cur)
|
2005-07-08 07:48:58 +08:00
|
|
|
{
|
2005-07-09 05:24:55 +08:00
|
|
|
ArrayList *prev = cur;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-09 05:24:55 +08:00
|
|
|
cur = cur->next;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-09 06:33:18 +08:00
|
|
|
if (prev->owned)
|
|
|
|
g_free (prev->array);
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
g_free (prev);
|
2005-07-09 05:24:55 +08:00
|
|
|
}
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
static void
|
2005-07-28 06:52:34 +08:00
|
|
|
calc_lab (const guchar *src,
|
|
|
|
gint bpp,
|
|
|
|
const guchar *colormap,
|
|
|
|
lab *pixel)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-07-28 06:52:34 +08:00
|
|
|
gdouble l, a, b;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
switch (bpp)
|
|
|
|
{
|
|
|
|
case 3: /* RGB */
|
|
|
|
case 4: /* RGBA */
|
|
|
|
cpercep_rgb_to_space (src[RED_PIX],
|
|
|
|
src[GREEN_PIX],
|
|
|
|
src[BLUE_PIX], &l, &a, &b);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
case 1:
|
|
|
|
if (colormap) /* INDEXED(A) */
|
|
|
|
{
|
|
|
|
gint i = *src * 3;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
cpercep_rgb_to_space (colormap[i + RED_PIX],
|
|
|
|
colormap[i + GREEN_PIX],
|
|
|
|
colormap[i + BLUE_PIX], &l, &a, &b);
|
|
|
|
}
|
|
|
|
else /* GRAY(A) */
|
|
|
|
{
|
|
|
|
/* FIXME: there should be cpercep_gray_to_space */
|
|
|
|
cpercep_rgb_to_space (*src, *src, *src, &l, &a, &b);
|
|
|
|
}
|
|
|
|
break;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
default:
|
|
|
|
g_return_if_reached ();
|
|
|
|
}
|
2005-07-08 07:45:38 +08:00
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
pixel->l = l;
|
|
|
|
pixel->a = a;
|
|
|
|
pixel->b = b;
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
2005-08-08 04:20:53 +08:00
|
|
|
|
2005-08-08 00:07:32 +08:00
|
|
|
/* assumes that lab starts with an array of floats (l,a,b) */
|
|
|
|
#define CURRENT_VALUE(points, i, dim) (((const gfloat *) (points + i))[dim])
|
|
|
|
|
2005-07-08 07:45:38 +08:00
|
|
|
/* Stage one of modified KD-Tree algorithm */
|
2005-07-08 07:48:58 +08:00
|
|
|
static void
|
2005-07-28 06:52:34 +08:00
|
|
|
stageone (lab *points,
|
|
|
|
gint dims,
|
|
|
|
gint depth,
|
|
|
|
ArrayList *clusters,
|
2005-08-08 00:07:32 +08:00
|
|
|
const gfloat *limits,
|
2005-07-28 06:52:34 +08:00
|
|
|
gint length)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-07-28 06:52:34 +08:00
|
|
|
gint curdim = depth % dims;
|
2005-08-08 00:07:32 +08:00
|
|
|
gfloat min, max;
|
2005-08-08 04:20:53 +08:00
|
|
|
gfloat curval;
|
|
|
|
gint i;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
if (length < 1)
|
|
|
|
return;
|
|
|
|
|
2005-08-08 00:07:32 +08:00
|
|
|
curval = CURRENT_VALUE (points, 0, curdim);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
min = curval;
|
|
|
|
max = curval;
|
|
|
|
|
|
|
|
for (i = 1; i < length; i++)
|
|
|
|
{
|
2005-08-08 00:07:32 +08:00
|
|
|
curval = CURRENT_VALUE (points, i, curdim);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
if (min > curval)
|
|
|
|
min = curval;
|
2005-07-08 07:45:38 +08:00
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
if (max < curval)
|
|
|
|
max = curval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Split according to Rubner-Rule */
|
|
|
|
if (max - min > limits[curdim])
|
|
|
|
{
|
2005-08-08 04:20:53 +08:00
|
|
|
lab *smallerpoints;
|
|
|
|
lab *biggerpoints;
|
|
|
|
gint countsm = 0;
|
|
|
|
gint countgr = 0;
|
|
|
|
gint smallc = 0;
|
|
|
|
gint bigc = 0;
|
|
|
|
gfloat pivot = (min + max) / 2.0;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
/* find out cluster sizes */
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
2005-08-08 00:07:32 +08:00
|
|
|
curval = CURRENT_VALUE (points, i, curdim);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 04:20:53 +08:00
|
|
|
if (curval <= pivot)
|
2005-08-08 00:07:32 +08:00
|
|
|
countsm++;
|
2005-07-08 07:48:58 +08:00
|
|
|
else
|
2005-08-08 00:07:32 +08:00
|
|
|
countgr++;
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
|
|
|
|
2005-07-16 08:00:11 +08:00
|
|
|
/* FIXME: consider to sort the array and split in place instead
|
|
|
|
* of allocating memory here
|
|
|
|
*/
|
2005-07-08 07:48:58 +08:00
|
|
|
smallerpoints = g_new (lab, countsm);
|
2005-08-08 00:07:32 +08:00
|
|
|
biggerpoints = g_new (lab, countgr);
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
for (i = 0; i < length; i++)
|
2005-08-08 00:07:32 +08:00
|
|
|
{
|
|
|
|
/* do actual split */
|
|
|
|
curval = CURRENT_VALUE (points, i, curdim);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 04:20:53 +08:00
|
|
|
if (curval <= pivot)
|
2005-08-08 00:07:32 +08:00
|
|
|
smallerpoints[smallc++] = points[i];
|
2005-07-08 07:48:58 +08:00
|
|
|
else
|
2005-08-08 00:07:32 +08:00
|
|
|
biggerpoints[bigc++] = points[i];
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
|
|
|
|
2005-07-09 06:33:18 +08:00
|
|
|
if (depth > 0)
|
|
|
|
g_free (points);
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
/* create subtrees */
|
|
|
|
stageone (smallerpoints, dims, depth + 1, clusters, limits, countsm);
|
|
|
|
stageone (biggerpoints, dims, depth + 1, clusters, limits, countgr);
|
|
|
|
}
|
|
|
|
else
|
2005-08-08 08:31:55 +08:00
|
|
|
{
|
|
|
|
/* create leave */
|
2005-07-09 06:33:18 +08:00
|
|
|
add_to_list (clusters, points, length, depth != 0);
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-08 07:45:38 +08:00
|
|
|
/* Stage two of modified KD-Tree algorithm */
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
/* This is very similar to stageone... but in future there will be more
|
|
|
|
* differences => not integrated into method stageone()
|
|
|
|
*/
|
|
|
|
static void
|
2005-07-28 06:52:34 +08:00
|
|
|
stagetwo (lab *points,
|
|
|
|
gint dims,
|
|
|
|
gint depth,
|
|
|
|
ArrayList *clusters,
|
2005-08-08 00:07:32 +08:00
|
|
|
const gfloat *limits,
|
2005-07-28 06:52:34 +08:00
|
|
|
gint length,
|
|
|
|
gint total,
|
|
|
|
gfloat threshold)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-07-28 06:52:34 +08:00
|
|
|
gint curdim = depth % dims;
|
|
|
|
gfloat min, max;
|
2005-08-08 04:20:53 +08:00
|
|
|
gfloat curval;
|
|
|
|
gint i;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
if (length < 1)
|
|
|
|
return;
|
|
|
|
|
2005-08-08 00:07:32 +08:00
|
|
|
curval = CURRENT_VALUE (points, 0, curdim);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
min = curval;
|
|
|
|
max = curval;
|
|
|
|
|
|
|
|
for (i = 1; i < length; i++)
|
|
|
|
{
|
2005-08-08 00:07:32 +08:00
|
|
|
curval = CURRENT_VALUE (points, i, curdim);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
if (min > curval)
|
|
|
|
min = curval;
|
2005-08-08 00:07:32 +08:00
|
|
|
else if (max < curval)
|
2005-07-08 07:48:58 +08:00
|
|
|
max = curval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Split according to Rubner-Rule */
|
|
|
|
if (max - min > limits[curdim])
|
|
|
|
{
|
2005-08-08 04:20:53 +08:00
|
|
|
lab *smallerpoints;
|
|
|
|
lab *biggerpoints;
|
|
|
|
gint countsm = 0;
|
|
|
|
gint countgr = 0;
|
|
|
|
gint smallc = 0;
|
|
|
|
gint bigc = 0;
|
|
|
|
gfloat pivot = (min + max) / 2.0;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 08:31:55 +08:00
|
|
|
#ifdef SIOX_DEBUG
|
2005-08-08 04:20:53 +08:00
|
|
|
g_printerr ("max=%f min=%f pivot=%f\n", max, min, pivot);
|
2005-07-28 08:18:39 +08:00
|
|
|
#endif
|
2005-07-28 06:52:34 +08:00
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
for (i = 0; i < length; i++)
|
2005-08-08 00:07:32 +08:00
|
|
|
{
|
|
|
|
/* find out cluster sizes */
|
|
|
|
curval = CURRENT_VALUE (points, i, curdim);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 04:20:53 +08:00
|
|
|
if (curval <= pivot)
|
2005-08-08 00:07:32 +08:00
|
|
|
countsm++;
|
2005-07-08 07:48:58 +08:00
|
|
|
else
|
2005-08-08 00:07:32 +08:00
|
|
|
countgr++;
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
|
|
|
|
2005-07-16 08:00:11 +08:00
|
|
|
/* FIXME: consider to sort the array and split in place instead
|
|
|
|
* of allocating memory here
|
|
|
|
*/
|
2005-07-09 03:42:29 +08:00
|
|
|
smallerpoints = g_new (lab, countsm);
|
2005-08-08 00:07:32 +08:00
|
|
|
biggerpoints = g_new (lab, countgr);
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
/* do actual split */
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
2005-08-08 00:07:32 +08:00
|
|
|
curval = CURRENT_VALUE (points, i, curdim);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 04:20:53 +08:00
|
|
|
if (curval <= pivot)
|
2005-08-08 00:07:32 +08:00
|
|
|
smallerpoints[smallc++] = points[i];
|
2005-07-08 07:48:58 +08:00
|
|
|
else
|
2005-08-08 00:07:32 +08:00
|
|
|
biggerpoints[bigc++] = points[i];
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
|
|
|
|
2005-07-09 05:24:55 +08:00
|
|
|
g_free (points);
|
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
/* create subtrees */
|
|
|
|
stagetwo (smallerpoints, dims, depth + 1, clusters, limits,
|
|
|
|
countsm, total, threshold);
|
|
|
|
stagetwo (biggerpoints, dims, depth + 1, clusters, limits,
|
|
|
|
countgr, total, threshold);
|
|
|
|
}
|
|
|
|
else /* create leave */
|
|
|
|
{
|
2005-08-08 04:20:53 +08:00
|
|
|
gint sum = 0;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
for (i = 0; i < length; i++)
|
2005-08-08 00:32:32 +08:00
|
|
|
sum += points[i].cardinality;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
if (((sum * 100.0) / total) >= threshold)
|
|
|
|
{
|
2005-08-08 04:20:53 +08:00
|
|
|
lab *point = g_new0 (lab, 1);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
for (i = 0; i < length; i++)
|
|
|
|
{
|
|
|
|
point->l += points[i].l;
|
|
|
|
point->a += points[i].a;
|
|
|
|
point->b += points[i].b;
|
|
|
|
}
|
|
|
|
|
2005-08-08 00:07:32 +08:00
|
|
|
point->l /= length;
|
|
|
|
point->a /= length;
|
|
|
|
point->b /= length;
|
2005-07-09 03:42:29 +08:00
|
|
|
|
2005-08-08 08:31:55 +08:00
|
|
|
#ifdef SIOX_DEBUG
|
2005-07-28 08:18:39 +08:00
|
|
|
g_printerr ("cluster=%f, %f, %f sum=%d\n",
|
|
|
|
point->l, point->a, point->b, sum);
|
|
|
|
#endif
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-09 06:33:18 +08:00
|
|
|
add_to_list (clusters, point, 1, TRUE);
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
g_free (points);
|
|
|
|
}
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* squared euclidean distance */
|
2005-07-08 07:48:58 +08:00
|
|
|
static inline float
|
2005-08-08 00:07:32 +08:00
|
|
|
euklid (const lab *p,
|
|
|
|
const lab *q)
|
|
|
|
{
|
2005-08-08 04:20:53 +08:00
|
|
|
return (SQR (p->l - q->l) + SQR (p->a - q->a) + SQR (p->b - q->b));
|
2005-08-08 00:07:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Returns squared clustersize */
|
|
|
|
static gfloat
|
|
|
|
get_clustersize (const gfloat *limits)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-08-08 04:20:53 +08:00
|
|
|
return (SQR (limits[0] - (-limits[0])) +
|
|
|
|
SQR (limits[1] - (-limits[1])) +
|
|
|
|
SQR (limits[2] - (-limits[2])));
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Creates a color signature for a given set of pixels */
|
2005-07-08 07:48:58 +08:00
|
|
|
static lab *
|
2005-07-28 06:52:34 +08:00
|
|
|
create_signature (lab *input,
|
|
|
|
gint length,
|
2005-08-08 00:07:32 +08:00
|
|
|
const gfloat *limits,
|
2005-07-28 06:52:34 +08:00
|
|
|
gint *returnlength)
|
2005-07-08 07:45:38 +08:00
|
|
|
{
|
2005-08-08 00:32:32 +08:00
|
|
|
ArrayList *clusters;
|
2005-07-08 07:48:58 +08:00
|
|
|
ArrayList *curelem;
|
|
|
|
lab *centroids;
|
|
|
|
lab *rval;
|
2005-08-08 00:32:32 +08:00
|
|
|
gint size;
|
2005-08-08 04:20:53 +08:00
|
|
|
gint i;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-09 06:33:18 +08:00
|
|
|
if (length < 1)
|
|
|
|
{
|
|
|
|
*returnlength = 0;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-08-08 00:32:32 +08:00
|
|
|
clusters = list_new ();
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 00:32:32 +08:00
|
|
|
stageone (input, SIOX_DIMS, 0, clusters, limits, length);
|
|
|
|
size = list_size (clusters);
|
|
|
|
centroids = g_new (lab, size);
|
|
|
|
curelem = clusters;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
i = 0;
|
|
|
|
while (curelem->array)
|
|
|
|
{
|
2005-08-08 04:20:53 +08:00
|
|
|
lab *cluster = curelem->array;
|
|
|
|
gfloat l = 0;
|
|
|
|
gfloat a = 0;
|
|
|
|
gfloat b = 0;
|
|
|
|
gint k;
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
for (k = 0; k < curelem->arraylength; k++)
|
|
|
|
{
|
2005-08-08 00:07:32 +08:00
|
|
|
l += cluster[k].l;
|
|
|
|
a += cluster[k].a;
|
|
|
|
b += cluster[k].b;
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
|
|
|
|
2005-08-08 00:07:32 +08:00
|
|
|
centroids[i].l = l / curelem->arraylength;
|
|
|
|
centroids[i].a = a / curelem->arraylength;
|
|
|
|
centroids[i].b = b / curelem->arraylength;
|
2005-08-08 00:32:32 +08:00
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
centroids[i].cardinality = curelem->arraylength;
|
2005-07-15 00:26:26 +08:00
|
|
|
|
2005-07-08 07:48:58 +08:00
|
|
|
i++;
|
|
|
|
curelem = curelem->next;
|
|
|
|
}
|
|
|
|
|
2005-08-08 08:31:55 +08:00
|
|
|
#ifdef SIOX_DEBUG
|
2005-08-08 00:32:32 +08:00
|
|
|
g_printerr ("step #1 -> %d clusters\n", size);
|
2005-07-28 08:18:39 +08:00
|
|
|
#endif
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 00:32:32 +08:00
|
|
|
free_list (clusters);
|
|
|
|
|
|
|
|
clusters = list_new ();
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-15 00:26:26 +08:00
|
|
|
stagetwo (centroids,
|
2005-08-08 00:32:32 +08:00
|
|
|
SIOX_DIMS, 0, clusters, limits, size, length,
|
2005-07-16 03:07:38 +08:00
|
|
|
0.1 /* magic constant, see paper by tomasi */);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 00:32:32 +08:00
|
|
|
rval = list_to_array (clusters, returnlength);
|
2005-07-09 03:42:29 +08:00
|
|
|
|
2005-08-08 00:32:32 +08:00
|
|
|
free_list (clusters);
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-08-08 08:31:55 +08:00
|
|
|
#ifdef SIOX_DEBUG
|
2005-07-28 08:18:39 +08:00
|
|
|
g_printerr ("step #2 -> %d clusters\n", returnlength[0]);
|
|
|
|
#endif
|
2005-07-08 07:48:58 +08:00
|
|
|
|
|
|
|
return rval;
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
static void
|
|
|
|
threshold_mask (TileManager *mask,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
PixelRegion region;
|
|
|
|
gpointer pr;
|
|
|
|
gint row, col;
|
|
|
|
|
|
|
|
pixel_region_init (®ion, mask, x, y, width, height, TRUE);
|
|
|
|
|
|
|
|
for (pr = pixel_regions_register (1, ®ion);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
guchar *data = region.data;
|
|
|
|
|
|
|
|
for (row = 0; row < region.h; row++)
|
|
|
|
{
|
|
|
|
guchar *d = data;
|
|
|
|
|
|
|
|
for (col = 0; col < region.w; col++, d++)
|
2005-08-08 00:07:32 +08:00
|
|
|
*d = (*d & 0x80) ? 255 : 0;
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
data += region.rowstride;
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
2005-07-10 06:44:26 +08:00
|
|
|
}
|
|
|
|
}
|
2005-07-08 07:48:58 +08:00
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
static void
|
|
|
|
smooth_mask (TileManager *mask,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
2005-07-12 05:03:10 +08:00
|
|
|
PixelRegion region;
|
|
|
|
|
|
|
|
pixel_region_init (®ion, mask, x, y, width, height, TRUE);
|
|
|
|
|
2005-07-14 01:27:37 +08:00
|
|
|
smooth_region (®ion);
|
2005-07-10 06:44:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
erode_mask (TileManager *mask,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
PixelRegion region;
|
|
|
|
|
|
|
|
pixel_region_init (®ion, mask, x, y, width, height, TRUE);
|
|
|
|
|
2005-07-14 23:40:19 +08:00
|
|
|
erode_region (®ion);
|
2005-07-10 06:44:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dilate_mask (TileManager *mask,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
PixelRegion region;
|
|
|
|
|
|
|
|
pixel_region_init (®ion, mask, x, y, width, height, TRUE);
|
|
|
|
|
2005-07-14 23:40:19 +08:00
|
|
|
dilate_region (®ion);
|
2005-07-10 06:44:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
find_max_blob (TileManager *mask,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
GQueue *q = g_queue_new ();
|
|
|
|
gint length = width * height;
|
|
|
|
gint *labelfield = g_new0 (gint, length);
|
|
|
|
PixelRegion region;
|
|
|
|
gpointer pr;
|
|
|
|
gint row, col;
|
|
|
|
gint curlabel = 1;
|
|
|
|
gint maxregion = 0;
|
|
|
|
gint maxblob = 0;
|
|
|
|
|
|
|
|
pixel_region_init (®ion, mask, x, y, width, height, FALSE);
|
|
|
|
|
|
|
|
for (pr = pixel_regions_register (1, ®ion);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
const guchar *data = region.data;
|
|
|
|
gint index = (region.x - x) + (region.y - y) * width;
|
|
|
|
|
|
|
|
for (row = 0; row < region.h; row++)
|
2005-07-08 07:48:58 +08:00
|
|
|
{
|
2005-07-10 06:44:26 +08:00
|
|
|
const guchar *d = data;
|
|
|
|
gint i = index;
|
2005-07-09 05:24:55 +08:00
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
for (col = 0; col < region.w; col++, d++, i++)
|
|
|
|
{
|
|
|
|
gint regioncount = 0;
|
|
|
|
|
|
|
|
if (labelfield[i] == 0 && *d > 127)
|
|
|
|
g_queue_push_tail (q, GINT_TO_POINTER (i));
|
|
|
|
|
|
|
|
while (! g_queue_is_empty (q))
|
|
|
|
{
|
|
|
|
gint pos = GPOINTER_TO_INT (g_queue_pop_head (q));
|
|
|
|
|
|
|
|
if (pos < 0 || pos >= length)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (labelfield[pos] == 0)
|
|
|
|
{
|
|
|
|
guchar val;
|
|
|
|
|
|
|
|
read_pixel_data_1 (mask, pos % width, pos / width, &val);
|
|
|
|
if (val > 127)
|
|
|
|
{
|
|
|
|
labelfield[pos] = curlabel;
|
|
|
|
|
|
|
|
regioncount++;
|
|
|
|
|
|
|
|
g_queue_push_tail (q, GINT_TO_POINTER (pos + 1));
|
|
|
|
g_queue_push_tail (q, GINT_TO_POINTER (pos - 1));
|
|
|
|
g_queue_push_tail (q, GINT_TO_POINTER (pos + width));
|
|
|
|
g_queue_push_tail (q, GINT_TO_POINTER (pos - width));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (regioncount > maxregion)
|
|
|
|
{
|
|
|
|
maxregion = regioncount;
|
|
|
|
maxblob = curlabel;
|
|
|
|
}
|
|
|
|
|
|
|
|
curlabel++;
|
|
|
|
}
|
|
|
|
|
|
|
|
data += region.rowstride;
|
|
|
|
index += width;
|
|
|
|
}
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
pixel_region_init (®ion, mask, x, y, width, height, TRUE);
|
|
|
|
|
|
|
|
for (pr = pixel_regions_register (1, ®ion);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
guchar *data = region.data;
|
|
|
|
gint index = (region.x - x) + (region.y - y) * width;
|
|
|
|
|
|
|
|
for (row = 0; row < region.h; row++)
|
2005-07-08 07:48:58 +08:00
|
|
|
{
|
2005-07-10 06:44:26 +08:00
|
|
|
guchar *d = data;
|
|
|
|
gint i = index;
|
|
|
|
|
|
|
|
for (col = 0; col < region.w; col++, d++, i++)
|
|
|
|
{
|
|
|
|
if (labelfield[i] != 0 && labelfield[i] != maxblob)
|
|
|
|
*d = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
data += region.rowstride;
|
|
|
|
index += width;
|
2005-07-08 07:48:58 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-09 05:24:55 +08:00
|
|
|
g_queue_free (q);
|
2005-07-09 03:42:29 +08:00
|
|
|
g_free (labelfield);
|
2005-07-08 07:45:38 +08:00
|
|
|
}
|
|
|
|
|
2005-07-30 00:09:16 +08:00
|
|
|
static inline void
|
|
|
|
siox_progress_update (SioxProgressFunc progress_callback,
|
|
|
|
gpointer progress_data,
|
|
|
|
gdouble value)
|
|
|
|
{
|
|
|
|
if (progress_data)
|
|
|
|
progress_callback (progress_data, value);
|
|
|
|
}
|
2005-07-10 06:44:26 +08:00
|
|
|
|
2005-07-14 03:30:25 +08:00
|
|
|
/**
|
|
|
|
* siox_foreground_extract:
|
|
|
|
* @pixels: the tiles to extract the foreground from
|
2005-07-28 06:52:34 +08:00
|
|
|
* @colormap: colormap in case @pixels are indexed, %NULL otherwise
|
2005-07-29 04:12:16 +08:00
|
|
|
* @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]).
|
2005-08-06 08:51:22 +08:00
|
|
|
* @x: horizontal offset into the mask
|
|
|
|
* @y: vertical offset into the mask
|
|
|
|
* @width: width of working area on mask
|
|
|
|
* @height: height of working area on mask
|
2005-08-07 03:08:43 +08:00
|
|
|
* @limits: a double array with three entries specifing the accuracy,
|
2005-07-14 03:30:25 +08:00
|
|
|
* a good value is: { 0.66, 1.25, 2.5 }
|
|
|
|
* @smoothness: boundary smoothness (a good value is 3)
|
|
|
|
*
|
|
|
|
* Writes the resulting segmentation into @mask.
|
2005-07-10 06:44:26 +08:00
|
|
|
*/
|
|
|
|
void
|
2005-07-30 00:09:16 +08:00
|
|
|
siox_foreground_extract (TileManager *pixels,
|
|
|
|
const guchar *colormap,
|
|
|
|
gint offset_x,
|
|
|
|
gint offset_y,
|
|
|
|
TileManager *mask,
|
2005-08-06 08:51:22 +08:00
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
2005-07-30 00:09:16 +08:00
|
|
|
gint smoothness,
|
2005-08-07 04:01:22 +08:00
|
|
|
const gdouble limits[SIOX_DIMS],
|
2005-07-30 00:09:16 +08:00
|
|
|
SioxProgressFunc progress_callback,
|
|
|
|
gpointer progress_data)
|
2005-07-10 06:44:26 +08:00
|
|
|
{
|
|
|
|
PixelRegion srcPR;
|
|
|
|
PixelRegion mapPR;
|
|
|
|
gpointer pr;
|
|
|
|
gint bpp;
|
|
|
|
gint row, col;
|
2005-08-07 03:08:43 +08:00
|
|
|
gfloat clustersize;
|
2005-07-28 06:52:34 +08:00
|
|
|
gint surebgcount = 0;
|
|
|
|
gint surefgcount = 0;
|
|
|
|
gint i, j;
|
|
|
|
gint bgsiglen, fgsiglen;
|
|
|
|
lab *surebg;
|
|
|
|
lab *surefg;
|
|
|
|
lab *bgsig;
|
|
|
|
lab *fgsig;
|
2005-08-07 03:08:43 +08:00
|
|
|
gfloat flimits[3];
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
g_return_if_fail (pixels != NULL);
|
|
|
|
g_return_if_fail (mask != NULL && tile_manager_bpp (mask) == 1);
|
2005-08-06 08:51:22 +08:00
|
|
|
g_return_if_fail (x >= 0);
|
|
|
|
g_return_if_fail (y >= 0);
|
|
|
|
g_return_if_fail (x + width <= tile_manager_width (mask));
|
|
|
|
g_return_if_fail (y + height <= tile_manager_height (mask));
|
2005-08-07 04:01:22 +08:00
|
|
|
g_return_if_fail (smoothness >= 0);
|
2005-07-30 00:09:16 +08:00
|
|
|
g_return_if_fail (progress_data == NULL || progress_callback != NULL);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
2005-08-06 08:51:22 +08:00
|
|
|
cpercep_init ();
|
|
|
|
|
2005-07-30 00:09:16 +08:00
|
|
|
siox_progress_update (progress_callback, progress_data, 0.0);
|
|
|
|
|
2005-08-07 03:08:43 +08:00
|
|
|
flimits[0] = limits[0];
|
|
|
|
flimits[1] = limits[1];
|
|
|
|
flimits[2] = limits[2];
|
|
|
|
|
|
|
|
clustersize = get_clustersize (flimits);
|
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
/* count given foreground and background pixels */
|
2005-07-29 04:12:16 +08:00
|
|
|
pixel_region_init (&mapPR, mask, x, y, width, height, FALSE);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
for (pr = pixel_regions_register (1, &mapPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
const guchar *map = mapPR.data;
|
|
|
|
|
|
|
|
for (row = 0; row < mapPR.h; row++)
|
|
|
|
{
|
|
|
|
const guchar *m = map;
|
|
|
|
|
|
|
|
for (col = 0; col < mapPR.w; col++, m++)
|
|
|
|
{
|
2005-08-07 03:08:43 +08:00
|
|
|
if (*m < SIOX_LOW)
|
2005-07-10 06:44:26 +08:00
|
|
|
surebgcount++;
|
2005-08-07 03:08:43 +08:00
|
|
|
else if (*m > SIOX_HIGH)
|
2005-07-10 06:44:26 +08:00
|
|
|
surefgcount++;
|
|
|
|
}
|
|
|
|
|
|
|
|
map += mapPR.rowstride;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
surebg = g_new (lab, surebgcount);
|
|
|
|
surefg = g_new (lab, surefgcount);
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
j = 0;
|
|
|
|
|
2005-07-30 00:09:16 +08:00
|
|
|
siox_progress_update (progress_callback, progress_data, 0.1);
|
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
bpp = tile_manager_bpp (pixels);
|
|
|
|
|
2005-08-06 08:51:22 +08:00
|
|
|
/* create inputs for color signatures */
|
2005-07-29 04:12:16 +08:00
|
|
|
pixel_region_init (&srcPR, pixels,
|
|
|
|
x - offset_x, y - offset_y, width, height, FALSE);
|
|
|
|
pixel_region_init (&mapPR, mask, x, y, width, height, FALSE);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &mapPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
const guchar *src = srcPR.data;
|
|
|
|
const guchar *map = mapPR.data;
|
|
|
|
|
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
const guchar *s = src;
|
|
|
|
const guchar *m = map;
|
|
|
|
|
|
|
|
for (col = 0; col < srcPR.w; col++, m++, s += bpp)
|
|
|
|
{
|
2005-08-07 03:08:43 +08:00
|
|
|
if (*m < SIOX_LOW)
|
2005-07-10 06:44:26 +08:00
|
|
|
{
|
2005-07-28 06:52:34 +08:00
|
|
|
calc_lab (s, bpp, colormap, surebg + i);
|
2005-07-10 06:44:26 +08:00
|
|
|
i++;
|
|
|
|
}
|
2005-08-07 03:08:43 +08:00
|
|
|
else if (*m > SIOX_HIGH)
|
2005-07-10 06:44:26 +08:00
|
|
|
{
|
2005-07-28 06:52:34 +08:00
|
|
|
calc_lab (s, bpp, colormap, surefg + j);
|
2005-07-10 06:44:26 +08:00
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.rowstride;
|
|
|
|
map += mapPR.rowstride;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-07-30 00:09:16 +08:00
|
|
|
siox_progress_update (progress_callback, progress_data, 0.2);
|
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
/* Create color signature for the background */
|
2005-08-07 03:08:43 +08:00
|
|
|
bgsig = create_signature (surebg, surebgcount, flimits, &bgsiglen);
|
2005-07-10 06:44:26 +08:00
|
|
|
g_free (surebg);
|
|
|
|
|
|
|
|
if (bgsiglen < 1)
|
|
|
|
{
|
|
|
|
g_free (surefg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-07-30 00:09:16 +08:00
|
|
|
siox_progress_update (progress_callback, progress_data, 0.3);
|
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
/* Create color signature for the foreground */
|
2005-08-07 03:08:43 +08:00
|
|
|
fgsig = create_signature (surefg, surefgcount, flimits, &fgsiglen);
|
2005-07-10 06:44:26 +08:00
|
|
|
g_free (surefg);
|
|
|
|
|
2005-07-30 00:09:16 +08:00
|
|
|
siox_progress_update (progress_callback, progress_data, 0.4);
|
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
/* Classify - the slow way....Better: Tree traversation */
|
2005-07-29 04:12:16 +08:00
|
|
|
pixel_region_init (&srcPR, pixels,
|
|
|
|
x - offset_x, y - offset_y, width, height, FALSE);
|
|
|
|
pixel_region_init (&mapPR, mask, x, y, width, height, TRUE);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
lab labpixel;
|
|
|
|
gboolean background = FALSE;
|
|
|
|
gfloat min, d;
|
|
|
|
|
2005-08-07 03:08:43 +08:00
|
|
|
if (*m < SIOX_LOW || *m > SIOX_HIGH)
|
2005-07-10 06:44:26 +08:00
|
|
|
continue;
|
|
|
|
|
2005-07-28 06:52:34 +08:00
|
|
|
calc_lab (s, bpp, colormap, &labpixel);
|
2005-07-10 06:44:26 +08:00
|
|
|
background = TRUE;
|
2005-08-08 00:07:32 +08:00
|
|
|
min = euklid (&labpixel, bgsig + 0);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
for (i = 1; i < bgsiglen; i++)
|
|
|
|
{
|
2005-08-08 00:07:32 +08:00
|
|
|
d = euklid (&labpixel, bgsig + i);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
if (d < min)
|
|
|
|
min = d;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fgsiglen == 0)
|
|
|
|
{
|
|
|
|
if (min < clustersize)
|
|
|
|
background = TRUE;
|
|
|
|
else
|
|
|
|
background = FALSE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 0; i < fgsiglen; i++)
|
|
|
|
{
|
2005-08-08 00:07:32 +08:00
|
|
|
d = euklid (&labpixel, fgsig + i);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
if (d < min)
|
|
|
|
{
|
|
|
|
min = d;
|
|
|
|
background = FALSE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*m = background ? 0 : 255;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.rowstride;
|
|
|
|
map += mapPR.rowstride;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (fgsig);
|
|
|
|
g_free (bgsig);
|
|
|
|
|
2005-07-30 00:09:16 +08:00
|
|
|
siox_progress_update (progress_callback, progress_data, 0.9);
|
|
|
|
|
2005-07-10 06:44:26 +08:00
|
|
|
/* Smooth a bit for error killing */
|
2005-07-29 04:12:16 +08:00
|
|
|
smooth_mask (mask, x, y, width, height);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
/* Now erode, to make sure only "strongly connected components"
|
|
|
|
* keep being connected
|
|
|
|
*/
|
2005-07-29 04:12:16 +08:00
|
|
|
erode_mask (mask, x, y, width, height);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
/* search the biggest connected component */
|
2005-07-29 04:12:16 +08:00
|
|
|
find_max_blob (mask, x, y, width, height);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
/* smooth again - as user specified */
|
|
|
|
for (i = 0; i < smoothness; i++)
|
2005-07-29 04:12:16 +08:00
|
|
|
smooth_mask (mask, x, y, width, height);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
/* Threshold the values */
|
2005-07-29 04:12:16 +08:00
|
|
|
threshold_mask (mask, x, y, width, height);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
/* search the biggest connected component again to kill jitter */
|
2005-07-29 04:12:16 +08:00
|
|
|
find_max_blob (mask, x, y, width, height);
|
2005-07-10 06:44:26 +08:00
|
|
|
|
|
|
|
/* Now dilate, to fill up boundary pixels killed by erode */
|
2005-07-29 04:12:16 +08:00
|
|
|
dilate_mask (mask, x, y, width, height);
|
2005-07-30 00:09:16 +08:00
|
|
|
|
|
|
|
siox_progress_update (progress_callback, progress_data, 1.0);
|
2005-07-10 06:44:26 +08:00
|
|
|
}
|