Commit eef03d4d authored by Marek Dvoroznak's avatar Marek Dvoroznak Committed by Mikael Magnusson

libs: npd: integrate mesh cutting algorithm and fix a bug in it

parent 2dfa9523
......@@ -11,7 +11,7 @@ GEGL_NPD_public_HEADERS = \
deformation.h \
npd_math.h \
graphics.h \
refine.h \
lattice_cut.h \
npd_gegl.h \
npd.h
......@@ -21,7 +21,7 @@ GEGL_NPD_SOURCES = \
deformation.c \
npd_math.c \
graphics.c \
refine.c \
lattice_cut.c \
npd_gegl.c
libgegl_npd_@GEGL_API_VERSION@_la_SOURCES = \
......
......@@ -55,6 +55,7 @@ NPD_COMPUTE_CENTROID (from_weighted_points,
gfloat weights[],
weights[i],
.)
#undef NPD_COMPUTE_CENTROID
void
npd_compute_ARSAP_transformation (gint num_of_points,
......
......@@ -23,6 +23,9 @@
#include "glib.h"
#include <math.h>
#include "npd_math.h"
#include "lattice_cut.h"
#include <stdio.h>
#include <string.h>
void
npd_create_mesh_from_image (NPDModel *model,
......@@ -31,105 +34,186 @@ npd_create_mesh_from_image (NPDModel *model,
gint position_x,
gint position_y)
{
gint square_size = model->mesh_square_size;
NPDHiddenModel *hidden_model = model->hidden_model;
gint square_size = model->mesh_square_size;
NPDImage *image = model->reference_image;
gint i, cy, cx, y, x;
NPDColor pixel_color = { 0, 0, 0, 0 };
GPtrArray *current_bones, *reference_bones;
gint i, cy, cx, y, x, r, c, ow, oh;
NPDColor pixel_color = { 0, 0, 0, 0 };
GArray *squares;
gint *sq2id;
gboolean *empty_squares;
GList *tmp_ops = NULL;
GArray *ops = g_array_new (FALSE, FALSE, sizeof (NPDOverlappingPoints));
GList **edges;
NPDHiddenModel *hm = model->hidden_model;
/* create quadrilaterals above the image */
/* create squares above the image */
width = ceil ((gfloat) width / square_size);
height = ceil ((gfloat) height / square_size);
current_bones = g_ptr_array_new ();
reference_bones = g_ptr_array_new ();
squares = g_array_new (FALSE, FALSE, sizeof (NPDBone));
empty_squares = g_new0 (gboolean, width * height);
sq2id = g_new0 (gint, width * height);
i = 0;
for (cy = 0; cy < height; cy++)
for (cx = 0; cx < width; cx++)
{
for (cx = 0; cx < width; cx++)
{
gboolean is_empty = TRUE;
gboolean is_empty = TRUE;
for (y = cy * square_size; y < (cy + 1) * square_size; y++)
for (y = cy * square_size; y < (cy + 1) * square_size; y++)
for (x = cx * square_size; x < (cx + 1) * square_size; x++)
{
/* test of emptiness */
npd_get_pixel_color (image, x, y, &pixel_color);
if (!npd_is_color_transparent (&pixel_color))
{
for (x = cx * square_size; x < (cx + 1) * square_size; x++)
{
npd_get_pixel_color (image, x, y, &pixel_color);
if (!npd_is_color_transparent (&pixel_color))
{
is_empty = FALSE;
goto not_empty;
}
}
is_empty = FALSE;
goto not_empty;
}
}
#define NPD_SQ_ID cy * width + cx
not_empty:
if (!is_empty)
{
/* create a square */
NPDBone square;
npd_create_square (&square,
position_x + cx * square_size,
position_y + cy * square_size,
square_size, square_size);
g_array_append_val (squares, square);
sq2id[NPD_SQ_ID] = i;
i++;
}
else
empty_squares[NPD_SQ_ID] = TRUE;
}
/* find empty edges */
edges = npd_find_edges (model->reference_image, width, height, square_size);
/* create provisional overlapping points */
#define NPD_ADD_P(op,r,c,point) \
if ((r) > -1 && (r) < (oh - 1) && (c) > -1 && (c) < (ow - 1) && \
edges[op] == NULL && !empty_squares[(r) * width + (c)]) \
{ \
tmp_ops = g_list_append (tmp_ops, GINT_TO_POINTER ((r) * width + (c)));\
tmp_ops = g_list_append (tmp_ops, GINT_TO_POINTER (point)); \
num++; \
}
ow = width + 1; oh = height + 1;
for (r = 0; r < oh; r++)
for (c = 0; c < ow; c++)
{
gint index = r * ow + c, num = 0;
NPD_ADD_P (index, r - 1, c - 1, 2);
NPD_ADD_P (index, r - 1, c, 3);
NPD_ADD_P (index, r, c, 0);
NPD_ADD_P (index, r, c - 1, 1);
if (num > 0)
tmp_ops = g_list_insert_before (tmp_ops,
g_list_nth_prev (g_list_last (tmp_ops), 2 * num - 1),
GINT_TO_POINTER (num));
}
#undef NPD_ADD_P
/* cut lattice's edges and continue with creating of provisional overlapping points */
tmp_ops = g_list_concat (tmp_ops, npd_cut_edges (edges, ow, oh));
for (i = 0; i < ow * oh; i++) g_list_free (edges[i]);
g_free (edges);
/* create model's bones */
hm->num_of_bones = squares->len;
hm->current_bones = (NPDBone*) (gpointer) squares->data;
g_array_free (squares, FALSE);
not_empty:
if (!is_empty)
/* create model's overlapping points */
while (g_list_next (tmp_ops))
{
GPtrArray *ppts = g_ptr_array_new ();
gint count = GPOINTER_TO_INT (tmp_ops->data);
gint j = 0;
for (i = 0; i < count; i++)
{
gint sq, p;
tmp_ops = g_list_next (tmp_ops);
sq = GPOINTER_TO_INT (tmp_ops->data);
tmp_ops = g_list_next (tmp_ops);
p = GPOINTER_TO_INT (tmp_ops->data);
if (!empty_squares[sq])
{
NPDBone *current_bone, *reference_bone;
NPDPoint *current_points, *ref_points;
gfloat *weights;
gint coords[8] = {cx, cx + 1, cx + 1, cx,
cy, cy, cy + 1, cy + 1};
current_bone = g_new (NPDBone, 1);
reference_bone = g_new (NPDBone, 1);
current_points = g_new (NPDPoint, 4);
ref_points = g_new (NPDPoint, 4);
weights = g_new (gfloat, 4);
for (i = 0; i < 4; i++)
{
weights[i] = 1.0;
current_points[i].x = position_x + (coords[i] * square_size);
current_points[i].y = position_y + (coords[i + 4] * square_size);
current_points[i].weight = &weights[i];
current_points[i].fixed = FALSE;
current_points[i].current_bone = current_bone;
current_points[i].reference_bone = reference_bone;
current_points[i].index = i;
ref_points[i].x = current_points[i].x - position_x;
ref_points[i].y = current_points[i].y - position_y;
ref_points[i].weight = &weights[i];
ref_points[i].fixed = current_points[i].fixed;
ref_points[i].current_bone = current_bone;
ref_points[i].reference_bone = reference_bone;
ref_points[i].index = i;
current_points[i].counterpart = &ref_points[i];
ref_points[i].counterpart = &current_points[i];
}
current_bone->points = current_points;
current_bone->num_of_points = 4;
current_bone->weights = weights;
g_ptr_array_add (current_bones, current_bone);
reference_bone->points = ref_points;
reference_bone->num_of_points = 4;
reference_bone->weights = weights;
g_ptr_array_add (reference_bones, reference_bone);
hidden_model->num_of_bones++;
g_ptr_array_add (ppts, &hm->current_bones[sq2id[sq]].points[p]);
j++;
}
}
if (j > 0)
{
NPDOverlappingPoints op;
op.num_of_points = j;
op.points = (NPDPoint**) ppts->pdata;
g_ptr_array_free (ppts, FALSE);
op.representative = op.points[0];
g_array_append_val (ops, op);
}
tmp_ops = g_list_next (tmp_ops);
}
hidden_model->current_bones = g_new (NPDBone, current_bones->len);
hidden_model->reference_bones = g_new (NPDBone, reference_bones->len);
g_list_free (tmp_ops);
g_free (empty_squares);
g_free (sq2id);
for (i = 0; i < current_bones->len; i++)
hm->num_of_overlapping_points = ops->len;
hm->list_of_overlapping_points = (NPDOverlappingPoints*) (gpointer) ops->data;
g_array_free (ops, FALSE);
/* create reference bones according to current bones */
hm->reference_bones = g_new (NPDBone, hm->num_of_bones);
for (i = 0; i < hm->num_of_bones; i++)
{
hidden_model->current_bones[i] = *(NPDBone*) g_ptr_array_index (current_bones, i);
hidden_model->reference_bones[i] = *(NPDBone*) g_ptr_array_index (reference_bones, i);
NPDBone *current_bone = &hm->current_bones[i];
NPDBone *reference_bone = &hm->reference_bones[i];
NPDPoint *current_points, *ref_points;
gint j, n = current_bone->num_of_points;
reference_bone->num_of_points = n;
reference_bone->points = g_new (NPDPoint, n);
memcpy (reference_bone->points, current_bone->points, n * sizeof (NPDPoint));
reference_bone->weights = current_bone->weights;
current_points = current_bone->points;
ref_points = reference_bone->points;
for (j = 0; j < n; j++)
{
current_points[j].current_bone = current_bone;
current_points[j].reference_bone = reference_bone;
ref_points[j].current_bone = current_bone;
ref_points[j].reference_bone = reference_bone;
ref_points[j].x = current_points[j].x - position_x;
ref_points[j].y = current_points[j].y - position_y;
current_points[j].counterpart = &ref_points[j];
ref_points[j].counterpart = &current_points[j];
}
}
g_ptr_array_free (current_bones, TRUE);
g_ptr_array_free (reference_bones, TRUE);
/*
// could be useful later
gint j;
for (i = 0; i < hm->num_of_overlapping_points; i++)
for (j = 0; j < hm->list_of_overlapping_points[i].num_of_points; j++)
{
NPDPoint *p = hm->list_of_overlapping_points[i].points[j];
p->overlapping_points = &hm->list_of_overlapping_points[i];
p->counterpart->overlapping_points = &hm->list_of_overlapping_points[i];
}
*/
}
void
......
/*
* This file is part of N-point image deformation library.
*
* N-point image deformation library is free software: you can
* redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* N-point image deformation library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with N-point image deformation library.
* If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2013 Marek Dvoroznak <dvoromar@gmail.com>
*/
#include "lattice_cut.h"
#include "npd_common.h"
#include "graphics.h"
#include <glib.h>
#include "npd_math.h"
#define NPD_SWAP_INTS(i,j) { gint tmp = i; i = j; j = tmp; }
/* only works for straight lines */
gboolean
npd_is_edge_empty (NPDImage *image,
gint X1,
gint Y1,
gint X2,
gint Y2)
{
gint x, y;
NPDColor color;
if (Y1 > Y2) NPD_SWAP_INTS (Y1, Y2);
if (X1 > X2) NPD_SWAP_INTS (X1, X2);
for (y = Y1; y <= Y2; y++)
for (x = X1; x <= X2; x++)
{
npd_get_pixel_color (image, x, y, &color);
if (!npd_is_color_transparent (&color))
return FALSE;
}
return TRUE;
}
GList**
npd_find_edges (NPDImage *image,
gint count_x,
gint count_y,
gint square_size)
{
gint i, j;
gint ow = count_x + 1,
oh = count_y + 1;
GList **empty_edges = g_new0 (GList*, ow * oh);
for (j = 1; j <= count_y; j++)
for (i = 1; i <= count_x; i++)
{
#define NPD_TEST_EMPTY(from_op_x,from_op_y,to_op_x,to_op_y) \
if (npd_is_edge_empty (image, \
(from_op_x) * square_size, (from_op_y) * square_size, \
(to_op_x) * square_size, (to_op_y) * square_size)) \
{ \
gint from_op_id = (from_op_y) * ow + (from_op_x), \
to_op_id = (to_op_y) * ow + (to_op_x); \
empty_edges[from_op_id] = g_list_append (empty_edges[from_op_id], \
GINT_TO_POINTER (to_op_id)); \
empty_edges[to_op_id] = g_list_append (empty_edges[to_op_id], \
GINT_TO_POINTER (from_op_id)); \
}
if (j != count_y) NPD_TEST_EMPTY (i, j, i - 1, j);
if (i != count_x) NPD_TEST_EMPTY (i, j, i, j - 1);
#undef NPD_TEST_EMPTY
}
return empty_edges;
}
GList*
npd_cut_edges (GList **edges,
gint ow,
gint oh)
{
gint i, r, col, width = ow - 1;
GList *ops = NULL;
gint neighbors[4];
for (r = 0; r < oh; r++)
for (col = 0; col < ow; col++)
{
gint index = r * ow + col;
GList *op = edges[index];
gint num_of_neighbors = g_list_length (op);
if (num_of_neighbors == 0) continue;
#define NPD_ADD_COUNT(count) ops = g_list_append (ops, GINT_TO_POINTER (count))
#define NPD_ADD_P(r,col,point) \
if ((r) > -1 && (r) < (oh - 1) && (col) > -1 && (col) < (ow - 1)) \
{ \
ops = g_list_append (ops, GINT_TO_POINTER ((r) * width + (col))); \
ops = g_list_append (ops, GINT_TO_POINTER (point)); \
}
for (i = 0; i < num_of_neighbors; i++)
neighbors[i] = GPOINTER_TO_INT (g_list_nth_data (op, i));
if (num_of_neighbors == 1)
{
gboolean border = FALSE;
if (r == 0 || col == 0 || r == (oh - 1) || col == (ow - 1))
border = TRUE;
if (border) NPD_ADD_COUNT (1);
else NPD_ADD_COUNT (4);
NPD_ADD_P (r - 1, col - 1, 2);
NPD_ADD_P (r - 1, col, 3);
NPD_ADD_P (r, col, 0);
NPD_ADD_P (r, col - 1, 1);
if (border)
ops = g_list_insert_before (ops,
g_list_nth_prev (g_list_last (ops), 1),
GINT_TO_POINTER (1));
#undef NPD_ADD_P
}
else
if (num_of_neighbors == 2)
{
gboolean x_differs = FALSE, y_differs = FALSE;
gint a, b, c, d;
#define NPD_OP_X(op) ((op) % ow)
#define NPD_OP_Y(op) ((op) / ow)
if (NPD_OP_X (neighbors[0]) != NPD_OP_X (neighbors[1])) x_differs = TRUE;
if (NPD_OP_Y (neighbors[0]) != NPD_OP_Y (neighbors[1])) y_differs = TRUE;
if (x_differs && y_differs)
{
/* corner */
gint B = neighbors[0], C = neighbors[1];
if (NPD_OP_X (index) == NPD_OP_X (neighbors[0]))
{ B = neighbors[1]; C = neighbors[0]; }
if (NPD_OP_Y (index) < NPD_OP_Y (C))
{
if (NPD_OP_X (index) < NPD_OP_X (B))
{ /* IV. quadrant */ a = 2; b = 3; c = 1; d = 0; }
else
{ /* III. quadrant */ a = 2; b = 3; c = 0; d = 1; }
}
else
{
if (NPD_OP_X (index) < NPD_OP_X (B))
{ /* I. quadrant */ a = 2; b = 0; c = 1; d = 3; }
else
{ /* II. quadrant */ a = 0; b = 3; c = 1; d = 2; }
}
#define NPD_OP2SQ(op) (op == 0 ? ( r * width + col) : \
(op == 1 ? ( r * width + col - 1) : \
(op == 2 ? ((r - 1) * width + col - 1) : \
((r - 1) * width + col))))
#define NPD_ADD_P(square,point) \
ops = g_list_append (ops, GINT_TO_POINTER (square)); \
ops = g_list_append (ops, GINT_TO_POINTER (point));
NPD_ADD_COUNT (3);
NPD_ADD_P (NPD_OP2SQ (a), a);
NPD_ADD_P (NPD_OP2SQ (b), b);
NPD_ADD_P (NPD_OP2SQ (c), c);
NPD_ADD_COUNT (1);
NPD_ADD_P (NPD_OP2SQ (d), d);
}
else
{
/* segment */
a = 0; b = 1; c = 2; d = 3;
if (y_differs) { a = 1; b = 2; c = 0; d = 3; }
NPD_ADD_COUNT (2);
NPD_ADD_P (NPD_OP2SQ (a), a);
NPD_ADD_P (NPD_OP2SQ (b), b);
NPD_ADD_COUNT (2);
NPD_ADD_P (NPD_OP2SQ (c), c);
NPD_ADD_P (NPD_OP2SQ (d), d);
}
}
else
if (num_of_neighbors == 3)
{
gint B = neighbors[0], C = neighbors[1], D = neighbors[2];
gint a = 2, b = 1, c = 3, d = 0;
if ((NPD_OP_X (B) != NPD_OP_X (C)) && (NPD_OP_Y (B) != NPD_OP_Y (C)))
{
/* B and C form corner */
B = neighbors[2]; D = neighbors[0]; /* swap B and D */
if ((NPD_OP_X (B) != NPD_OP_X (C)) && (NPD_OP_Y (B) != NPD_OP_Y (C)))
{
/* (new) B and C form corner */
C = neighbors[0]; D = neighbors[1]; /* swap C and D */
}
}
/* B and C form segment */
if (NPD_OP_X (B) == NPD_OP_X (C))
{
if (NPD_OP_X (B) < NPD_OP_X (D))
{
/* |_
| */
a = 2; b = 1; c = 3; d = 0;
}
else
{
/* _|
| */
a = 3; b = 0; c = 2; d = 1;
}
}
else if (NPD_OP_Y (B) == NPD_OP_Y (C))
{
if (NPD_OP_Y (B) < NPD_OP_Y (D))
{
/* _ _
| */
a = 2; b = 3; c = 1; d = 0;
}
else
{
/* _|_ */
a = 1; b = 0; c = 2; d = 3;
}
}
NPD_ADD_COUNT (2);
NPD_ADD_P (NPD_OP2SQ (a), a);
NPD_ADD_P (NPD_OP2SQ (b), b);
NPD_ADD_COUNT (1);
NPD_ADD_P (NPD_OP2SQ (c), c);
NPD_ADD_COUNT (1);
NPD_ADD_P (NPD_OP2SQ (d), d);
}
else
if (num_of_neighbors == 4)
{
NPD_ADD_COUNT (1);
NPD_ADD_P (NPD_OP2SQ (0), 0);
NPD_ADD_COUNT (1);
NPD_ADD_P (NPD_OP2SQ (1), 1);
NPD_ADD_COUNT (1);
NPD_ADD_P (NPD_OP2SQ (2), 2);
NPD_ADD_COUNT (1);
NPD_ADD_P (NPD_OP2SQ (3), 3);
}
}
#undef NPD_ADD_P
#undef NPD_OP2SQ
#undef NPD_OP_X
#undef NPD_OP_Y
#undef NPD_ADD_COUNT
return ops;
}
......@@ -24,22 +24,17 @@
#include "npd_common.h"
void npd_swap_ints (gint *i,
gint *j);
gboolean npd_is_edge_empty (NPDImage *image,
NPDPoint *p1,
NPDPoint *p2);
GHashTable *npd_find_edges (NPDModel *model);
void npd_sort_overlapping_points (NPDOverlappingPoints *op);
void npd_handle_corner_and_segment (NPDOverlappingPoints *center,
NPDOverlappingPoints *p1,
NPDOverlappingPoints *p2,
GHashTable *changed_ops);
void npd_handle_3_neighbors (NPDOverlappingPoints *center,
NPDOverlappingPoints *p1,
NPDOverlappingPoints *p2,
NPDOverlappingPoints *p3,
GHashTable *changed_ops);
void npd_cut_edges (NPDHiddenModel *hm, GHashTable *edges);
gboolean npd_is_edge_empty (NPDImage *image,
gint X1,
gint Y1,
gint X2,
gint Y2);
GList** npd_find_edges (NPDImage *image,
gint count_x,
gint count_y,
gint square_size);
GList* npd_cut_edges (GList **edges,
gint ow,
gint oh);
#endif /* __REFINE_H__ */
......@@ -26,7 +26,7 @@
#include "graphics.h"
#include "deformation.h"
#include "npd_math.h"
/*#include "refine.h"*/
#include "lattice_cut.h"
#endif /* __NPD_H__ */
......@@ -295,6 +295,32 @@ npd_get_control_point_at (NPDModel *model,
model->control_point_radius);
}
void
npd_create_square (NPDBone *square,
gint x,
gint y,
gint width,
gint height)
{
gint i;
square->num_of_points = 4;
square->points = g_new (NPDPoint, 4);
square->weights = g_new (gfloat, 4);
square->points[0].x = x; square->points[0].y = y;
square->points[1].x = x + width; square->points[1].y = y;
square->points[2].x = x + width; square->points[2].y = y + height;
square->points[3].x = x; square->points[3].y = y + height;
for (i = 0; i < 4; i++)
{
square->weights[i] = 1.0;
square->points[i].weight = &square->weights[i];
square->points[i].fixed = FALSE;
square->points[i].index = i;
}
}
void
npd_create_list_of_overlapping_points (NPDHiddenModel *hm)
{
......
......@@ -124,6 +124,11 @@ NPDControlPoint *npd_get_control_point_with_radius_at
gfloat control_point_radius);
NPDControlPoint *npd_get_control_point_at (NPDModel *model,
NPDPoint *coord);
void npd_create_square (NPDBone *square,
gint x,
gint y,
gint width,
gint height);
void npd_create_list_of_overlapping_points
(NPDHiddenModel *model);
void add_point_to_suitable_cluster (GHashTable *coords_to_cluster,
......
<
/*
* This file is part of N-point image deformation library.
*
* N-point image deformation library is free software: you can
* redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License,
* or (at your option) any later version.
*
* N-point image deformation library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with N-point image deformation library.
* If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2013 Marek Dvoroznak <dvoromar@gmail.com>
*/
#include "refine.h"
#include "npd_common.h"
#include "graphics.h"
#include <glib.h>
#include <stdio.h>
#include "npd_math.h"
void
npd_swap_ints (gint *i,
gint *j)
{
gint tmp = *i;
*i = *j;
*j = tmp;
}
gboolean
npd_is_edge_empty (NPDImage *image,
NPDPoint *p1,
NPDPoint *p2)
{
gint x, y;
gint X1 = p1->x, Y1 = p1->y, X2 = p2->x, Y2 = p2->y;
NPDColor color;
if (Y1 > Y2) npd_swap_ints (&Y1, &Y2);
if (X1 > X2) npd_swap_ints (&X1, &X2);
if (Y1 == Y2) Y2++;
if (X1 == X2) X2++;
for (y = Y1; y < Y2; y++)
{
for (x = X1; x < X2; x++)
{
npd_get_pixel_color (image, x, y, &color);
if (!npd_is_color_transparent (&color))
return FALSE;