app, libgimpbase: add GIMP_SELECT_CRITERION_LINE_ART selection type.

This commit implements part of the research paper "A Fast and Efficient
Semi-guided Algorithm for Flat Coloring Line-arts" from the GREYC (the
people from G'Mic). It is meant to select regions from drawn sketchs in
a "smart" way, in particular it tries to close non-perfectly closed
regions, which is a common headache for digital painters and colorists.

The implementation is not finished as it needs some watersheding as well
so that the selected area does not leave "holes" near stroke borders.
The research paper proposes a new watersheding algorithm, but I may not
have to implement it, as it is more focused on automatic colorization
with prepared spots (instead of bucket fill-type interaction).

This will be used in particular with the fuzzy select and bucket fill
tools.

Note that this first version is a bit slow once we get to big images,
but I hope to be able to optimize this.
Also no options from the algorithm are made available in the GUI yet.
This commit is contained in:
Jehan 2018-10-10 20:28:47 +02:00
parent 85e6700aac
commit 8ed12b1b98
8 changed files with 2371 additions and 5 deletions

View File

@ -363,6 +363,8 @@ libappcore_a_sources = \
gimplayerstack.h \
gimplayerundo.c \
gimplayerundo.h \
gimplineart.c \
gimplineart.h \
gimplist.c \
gimplist.h \
gimpmaskundo.c \

2250
app/core/gimplineart.c Normal file

File diff suppressed because it is too large Load Diff

43
app/core/gimplineart.h Normal file
View File

@ -0,0 +1,43 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* Copyright (C) 2017 Sébastien Fourey & David Tchumperlé
* Copyright (C) 2018 Jehan
*
* 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 3 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, see <https://www.gnu.org/licenses/>.
*/
#ifndef __GIMP_LINEART__
#define __GIMP_LINEART__
GeglBuffer * gimp_lineart_close (GeglBuffer *line_art,
gboolean select_transparent,
gfloat stroke_threshold,
gint erosion,
gint minimal_lineart_area,
gint normal_estimate_mask_size,
gfloat end_point_rate,
gint spline_max_length,
gfloat spline_max_angle,
gint end_point_connectivity,
gfloat spline_roundness,
gboolean allow_self_intersections,
gint created_regions_significant_area,
gint created_regions_minimum_area,
gboolean small_segments_from_spline_sources,
gint segments_max_length);
#endif /* __GIMP_LINEART__ */

View File

@ -32,6 +32,7 @@
#include "gegl/gimp-babl.h"
#include "gimp-utils.h" /* GIMP_TIMER */
#include "gimplineart.h"
#include "gimppickable.h"
#include "gimppickable-contiguous-region.h"
@ -115,6 +116,7 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
gint n_components;
gboolean has_alpha;
gfloat start_col[MAX_CHANNELS];
gboolean free_src_buffer = FALSE;
g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
@ -144,6 +146,58 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
select_transparent = FALSE;
}
if (select_criterion == GIMP_SELECT_CRITERION_LINE_ART)
{
/* For smart selection, we generate a binarized image with close
* regions, then run a composite selection with no threshold on
* this intermediate buffer.
*/
GIMP_TIMER_START();
src_buffer = gimp_lineart_close (src_buffer,
select_transparent,
/*contour_detection_level,*/
0.92,
/* erosion, */
-1,
/*minimal_lineart_area,*/
5,
/*normal_estimate_mask_size,*/
5,
/*end_point_rate,*/
0.85,
/*spline_max_length,*/
60,
/*spline_max_angle,*/
90.0,
/*end_point_connectivity,*/
2,
/*spline_roundness,*/
1.0,
/*allow_self_intersections,*/
TRUE,
/*created_regions_significant_area,*/
4,
/*created_regions_minimum_area,*/
100,
/*small_segments_from_spline_sources,*/
TRUE,
/*segments_max_length*/
20);
free_src_buffer = TRUE;
antialias = FALSE;
threshold = 0.0;
select_transparent = FALSE;
select_criterion = GIMP_SELECT_CRITERION_COMPOSITE;
diagonal_neighbors = FALSE;
format = choose_format (src_buffer, select_criterion,
&n_components, &has_alpha);
gegl_buffer_sample (src_buffer, x, y, NULL, start_col, format,
GEGL_SAMPLER_NEAREST, GEGL_ABYSS_NONE);
GIMP_TIMER_END("close line-art");
}
extent = *gegl_buffer_get_extent (src_buffer);
mask_buffer = gegl_buffer_new (&extent, babl_format ("Y float"));
@ -161,6 +215,8 @@ gimp_pickable_contiguous_region_by_seed (GimpPickable *pickable,
GIMP_TIMER_END("foo");
}
if (free_src_buffer)
g_object_unref (src_buffer);
return mask_buffer;
}
@ -297,6 +353,10 @@ choose_format (GeglBuffer *buffer,
format = babl_format ("CIE LCH(ab) alpha float");
break;
case GIMP_SELECT_CRITERION_LINE_ART:
format = babl_format ("Y'A u8");
break;
default:
g_return_val_if_reached (NULL);
break;
@ -387,6 +447,10 @@ pixel_difference (const gfloat *col1,
max = fabs (col1[2] - col2[2]) / 360.0;
max = MIN (max, 1.0 - max);
break;
case GIMP_SELECT_CRITERION_LINE_ART:
/* Smart selection is handled before. */
g_return_val_if_reached (0.0);
}
}
@ -610,7 +674,7 @@ find_contiguous_region (GeglBuffer *src_buffer,
row = g_new (gfloat, gegl_buffer_get_width (src_buffer) * n_components);
#endif
src_sampler = gegl_buffer_sampler_new (src_buffer,
src_sampler = gegl_buffer_sampler_new (src_buffer,
format, GEGL_SAMPLER_NEAREST);
segment_queue = g_queue_new ();

View File

@ -1666,6 +1666,7 @@ gimp_select_criterion_get_type (void)
{ GIMP_SELECT_CRITERION_LCH_L, "GIMP_SELECT_CRITERION_LCH_L", "lch-l" },
{ GIMP_SELECT_CRITERION_LCH_C, "GIMP_SELECT_CRITERION_LCH_C", "lch-c" },
{ GIMP_SELECT_CRITERION_LCH_H, "GIMP_SELECT_CRITERION_LCH_H", "lch-h" },
{ GIMP_SELECT_CRITERION_LINE_ART, "GIMP_SELECT_CRITERION_LINE_ART", "line-art" },
{ 0, NULL, NULL }
};
@ -1682,6 +1683,7 @@ gimp_select_criterion_get_type (void)
{ GIMP_SELECT_CRITERION_LCH_L, NC_("select-criterion", "LCh Lightness"), NULL },
{ GIMP_SELECT_CRITERION_LCH_C, NC_("select-criterion", "LCh Chroma"), NULL },
{ GIMP_SELECT_CRITERION_LCH_H, NC_("select-criterion", "LCh Hue"), NULL },
{ GIMP_SELECT_CRITERION_LINE_ART, NC_("select-criterion", "Line Art"), NULL },
{ 0, NULL, NULL }
};

