workshop: implement rotational invariance for inpainting neighborhoods

parent 2bbae36e
Pipeline #91421 failed with stages
in 11 minutes and 31 seconds
......@@ -101,6 +101,7 @@ gegl_common_la_SOURCES =\
remap.c \
rgb-clip.c \
saturation.c \
saturation2.c \
save.c \
sepia.c \
slic.c \
......
......@@ -20,7 +20,6 @@ ops = \
gradient-map.la \
gcr.la \
hstack.la \
enlarge.la \
aces-rrt.la \
inpaint.la \
integral-image.la \
......
/* This file is an image processing operation for GEGL
*
* GEGL 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.
*
* GEGL 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 GEGL; if not, see <https://www.gnu.org/licenses/>.
*
* Copyright 2018, 2019 Øyvind Kolås <pippin@gimp.org>
*
*/
#include <stdio.h>
#include "config.h"
#include <glib/gi18n-lib.h>
#include <math.h>
#ifdef GEGL_PROPERTIES
/* most of these should go away - here for ease of algorithm experimentation */
property_int (seek_distance, "seek radius", 332)
value_range (4, 512)
property_int (max_k, "max k", 1)
value_range (1, 4)
property_double (scale, "scale", 2.0)
value_range (0.01, 16.0)
#else
#define GEGL_OP_FILTER
#define GEGL_OP_NAME enlarge
#define GEGL_OP_C_SOURCE enlarge.c
#include "gegl-op.h"
#include <stdio.h>
#include "pixel-duster.h"
static GeglRectangle
get_required_for_output (GeglOperation *operation,
const gchar *input_pad,
const GeglRectangle *roi)
{
GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
if (gegl_rectangle_is_infinite_plane (&result))
return *roi;
return result;
}
static void
prepare (GeglOperation *operation)
{
const Babl *format = babl_format ("RGBA float");
gegl_operation_set_format (operation, "input", format);
gegl_operation_set_format (operation, "output", format);
}
static void scaled_copy (PixelDuster *duster,
GeglBuffer *in,
GeglBuffer *out,
gfloat scale)
{
GeglRectangle rect;
const Babl *format = babl_format ("RGBA float");
gint x, y;
rect = *gegl_buffer_get_extent (out);
for (y = 0; y < rect.height; y++)
for (x = 0; x < rect.width; x++)
{
float rgba[4];
GeglRectangle r = {x, y, 1, 1};
gegl_sampler_get (duster->in_sampler_f, x / scale+0.5, y / scale+0.5, NULL,
&rgba[0], 0);
gegl_buffer_set (out, &r, 0, format, &rgba[0], 0);
}
}
static void remove_checker (GeglBuffer *out,
int phase)
{
GeglRectangle rect;
const Babl *format = babl_format ("RGBA float");
GeglBufferIterator *iterator;
rect = *gegl_buffer_get_extent (out);
iterator = gegl_buffer_iterator_new (out, &rect, 0, format, GEGL_BUFFER_READWRITE, GEGL_ABYSS_NONE, 1);
while (gegl_buffer_iterator_next (iterator))
{
int x = iterator->items[0].roi.x;
int y = iterator->items[0].roi.y;
gfloat *data = iterator->items[0].data;
int i;
for (i = 0; i < iterator->length; i++)
{
if (phase == 0)
{
if ( ((x%2==0) && (y%2==0)) ||
((x%2==1) && (y%2==1)))
{
data[0]=0.0;
data[1]=0.0;
data[2]=0.0;
data[3]=0.0;
}
}
else
{
if ( ((x%2==1) && (y%2==0)) ||
((x%2==0) && (y%2==1)))
{
data[0]=0.0;
data[1]=0.0;
data[2]=0.0;
data[3]=0.0;
}
}
data += 4;
x++;
if (x >= iterator->items[0].roi.x + iterator->items[0].roi.width)
{
x = iterator->items[0].roi.x;
y++;
}
}
}
}
static void improve (PixelDuster *duster,
GeglBuffer *in,
GeglBuffer *out,
gfloat scale)
{
GeglRectangle rect;
const Babl *format = babl_format ("RGBA float");
gint x, y;
rect = *gegl_buffer_get_extent (out);
for (y = 0; y < rect.height; y++)
{
int xstart = 0;
int xinc = 1;
int xend = rect.width;
#if 0 /* consistent scanline direction produces more consistent results */
if (y%2)
{
xstart = rect.width - 1;
xinc = -1;
xend = -1;
}
#endif
for (x = xstart; x != xend; x+=xinc)
{
Probe *probe;
probe = add_probe (duster, x, y);
if (probes_improve (duster) == 0)
{
#if PIXDUST_REL_DIGEST==0
gfloat rgba[4*MAX_K];
for (int j = 0; j < MAX_K; j++)
gegl_sampler_get (duster->in_sampler_f, probe->source_x[j],
probe->source_y[j], NULL, &rgba[j*4], 0);
for (int j = 1; j < probe->k; j++)
for (int c = 0; c < 4; c++)
{
rgba[0+c] += rgba[j*4+c];
}
for (int c = 0; c < 4; c++)
{
rgba[c] /= probe->k;
}
if (rgba[3] <= 0.01)
fprintf (stderr, "eek %f,%f %f %f %f %f\n", probe->source_x[MAX_K/2], probe->source_y[MAX_K/2], rgba[0], rgba[1], rgba[2], rgba[3]);
gegl_buffer_set (duster->output, GEGL_RECTANGLE(probe->target_x, probe->target_y, 1, 1), 0, format, &rgba[0], 0);
#else
gfloat rgba[4];
gfloat delta[4]={0,0,0,0};
{
int dx = 0, dy = 0;
duster_idx_to_x_y (duster, 1, probe->hay[0][0], &dx, &dy);
gegl_buffer_sample (duster->output, probe->target_x + dx, probe->target_y + dy, NULL, &rgba[0], format, GEGL_SAMPLER_NEAREST, 0);
}
for (int k = 0; k < probe->k; k++)
{
for (int c = 0; c < 3; c ++)
delta[c] += ((probe->hay[k][4+c]-127.0) / 128);
}
for (int c = 0; c < 3; c ++)
rgba[c] = rgba[c] - delta[c] / probe->k;
rgba[3]=1.0;
gegl_buffer_set (duster->output, GEGL_RECTANGLE(probe->target_x, probe->target_y, 1, 1), 0, format, &rgba[0], 0);
#endif
}
g_hash_table_remove (duster->probes_ht, xy2offset(probe->target_x, probe->target_y));
}
fprintf (stderr, "\r%2.2f ", y * 100.0 / rect.height);
}
fprintf (stderr, "\n");
}
// rgba x y scale score
//
static gboolean
process (GeglOperation *operation,
GeglBuffer *input,
GeglBuffer *output,
const GeglRectangle *result,
gint level)
{
GeglProperties *o = GEGL_PROPERTIES (operation);
GeglRectangle in_rect = *gegl_buffer_get_extent (input);
GeglRectangle out_rect = *gegl_buffer_get_extent (output);
PixelDuster *duster;
duster = pixel_duster_new (input, input, output,
&in_rect, &out_rect,
o->seek_distance,
o->max_k,
1, // min neighbors
100, // min iterations
1.0, // try chance
0.8, // re-try chance
o->scale,
o->scale,
NULL);
scaled_copy (duster, input, output, o->scale);
remove_checker (output, 0);
seed_db (duster);
pixel_duster_add_probes_for_transparent (duster);
pixel_duster_fill (duster);
#if 0
//improve (duster, input, output, o->scale);
remove_checker (output, 1);
pixel_duster_add_probes_for_transparent (duster);
pixel_duster_fill (duster);
#endif
#if 0
remove_checker (output, 0);
pixel_duster_add_probes_for_transparent (duster);
pixel_duster_fill (duster);
pixel_duster_destroy (duster);
#endif
return TRUE;
}
static GeglRectangle
get_bounding_box (GeglOperation *operation)
{
GeglProperties *o = GEGL_PROPERTIES (operation);
GeglRectangle *res = gegl_operation_source_get_bounding_box (operation, "input");
GeglRectangle result = {0,0,100,100};
if (res)
result = *res;
result.x = 0;
result.y = 0;
result.width = ceilf (result.width * o->scale);
result.height = ceilf (result.height * o->scale);
return result;
}
static GeglRectangle
get_cached_region (GeglOperation *operation,
const GeglRectangle *roi)
{
GeglProperties *o = GEGL_PROPERTIES (operation);
GeglRectangle result = *gegl_operation_source_get_bounding_box (operation, "input");
if (gegl_rectangle_is_infinite_plane (&result))
return *roi;
result.x = 0;
result.y = 0;
result.width *= o->scale;
result.height *= o->scale;
return result;
}
static gboolean
operation_process (GeglOperation *operation,
GeglOperationContext *context,
const gchar *output_prop,
const GeglRectangle *result,
gint level)
{
GeglOperationClass *operation_class;
const GeglRectangle *in_rect =
gegl_operation_source_get_bounding_box (operation, "input");
operation_class = GEGL_OPERATION_CLASS (gegl_op_parent_class);
if (in_rect && gegl_rectangle_is_infinite_plane (in_rect))
{
gpointer in = gegl_operation_context_get_object (context, "input");
gegl_operation_context_take_object (context, "output",
g_object_ref (G_OBJECT (in)));
return TRUE;
}
return operation_class->process (operation, context, output_prop, result,
gegl_operation_context_get_level (context));
}
static void
gegl_op_class_init (GeglOpClass *klass)
{
GeglOperationClass *operation_class;
GeglOperationFilterClass *filter_class;
operation_class = GEGL_OPERATION_CLASS (klass);
filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
filter_class->process = process;
operation_class->prepare = prepare;
operation_class->process = operation_process;
operation_class->get_bounding_box = get_bounding_box;
operation_class->get_required_for_output = get_required_for_output;
operation_class->get_cached_region = get_cached_region;
operation_class->opencl_support = FALSE;
operation_class->threaded = FALSE;
gegl_operation_class_set_keys (operation_class,
"name", "gegl:enlarge",
"title", "Smart resize",
"categories", "heal",
"description", "scales an image with cubic, remove half in a checker board, guesses blank pixels, removes other checkerboard half and guesses blank pixels again.",
NULL);
}
#endif
......@@ -29,16 +29,16 @@
property_int (seek_distance, "seek radius", 30)
value_range (4, 512)
property_int (min_neigh, "min neigh", 4)
property_int (min_neigh, "min neigh", 2)
value_range (1, 10)
property_int (min_iter, "min iter", 100)
value_range (1, 512)
property_double (chance_try, "try chance", 1.0)
property_double (chance_try, "try chance", 0.5)
value_range (0.0, 1.0)
property_double (chance_retry, "retry chance", 0.75)
property_double (chance_retry, "retry chance", 0.4)
value_range (0.0, 1.0)
#else
......
......@@ -88,21 +88,13 @@ typedef struct
#define MAX_K 4
#define RINGS 3
#define RAYS 16
#define GAP 1.1
#define RINGGAMMA 1.45
#define TWIST 0.03
#define IMPROVEMENT_ITERATIONS 2
#define RAYS 12
#define GAP 1.3
#define RINGGAMMA 1.2
#define TWIST 0.0
#define NEIGHBORHOOD (RINGS*RAYS+1)
#define MAX_DIR 4
// safe to leave always on since only really used when epplies
#define PIXDUST_DIR_INVARIANT 1
//#define ONLY_DIR 1
typedef struct Probe {
int target_x;
int target_y;
......@@ -143,15 +135,15 @@ static void init_order(PixelDuster *duster)
i = 1;
{
for (float angleno = 0; angleno < RAYS; angleno++)
for (int circleno = 0; circleno < RINGS; circleno++)
for (float angleno = 0; angleno < RAYS; angleno++)
{
float mag = pow(GAP * (circleno + 1), RINGGAMMA);
float x = cosf ((angleno / RAYS + TWIST*circleno) * M_PI * 2) * mag;
float y = sinf ((angleno / RAYS + TWIST*circleno) * M_PI * 2) * mag;
duster->order[i][0] = x;
duster->order[i][1] = y;
duster->order[i][2] = powf (1.0 / (POW2(x)+POW2(y)), 0.8);
duster->order[i][2] = powf (1.0 / (POW2(x)+POW2(y)), 1.0);
i++;
}
}
......@@ -170,45 +162,10 @@ static void init_order(PixelDuster *duster)
#endif
}
static void duster_idx_to_x_y (PixelDuster *duster, int index, int dir, int *x, int *y)
static void duster_idx_to_x_y (PixelDuster *duster, int index, int *x, int *y)
{
switch (dir)
{
default:
case 0: /* right */
*x = -duster->order[index][0];
*y = -duster->order[index][1];
break;
case 1: /* left */
*x = duster->order[index][0];
*y = duster->order[index][1];
break;
case 2: /* down */
*x = -duster->order[index][1];
*y = -duster->order[index][0];
break;
case 3: /* up */
*x = duster->order[index][1];
*y = duster->order[index][0];
break;
case 4: /* right */
*x = -duster->order[index][0];
*y = duster->order[index][1];
break;
case 5: /* left */
*x = duster->order[index][0];
*y = -duster->order[index][1];
break;
case 6: /* down */
*x = -duster->order[index][1];
*y = duster->order[index][0];
break;
case 7: /* up */
*x = duster->order[index][1];
*y = -duster->order[index][0];
break;
}
*x = duster->order[index][0];
*y = duster->order[index][1];
}
......@@ -241,7 +198,7 @@ static PixelDuster * pixel_duster_new (GeglBuffer *reference,
ret->max_y = 0;
ret->min_x = 10000;
ret->min_y = 10000;
ret->max_age = 5;
ret->max_age = IMPROVEMENT_ITERATIONS;
if (max_k < 1) max_k = 1;
if (max_k > MAX_K) max_k = MAX_K;
......@@ -316,9 +273,6 @@ void gegl_sampler_prepare (GeglSampler *sampler);
static void extract_site (PixelDuster *duster, GeglBuffer *buffer, double x, double y, float scale, gfloat *dst)
{
static const Babl *format = NULL;
guchar lum[8];
int bdir, maxlum;
//uint64_t hist3dmask=0;
GeglSampler *sampler_yu8;
GeglSampler *sampler_f;
......@@ -333,75 +287,62 @@ static void extract_site (PixelDuster *duster, GeglBuffer *buffer, double x, dou
else if (buffer == duster->reference)
{
sampler_yu8 = duster->ref_sampler_yu8;
sampler_f = duster->ref_sampler_f;
sampler_f = duster->ref_sampler_f;
}
else if (buffer == duster->input)
{
sampler_yu8 = duster->in_sampler_yu8;
sampler_f = duster->in_sampler_f;
sampler_f = duster->in_sampler_f;
}
if (!format){
format = babl_format ("RGBA float");
}
#if PIXDUST_DIR_INVARIANT==1
/* figure out which of the up/down/left/right pixels are brightest,
using premultiplied alpha - do punish blank spots */
gegl_sampler_get (sampler_yu8, x + 1 *scale, y + 0, NULL, &lum[0], 0);
gegl_sampler_get (sampler_yu8, x - 1 *scale, y + 0, NULL, &lum[2], 0);
gegl_sampler_get (sampler_yu8, x + 0, y + 1 * scale, NULL, &lum[4], 0);
gegl_sampler_get (sampler_yu8, x + 0, y - 1 * scale, NULL, &lum[6], 0);
bdir = 0;
maxlum = lum[0*2];
for (int i = 1; i < MIN(4,MAX_DIR); i++)
if (lum[i*2] > maxlum)
{
bdir = i;
maxlum = lum[i*2];
}
if (MAX_DIR > 4)
{
switch (bdir)
{
case 0: if (lum[4] > lum[6]) bdir += 4; break;
case 1: if (lum[6] > lum[4]) bdir += 4; break;
case 2: if (lum[0] > lum[2]) bdir += 4; break;
case 3: if (lum[2] > lum[0]) bdir += 4; break;
}
}
#ifdef ONLY_DIR
bdir = ONLY_DIR;
#endif
#endif
#if 1
for (int i = 0; i <= NEIGHBORHOOD; i++)
{
int dx, dy;
duster_idx_to_x_y (duster, i, bdir, &dx, &dy);
duster_idx_to_x_y (duster, i, &dx, &dy);
gegl_sampler_get (sampler_f, x + dx * scale, y + dy * scale, NULL, &dst[i*4], 0);
#if 0
}
{
int warmest_ray = 0;
float warmest_ray_energy = 0;
gfloat tmp[4 * NEIGHBORHOOD + 8];
for (int ray = 0; ray < RAYS; ray ++)
{
int hist_r = dst[i*4+0]/80;
int hist_g = dst[i*4+1]/80;
int hist_b = dst[i*4+2]/80;
int hist_bit = hist_r * 4 * 4 + hist_g * 4 + hist_b;
hist3dmask |= (1 << hist_bit);
float energy = 0.0;
for (int circle = 0; circle < RINGS; circle++)
for (int c = 0; c < 3; c++)
energy += dst[ ( circle * RAYS + ray )*4 + c];
if (energy > warmest_ray_energy)
{
warmest_ray = ray;
warmest_ray_energy = energy;
}
}
if (warmest_ray)
{
for (int i = 0; i <= NEIGHBORHOOD*4; i++)
tmp[i] = dst[i];
for (int ray = 0; ray < RAYS; ray ++)
{
int swapped_ray = ray + warmest_ray_energy;
if (swapped_ray >= RAYS) swapped_ray -= RAYS;
for (int circle = 0; circle < RINGS; circle++)
for (int c = 0; c < 4; c++)
dst[ ( circle * RAYS + ray )*4 + c] =
tmp[ ( circle * RAYS + swapped_ray )*4 + c];
}
}
#endif
}
dst[0] = bdir;
#if 0
*((uint64_t*)(&dst[4*NEIGHBORHOOD])) = hist3dmask;
#endif
#endif
}
static inline int u8_rgb_diff (guchar *a, guchar *b)
......@@ -430,33 +371,16 @@ score_site (PixelDuster *duster,
return INITIAL_SCORE;
}
#if 0
{
uint64_t *needle_hist = (void*)&needle[NEIGHBORHOOD * 4];
uint64_t *hay_hist = (void*)&hay[NEIGHBORHOOD * 4];
uint64_t diff_hist = *needle_hist ^ *hay_hist;
int missing = 0;
for (i = 0; i < 64; i ++)
{
if ((diff_hist & (1 << i)) &&
(*needle_hist & ( 1 <<i)))
missing ++;
//else if ( *needle_hist & (i<<i)) missing ++;
}
if (missing > 23)
return INITIAL_SCORE;
}
#endif
for (i = 1; i < NEIGHBORHOOD && score < bail; i++)
for (i = 0; i < NEIGHBORHOOD && score < bail; i++)
{
if (needle[i*4 + 3]>0.001 && hay[i*4 + 3]>0.001)
if (needle[i*4 + 3]<1.0 && hay[i*4 + 3]>0.001)
{
score += f_rgb_diff (&needle[i*4 + 0], &hay[i*4 + 0]) * duster->order[i][2];
}
else
{
score += 10;//256;// * duster->order[i][2];
/* transparent hay or needle - give bad score */
score += 5;
}
}
return score;
......@@ -707,6 +631,12 @@ static int probe_improve (PixelDuster *duster,
float old_score = probe->score;
static const Babl *format = NULL;
if (probe->age > duster->max_age)
{
g_hash_table_remove (duster->probes_ht, xy2offset(probe->target_x, probe->target_y));
return -1;
}
if (!format)
format = babl_format ("RGBA float");
......@@ -721,18 +651,6 @@ static int probe_improve (PixelDuster *duster,
probe->age++;
#if 0
spread_relative (duster, probe, -1, -1);
spread_relative (duster, probe, -1, 0);
spread_relative (duster, probe, 0, -1);
spread_relative (duster, probe, 1, 0);
spread_relative (duster, probe, 0, 1);
#endif
if (probe->age > duster->max_age)
{
g_hash_table_remove (duster->probes_ht, xy2offset(probe->target_x, probe->target_y));
}
if (probe->score == old_score)
return -1;
......@@ -830,8 +748,8 @@ static inline void pixel_duster_fill (PixelDuster *duster)
probe_neighbors (duster, duster->output, probe, duster->minimum_neighbors) >=
duster->minimum_neighbors)
{
//if(try_replace)
// probe->score = INITIAL_SCORE;
if(try_replace)
probe->score = INITIAL_SCORE;
if (probe_improve (duster, probe) == 0)
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment