|
Packit |
bc1512 |
/* This file is an image processing operation for GEGL
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* GEGL is free software; you can redistribute it and/or
|
|
Packit |
bc1512 |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
bc1512 |
* License as published by the Free Software Foundation; either
|
|
Packit |
bc1512 |
* version 3 of the License, or (at your option) any later version.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* GEGL is distributed in the hope that it will be useful,
|
|
Packit |
bc1512 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
bc1512 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
bc1512 |
* Lesser General Public License for more details.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
bc1512 |
* License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* Copyright 2010 Danny Robson <danny@blubinc.net>
|
|
Packit |
bc1512 |
* Based off 2006 Anat Levin
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
#include "config.h"
|
|
Packit |
bc1512 |
#include <glib/gi18n-lib.h>
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
#ifdef GEGL_CHANT_PROPERTIES
|
|
Packit |
bc1512 |
gegl_chant_int (epsilon, _("Epsilon"),
|
|
Packit |
bc1512 |
-9, -1, -6,
|
|
Packit |
bc1512 |
_("Log of the error weighting"))
|
|
Packit |
bc1512 |
gegl_chant_int (radius, _("Radius"),
|
|
Packit |
bc1512 |
1, 3, 1,
|
|
Packit |
bc1512 |
_("Radius of the processing window"))
|
|
Packit |
bc1512 |
gegl_chant_double (threshold, _("Threshold"),
|
|
Packit |
bc1512 |
0.0, 0.1, 0.02,
|
|
Packit |
bc1512 |
_("Alpha threshold for multilevel processing"))
|
|
Packit |
bc1512 |
gegl_chant_double (lambda, _("Lambda"),
|
|
Packit |
bc1512 |
0.0, 100.0, 100.0, _("Trimap influence factor"))
|
|
Packit |
bc1512 |
gegl_chant_int (levels, _("Levels"),
|
|
Packit |
bc1512 |
0, 8, 4,
|
|
Packit |
bc1512 |
_("Number of downsampled levels to use"))
|
|
Packit |
bc1512 |
gegl_chant_int (active_levels, _("Active Levels"),
|
|
Packit |
bc1512 |
0, 8, 2,
|
|
Packit |
bc1512 |
_("Number of levels to perform solving"))
|
|
Packit |
bc1512 |
#else
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
#define GEGL_CHANT_TYPE_COMPOSER
|
|
Packit |
bc1512 |
#define GEGL_CHANT_C_FILE "matting-levin.c"
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
#include "gegl-chant.h"
|
|
Packit |
bc1512 |
#include "gegl-debug.h"
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
#include <stdlib.h>
|
|
Packit |
bc1512 |
#include <stdio.h>
|
|
Packit |
bc1512 |
#include <math.h>
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* XXX: We have two options for the two common installation locations of
|
|
Packit |
bc1512 |
* UMFPACK. Ideally this would be sorted out purely in autoconf; see
|
|
Packit |
bc1512 |
* configure.ac for the issues.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
#if defined(HAVE_UMFPACK_H)
|
|
Packit |
bc1512 |
#include <umfpack.h>
|
|
Packit |
bc1512 |
#elif defined (HAVE_SUITESPARSE_UMFPACK_H)
|
|
Packit |
bc1512 |
#include <suitesparse/umfpack.h>
|
|
Packit |
bc1512 |
#endif
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
#include "matting-levin-cblas.h"
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* We don't use the babl_format_get_n_components function for these values,
|
|
Packit |
bc1512 |
* as literal constants can be used for stack allocation of array sizes. They
|
|
Packit |
bc1512 |
* are double checked in matting_process.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
#define COMPONENTS_AUX 2
|
|
Packit |
bc1512 |
#define COMPONENTS_INPUT 3
|
|
Packit |
bc1512 |
#define COMPONENTS_OUTPUT 1
|
|
Packit |
bc1512 |
#define COMPONENTS_COEFF 4
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
#define CONVOLVE_RADIUS 2
|
|
Packit |
bc1512 |
#define CONVOLVE_LEN ((CONVOLVE_RADIUS * 2) + 1)
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* A simple structure holding a compressed column sparse matrix. Data fields
|
|
Packit |
bc1512 |
* correspond directly to the expected format used by UMFPACK. This restricts
|
|
Packit |
bc1512 |
* us to using square matrices.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
typedef struct
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
guint elems,
|
|
Packit |
bc1512 |
columns,
|
|
Packit |
bc1512 |
rows;
|
|
Packit |
bc1512 |
glong *col_idx,
|
|
Packit |
bc1512 |
*row_idx;
|
|
Packit |
bc1512 |
gdouble *values;
|
|
Packit |
bc1512 |
} sparse_t;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* All channels use double precision. Despite it being overly precise, slower,
|
|
Packit |
bc1512 |
* and larger; it's much more convenient:
|
|
Packit |
bc1512 |
* - Input R'G'B' needs to be converted into doubles later when calculating
|
|
Packit |
bc1512 |
* the matting laplacian, as the extra precision is actually useful here,
|
|
Packit |
bc1512 |
* and UMFPACK requires doubles.
|
|
Packit |
bc1512 |
* - AUX Y' is easier to use as a double when dealing with the matting
|
|
Packit |
bc1512 |
* laplacian which is already in doubles.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static const gchar *FORMAT_AUX = "Y'A double";
|
|
Packit |
bc1512 |
static const gchar *FORMAT_INPUT = "R'G'B' double";
|
|
Packit |
bc1512 |
static const gchar *FORMAT_OUTPUT = "Y' double";
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static const guint AUX_VALUE = 0;
|
|
Packit |
bc1512 |
static const guint AUX_ALPHA = 1;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Threshold below which we consider the Y channel to be undefined (or
|
|
Packit |
bc1512 |
* masked). This is a binary specification, either fully masked or fully
|
|
Packit |
bc1512 |
* defined.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static const gdouble TRIMAP_ALPHA_THRESHOLD = 0.01;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* The smallest dimension of a buffer which we allow during downsampling.
|
|
Packit |
bc1512 |
* This must allow sufficient room for CONVOLVE_RADIUS convolutions to work
|
|
Packit |
bc1512 |
* usefully.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static const gint MIN_LEVEL_DIAMETER = 30;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Round upwards with performing `x / y' */
|
|
Packit |
bc1512 |
static guint
|
|
Packit |
bc1512 |
ceil_div (gint x, gint y)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
return (x + y - 1) / y;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Perform a floating point comparison, returning true if the values are
|
|
Packit |
bc1512 |
* within the percentage tolerance specified in FLOAT_TOLERANCE. Note: this
|
|
Packit |
bc1512 |
* is different to GEGL_FLOAT_EQUAL which specifies an absolute delta. This
|
|
Packit |
bc1512 |
* won't work with very small values, however our approach can slower.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gboolean
|
|
Packit |
bc1512 |
float_cmp (gfloat a, gfloat b)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
static const gfloat FLOAT_TOLERANCE = 0.0001f;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
return (a - b) <= FLOAT_TOLERANCE * fabsf (a) ||
|
|
Packit |
bc1512 |
(a - b) <= FLOAT_TOLERANCE * fabsf (b);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Return the offset for the integer coordinates (X, Y), in surface of
|
|
Packit |
bc1512 |
* dimensions R, which has C channels. Does not take into account the channel
|
|
Packit |
bc1512 |
* width, so should be used for indexing into properly typed arrays/pointers.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* Quite expensive without inlining (~15% runtime).
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static inline off_t
|
|
Packit |
bc1512 |
offset (gint x,
|
|
Packit |
bc1512 |
gint y,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict roi,
|
|
Packit |
bc1512 |
gint components)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
return (x + y * roi->width) * components;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Similar to `offset', inlining buys us a good speedup (though is less
|
|
Packit |
bc1512 |
* frequently used than the general purpose `offset').
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static inline gboolean
|
|
Packit |
bc1512 |
trimap_masked (const gdouble *restrict trimap,
|
|
Packit |
bc1512 |
gint x,
|
|
Packit |
bc1512 |
gint y,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict roi)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gdouble value = trimap[offset (x, y, roi, COMPONENTS_AUX) + AUX_ALPHA];
|
|
Packit |
bc1512 |
return value < TRIMAP_ALPHA_THRESHOLD;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static const char*
|
|
Packit |
bc1512 |
matting_umf_error_to_string (guint err)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
switch (err)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
case UMFPACK_OK:
|
|
Packit |
bc1512 |
return "UMFPACK_OK";
|
|
Packit |
bc1512 |
case UMFPACK_WARNING_singular_matrix:
|
|
Packit |
bc1512 |
return "UMFPACK_WARNING_singular_matrix";
|
|
Packit |
bc1512 |
case UMFPACK_WARNING_determinant_underflow:
|
|
Packit |
bc1512 |
return "UMFPACK_WARNING_determinant_underflow";
|
|
Packit |
bc1512 |
case UMFPACK_WARNING_determinant_overflow:
|
|
Packit |
bc1512 |
return "UMFPACK_WARNING_determinant_overflow";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_out_of_memory:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_out_of_memory";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_invalid_Numeric_object:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_invalid_Numeric_object";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_invalid_Symbolic_object:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_invalid_Symbolic_object";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_argument_missing:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_argument_missing";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_n_nonpositive:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_n_nonpositive";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_invalid_matrix:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_invalid_matrix";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_different_pattern:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_different_pattern";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_invalid_system:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_invalid_system";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_invalid_permutation:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_invalid_permutation";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_internal_error:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_internal_error";
|
|
Packit |
bc1512 |
case UMFPACK_ERROR_file_IO:
|
|
Packit |
bc1512 |
return "UMFPACK_ERROR_file_IO";
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
default:
|
|
Packit |
bc1512 |
g_return_val_if_reached ("Unknown UMFPACK error");
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static void
|
|
Packit |
bc1512 |
matting_prepare (GeglOperation *operation)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gegl_operation_set_format (operation, "input", babl_format (FORMAT_INPUT));
|
|
Packit |
bc1512 |
gegl_operation_set_format (operation, "aux", babl_format (FORMAT_AUX));
|
|
Packit |
bc1512 |
gegl_operation_set_format (operation, "output", babl_format (FORMAT_OUTPUT));
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static GeglRectangle
|
|
Packit |
bc1512 |
matting_get_required_for_output (GeglOperation *operation,
|
|
Packit |
bc1512 |
const gchar *input_pad,
|
|
Packit |
bc1512 |
const GeglRectangle *roi)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
GeglRectangle result = *gegl_operation_source_get_bounding_box (operation,
|
|
Packit |
bc1512 |
"input");
|
|
Packit |
bc1512 |
return result;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static GeglRectangle
|
|
Packit |
bc1512 |
matting_get_cached_region (GeglOperation * operation,
|
|
Packit |
bc1512 |
const GeglRectangle * roi)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
return *gegl_operation_source_get_bounding_box (operation, "input");
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* An element-wise subtraction on two 3x3 matrices. */
|
|
Packit |
bc1512 |
static void
|
|
Packit |
bc1512 |
matting_matrix3_matrix3_sub (gdouble _in1[3][3],
|
|
Packit |
bc1512 |
gdouble _in2[3][3],
|
|
Packit |
bc1512 |
gdouble _out[3][3])
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
const gdouble *in1 = (gdouble*)_in1,
|
|
Packit |
bc1512 |
*in2 = (gdouble*)_in2;
|
|
Packit |
bc1512 |
gdouble *out = (gdouble*)_out;
|
|
Packit |
bc1512 |
guint i;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (i = 0; i < 3 * 3; ++i)
|
|
Packit |
bc1512 |
out[i] = in1[i] - in2[i];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* An element-wise division on one 3x3 matrix, by one scalar */
|
|
Packit |
bc1512 |
static void
|
|
Packit |
bc1512 |
matting_matrix3_scalar_div (gdouble _in[3][3],
|
|
Packit |
bc1512 |
gdouble arg,
|
|
Packit |
bc1512 |
gdouble _out[3][3])
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
const gdouble *in = (gdouble*)_in;
|
|
Packit |
bc1512 |
gdouble *out = (gdouble*)_out;
|
|
Packit |
bc1512 |
guint i;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (i = 0; i < 3 * 3; ++i)
|
|
Packit |
bc1512 |
out[i] = in[i] / arg;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Shortcut for a 3x3 matrix inversion. Assumes the matrix is stored in row
|
|
Packit |
bc1512 |
* major format. Parameters 'in' and 'out' must not overlap. The output
|
|
Packit |
bc1512 |
* matrix may be overwritten on error. Returns TRUE if an inversion exists.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* If the matrix consists of column vectors, A = (v_0, v_1, v_2)
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* 1 / (v_1 x v_2)' \
|
|
Packit |
bc1512 |
* inv(A) = ___ | (v_2 x v_0)' |
|
|
Packit |
bc1512 |
* det \ (v_0 x v_1)' /
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gboolean
|
|
Packit |
bc1512 |
matting_matrix3_inverse (gdouble in[3][3],
|
|
Packit |
bc1512 |
gdouble out[3][3])
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gdouble determinant;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Save the column vector cross products straight into the output matrix */
|
|
Packit |
bc1512 |
out[0][0] = in[1][1] * in[2][2] - in[1][2] * in[2][1];
|
|
Packit |
bc1512 |
out[1][0] = in[1][2] * in[2][0] - in[1][0] * in[2][2];
|
|
Packit |
bc1512 |
out[2][0] = in[1][0] * in[2][1] - in[1][1] * in[2][0];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
out[0][1] = in[2][1] * in[0][2] - in[2][2] * in[0][1];
|
|
Packit |
bc1512 |
out[1][1] = in[2][2] * in[0][0] - in[2][0] * in[0][2];
|
|
Packit |
bc1512 |
out[2][1] = in[2][0] * in[0][1] - in[2][1] * in[0][0];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
out[0][2] = in[0][1] * in[1][2] - in[0][2] * in[1][1];
|
|
Packit |
bc1512 |
out[1][2] = in[0][2] * in[1][0] - in[0][0] * in[1][2];
|
|
Packit |
bc1512 |
out[2][2] = in[0][0] * in[1][1] - in[0][1] * in[1][0];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* For a 3x3 matrix, det = v_0 . (v_1 x v_2)
|
|
Packit |
bc1512 |
* We use the cross product that was previously stored into row zero of the
|
|
Packit |
bc1512 |
* output matrix.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
determinant = in[0][0] * out[0][0] +
|
|
Packit |
bc1512 |
in[0][1] * out[1][0] +
|
|
Packit |
bc1512 |
in[0][2] * out[2][0];
|
|
Packit |
bc1512 |
if (determinant == 0)
|
|
Packit |
bc1512 |
return FALSE;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Scale the output by the determinant*/
|
|
Packit |
bc1512 |
matting_matrix3_scalar_div (out, determinant, out);
|
|
Packit |
bc1512 |
return TRUE;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Expanded form for 4x4 matrix inversion, derived from adjugate matrix and
|
|
Packit |
bc1512 |
* division by determinant. Extensively uses values of 2x2 submatrix
|
|
Packit |
bc1512 |
* determinants.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* Implementation based on David Eberly's article `The Laplace Expansion
|
|
Packit |
bc1512 |
* Theorem: Computing the Determinants and Inverses of Matrices'.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* Input and output are in row-major format. Input and output must not
|
|
Packit |
bc1512 |
* overlap. Output will not be altered on error. Returns TRUE if the inverse
|
|
Packit |
bc1512 |
* exists.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gboolean
|
|
Packit |
bc1512 |
matting_matrix4_inverse (gdouble in[4][4],
|
|
Packit |
bc1512 |
gdouble out[4][4])
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gdouble s[6], c[6];
|
|
Packit |
bc1512 |
gdouble det;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
s[0] = in[0][0] * in[1][1] - in[1][0] * in[1][0];
|
|
Packit |
bc1512 |
s[1] = in[0][0] * in[2][1] - in[2][0] * in[0][1];
|
|
Packit |
bc1512 |
s[2] = in[0][0] * in[3][1] - in[3][0] * in[0][1];
|
|
Packit |
bc1512 |
s[3] = in[1][0] * in[2][1] - in[2][0] * in[1][1];
|
|
Packit |
bc1512 |
s[4] = in[1][0] * in[3][1] - in[3][0] * in[1][1];
|
|
Packit |
bc1512 |
s[5] = in[2][0] * in[3][1] - in[3][0] * in[2][1];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
c[5] = in[2][2] * in[3][3] - in[3][2] * in[2][3];
|
|
Packit |
bc1512 |
c[4] = in[1][2] * in[3][3] - in[3][2] * in[1][3];
|
|
Packit |
bc1512 |
c[3] = in[1][2] * in[2][3] - in[2][2] * in[1][3];
|
|
Packit |
bc1512 |
c[2] = in[0][2] * in[3][3] - in[3][2] * in[0][3];
|
|
Packit |
bc1512 |
c[1] = in[0][2] * in[2][3] - in[2][2] * in[0][3];
|
|
Packit |
bc1512 |
c[0] = in[0][2] * in[1][3] - in[1][2] * in[0][3];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
det = s[0] * c[5] -
|
|
Packit |
bc1512 |
s[1] * c[4] +
|
|
Packit |
bc1512 |
s[2] * c[3] +
|
|
Packit |
bc1512 |
s[3] * c[2] -
|
|
Packit |
bc1512 |
s[4] * c[1] +
|
|
Packit |
bc1512 |
s[5] * c[0];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* The determinant can be extremely small in real cases (eg, 1e-15). So
|
|
Packit |
bc1512 |
* existing checks like GEGL_FLOAT_IS_ZERO are no-where near precise enough
|
|
Packit |
bc1512 |
* in the general case.
|
|
Packit |
bc1512 |
* Most of the time we assume there is an inverse, so the lack of precision
|
|
Packit |
bc1512 |
* in here isn't a dealbreaker, and we just compare against an actual zero
|
|
Packit |
bc1512 |
* to avoid divide-by-zero errors.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
if (det == 0.0)
|
|
Packit |
bc1512 |
return FALSE;
|
|
Packit |
bc1512 |
det = 1.0 / det;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
out[0][0] = ( in[1][1] * c[5] - in[2][1] * c[4] + in[3][1] * c[3]) * det;
|
|
Packit |
bc1512 |
out[0][1] = ( -in[1][0] * c[5] + in[2][0] * c[4] - in[3][0] * c[3]) * det;
|
|
Packit |
bc1512 |
out[0][2] = ( in[1][3] * s[5] - in[2][3] * s[4] + in[3][3] * s[3]) * det;
|
|
Packit |
bc1512 |
out[0][3] = ( -in[1][2] * s[5] + in[2][2] * s[4] - in[3][2] * s[3]) * det;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
out[1][0] = ( -in[0][1] * c[5] + in[2][1] * c[2] - in[3][1] * c[1]) * det;
|
|
Packit |
bc1512 |
out[1][1] = ( in[0][0] * c[5] - in[2][0] * c[2] + in[3][0] * c[1]) * det;
|
|
Packit |
bc1512 |
out[1][2] = ( -in[0][3] * s[5] + in[2][3] * s[2] - in[3][3] * s[1]) * det;
|
|
Packit |
bc1512 |
out[1][3] = ( in[0][2] * s[5] - in[2][2] * s[2] + in[3][2] * s[1]) * det;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
out[2][0] = ( in[0][1] * c[4] - in[1][1] * c[2] + in[3][1] * c[0]) * det;
|
|
Packit |
bc1512 |
out[2][1] = ( -in[0][0] * c[4] + in[1][0] * c[2] - in[3][0] * c[0]) * det;
|
|
Packit |
bc1512 |
out[2][2] = ( in[0][3] * s[4] - in[1][3] * s[2] + in[3][3] * s[0]) * det;
|
|
Packit |
bc1512 |
out[2][3] = ( -in[0][2] * s[4] + in[1][2] * s[2] - in[3][2] * s[0]) * det;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
out[3][0] = ( -in[0][1] * c[3] + in[1][1] * c[1] - in[2][1] * c[0]) * det;
|
|
Packit |
bc1512 |
out[3][1] = ( in[0][0] * c[3] - in[1][0] * c[1] + in[2][0] * c[0]) * det;
|
|
Packit |
bc1512 |
out[3][2] = ( -in[0][3] * s[3] + in[1][3] * s[1] - in[2][3] * s[0]) * det;
|
|
Packit |
bc1512 |
out[3][3] = ( in[0][2] * s[3] - in[1][2] * s[1] + in[2][2] * s[0]) * det;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
return TRUE;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Takes a vector and multiplies by its transpose to form a matrix in row
|
|
Packit |
bc1512 |
* major format.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static void
|
|
Packit |
bc1512 |
matting_vector3_self_product (gdouble in[3],
|
|
Packit |
bc1512 |
gdouble out[3][3])
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
out[0][0] = in[0] * in[0];
|
|
Packit |
bc1512 |
out[1][0] = in[0] * in[1];
|
|
Packit |
bc1512 |
out[2][0] = in[0] * in[2];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
out[0][1] = in[1] * in[0];
|
|
Packit |
bc1512 |
out[1][1] = in[1] * in[1];
|
|
Packit |
bc1512 |
out[2][1] = in[1] * in[2];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
out[0][2] = in[2] * in[0];
|
|
Packit |
bc1512 |
out[1][2] = in[2] * in[1];
|
|
Packit |
bc1512 |
out[2][2] = in[2] * in[2];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Perform an erosion on the last component of `pixels'. If all neighbour
|
|
Packit |
bc1512 |
* pixels are greater than low and lesser than 1 - high, keep the pixel
|
|
Packit |
bc1512 |
* value, otherwise set it to NAN.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* Note, the condition is NOT low < pixel < high. Setting high to negative
|
|
Packit |
bc1512 |
* expands the non-masking range.
|
|
Packit |
bc1512 |
* XXX: This could probably be done with seperable passes, however there are
|
|
Packit |
bc1512 |
* more immediate performance bottlenecks.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gdouble *
|
|
Packit |
bc1512 |
matting_erode_range (const gdouble *restrict pixels,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict region,
|
|
Packit |
bc1512 |
guint components,
|
|
Packit |
bc1512 |
guint radius,
|
|
Packit |
bc1512 |
gdouble low,
|
|
Packit |
bc1512 |
gdouble high)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gdouble *new_pixels;
|
|
Packit |
bc1512 |
guint x, y, i, j,
|
|
Packit |
bc1512 |
diameter = radius * 2 + 1;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
new_pixels = g_new0 (gdouble, region->width * region->height);
|
|
Packit |
bc1512 |
for (y = radius; y < region->height - radius; ++y)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (x = radius; x < region->width - radius; ++x)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gdouble home = pixels[offset (x, y,
|
|
Packit |
bc1512 |
region,
|
|
Packit |
bc1512 |
components) + components - 1],
|
|
Packit |
bc1512 |
value;
|
|
Packit |
bc1512 |
if (home == 0.0)
|
|
Packit |
bc1512 |
continue;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
if (home < 0.0 + low || home > 1.00 - high)
|
|
Packit |
bc1512 |
goto masked;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (i = 0; i < diameter; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (j = 0; j < diameter; ++j)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
value = pixels[offset (x - radius + i,
|
|
Packit |
bc1512 |
y - radius + j,
|
|
Packit |
bc1512 |
region,
|
|
Packit |
bc1512 |
components) + components - 1];
|
|
Packit |
bc1512 |
if (value < low || value > 1.0 - high)
|
|
Packit |
bc1512 |
goto masked;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
new_pixels[offset (x, y, region, 1)] = home;
|
|
Packit |
bc1512 |
continue;
|
|
Packit |
bc1512 |
masked:
|
|
Packit |
bc1512 |
new_pixels[offset (x, y, region, 1)] = NAN;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
return new_pixels;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Fill the borders of an image with the pixels from the first row/column
|
|
Packit |
bc1512 |
* outside of `radius'. Does not expand the image. Operates in place.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static void
|
|
Packit |
bc1512 |
matting_fill_borders (gdouble *restrict image,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict region,
|
|
Packit |
bc1512 |
const gint components,
|
|
Packit |
bc1512 |
const gint radius)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gint x, y, c;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_if_fail (image != NULL);
|
|
Packit |
bc1512 |
g_return_if_fail (region != NULL);
|
|
Packit |
bc1512 |
g_return_if_fail (components > 0);
|
|
Packit |
bc1512 |
g_return_if_fail (radius > 0);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Radius shouldn't be greater than the region radius. */
|
|
Packit |
bc1512 |
g_return_if_fail (radius < region->width / 2);
|
|
Packit |
bc1512 |
g_return_if_fail (radius < region->height / 2);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Extend the edges of the convolution outwards */
|
|
Packit |
bc1512 |
for (y = 0; y <= radius; ++y)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
/* Copy the first convolved line into the top `radius' rows */
|
|
Packit |
bc1512 |
memcpy (&image[offset (0, y, region, components)],
|
|
Packit |
bc1512 |
&image[offset (0, radius + 1, region, components)],
|
|
Packit |
bc1512 |
region->width * sizeof (image[0]) * components);
|
|
Packit |
bc1512 |
/* Copy the last convolved line into the last `radius' rows */
|
|
Packit |
bc1512 |
memcpy (&image[offset (0, region->height - y - 1, region, components)],
|
|
Packit |
bc1512 |
&image[offset (0, region->height - radius - 2, region, components)],
|
|
Packit |
bc1512 |
region->width * sizeof (image[0]) * components);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (y = radius; y < region->height - radius; ++y)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (x = 0; x <= radius; ++x)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (c = 0; c < components; ++c)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
image[offset (x, y, region, components) + c] =
|
|
Packit |
bc1512 |
image[offset (radius + 1, y, region, components) + c];
|
|
Packit |
bc1512 |
image[offset (region->width - x - 1, y, region, components) + c] =
|
|
Packit |
bc1512 |
image[offset (region->width - radius - 2, y, region, components) + c];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Calculate the coefficients needed to upsample a previously computed output
|
|
Packit |
bc1512 |
* alpha map. Returns a surface of 4*doubles which correspond to:
|
|
Packit |
bc1512 |
* red * out[0] + green * out[1] + blue * out[2] + out[3]
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gdouble *
|
|
Packit |
bc1512 |
matting_get_linear_coefficients (const gdouble *restrict image,
|
|
Packit |
bc1512 |
const gdouble *restrict alpha,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict rect,
|
|
Packit |
bc1512 |
const gdouble epsilon,
|
|
Packit |
bc1512 |
const gint radius)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gint diameter = radius * 2 + 1,
|
|
Packit |
bc1512 |
window_elems = diameter * diameter,
|
|
Packit |
bc1512 |
image_elems = rect->width * rect->height;
|
|
Packit |
bc1512 |
gdouble *coeffs = g_new0 (gdouble, image_elems * (COMPONENTS_INPUT + 1));
|
|
Packit |
bc1512 |
gint x, y, i, j;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
gdouble window [window_elems + COMPONENTS_INPUT][COMPONENTS_INPUT + 1],
|
|
Packit |
bc1512 |
winprod [COMPONENTS_INPUT + 1][COMPONENTS_INPUT + 1],
|
|
Packit |
bc1512 |
inverse [COMPONENTS_INPUT + 1][COMPONENTS_INPUT + 1],
|
|
Packit |
bc1512 |
invprod [COMPONENTS_INPUT + 1][window_elems + COMPONENTS_INPUT],
|
|
Packit |
bc1512 |
alphmat [window_elems + COMPONENTS_INPUT][1];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (image, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (alpha, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (rect, NULL);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (epsilon != 0.0, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (radius > 0, NULL);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (COMPONENTS_INPUT + 1 == COMPONENTS_COEFF, NULL);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Zero out the main window matrix, and pre-set the lower window identity
|
|
Packit |
bc1512 |
* matrix, ones, and zeroes.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
memset (window, 0, sizeof (window));
|
|
Packit |
bc1512 |
memset (alphmat, 0, sizeof (alphmat));
|
|
Packit |
bc1512 |
for (i = 0; i < COMPONENTS_INPUT; ++i)
|
|
Packit |
bc1512 |
window[window_elems + i][i] = sqrtf (epsilon);
|
|
Packit |
bc1512 |
for (i = 0; i < window_elems; ++i)
|
|
Packit |
bc1512 |
window[i][COMPONENTS_INPUT] = 1.0;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Calculate window's coefficients */
|
|
Packit |
bc1512 |
for (x = radius; x < rect->width - radius; ++x)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (y = radius; y < rect->height - radius; ++y)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
/* / I_r, I_g, I_b, 1 \
|
|
Packit |
bc1512 |
* | ... ... ... 1 |
|
|
Packit |
bc1512 |
* window = | eps 0 0 0 |
|
|
Packit |
bc1512 |
* | 0 eps 0 0 |
|
|
Packit |
bc1512 |
* \ 0 0 eps 0 /
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
for (j = 0; j < diameter; ++j)
|
|
Packit |
bc1512 |
for (i = 0; i < diameter; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
guint image_offset = x - radius + i;
|
|
Packit |
bc1512 |
image_offset += (y - radius + j) * rect->width;
|
|
Packit |
bc1512 |
image_offset *= COMPONENTS_INPUT;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
window[i + j * diameter][0] = image[image_offset + 0];
|
|
Packit |
bc1512 |
window[i + j * diameter][1] = image[image_offset + 1];
|
|
Packit |
bc1512 |
window[i + j * diameter][2] = image[image_offset + 2];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* window' x window */
|
|
Packit |
bc1512 |
cblas_dgemm (CblasRowMajor, CblasTrans, CblasNoTrans,
|
|
Packit |
bc1512 |
COMPONENTS_INPUT + 1,
|
|
Packit |
bc1512 |
COMPONENTS_INPUT + 1,
|
|
Packit |
bc1512 |
window_elems + COMPONENTS_INPUT, 1.0,
|
|
Packit |
bc1512 |
(gdouble *)window, COMPONENTS_INPUT + 1,
|
|
Packit |
bc1512 |
(gdouble *)window, COMPONENTS_INPUT + 1,
|
|
Packit |
bc1512 |
0.0, (gdouble *)winprod, COMPONENTS_INPUT + 1);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* inv ($_) */
|
|
Packit |
bc1512 |
matting_matrix4_inverse (winprod, inverse);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* $_ x window' */
|
|
Packit |
bc1512 |
cblas_dgemm (CblasRowMajor, CblasNoTrans, CblasTrans,
|
|
Packit |
bc1512 |
COMPONENTS_INPUT + 1,
|
|
Packit |
bc1512 |
window_elems + COMPONENTS_INPUT,
|
|
Packit |
bc1512 |
COMPONENTS_INPUT + 1, 1.0,
|
|
Packit |
bc1512 |
(gdouble *)inverse, COMPONENTS_INPUT + 1,
|
|
Packit |
bc1512 |
(gdouble *)window, COMPONENTS_INPUT + 1,
|
|
Packit |
bc1512 |
0.0, (gdouble*)invprod, window_elems + COMPONENTS_INPUT);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* alphmat = | a[x,y], .., a[x+d,y+d], 0, 0, 0, 0 | */
|
|
Packit |
bc1512 |
for (j = 0; j < diameter; ++j)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (i = 0; i < diameter; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
alphmat[i + j * diameter][0] = alpha[offset (x - radius + i,
|
|
Packit |
bc1512 |
y - radius + j,
|
|
Packit |
bc1512 |
rect, 1)];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* $_ x alphmat = | w, x, y, z | */
|
|
Packit |
bc1512 |
cblas_dgemm (CblasRowMajor, CblasNoTrans, CblasNoTrans,
|
|
Packit |
bc1512 |
COMPONENTS_INPUT + 1, 1,
|
|
Packit |
bc1512 |
window_elems + COMPONENTS_INPUT, 1.0,
|
|
Packit |
bc1512 |
(gdouble *)invprod, window_elems + COMPONENTS_INPUT,
|
|
Packit |
bc1512 |
(gdouble *)alphmat, 1,
|
|
Packit |
bc1512 |
0.0, coeffs + offset (x, y, rect, COMPONENTS_INPUT + 1), 1);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
matting_fill_borders (coeffs, rect, COMPONENTS_COEFF, radius);
|
|
Packit |
bc1512 |
return coeffs;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/*
|
|
Packit |
bc1512 |
* Convolves with a seperable 5 element kernel. Modifies the input data in
|
|
Packit |
bc1512 |
* place.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static void
|
|
Packit |
bc1512 |
matting_convolve5 (gdouble *restrict pixels,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict region,
|
|
Packit |
bc1512 |
guint components,
|
|
Packit |
bc1512 |
const gdouble kernel[CONVOLVE_LEN])
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gint x, y, i;
|
|
Packit |
bc1512 |
guint c;
|
|
Packit |
bc1512 |
gdouble *temp = g_new0 (gdouble, region->width * region->height * components);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_if_fail (CONVOLVE_LEN % 2 == 1);
|
|
Packit |
bc1512 |
/* Horizontal convolution */
|
|
Packit |
bc1512 |
for (y = 0; y < region->height; ++y)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (x = CONVOLVE_RADIUS; x < region->width - CONVOLVE_RADIUS; ++x)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (i = 0; i < CONVOLVE_LEN; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (c = 0; c < components; ++c)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
temp [offset ( x, y, region, components) + c] +=
|
|
Packit |
bc1512 |
pixels[offset (x + i - CONVOLVE_RADIUS, y, region, components) + c] * kernel[i];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Vertical convolution */
|
|
Packit |
bc1512 |
memset (pixels, 0, (sizeof (pixels[0]) *
|
|
Packit |
bc1512 |
region->width *
|
|
Packit |
bc1512 |
region->height *
|
|
Packit |
bc1512 |
components));
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (y = CONVOLVE_RADIUS; y < region->height - CONVOLVE_RADIUS; ++y)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (x = 0; x < region->width; ++x)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (i = 0; i < CONVOLVE_LEN; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (c = 0; c < components; ++c)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
pixels[offset (x, y - CONVOLVE_RADIUS, region, components) + c] +=
|
|
Packit |
bc1512 |
temp [offset (x, y + i - CONVOLVE_RADIUS, region, components) + c] * kernel[i];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_free (temp);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
matting_fill_borders (pixels, region, components, CONVOLVE_RADIUS + 1);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static gdouble *
|
|
Packit |
bc1512 |
matting_downsample (gdouble *restrict pixels,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict input,
|
|
Packit |
bc1512 |
GeglRectangle *restrict output,
|
|
Packit |
bc1512 |
guint components)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
/* Downsamples a buffer by a factor of two, and performs a gaussian blur.
|
|
Packit |
bc1512 |
* Returns the output size via the provided pointer; this is not respected as
|
|
Packit |
bc1512 |
* an input parameter.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static const gdouble DOWNSAMPLE_KERNEL[] =
|
|
Packit |
bc1512 |
{ 0.0625, 0.25, 0.375, 0.25, 0.0625 };
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
gint x, y;
|
|
Packit |
bc1512 |
guint c;
|
|
Packit |
bc1512 |
gdouble *down,
|
|
Packit |
bc1512 |
*copy;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (input->x == 0 && input->y == 0, NULL);
|
|
Packit |
bc1512 |
output->x = input->x;
|
|
Packit |
bc1512 |
output->y = input->y;
|
|
Packit |
bc1512 |
output->width = ceil_div (input->width, 2);
|
|
Packit |
bc1512 |
output->height = ceil_div (input->height, 2);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* convolve a copy of the pixels */
|
|
Packit |
bc1512 |
copy = g_new (gdouble, input->width * input->height * components);
|
|
Packit |
bc1512 |
memcpy (copy, pixels, sizeof (pixels[0]) *
|
|
Packit |
bc1512 |
input->width *
|
|
Packit |
bc1512 |
input->height *
|
|
Packit |
bc1512 |
components);
|
|
Packit |
bc1512 |
matting_convolve5 (copy, input, components, DOWNSAMPLE_KERNEL);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* downscale the copy into a new buffer */
|
|
Packit |
bc1512 |
down = g_new (gdouble, output->width * output->height * components);
|
|
Packit |
bc1512 |
for (x = 0; x < input->width; x += 2)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (y = 0; y < input->height; y += 2)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
guint down_offset = (offset (x / 2 , y / 2, output, components)),
|
|
Packit |
bc1512 |
copy_offset = (offset (x , y, input, components));
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (c = 0; c < components; ++c)
|
|
Packit |
bc1512 |
down[down_offset + c] = copy[copy_offset + c];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_free (copy);
|
|
Packit |
bc1512 |
return down;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static gdouble *
|
|
Packit |
bc1512 |
matting_upsample (const gdouble *restrict pixels,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict input,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict output,
|
|
Packit |
bc1512 |
guint components)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
/* Upsample to the size given in output, which must equate to a factor of ~2.
|
|
Packit |
bc1512 |
* Copies in input pixels into the corresponding output locations, leaving
|
|
Packit |
bc1512 |
* the gaps black. Then performs a gaussian blur with a double weighted
|
|
Packit |
bc1512 |
* kernel.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static const gdouble UPSAMPLE_KERNEL[] =
|
|
Packit |
bc1512 |
{ 0.125, 0.5, 0.75, 0.5, 0.125 };
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
gint x_start, x_end,
|
|
Packit |
bc1512 |
y_start, y_end;
|
|
Packit |
bc1512 |
gint x, y;
|
|
Packit |
bc1512 |
guint c;
|
|
Packit |
bc1512 |
gdouble *newpix = NULL;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (pixels, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (input, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (output, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (input->width < output->width &&
|
|
Packit |
bc1512 |
input->height < output->height, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (abs (output->width - 2 * input->width ) <= 1, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (abs (output->height - 2 * input->height) <= 1, NULL);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
x_start = 1;
|
|
Packit |
bc1512 |
y_start = 1;
|
|
Packit |
bc1512 |
x_end = output->width - output->width % 2;
|
|
Packit |
bc1512 |
y_end = output->height - output->height % 2;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
newpix = g_new0 (gdouble, output->width * output->height * components);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (y = y_start; y < output->height; y += 2)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (x = x_start; x < output->width; x += 2)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
guint newoff = (x + y * output->width) * components,
|
|
Packit |
bc1512 |
oldoff = (x / 2 + (y / 2) * input->width ) * components;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (c = 0; c < components; ++c)
|
|
Packit |
bc1512 |
newpix[newoff + c] = pixels[oldoff + c];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
matting_convolve5 (newpix, output, components, UPSAMPLE_KERNEL);
|
|
Packit |
bc1512 |
return newpix;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Upsample a previously computed alpha mat, using linear coefficients taken
|
|
Packit |
bc1512 |
* from the source image. Resizes from small_rect to large_rect, and assumes
|
|
Packit |
bc1512 |
* the factor is 2x +/- 1pixel.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gdouble *
|
|
Packit |
bc1512 |
matting_upsample_alpha (const gdouble *restrict small_pixels,
|
|
Packit |
bc1512 |
const gdouble *restrict large_pixels,
|
|
Packit |
bc1512 |
const gdouble *restrict small_alpha,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict small_rect,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict large_rect,
|
|
Packit |
bc1512 |
gdouble epsilon,
|
|
Packit |
bc1512 |
guint radius)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gdouble *small_coeff = NULL,
|
|
Packit |
bc1512 |
*large_coeff = NULL,
|
|
Packit |
bc1512 |
*new_alpha = NULL;
|
|
Packit |
bc1512 |
gint i;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
small_coeff = matting_get_linear_coefficients (small_pixels, small_alpha,
|
|
Packit |
bc1512 |
small_rect, epsilon,
|
|
Packit |
bc1512 |
radius);
|
|
Packit |
bc1512 |
if (!small_coeff)
|
|
Packit |
bc1512 |
goto cleanup;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
large_coeff = matting_upsample (small_coeff, small_rect, large_rect, COMPONENTS_COEFF);
|
|
Packit |
bc1512 |
if (!large_coeff)
|
|
Packit |
bc1512 |
goto cleanup;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
new_alpha = g_new (gdouble, large_rect->width * large_rect->height);
|
|
Packit |
bc1512 |
for (i = 0; i < large_rect->width * large_rect->height; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
new_alpha[i] = large_coeff[i * COMPONENTS_COEFF + 3];
|
|
Packit |
bc1512 |
new_alpha[i] += large_coeff[i * COMPONENTS_COEFF + 0] * large_pixels[i * COMPONENTS_INPUT + 0];
|
|
Packit |
bc1512 |
new_alpha[i] += large_coeff[i * COMPONENTS_COEFF + 1] * large_pixels[i * COMPONENTS_INPUT + 1];
|
|
Packit |
bc1512 |
new_alpha[i] += large_coeff[i * COMPONENTS_COEFF + 2] * large_pixels[i * COMPONENTS_INPUT + 2];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
cleanup:
|
|
Packit |
bc1512 |
g_free (small_coeff);
|
|
Packit |
bc1512 |
g_free (large_coeff);
|
|
Packit |
bc1512 |
return new_alpha;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static sparse_t *
|
|
Packit |
bc1512 |
matting_sparse_new (guint cols, guint rows, guint elems)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
sparse_t *s = g_new (sparse_t, 1);
|
|
Packit |
bc1512 |
s->columns = cols;
|
|
Packit |
bc1512 |
s->rows = rows;
|
|
Packit |
bc1512 |
s->col_idx = g_new (UF_long, cols + 1);
|
|
Packit |
bc1512 |
s->row_idx = g_new (UF_long, elems);
|
|
Packit |
bc1512 |
s->values = g_new0 (gdouble, elems);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
return s;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static void
|
|
Packit |
bc1512 |
matting_sparse_free (sparse_t *s)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
if (!s)
|
|
Packit |
bc1512 |
return;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_free (s->row_idx);
|
|
Packit |
bc1512 |
g_free (s->col_idx);
|
|
Packit |
bc1512 |
g_free (s->values);
|
|
Packit |
bc1512 |
g_free (s);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static guint
|
|
Packit |
bc1512 |
matting_sparse_elems (const sparse_t *s)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
return s->col_idx[s->columns];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Debugging function which ensures the sparse matrix fields are consistent
|
|
Packit |
bc1512 |
* with what UMFPACK, and the matting algorithm, would expect.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* Returns FALSE, using glib debugging routines, if there is an error. Else,
|
|
Packit |
bc1512 |
* returns TRUE.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gboolean
|
|
Packit |
bc1512 |
matting_verify (const sparse_t *s)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
guint i, j;
|
|
Packit |
bc1512 |
gboolean rows[s->rows];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Must be a square matrix */
|
|
Packit |
bc1512 |
g_return_val_if_fail (s->columns == s->rows, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (s->col_idx[0] == 0, FALSE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (i = 1; i < s->columns; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
/* Strictly ascending column indices */
|
|
Packit |
bc1512 |
guint col = s->col_idx[i];
|
|
Packit |
bc1512 |
g_return_val_if_fail (s->col_idx[i - 1] <= col, FALSE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (j = s->col_idx[i] + 1; j < s->col_idx[i + 1]; ++j)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
/* Strictly ascending row indices, within a column */
|
|
Packit |
bc1512 |
guint row = s->row_idx[j];
|
|
Packit |
bc1512 |
g_return_val_if_fail (s->row_idx[j - 1] < row, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (row < s->rows, FALSE);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* We expect to have entries for each column in the matrix. Note: this is
|
|
Packit |
bc1512 |
* not a requirement of the UMFPACK format; rather, something we expect of
|
|
Packit |
bc1512 |
* the matrix from the matting algorithm.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
for (i = 0; i < s->rows; ++i)
|
|
Packit |
bc1512 |
rows [i] = FALSE;
|
|
Packit |
bc1512 |
for (i = 0; i < matting_sparse_elems (s); ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
guint row = s->row_idx[i];
|
|
Packit |
bc1512 |
g_return_val_if_fail (row < s->rows, FALSE);
|
|
Packit |
bc1512 |
rows[row] = TRUE;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
for (i = 0; i < s->rows; ++i)
|
|
Packit |
bc1512 |
g_return_val_if_fail (rows[i], FALSE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
return TRUE;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Calculate the matting laplacian for an image, given a user trimap.
|
|
Packit |
bc1512 |
* We accumulate entries in a sparse banded matrix, for a radius around each
|
|
Packit |
bc1512 |
* pixel in the image.
|
|
Packit |
bc1512 |
*
|
|
Packit |
bc1512 |
* We construct a triplet form of the matrix initially, then transform to
|
|
Packit |
bc1512 |
* compressed column. This is much simpler than directly constructing the
|
|
Packit |
bc1512 |
* compressed column form, and does not appear to cause a performance
|
|
Packit |
bc1512 |
* bottleneck (though does consume additional memory).
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static sparse_t*
|
|
Packit |
bc1512 |
matting_get_laplacian (const gdouble *restrict image,
|
|
Packit |
bc1512 |
const gdouble *restrict trimap,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict roi,
|
|
Packit |
bc1512 |
const gint radius,
|
|
Packit |
bc1512 |
const gdouble epsilon,
|
|
Packit |
bc1512 |
const gdouble lambda)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gint diameter = radius * 2 + 1,
|
|
Packit |
bc1512 |
window_elems = diameter * diameter,
|
|
Packit |
bc1512 |
image_elems = roi->width * roi->height,
|
|
Packit |
bc1512 |
i, j, k, x, y,
|
|
Packit |
bc1512 |
status;
|
|
Packit |
bc1512 |
UF_long *trip_col,
|
|
Packit |
bc1512 |
*trip_row;
|
|
Packit |
bc1512 |
glong trip_nz = 0,
|
|
Packit |
bc1512 |
trip_cursor = 0,
|
|
Packit |
bc1512 |
trip_masked = 0;
|
|
Packit |
bc1512 |
gdouble *trip_val;
|
|
Packit |
bc1512 |
sparse_t *laplacian;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
gdouble mean[COMPONENTS_INPUT],
|
|
Packit |
bc1512 |
mean_matrix[COMPONENTS_INPUT][COMPONENTS_INPUT],
|
|
Packit |
bc1512 |
covariance[COMPONENTS_INPUT][COMPONENTS_INPUT],
|
|
Packit |
bc1512 |
inverse[COMPONENTS_INPUT][COMPONENTS_INPUT],
|
|
Packit |
bc1512 |
window[COMPONENTS_INPUT][window_elems],
|
|
Packit |
bc1512 |
winxinv[COMPONENTS_INPUT][window_elems],
|
|
Packit |
bc1512 |
values[window_elems][window_elems];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (radius > 0, NULL);
|
|
Packit |
bc1512 |
g_return_val_if_fail (COMPONENTS_INPUT == 3, NULL);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (j = radius; j < roi->height - radius; ++j)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (i = radius; i < roi->width - radius; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
if (trimap_masked (trimap, i, j, roi))
|
|
Packit |
bc1512 |
trip_masked++;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
trip_nz = trip_masked * window_elems * window_elems;
|
|
Packit |
bc1512 |
trip_nz += image_elems; // Sparse diagonal and row summing at conclusion
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
trip_col = g_new (UF_long, trip_nz);
|
|
Packit |
bc1512 |
trip_row = g_new (UF_long, trip_nz);
|
|
Packit |
bc1512 |
trip_val = g_new0 (gdouble, trip_nz);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Compute the contribution of each pixel in the image to the laplacian */
|
|
Packit |
bc1512 |
for (i = radius; i < roi->width - radius; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
for (j = radius; j < roi->height - radius; ++j)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
/* Skip if the pixel is valid in the the trimap */
|
|
Packit |
bc1512 |
if (!trimap_masked (trimap, i, j, roi))
|
|
Packit |
bc1512 |
continue;
|
|
Packit |
bc1512 |
trip_masked--;
|
|
Packit |
bc1512 |
g_return_val_if_fail (trip_masked >= 0, FALSE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Calculate window's component means, and their vector product
|
|
Packit |
bc1512 |
* (which we will use later to calculate the covariance matrix).
|
|
Packit |
bc1512 |
* Store the values into the window matrix as we go.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
mean[0] = mean[1] = mean[2] = 0.0;
|
|
Packit |
bc1512 |
k = 0;
|
|
Packit |
bc1512 |
for (y = j - radius; y <= j + radius; ++y)
|
|
Packit |
bc1512 |
for (x = i - radius; x <= i + radius; ++x)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
mean[0] += window[0][k] = image[(x + y * roi->width) * COMPONENTS_INPUT + 0];
|
|
Packit |
bc1512 |
mean[1] += window[1][k] = image[(x + y * roi->width) * COMPONENTS_INPUT + 1];
|
|
Packit |
bc1512 |
mean[2] += window[2][k] = image[(x + y * roi->width) * COMPONENTS_INPUT + 2];
|
|
Packit |
bc1512 |
++k;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
mean[0] /= window_elems;
|
|
Packit |
bc1512 |
mean[1] /= window_elems;
|
|
Packit |
bc1512 |
mean[2] /= window_elems;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
matting_vector3_self_product (mean, mean_matrix);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/*
|
|
Packit |
bc1512 |
* Calculate inverse covariance matrix.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Multiply the 'component x window' matrix with its transpose to
|
|
Packit |
bc1512 |
* form a 3x3 matrix which is the first component of the covariance
|
|
Packit |
bc1512 |
* matrix.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
cblas_dgemm (CblasRowMajor, CblasNoTrans, CblasTrans,
|
|
Packit |
bc1512 |
COMPONENTS_INPUT, COMPONENTS_INPUT, window_elems,
|
|
Packit |
bc1512 |
1.0 / window_elems,
|
|
Packit |
bc1512 |
(gdouble *)window, window_elems,
|
|
Packit |
bc1512 |
(gdouble *)window, window_elems,
|
|
Packit |
bc1512 |
0.0, (gdouble *)covariance, COMPONENTS_INPUT);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Subtract the mean to create the covariance matrix, then add the
|
|
Packit |
bc1512 |
* epsilon term and invert.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
matting_matrix3_matrix3_sub (covariance, mean_matrix, covariance);
|
|
Packit |
bc1512 |
covariance[0][0] += epsilon / window_elems;
|
|
Packit |
bc1512 |
covariance[1][1] += epsilon / window_elems;
|
|
Packit |
bc1512 |
covariance[2][2] += epsilon / window_elems;
|
|
Packit |
bc1512 |
matting_matrix3_inverse (covariance, inverse);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Subtract each component's mean from the pixels */
|
|
Packit |
bc1512 |
for (k = 0; k < window_elems; ++k)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
window[0][k] -= mean[0];
|
|
Packit |
bc1512 |
window[1][k] -= mean[1];
|
|
Packit |
bc1512 |
window[2][k] -= mean[2];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Calculate the values for the matting matrix */
|
|
Packit |
bc1512 |
cblas_dgemm (CblasRowMajor, CblasNoTrans, CblasNoTrans,
|
|
Packit |
bc1512 |
COMPONENTS_INPUT, window_elems, COMPONENTS_INPUT,
|
|
Packit |
bc1512 |
1.0,
|
|
Packit |
bc1512 |
(gdouble *)inverse, COMPONENTS_INPUT,
|
|
Packit |
bc1512 |
(gdouble *) window, window_elems,
|
|
Packit |
bc1512 |
0.0, (gdouble *)winxinv, window_elems);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
cblas_dgemm (CblasRowMajor, CblasTrans, CblasNoTrans,
|
|
Packit |
bc1512 |
window_elems, window_elems, COMPONENTS_INPUT,
|
|
Packit |
bc1512 |
1.0,
|
|
Packit |
bc1512 |
(gdouble *) window, window_elems,
|
|
Packit |
bc1512 |
(gdouble *)winxinv, window_elems,
|
|
Packit |
bc1512 |
0.0, (gdouble *)values, window_elems);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Store the values and coordinates */
|
|
Packit |
bc1512 |
for (y = 0; y < window_elems; ++y)
|
|
Packit |
bc1512 |
for (x = 0; x < window_elems; ++x)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
UF_long yx = y % diameter,
|
|
Packit |
bc1512 |
yy = y / diameter,
|
|
Packit |
bc1512 |
xx = x % diameter,
|
|
Packit |
bc1512 |
xy = x / diameter;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (trip_cursor < trip_nz, FALSE);
|
|
Packit |
bc1512 |
trip_col[trip_cursor] = (i - radius + yx) + (j - radius + yy) * roi->width,
|
|
Packit |
bc1512 |
trip_row[trip_cursor] = (i - radius + xx) + (j - radius + xy) * roi->width,
|
|
Packit |
bc1512 |
trip_val[trip_cursor] = (1.0 + values[y][x]) / window_elems;
|
|
Packit |
bc1512 |
++trip_cursor;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gdouble row_sum[image_elems];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Calculate the sum of all the elements in each row */
|
|
Packit |
bc1512 |
for (i = 0; i < image_elems; ++i)
|
|
Packit |
bc1512 |
row_sum[i] = 0.0;
|
|
Packit |
bc1512 |
for (i = 0; i < trip_cursor; ++i)
|
|
Packit |
bc1512 |
row_sum[trip_row[i]] += trip_val[i];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Negate each entry of the matrix. This partially implements a
|
|
Packit |
bc1512 |
* subtraction from the diagonal matrix:
|
|
Packit |
bc1512 |
* [lambda + sum, lambda + sum, ..., lambda + sum]
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
for (i = 0; i < trip_cursor; ++i)
|
|
Packit |
bc1512 |
trip_val[i] = -trip_val[i];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Set the diagonal such that the sum of the row equals `lambda' if the
|
|
Packit |
bc1512 |
* trimap entry is valid
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
for (i = 0; i < image_elems; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
trip_col[trip_cursor] = i;
|
|
Packit |
bc1512 |
trip_row[trip_cursor] = i;
|
|
Packit |
bc1512 |
trip_val[trip_cursor] = row_sum[i];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
if (!trimap_masked (trimap, i, 0, roi))
|
|
Packit |
bc1512 |
trip_val[trip_cursor] += lambda;
|
|
Packit |
bc1512 |
trip_cursor++;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Double check that each row equals either 0.0 or lambda */
|
|
Packit |
bc1512 |
for (i = 0; i < image_elems; ++i)
|
|
Packit |
bc1512 |
row_sum[i] = 0.0;
|
|
Packit |
bc1512 |
for (i = 0; i < trip_cursor; ++i)
|
|
Packit |
bc1512 |
row_sum[trip_row[i]] += trip_val[i];
|
|
Packit |
bc1512 |
for (i = 0; i < image_elems; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
g_warn_if_fail (float_cmp (row_sum [i], 0.0) ||
|
|
Packit |
bc1512 |
float_cmp (row_sum [i], lambda));
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_warn_if_fail (trip_cursor == trip_nz);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Convert to the compressed column format expected by UMFPACK */
|
|
Packit |
bc1512 |
laplacian = matting_sparse_new (image_elems, image_elems, trip_cursor);
|
|
Packit |
bc1512 |
status = umfpack_dl_triplet_to_col (laplacian->rows,
|
|
Packit |
bc1512 |
laplacian->columns,
|
|
Packit |
bc1512 |
trip_cursor,
|
|
Packit |
bc1512 |
trip_row, trip_col, trip_val,
|
|
Packit |
bc1512 |
laplacian->col_idx,
|
|
Packit |
bc1512 |
laplacian->row_idx,
|
|
Packit |
bc1512 |
laplacian->values,
|
|
Packit |
bc1512 |
NULL);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_free (trip_col);
|
|
Packit |
bc1512 |
g_free (trip_row);
|
|
Packit |
bc1512 |
g_free (trip_val);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (status == UMFPACK_OK, FALSE);
|
|
Packit |
bc1512 |
return laplacian;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static gboolean
|
|
Packit |
bc1512 |
matting_solve_laplacian (gdouble *restrict trimap,
|
|
Packit |
bc1512 |
sparse_t *restrict laplacian,
|
|
Packit |
bc1512 |
gdouble *restrict solution,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict roi,
|
|
Packit |
bc1512 |
gdouble lambda)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
void *symbolic = NULL,
|
|
Packit |
bc1512 |
*numeric = NULL;
|
|
Packit |
bc1512 |
gint status;
|
|
Packit |
bc1512 |
guint image_elems, i;
|
|
Packit |
bc1512 |
gboolean success = FALSE;
|
|
Packit |
bc1512 |
gdouble umfcontrol[UMFPACK_CONTROL],
|
|
Packit |
bc1512 |
umfinfo[UMFPACK_INFO];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (trimap, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (laplacian, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (solution, FALSE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (roi, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (!gegl_rectangle_is_empty (roi), FALSE);
|
|
Packit |
bc1512 |
image_elems = roi->width * roi->height;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (laplacian->columns == image_elems, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (laplacian->rows == image_elems, FALSE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
matting_verify (laplacian);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
umfpack_di_defaults (umfcontrol);
|
|
Packit |
bc1512 |
/* Pre-process the matrix */
|
|
Packit |
bc1512 |
if ((status = umfpack_dl_symbolic (laplacian->rows,
|
|
Packit |
bc1512 |
laplacian->columns,
|
|
Packit |
bc1512 |
laplacian->col_idx,
|
|
Packit |
bc1512 |
laplacian->row_idx,
|
|
Packit |
bc1512 |
laplacian->values,
|
|
Packit |
bc1512 |
&symbolic,
|
|
Packit |
bc1512 |
umfcontrol, umfinfo)) < 0)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
symbolic = NULL;
|
|
Packit |
bc1512 |
goto cleanup;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
if ((status = umfpack_dl_numeric (laplacian->col_idx,
|
|
Packit |
bc1512 |
laplacian->row_idx,
|
|
Packit |
bc1512 |
laplacian->values,
|
|
Packit |
bc1512 |
symbolic, &numeric,
|
|
Packit |
bc1512 |
umfcontrol, umfinfo)) < 0)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
numeric = NULL;
|
|
Packit |
bc1512 |
goto cleanup;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Solve and exit */
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gdouble *residual = g_new (gdouble, image_elems);
|
|
Packit |
bc1512 |
for (i = 0; i < image_elems; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
if (trimap_masked (trimap, i, 0, roi))
|
|
Packit |
bc1512 |
residual[i] = 0;
|
|
Packit |
bc1512 |
else
|
|
Packit |
bc1512 |
residual[i] = lambda * trimap[i * COMPONENTS_AUX + AUX_VALUE];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
status = umfpack_dl_solve (UMFPACK_A,
|
|
Packit |
bc1512 |
laplacian->col_idx,
|
|
Packit |
bc1512 |
laplacian->row_idx,
|
|
Packit |
bc1512 |
laplacian->values,
|
|
Packit |
bc1512 |
solution,
|
|
Packit |
bc1512 |
residual,
|
|
Packit |
bc1512 |
numeric,
|
|
Packit |
bc1512 |
umfcontrol, umfinfo);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Positive numbers are warnings. We don't care if the matrix is
|
|
Packit |
bc1512 |
* singular, as the computed result is still usable, so just check for
|
|
Packit |
bc1512 |
* errors.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
g_free (residual);
|
|
Packit |
bc1512 |
if (status < 0)
|
|
Packit |
bc1512 |
goto cleanup;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Courtesy clamping of the solution to normal alpha range */
|
|
Packit |
bc1512 |
for (i = 0; i < image_elems; ++i)
|
|
Packit |
bc1512 |
solution[i] = CLAMP (solution[i], 0.0, 1.0);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
success = TRUE;
|
|
Packit |
bc1512 |
cleanup:
|
|
Packit |
bc1512 |
/* Singular matrices appear to work correctly, provided that we clamp the
|
|
Packit |
bc1512 |
* results (which needs to be done regardless). I'm not sure if this is a
|
|
Packit |
bc1512 |
* result of an incorrect implementation of the algorithm, or if it's
|
|
Packit |
bc1512 |
* inherent to the design; either way it seems to work.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
if (status != UMFPACK_OK && status != UMFPACK_WARNING_singular_matrix)
|
|
Packit |
bc1512 |
g_warning ("%s", matting_umf_error_to_string (status));
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
if (numeric)
|
|
Packit |
bc1512 |
umfpack_dl_free_numeric (&numeric);
|
|
Packit |
bc1512 |
if (symbolic)
|
|
Packit |
bc1512 |
umfpack_dl_free_symbolic (&symbolic);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
return success;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Recursively downsample, solve, then upsample the matting laplacian.
|
|
Packit |
bc1512 |
* Perform up to `levels' recursions (provided the image remains large
|
|
Packit |
bc1512 |
* enough), with up to `active_levels' number of full laplacian solves (not
|
|
Packit |
bc1512 |
* just extrapolation).
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gdouble *
|
|
Packit |
bc1512 |
matting_solve_level (gdouble *restrict pixels,
|
|
Packit |
bc1512 |
gdouble *restrict trimap,
|
|
Packit |
bc1512 |
const GeglRectangle *restrict region,
|
|
Packit |
bc1512 |
guint active_levels,
|
|
Packit |
bc1512 |
guint levels,
|
|
Packit |
bc1512 |
guint radius,
|
|
Packit |
bc1512 |
gdouble epsilon,
|
|
Packit |
bc1512 |
gdouble lambda,
|
|
Packit |
bc1512 |
gdouble threshold)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
gint i;
|
|
Packit |
bc1512 |
gdouble *new_alpha = NULL,
|
|
Packit |
bc1512 |
*eroded_alpha = NULL;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
if (region->width < MIN_LEVEL_DIAMETER ||
|
|
Packit |
bc1512 |
region->height < MIN_LEVEL_DIAMETER)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
GEGL_NOTE (GEGL_DEBUG_PROCESS,
|
|
Packit |
bc1512 |
"skipping subdivision with level %dx%d\n",
|
|
Packit |
bc1512 |
region->width, region->height);
|
|
Packit |
bc1512 |
levels = 0;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
if (levels > 0)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
GeglRectangle small_region;
|
|
Packit |
bc1512 |
gdouble *small_pixels,
|
|
Packit |
bc1512 |
*small_trimap;
|
|
Packit |
bc1512 |
gdouble *small_alpha;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Downsample, solve, then upsample again */
|
|
Packit |
bc1512 |
small_pixels = matting_downsample (pixels, region, &small_region,
|
|
Packit |
bc1512 |
COMPONENTS_INPUT);
|
|
Packit |
bc1512 |
small_trimap = matting_downsample (trimap, region, &small_region,
|
|
Packit |
bc1512 |
COMPONENTS_AUX);
|
|
Packit |
bc1512 |
for (i = 0; i < small_region.width *
|
|
Packit |
bc1512 |
small_region.height *
|
|
Packit |
bc1512 |
COMPONENTS_AUX; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
small_trimap[i] = roundf (small_trimap[i]);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
small_alpha = matting_solve_level (small_pixels, small_trimap,
|
|
Packit |
bc1512 |
&small_region, active_levels,
|
|
Packit |
bc1512 |
levels - 1, radius, epsilon,
|
|
Packit |
bc1512 |
lambda, threshold);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
new_alpha = matting_upsample_alpha (small_pixels, pixels, small_alpha,
|
|
Packit |
bc1512 |
&small_region, region, epsilon,
|
|
Packit |
bc1512 |
radius);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Erode the result:
|
|
Packit |
bc1512 |
* If the trimap alpha has not been set high (ie, valid), update the
|
|
Packit |
bc1512 |
* trimap value with our computed result.
|
|
Packit |
bc1512 |
* If it was eroded, then set the trimap pixel as valid by setting
|
|
Packit |
bc1512 |
* alpha high.
|
|
Packit |
bc1512 |
* Set all trimap values as either high or low.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
eroded_alpha = matting_erode_range (new_alpha, region, 1, radius,
|
|
Packit |
bc1512 |
threshold, threshold);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_free (small_pixels);
|
|
Packit |
bc1512 |
g_free (small_trimap);
|
|
Packit |
bc1512 |
g_free (small_alpha);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
for (i = 0; i < region->width * region->height; ++i)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
if (trimap[i * COMPONENTS_AUX + AUX_ALPHA] < TRIMAP_ALPHA_THRESHOLD)
|
|
Packit |
bc1512 |
trimap[i * COMPONENTS_AUX + AUX_VALUE] = new_alpha[i];
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
if (isnan (eroded_alpha[i]))
|
|
Packit |
bc1512 |
trimap[i * COMPONENTS_AUX + AUX_ALPHA] = 1.0;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
trimap[i * COMPONENTS_AUX + AUX_VALUE] *= roundf (trimap[i * COMPONENTS_AUX + AUX_VALUE]) *
|
|
Packit |
bc1512 |
trimap[i * COMPONENTS_AUX + AUX_ALPHA];
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
g_free (eroded_alpha);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Ordinary solution of the matting laplacian */
|
|
Packit |
bc1512 |
if (active_levels >= levels || levels == 0)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
sparse_t *laplacian;
|
|
Packit |
bc1512 |
g_free (new_alpha);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
if (!(laplacian = matting_get_laplacian (pixels, trimap, region,
|
|
Packit |
bc1512 |
radius, epsilon, lambda)))
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
g_warning ("unable to construct laplacian matrix");
|
|
Packit |
bc1512 |
return NULL;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
new_alpha = g_new (gdouble, region->width * region->height);
|
|
Packit |
bc1512 |
matting_solve_laplacian (trimap, laplacian, new_alpha, region, lambda);
|
|
Packit |
bc1512 |
matting_sparse_free (laplacian);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (new_alpha != NULL, NULL);
|
|
Packit |
bc1512 |
return new_alpha;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
/* Simple wrapper around matting_solve_level, which extracts the relevant
|
|
Packit |
bc1512 |
* pixel data and writes the solution to output.
|
|
Packit |
bc1512 |
*/
|
|
Packit |
bc1512 |
static gboolean
|
|
Packit |
bc1512 |
matting_process (GeglOperation *operation,
|
|
Packit |
bc1512 |
GeglBuffer *input_buf,
|
|
Packit |
bc1512 |
GeglBuffer *aux_buf,
|
|
Packit |
bc1512 |
GeglBuffer *output_buf,
|
|
Packit |
bc1512 |
const GeglRectangle *result,
|
|
Packit |
bc1512 |
gint level)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
const GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
|
|
Packit |
bc1512 |
gdouble *input = NULL,
|
|
Packit |
bc1512 |
*trimap = NULL;
|
|
Packit |
bc1512 |
gdouble *output = NULL;
|
|
Packit |
bc1512 |
gboolean success = FALSE;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (babl_format_get_n_components (babl_format (FORMAT_INPUT )) == COMPONENTS_INPUT, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (babl_format_get_n_components (babl_format (FORMAT_AUX )) == COMPONENTS_AUX, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (babl_format_get_n_components (babl_format (FORMAT_OUTPUT)) == COMPONENTS_OUTPUT, FALSE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_return_val_if_fail (operation, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (input_buf, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (aux_buf, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (output_buf, FALSE);
|
|
Packit |
bc1512 |
g_return_val_if_fail (result, FALSE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
input = g_new (gdouble, result->width * result->height * COMPONENTS_INPUT);
|
|
Packit |
bc1512 |
trimap = g_new (gdouble, result->width * result->height * COMPONENTS_AUX);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
gegl_buffer_get (input_buf, result, 1.0, babl_format (FORMAT_INPUT), input, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
Packit |
bc1512 |
gegl_buffer_get ( aux_buf, result, 1.0, babl_format (FORMAT_AUX), trimap, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
output = matting_solve_level (input, trimap, result,
|
|
Packit |
bc1512 |
MIN (o->active_levels, o->levels), o->levels,
|
|
Packit |
bc1512 |
o->radius, powf (10, o->epsilon), o->lambda,
|
|
Packit |
bc1512 |
o->threshold);
|
|
Packit |
bc1512 |
gegl_buffer_set (output_buf, result, 0, babl_format (FORMAT_OUTPUT), output,
|
|
Packit |
bc1512 |
GEGL_AUTO_ROWSTRIDE);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
success = TRUE;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
g_free (input);
|
|
Packit |
bc1512 |
g_free (trimap);
|
|
Packit |
bc1512 |
g_free (output);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
return success;
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
static void
|
|
Packit |
bc1512 |
gegl_chant_class_init (GeglChantClass *klass)
|
|
Packit |
bc1512 |
{
|
|
Packit |
bc1512 |
GeglOperationClass *operation_class;
|
|
Packit |
bc1512 |
GeglOperationComposerClass *composer_class;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
operation_class = GEGL_OPERATION_CLASS (klass);
|
|
Packit |
bc1512 |
composer_class = GEGL_OPERATION_COMPOSER_CLASS (klass);
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
composer_class->process = matting_process;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
operation_class->prepare = matting_prepare;
|
|
Packit |
bc1512 |
operation_class->get_required_for_output = matting_get_required_for_output;
|
|
Packit |
bc1512 |
operation_class->get_cached_region = matting_get_cached_region;
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
gegl_operation_class_set_keys (operation_class,
|
|
Packit |
bc1512 |
"name" , "gegl:matting-levin",
|
|
Packit |
bc1512 |
"categories" , "misc",
|
|
Packit |
bc1512 |
"description" ,
|
|
Packit |
bc1512 |
_("Given a sparse user supplied tri-map and an input image, create a "
|
|
Packit |
bc1512 |
"foreground alpha mat. Set white as selected, black as unselected, "
|
|
Packit |
bc1512 |
"for the tri-map."),
|
|
Packit |
bc1512 |
NULL);
|
|
Packit |
bc1512 |
}
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
|
|
Packit |
bc1512 |
#endif
|