/
synthesize.h
637 lines (559 loc) · 23.3 KB
/
synthesize.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
/*
Innermost routines of image synthesis.
Copyright (C) 2010, 2011 Lloyd Konneker
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef VECTORIZED
#include <mmintrin.h> // intrinsics for assembly language MMX op codes, for sse2 xmmintrin.h
#endif
#ifdef SYNTH_THREADED
/*
* Threaded synthesis uses mutex on read and write to certain shared data among threads.
* You COULD use threads without mutex.
* Then there is a very small chance that color will be scrambled, resulting in a color not found in corpus.
* Also, if integer writes are not atomic, there is a small chance that sourceOf will be scrambled,
* resulting in soft errors.
* And since mutex locking is fast in Linux, we use mutex rather than risk soft errors.
*/
#ifdef SYNTH_USE_GLIB_THREADS
GStaticMutex mutex;
#else
// Use POSIX instead
#include <pthread.h>
pthread_mutex_t mutex;
// proxies for glib mutex functions are in glibProxy.c, .h
#endif
#else // No threads: redefine mutex functions to nil
#define g_static_mutex_lock(A)
#define g_static_mutex_unlock(A)
#endif
// Match result kind
typedef enum BettermentKindEnum
{
PERFECT_MATCH, // Patches equal
NO_BETTERMENT, // Match worse than previous best
GENERIC_BETTERMENT,
NEIGHBORS_SOURCE,
RANDOM_CORPUS,
MAX_BETTERMENT_KIND
} tBettermentKind;
/*
Is point in the target image or wrapped into it.
!!! Note it can have side effects, wrapping the point into the target image when tiling.
If tiling, wrap the coords into the pixmap (and return True.)
Otherwise, return whether the resulting coords are in target pixmap (whether not clipped.)
!!! See elsewhere, tiling is only pertinent if matchContext is False.
IN image parameter is the target and context pixmap
IN/OUT point parameter is a neighbor (target point plus offset).
Point can be outside the pixmap, requiring clipping or wrapping.
Here clipping means: return false if outside the pixmap.
Note this is not in the innermost, bottleneck.
TODO It makes the engine more capable,
at the cost of slightly slowing down the most frequent use: healing to matchContext.
*/
static inline gboolean
clipToTargetOrWrapIfTiled (
const TImageSynthParameters *parameters,
Map *image,
Coordinates *point // !!! IN/OUT
)
{
while(point->x < 0)
if (parameters->isMakeSeamlesslyTileableHorizontally)
point->x += image->width;
else
return FALSE;
while(point->x >= (gint) image->width)
if (parameters->isMakeSeamlesslyTileableHorizontally)
point->x -= image->width;
else
return FALSE;
while(point->y < 0)
if (parameters->isMakeSeamlesslyTileableVertically)
point->y += image->height;
else
return FALSE;
while(point->y >= (gint) image->height)
if (parameters->isMakeSeamlesslyTileableVertically)
point->y -= image->height;
else
return FALSE;
return TRUE;
}
/*
Neighbor (patch element)
Element of array of points from target image (selection and context).
Copied from target image for better memory locality.
*/
typedef struct neighborStruct {
Pixelel pixel[MAX_IMAGE_SYNTH_BPP] __attribute__((aligned(8))); // Copy of target pixel
Coordinates offset; // Offset from patch center
// Coords of corpus point this target synthed from, or -1 if this neighbor is context
Coordinates sourceOf;
} TNeighbor;
/*
Class neighbor_source
Similar to sourceOfMap target points, but for neigbhors.
*/
static inline gboolean
has_source_neighbor (
guint j,
const TNeighbor neighbors[] // Index in neighbors array (the patch)
)
{
return neighbors[j].sourceOf.x != -1;
// A neighbor only has a source if it is also in the target and has been synthed.
}
/* Copy the source of a neighbor point into the neighbor array. */
static inline void
set_neighbor_state (
guint n_neighbour, // index in neighbors
Coordinates neighbor_point, // coords in image (context or target)
Map* sourceOfMap,
TNeighbor neighbors[]
)
{
/* Assert neighbor point has values (we already checked that the candidate neighbor had a value.) */
neighbors[n_neighbour].sourceOf = getSourceOf(neighbor_point, sourceOfMap);
}
/* Create a neighbor. Initialize: offset, status, and pixel. */
static inline void
new_neighbor(
const guint index,
Coordinates offset,
Coordinates neighbor_point,
TFormatIndices* indices,
Map* targetMap,
Map* sourceOfMap,
TNeighbor neighbors[]
)
{
neighbors[index].offset = offset;
g_static_mutex_lock(&mutex); // Read color and source atomically
set_neighbor_state(index, neighbor_point, sourceOfMap, neighbors);
{
TPixelelIndex k;
for (k=0; k<indices->total_bpp; k++) // !!! Copy whole Pixel, all pixelels
neighbors[index].pixel[k] = pixmap_index(targetMap, neighbor_point)[k];
}
g_static_mutex_unlock(&mutex);
}
/*
Prepare patch (array of neighbors) with values, both inside the target, and outside i.e. in the context (if use_border).
Neighbors are in the source (the target or its context.)
If repeating a pixel, now might have more, different, closer neighbors than on the first pass.
Neighbors array is global, used both for heuristic and in synthing every point ( in computeBestFit() )
Neighbors describes a patch, a shotgun pattern in the first pass, or a contiguous patch in later passes.
It is stored in an array, but is not necessarily a square, contiguous patch.
*/
static guint
prepare_neighbors(
Coordinates position, // IN target point
TImageSynthParameters *parameters, // IN
TFormatIndices* indices,
Map* targetMap,
Map* hasValueMap,
Map* sourceOfMap,
pointVector sortedOffsets,
TNeighbor neighbors[]
)
{
guint j;
guint count = 0;
Coordinates offset;
Coordinates neighbor_point;
// Target point is always its own first neighbor, even though on startup and first pass it doesn't have a value.
offset = g_array_index(sortedOffsets, Coordinates, 0);
new_neighbor(count, offset, position, indices, targetMap, sourceOfMap, neighbors);
count++;
for(j=1; j<sortedOffsets->len; j++) // !!! Start at 1
{
offset = g_array_index(sortedOffsets, Coordinates, j);
neighbor_point = add_points(position, offset);
// !!! Note side effects: clipToTargetOrWrapIfTiled might change neighbor_point coordinates !!!
if (clipToTargetOrWrapIfTiled(parameters, targetMap, &neighbor_point) // is neighbor in target image or wrappable
&& getHasValue(neighbor_point, hasValueMap)
// AND ( is neighbor outside target (context) OR inside target with already synthed value )
)
{
new_neighbor(count, offset, neighbor_point, indices, targetMap, sourceOfMap, neighbors);
count++;
if (count >= (guint) parameters->patchSize) break;
}
}
/*
Note the neighbors are in order of distance from the target pixel.
But the matching does not depend on the order, it only matters that neighbors are nearest.
Experiment to sort the nearest neighbors in other orders, such as in row major order
(so there might be better memory locality) didn't seem to help speed.
Note we can't assert(count==parameters.patchSize)
If use_border, there is a full neighborhood unless context or corpus small, that is,
there are usually plenty of distant neighbors in the context and corpus.
If not use_border, there is a full neighborhood except for first n_neighbor synthesis tries
on the very first pass.
*/
return count;
}
/*
This is the inner crux: comparing target patch to corpus patch, pixel by pixel.
Also the bottleneck in performance.
Computing a best fit metric, with early out when exceed known best.
Because of a log transform of a product, this is a summing.
The following discussion depends on how repetition (passes) are configured:
if the first pass is not a complete pass over the target, it doesn't apply.
On the first pass the candidate patch might be a shotgun pattern, to distant context.
On subsequent passes, the candidate patch is often a rectangular pixmap (since the target is filled in.)
But since pixels can be masked, the actual patch tested might be irregularly shaped.
Note that size of patch (n_neighbors) is usually the same for each target pixel,
but in rare cases, it might not be.
(If there is no context, the first probe has 0 neighbors, the second probe 1 neighbor, ...)
Then does it make sense to also use MAX_WEIGHT for missing neighbors?
*/
static inline gboolean // TODO tBettermentKind, but very subtle
computeBestFit(
const Coordinates point,
const TFormatIndices * const indices,
const Map * const corpusMap,
guint * const bestPatchDiff, // OUT
Coordinates * const bestMatchCorpusPoint, // OUT
const guint countNeighbors,
const TNeighbor const neighbors[],
tBettermentKind* latestBettermentKind,
const tBettermentKind bettermentKind,
const TPixelelMetricFunc corpusTargetMetric, // array pointers
const TMapPixelelMetricFunc mapsMetric
)
{
guint sum = 0;
guint i;
#ifdef STATS
countSourceTries++;
#endif
// Iterate over neighbors of candidate point. Sum grows as more neighbors tested.
for(i=0; i<countNeighbors; i++)
{
Coordinates off_point = add_points(point, neighbors[i].offset);
if (clippedOrMaskedCorpus(off_point, corpusMap))
{
/*
Maximum weighted difference for this neighbor outside corpus.
!!! Note even if no maps are passed to engine, we weight by the map,
for this case of an invalid corpus point.
!!! Note the mapsMetric function is not scaled,
so we can't use a constant such as MAX_MAP_DIFF,'
but instead mapMetric[...], the extreme max value of the metric.
!!! Which will be zero if mapWeight parameter is zero.
*/
#ifdef SYMMETRIC_METRIC_TABLE
// mapsMetric[256] is the max
sum += MAX_WEIGHT*indices->img_match_bpp + mapsMetric[LIMIT_DOMAIN]*indices->map_match_bpp;
#else
sum += MAX_WEIGHT*indices->img_match_bpp + mapsMetric[0]*indices->map_match_bpp;
#endif
}
else
{
#ifndef VECTORIZED
// Iterate over color pixelels to compute weighted difference
const Pixelel * corpus_pixel;
const Pixelel * image_pixel;
#ifdef SYMMETRIC_METRIC_TABLE
gshort diff;
#endif
corpus_pixel = pixmap_index(corpusMap, off_point);
// ! Note target pixel comes not from targetPoints, but from copy neighbors
image_pixel = neighbors[i].pixel; // pixel is array, yields Pixelel pointer
/* If not the target point (its own 0th neighbor).
!!! On the first pass, the target point as its own 0th neighbor has no meaningful, unbiased value.
Even if e.g. we initialize target to all black, that biases the search.
*/
if (i)
{
TPixelelIndex j;
for(j=FIRST_PIXELEL_INDEX; j<indices->colorEndBip; j++)
{
#ifdef SYMMETRIC_METRIC_TABLE
diff = (gshort) image_pixel[j] - (gshort) corpus_pixel[j];
sum += corpusTargetMetric[ ((diff < 0) ? (-diff) : (diff)) ];
// OR sum += corpusTargetMetric[ abs(diff) ]; // abs() a macro? from stddef.h
#else
sum += corpusTargetMetric[ 256u + image_pixel[j] - corpus_pixel[j] ];
#endif
}
}
if (indices->map_match_bpp > 0) // If maps
{
TPixelelIndex j;
for(j=indices->map_start_bip; j<indices->map_end_bip; j++) // also sum mapped difference
{
#ifdef SYMMETRIC_METRIC_TABLE
diff = (gshort) image_pixel[j] - (gshort) corpus_pixel[j];
sum += mapsMetric[ ((diff < 0) ? (-diff) : (diff)) ];
// sum += mapsMetric[ abs(diff) ];
#else
sum += mapsMetric[256u + image_pixel[j] - corpus_pixel[j]];
#endif
}
}
#else
const Pixelel * __restrict__ corpus_pixel = pixmap_index(corpusMap, off_point);
const Pixelel * __restrict__ image_pixel = neighbors[i].pixel;
#define MMX_INTRINSICS_RESYNTH
#include "resynth-vectorized.h"
#endif
/*
!!! Very subtle: on the very first pass and very first target point, with no context,
the patch is only one point, the being synthesized pixel.
Above, the loop will execute exactly once.
At "if (i)", it will not compute a weighted difference.
Hence the sum will be zero, i.e. a perfect match, and the very first probe will be the best match.
In other words, it will be completely at random, with no actual searching.
*/
}
/*
lkk !!! bestMatchCorpusPoint not set.
Note: equals.
If this source is same as prior source for target or different from prior source
( whether picked randomly or for a repeat)
AND all neighbors checked (iteration completed) without finding a lesser bestPatchDiff (but maybe an equal bestPatchDiff)
bestMatchCorpusPoint is not changed even if it is a different source.
??? Study how many different but equal sources are found.
Are different source in later repeats closer distance?
*/
if (sum >= *bestPatchDiff) return FALSE; // !!! Short circuit for neighbors
}
// Assert sum strictly < bestPatchDiff
*bestPatchDiff = sum;
*latestBettermentKind = bettermentKind;
// bestMatchCorpusPoint might already equal point, but might be smaller sum because different neighbors or different neighbor values
*bestMatchCorpusPoint = point;
if (sum <=0)
{
#ifdef STATS
bettermentStats[PERFECT_MATCH]+=1;
#endif
return TRUE; // PERFECT_MATCH
}
else
return FALSE; // GENERIC_BETTERMENT;
}
static inline void
setColor(
TFormatIndices* indices,
Map* targetMap,
Coordinates targetPosition,
Map* corpusMap,
Coordinates corpusPosition
)
{
TPixelelIndex j;
// For all color pixelels (channels)
for(j=FIRST_PIXELEL_INDEX; j<indices->colorEndBip; j++)
// Overwrite prior with new color
pixmap_index(targetMap, targetPosition)[j] =
pixmap_index(corpusMap, corpusPosition)[j];
}
/*
The heart of the algorithm.
Called repeatedly: many passes over the data.
*/
static guint
synthesize(
TImageSynthParameters *parameters, // IN
guint threadIndex, // IN Zero if not threaded
guint startTargetIndex, // IN
guint endTargetIndex, // IN
TFormatIndices* indices, // IN
Map * targetMap, // IN/OUT
Map* corpusMap, // IN
Map* recentProberMap, // IN/OUT
Map* hasValueMap, // IN/OUT
Map* sourceOfMap, // IN/OUT
pointVector targetPoints, // IN
pointVector corpusPoints, // IN
pointVector sortedOffsets, // IN
GRand *prng,
TPixelelMetricFunc corpusTargetMetric, // array pointers
TMapPixelelMetricFunc mapsMetric,
void (*deepProgressCallback)()
)
{
guint target_index;
Coordinates position;
guint repeatCountBetters = 0;
tBettermentKind latestBettermentKind; // matchResult;
gboolean isPerfectMatch = FALSE;
// Best match in this pass search for a matching patch.
guint bestPatchDiff;
Coordinates bestMatchCorpusPoint = {0,0};
/*
Neighbors (patch)
Copied from source image for better memory locality.
*/
// TODO this is large and allocated on the stack
TNeighbor neighbors[IMAGE_SYNTH_MAX_NEIGHBORS];
guint countNeighbors = 0;
/* ALT: count progress once at start of pass countTargetTries += repetition_params[pass][1]; */
reset_color_change();
#ifdef SYNTH_THREADED2
// Changes part of alternative 2. See refinerThreaded.c
for(target_index=startTargetIndex;
target_index<endTargetIndex;
target_index += 1)
#endif
// Each thread works on a slice of targetPoints. Starting at the threadIndex, incremented by count of threads.
// If there is no threads or only one thread, starts at startTargetIndex, increments by 1
for(target_index=startTargetIndex + threadIndex % THREAD_LIMIT;
target_index<endTargetIndex;
target_index += THREAD_LIMIT)
{
#ifdef STATS
countTargetTries += 1;
#endif
#ifdef DEEP_PROGRESS
// Callback to the level which calculates percent and forwards to the ultimate calling process.
// Modulo is the intuitive way to do this.
// But here, we are testing for x lower bits all 1, say 4095, 1111111111.
// Don't AND with an arbitrary single bit, say 4096, since one bit is often set.
if ((target_index&IMAGE_SYNTH_CALLBACK_COUNT) == 0) deepProgressCallback();
#endif
position = g_array_index(targetPoints, Coordinates, target_index);
/*
In the original algorithm, here we called setHasValue(&position, TRUE, hasValueMap);
Which meant we are about to give it a value (and a source),
but also was a flag that meant to put offset (0,0) in neighbors !!!
Now, we always put offset(0,0) in neighbors, and don't call setHasValue
until after we actually synthesize the pixel.
This is safer for threading: it eliminates a window where hasValue is set but color is uninitialized.
*/
countNeighbors = prepare_neighbors(position, parameters, indices,
targetMap, hasValueMap, sourceOfMap, sortedOffsets,
neighbors
);
/*
Repeat a pixel even if found an exact match last pass, because neighbors might have changed.
On passes after the first, we don't explicitly start with best of the previous match,
but since a pixel is it's own first neighbor, the first best calculated will a be
from the source that gave the previous best, and should be a good starting best.
*/
bestPatchDiff = G_MAXUINT; /* A very large positive number. Was: 1<<30 */
// matchResult = NO_BETTERMENT;
isPerfectMatch = FALSE;
latestBettermentKind = NO_BETTERMENT;
/*
Heuristic 1, try neighbors of sources of neighbors of target pixel.
In other words, continue building this region of the target
from the corpus region (continuation) where neighbors of the target pixel came from.
Subtle: The target pixel is its own first neighbor (offset 0,0).
On the first pass, it has no source.
On subsequent passes, it has a source and thus its source is the first corpus point to be probed again,
and that will set bestPatchDiff to a low value!!!
*/
{
guint neighbor_index;
// TODO check for zero here is redundant
for(neighbor_index=0; neighbor_index<countNeighbors && bestPatchDiff != 0; neighbor_index++)
// If the neighbor is in the target (not the context) and has a source in the corpus (already synthesized.)
if ( has_source_neighbor(neighbor_index, neighbors) ) {
/*
Coord arithmetic: corpus source minus neighbor offset.
corpus_point is a pixel in the corpus with opposite offset to corpus source of neighbor
as target position has to this target neighbor.
!!! Note corpus_point is raw coordinate into corpus: might be masked.
!!! It is not an index into unmasked corpusPoints.
*/
Coordinates corpus_point = subtract_points(neighbors[neighbor_index].sourceOf,
neighbors[neighbor_index].offset);
/* !!! Must clip corpus_point before further use, its only potentially in the corpus. */
if (clippedOrMaskedCorpus(corpus_point, corpusMap)) continue;
if (*intmap_index(recentProberMap, corpus_point) == target_index) continue; // Heuristic 2
isPerfectMatch = computeBestFit(corpus_point, indices, corpusMap,
&bestPatchDiff, &bestMatchCorpusPoint,
countNeighbors, neighbors,
&latestBettermentKind, NEIGHBORS_SOURCE,
corpusTargetMetric, mapsMetric
);
// TODO stats: if bettered, is kind NEIGHBORS_SOURCE
// if ( matchResult == PERFECT_MATCH ) break; // Break neighbors loop
if ( isPerfectMatch ) break; // Break neighbors loop
/*
!!! Remember we probed corpus pixel point for target point target_index.
Heuristic 2: all target neighbors with values might come from the same corpus locus,
called a "continuation" in Harrison's thesis.
*/
/*
* Shared and written but no mutex. It should not be a problem,
* even if garbled, the value written is not used except for a comparison.
* At most, it would reduce the value of heuristic2.
* Different threads are probably working in different continuations and not contending.
*/
*intmap_index(recentProberMap, corpus_point) = target_index;
}
// Else the neighbor is not in the target (has no source) so we can't use the heuristic 1.
}
// if ( matchResult != PERFECT_MATCH )
if ( ! isPerfectMatch )
{
/*
Match patches at random source points from the corpus.
In later passes, many will be earlyouts.
*/
gint j;
for(j=0; j<parameters->maxProbeCount; j++)
{
isPerfectMatch = computeBestFit(randomCorpusPoint(corpusPoints, prng),
indices, corpusMap,
&bestPatchDiff, &bestMatchCorpusPoint,
countNeighbors, neighbors,
&latestBettermentKind, RANDOM_CORPUS,
corpusTargetMetric, mapsMetric
);
if ( isPerfectMatch ) break; /* Break loop over random corpus points */
// if ( matchResult == PERFECT_MATCH ) break; /* Break loop over random corpus points */
// Not set recentProberMap(point) since heuristic rarely works for random source.
// TODO if bettered is kind RANDOM_CORPUS
}
}
store_betterment_stats(matchResult);
/* DEBUG dump_target_resynthesis(position); */
/*
Store best match.
Compared to match from a previous pass:
The best match may be no better.
The best match may be the same source point.
The best match may be the same color from a different source point.
The best match may be the same source but a better match because the patch changed.
These are all independent.
We distinguish some of these cases: only store a better matching, new source.
*/
// if (matchResult != NO_BETTERMENT )
if (latestBettermentKind != NO_BETTERMENT )
{
/* if source different from previous pass */
if ( ! equal_points(getSourceOf(position, sourceOfMap), bestMatchCorpusPoint) )
{
repeatCountBetters++; /* feedback for termination. */
integrate_color_change(position); // Must be before we store the new color values.
g_static_mutex_lock(&mutex); // Atomic write to color and sourceOf
// Save the new color values (!!! not the alpha) for this target point
setColor( indices, targetMap, position, corpusMap, bestMatchCorpusPoint);
setSourceOf(position, bestMatchCorpusPoint, sourceOfMap); /* Remember new source */
// printf("Position %d %d source %d %d\n", position.x, position.y, bestMatchCorpusPoint.x, bestMatchCorpusPoint.y);
g_static_mutex_unlock(&mutex);
} /* else same source for target */
} /* else match is same or worse */
// Shared, but no mutex lock because all writers are setting to the same value, TRUE
setHasValue(&position, TRUE, hasValueMap);
} /* end for each target pixel */
return repeatCountBetters;
}