View File

@ -1144,6 +1144,7 @@ typedef enum
* @GIMP_SELECT_CRITERION_LCH_L: LCh Lightness
* @GIMP_SELECT_CRITERION_LCH_C: LCh Chroma
* @GIMP_SELECT_CRITERION_LCH_H: LCh Hue
* @GIMP_SELECT_CRITERION_LINE_ART: Line Art
*
* Criterions for color similarity.
**/
@ -1164,6 +1165,7 @@ typedef enum
GIMP_SELECT_CRITERION_LCH_L, /*< desc="LCh Lightness" >*/
GIMP_SELECT_CRITERION_LCH_C, /*< desc="LCh Chroma" >*/
GIMP_SELECT_CRITERION_LCH_H, /*< desc="LCh Hue" >*/
GIMP_SELECT_CRITERION_LINE_ART, /*< desc="Line Art" >*/
} GimpSelectCriterion;

View File

@ -535,7 +535,7 @@ gimp_prop_enum_combo_box_new (GObject *config,
{
/* ditto */
store = gimp_enum_store_new_with_values (param_spec->value_type,
11,
12,
GIMP_SELECT_CRITERION_COMPOSITE,
GIMP_SELECT_CRITERION_R,
GIMP_SELECT_CRITERION_G,
@ -546,7 +546,8 @@ gimp_prop_enum_combo_box_new (GObject *config,
GIMP_SELECT_CRITERION_V,
GIMP_SELECT_CRITERION_LCH_L,
GIMP_SELECT_CRITERION_LCH_C,
GIMP_SELECT_CRITERION_LCH_H);
GIMP_SELECT_CRITERION_LCH_H,
GIMP_SELECT_CRITERION_LINE_ART);
}
if (store)

View File

@ -568,7 +568,8 @@ package Gimp::CodeGen::enums;
GIMP_SELECT_CRITERION_A
GIMP_SELECT_CRITERION_LCH_L
GIMP_SELECT_CRITERION_LCH_C
GIMP_SELECT_CRITERION_LCH_H) ],
GIMP_SELECT_CRITERION_LCH_H
GIMP_SELECT_CRITERION_LINE_ART) ],
mapping => { GIMP_SELECT_CRITERION_COMPOSITE => '0',
GIMP_SELECT_CRITERION_R => '1',
GIMP_SELECT_CRITERION_G => '2',
@ -579,7 +580,8 @@ package Gimp::CodeGen::enums;
GIMP_SELECT_CRITERION_A => '7',
GIMP_SELECT_CRITERION_LCH_L => '8',
GIMP_SELECT_CRITERION_LCH_C => '9',
GIMP_SELECT_CRITERION_LCH_H => '10' }
GIMP_SELECT_CRITERION_LCH_H => '10',
GIMP_SELECT_CRITERION_LINE_ART => '11' }
},
GimpSizeType =>
{ contig => 1,