Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
Permutohedral.h
Go to the documentation of this file.
1/*
2 this file has been taken from ImageStack (http://code.google.com/p/imagestack/)
3 and adjusted slightly to fit darktable.
4
5 ImageStack is released under the new bsd license:
6
7Copyright (c) 2010, Andrew Adams
8All rights reserved.
9
10Redistribution and use in source and binary forms, with or without modification, are permitted provided that
11the following conditions are met:
12
13 * Redistributions of source code must retain the above copyright notice, this list of conditions and the
14following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
16the following disclaimer in the documentation and/or other materials provided with the distribution.
17 * Neither the name of the Stanford Graphics Lab nor the names of its contributors may be used to endorse
18or promote products derived from this software without specific prior written permission.
19
20THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
21WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
23DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
27DAMAGE.
28*/
29
30#pragma once
31
32/*******************************************************************
33 * Permutohedral Lattice implementation from: *
34 * Fast High-Dimensional Filtering using the Permutohedral Lattice *
35 * Andrew Adams, Jongmin Baek, Abe Davis *
36 *******************************************************************/
37
38#include <algorithm>
39#include <math.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43
44#include <iostream>
45
46/*******************************************************************
47 * Hash table implementation for permutohedral lattice *
48 * *
49 * The lattice points are stored sparsely using a hash table. *
50 * The key for each point is its spatial location in the (d+1)- *
51 * dimensional space. *
52 * *
53 *******************************************************************/
54template <int KD, int VD> class HashTablePermutohedral
55{
56public:
57 // Struct for a key
58 struct Key
59 {
60 Key() = default;
61
62 Key(const Key &origin, int dim, int direction) // construct neighbor in dimension 'dim'
63 {
64 for(int i = 0; i < KD; i++) key[i] = origin.key[i] + direction;
65 key[dim] = origin.key[dim] - direction * KD;
66 setHash();
67 }
68
69 Key(const Key &) = default; // let the compiler write the copy constructor
70
71 Key &operator=(const Key &) = default;
72
73 void setKey(int idx, short val)
74 {
75 key[idx] = val;
76 }
77
78 void setHash()
79 {
80 size_t k = 0;
81 for(int i = 0; i < KD; i++)
82 {
83 k += key[i];
84 k *= 2531011;
85 }
86 hash = (unsigned)k;
87 }
88
89 bool operator==(const Key &other) const
90 {
91 if(hash != other.hash) return false;
92 for(int i = 0; i < KD; i++)
93 {
94 if(key[i] != other.key[i]) return false;
95 }
96 return true;
97 }
98
99 unsigned hash{ 0 }; // cache the hash value for this key
100 short key[KD]{}; // key is a KD-dimensional vector
101 };
102
103public:
104 // Struct for an associated value
105 struct Value
106 {
107 Value() = default;
108
110 {
111 for(int i = 0; i < VD; i++)
112 {
113 value[i] = init;
114 }
115 }
116
117 Value(const Value &) = default; // let the compiler write the copy constructor
118
119 Value &operator=(const Value &) = default;
120
121 static void clear(float *val)
122 {
123 for(int i = 0; i < VD; i++) val[i] = 0;
124 }
125
126 void setValue(int idx, short val)
127 {
128 value[idx] = val;
129 }
130
131 void addValue(int idx, short val)
132 {
133 value[idx] += val;
134 }
135
136 void add(const Value &other)
137 {
138 for(int i = 0; i < VD; i++)
139 {
140 value[i] += other.value[i];
141 }
142 }
143
144 void add(const float *other, float weight)
145 {
146 for(int i = 0; i < VD; i++)
147 {
148 value[i] += weight * other[i];
149 }
150 }
151
152 void addTo(float *dest, float weight) const
153 {
154 for(int i = 0; i < VD; i++)
155 {
156 dest[i] += weight * value[i];
157 }
158 }
159
160 void mix(const Value *left, const Value *center, const Value *right)
161 {
162 for(int i = 0; i < VD; i++)
163 {
164 value[i] = (0.25f * left->value[i] + 0.5f * center->value[i] + 0.25f * right->value[i]);
165 }
166 }
167
168 Value &operator+=(const Value &other)
169 {
170 for(int i = 0; i < VD; i++)
171 {
172 value[i] += other.value[i];
173 }
174 return *this;
175 }
176
177 float value[VD]{};
178 };
179
180public:
181 /* Constructor
182 * kd_: the dimensionality of the position vectors on the hyperplane.
183 * vd_: the dimensionality of the value vectors
184 */
186 {
187 capacity = 1 << 15;
188 capacity_bits = 0x7fff;
189 filled = 0;
190 entries = new Entry[capacity];
191 keys = new Key[maxFill()];
192 values = new Value[maxFill()]{ 0 };
193 }
194
196
198 {
199 delete[] entries;
200 delete[] keys;
201 delete[] values;
202 }
203
205
206 // Returns the number of vectors stored.
207 int size() const
208 {
209 return filled;
210 }
211
212 size_t maxFill() const
213 {
214 return capacity / 2;
215 }
216
217 // Returns a pointer to the keys array.
218 const Key *getKeys() const
219 {
220 return keys;
221 }
222
223 // Returns a pointer to the values array.
225 {
226 return values;
227 }
228
229 /* Returns the index into the hash table for a given key.
230 * key: a reference to the position vector.
231 * create: a flag specifying whether an entry should be created,
232 * should an entry with the given key not found.
233 */
234 int lookupOffset(const Key &key, bool create = true)
235 {
236 size_t h = key.hash & capacity_bits;
237 // Find the entry with the given key
238 while(1)
239 {
240 Entry e = entries[h];
241 // check if the cell is empty
242 if(e.keyIdx == -1)
243 {
244 if(!create) return -1; // Return not found.
245 // Double hash table size if necessary
246 if(filled >= maxFill())
247 {
248 grow();
249 }
250 // need to create an entry. Store the given key.
251 keys[filled] = key;
252 entries[h].keyIdx = filled;
253 return filled++;
254 }
255
256 // check if the cell has a matching key
257 if(keys[e.keyIdx] == key) return e.keyIdx;
258
259 // increment the bucket with wraparound
260 h = (h + 1) & capacity_bits;
261 }
262 }
263
264 /* Looks up the value vector associated with a given key vector.
265 * k : reference to the key vector to be looked up.
266 * create : true if a non-existing key should be created.
267 */
268 Value *lookup(const Key &k, bool create = true)
269 {
270 int offset = lookupOffset(k, create);
271 return (offset < 0) ? nullptr : values + offset;
272 };
273
274 /* Grows the size of the hash table */
275 void grow(int order = 1)
276 {
277 size_t oldCapacity = capacity;
278 while(order-- > 0)
279 {
280 capacity *= 2;
281 capacity_bits = (capacity_bits << 1) | 1;
282 }
283
284 // Migrate the value vectors.
285 Value *newValues = new Value[maxFill()];
286 std::copy(values, values + filled, newValues);
287 delete[] values;
288 values = newValues;
289
290 // Migrate the key vectors.
291 Key *newKeys = new Key[maxFill()];
292 std::copy(keys, keys + filled, newKeys);
293 delete[] keys;
294 keys = newKeys;
295
296 Entry *newEntries = new Entry[capacity];
297
298 // Migrate the table of indices.
299 for(size_t i = 0; i < oldCapacity; i++)
300 {
301 if(entries[i].keyIdx == -1) continue;
302 size_t h = keys[entries[i].keyIdx].hash & capacity_bits;
303 while(newEntries[h].keyIdx != -1)
304 {
305 h = (h + 1) & capacity_bits;
306 }
307 newEntries[h] = entries[i];
308 }
309 delete[] entries;
310 entries = newEntries;
311 }
312
313private:
314 // Private struct for the hash table entries.
315 struct Entry
316 {
317 int keyIdx{ -1 };
318 };
319
324 unsigned long capacity_bits;
325};
326
327
328/******************************************************************
329 * The algorithm class that performs the filter *
330 * *
331 * PermutohedralLattice::splat(...) and *
332 * PermutohedralLattic::slice() do almost all the work. *
333 * *
334 ******************************************************************/
335template <int D, int VD> class PermutohedralLattice
336{
337private:
338 // short-hand for types we use
340 typedef typename HashTable::Key Key;
341 typedef typename HashTable::Value Value;
342
343public:
344 /* Constructor
345 * d_ : dimensionality of key vectors
346 * vd_ : dimensionality of value vectors
347 * nData_ : number of points in the input
348 */
349 PermutohedralLattice(size_t nData_, int nThreads_ = 1) : nData(nData_), nThreads(nThreads_)
350 {
351 // Allocate storage for various arrays
352 float *scaleFactorTmp = new float[D];
353 int *canonicalTmp = new int[(D + 1) * (D + 1)];
354
355 replay = new ReplayEntry[nData];
356
357 // compute the coordinates of the canonical simplex, in which
358 // the difference between a contained point and the zero
359 // remainder vertex is always in ascending order. (See pg.4 of paper.)
360 for(int i = 0; i <= D; i++)
361 {
362 for(int j = 0; j <= D - i; j++) canonicalTmp[i * (D + 1) + j] = i;
363 for(int j = D - i + 1; j <= D; j++) canonicalTmp[i * (D + 1) + j] = i - (D + 1);
364 }
365 canonical = canonicalTmp;
366
367 // Compute parts of the rotation matrix E. (See pg.4-5 of paper.)
368 for(int i = 0; i < D; i++)
369 {
370 // the diagonal entries for normalization
371 scaleFactorTmp[i] = 1.0f / (sqrtf((float)(i + 1) * (i + 2)));
372
373 /* We presume that the user would like to do a Gaussian blur of standard deviation
374 * 1 in each dimension (or a total variance of d, summed over dimensions.)
375 * Because the total variance of the blur performed by this algorithm is not d,
376 * we must scale the space to offset this.
377 *
378 * The total variance of the algorithm is (See pg.6 and 10 of paper):
379 * [variance of splatting] + [variance of blurring] + [variance of splatting]
380 * = d(d+1)(d+1)/12 + d(d+1)(d+1)/2 + d(d+1)(d+1)/12
381 * = 2d(d+1)(d+1)/3.
382 *
383 * So we need to scale the space by (d+1)sqrt(2/3).
384 */
385 scaleFactorTmp[i] *= (D + 1) * sqrtf(2.0 / 3);
386 }
387 scaleFactor = scaleFactorTmp;
388
390 }
391
393
395 {
396 delete[] scaleFactor;
397 delete[] replay;
398 delete[] canonical;
399 delete[] hashTables;
400 }
401
403
404 /* Performs splatting with given position and value vectors */
405 void splat(float *position, float *value, size_t replay_index, int thread_index = 0) const
406 {
407 float elevated[D + 1];
408 int greedy[D + 1];
409 int rank[D + 1];
410 float barycentric[D + 2];
411 Key key;
412
413 // first rotate position into the (d+1)-dimensional hyperplane
414 elevated[D] = -D * position[D - 1] * scaleFactor[D - 1];
415 for(int i = D - 1; i > 0; i--)
416 elevated[i]
417 = (elevated[i + 1] - i * position[i - 1] * scaleFactor[i - 1] + (i + 2) * position[i] * scaleFactor[i]);
418 elevated[0] = elevated[1] + 2 * position[0] * scaleFactor[0];
419
420 // prepare to find the closest lattice points
421 constexpr float scale = 1.0f / (D + 1);
422
423 // greedily search for the closest zero-colored lattice point
424 int sum = 0;
425 for(int i = 0; i <= D; i++)
426 {
427 float v = elevated[i] * scale;
428 float up = ceilf(v) * (D + 1);
429 float down = floorf(v) * (D + 1);
430
431 if(up - elevated[i] < elevated[i] - down)
432 greedy[i] = up;
433 else
434 greedy[i] = down;
435
436 sum += greedy[i];
437 }
438 sum /= D + 1;
439
440 // rank differential to find the permutation between this simplex and the canonical one.
441 // (See pg. 3-4 in paper.)
442 memset(rank, 0, sizeof rank);
443 for(int i = 0; i < D; i++)
444 for(int j = i + 1; j <= D; j++)
445 if(elevated[i] - greedy[i] < elevated[j] - greedy[j])
446 rank[i]++;
447 else
448 rank[j]++;
449
450 if(sum > 0)
451 {
452 // sum too large - the point is off the hyperplane.
453 // need to bring down the ones with the smallest differential
454 for(int i = 0; i <= D; i++)
455 {
456 if(rank[i] >= D + 1 - sum)
457 {
458 greedy[i] -= D + 1;
459 rank[i] += sum - (D + 1);
460 }
461 else
462 rank[i] += sum;
463 }
464 }
465 else if(sum < 0)
466 {
467 // sum too small - the point is off the hyperplane
468 // need to bring up the ones with largest differential
469 for(int i = 0; i <= D; i++)
470 {
471 if(rank[i] < -sum)
472 {
473 greedy[i] += D + 1;
474 rank[i] += (D + 1) + sum;
475 }
476 else
477 rank[i] += sum;
478 }
479 }
480
481 // Compute barycentric coordinates (See pg.10 of paper.)
482 memset(barycentric, 0, sizeof barycentric);
483 for(int i = 0; i <= D; i++)
484 {
485 barycentric[D - rank[i]] += (elevated[i] - greedy[i]) * scale;
486 barycentric[D + 1 - rank[i]] -= (elevated[i] - greedy[i]) * scale;
487 }
488 barycentric[0] += 1.0f + barycentric[D + 1];
489
490 // Splat the value into each vertex of the simplex, with barycentric weights.
491 replay[replay_index].table = thread_index;
492 for(int remainder = 0; remainder <= D; remainder++)
493 {
494 // Compute the location of the lattice point explicitly (all but the last coordinate - it's redundant
495 // because they sum to zero)
496 for(int i = 0; i < D; i++) key.key[i] = greedy[i] + canonical[remainder * (D + 1) + rank[i]];
497 key.setHash();
498
499 // Retrieve pointer to the value at this vertex.
500 Value *val = hashTables[thread_index].lookup(key, true);
501
502 // Accumulate values with barycentric weight.
503 val->add(value, barycentric[remainder]);
504
505 // Record this interaction to use later when slicing
506 replay[replay_index].offset[remainder] = val - hashTables[thread_index].getValues();
507 replay[replay_index].weight[remainder] = barycentric[remainder];
508 }
509 }
510
511 /* Merge the multiple threads' hash tables into the totals. */
513 {
514 if(nThreads <= 1) return;
515
516 /* Because growing the hash table is expensive, we want to avoid having to do it multiple times.
517 * Only a small percentage of entries in the individual hash tables have the same key, so we
518 * won't waste much space if we simply grow the destination table enough to hold the sum of the
519 * entries in the individual tables
520 */
521 size_t total_entries = hashTables[0].size();
522 for(int i = 1; i < nThreads; i++) total_entries += hashTables[i].size();
523 int order = 0;
524 while(total_entries > hashTables[0].maxFill())
525 {
526 order++;
527 total_entries /= 2;
528 }
529 if(order > 0) hashTables[0].grow(order);
530 /* Merge the multiple hash tables into one, creating an offset remap table. */
531 int **offset_remap = new int *[nThreads];
532 for(int i = 1; i < nThreads; i++)
533 {
534 const Key *oldKeys = hashTables[i].getKeys();
535 const Value *oldVals = hashTables[i].getValues();
536 const int filled = hashTables[i].size();
537 offset_remap[i] = new int[filled];
538 for(int j = 0; j < filled; j++)
539 {
540 Value *val = hashTables[0].lookup(oldKeys[j], true);
541 val->add(oldVals[j]);
542 offset_remap[i][j] = val - hashTables[0].getValues();
543 }
544 }
545
546 /* Rewrite the offsets in the replay structure from the above generated table. */
547 for(int i = 0; i < nData; i++)
548 {
549 if(replay[i].table > 0)
550 {
551 for(int dim = 0; dim <= D; dim++)
552 replay[i].offset[dim] = offset_remap[replay[i].table][replay[i].offset[dim]];
553 }
554 }
555
556 for(int i = 1; i < nThreads; i++) delete[] offset_remap[i];
557 delete[] offset_remap;
558 }
559
560 /* Performs slicing out of position vectors. Note that the barycentric weights and the simplex
561 * containing each position vector were calculated and stored in the splatting step.
562 * We may reuse this to accelerate the algorithm. (See pg. 6 in paper.)
563 */
564 void slice(float *col, size_t replay_index) const
565 {
566 const Value *base = hashTables[0].getValues();
567 Value::clear(col);
568 ReplayEntry &r = replay[replay_index];
569 for(int i = 0; i <= D; i++)
570 {
571 base[r.offset[i]].addTo(col, r.weight[i]);
572 }
573 }
574
575 /* Performs a Gaussian blur along each projected axis in the hyperplane. */
576 void blur() const
577 {
578 // Prepare arrays
579 Value *newValue = new Value[hashTables[0].size()];
580 Value *oldValue = hashTables[0].getValues();
581 const Value *hashTableBase = oldValue;
582 const Key *keyBase = hashTables[0].getKeys();
583 const Value zero{ 0 };
584
585 // For each of d+1 axes,
586 for(int j = 0; j <= D; j++)
587 {
588#ifdef _OPENMP
589#pragma omp parallel for shared(j, oldValue, newValue)
590#endif
591 // For each vertex in the lattice,
592 for(int i = 0; i < hashTables[0].size(); i++) // blur point i in dimension j
593 {
594 const Key &key = keyBase[i]; // keys to current vertex
595 // construct keys to the neighbors along the given axis.
596 Key neighbor1(key, j, +1);
597 Key neighbor2(key, j, -1);
598
599 const Value *oldVal = oldValue + i;
600
601 const Value *vm1 = hashTables[0].lookup(neighbor1, false); // look up first neighbor
602 vm1 = vm1 ? vm1 - hashTableBase + oldValue : &zero;
603
604 const Value *vp1 = hashTables[0].lookup(neighbor2, false); // look up second neighbor
605 vp1 = vp1 ? vp1 - hashTableBase + oldValue : &zero;
606
607 // Mix values of the three vertices
608 newValue[i].mix(vm1, oldVal, vp1);
609 }
610 std::swap(newValue, oldValue);
611 // the freshest data is now in oldValue, and newValue is ready to be written over
612 }
613
614 // depending where we ended up, we may have to copy data
615 if(oldValue != hashTableBase)
616 {
617 std::copy(oldValue, oldValue + hashTables[0].size(), hashTables[0].getValues());
618 delete[] oldValue;
619 }
620 else
621 {
622 delete[] newValue;
623 }
624 }
625
626private:
627 int nData;
629 const float *scaleFactor;
630 const int *canonical;
631
632 // slicing is done by replaying splatting (ie storing the sparse matrix)
634 {
635 // since every dimension of a lattice point gets handled by the same thread,
636 // we only need to store the id of the hash table once, instead of for each dimension
637 int table;
638 int offset[D + 1];
639 float weight[D + 1];
641
643};
644
645// clang-format off
646// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
647// vim: shiftwidth=2 expandtab tabstop=2 cindent
648// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
649// clang-format on
650
void init(dt_imageio_module_format_t *self)
Definition avif.c:142
int position()
Definition backgroundjobs.c:66
Definition Permutohedral.h:55
HashTablePermutohedral(const HashTablePermutohedral &)=delete
const Key * getKeys() const
Definition Permutohedral.h:218
unsigned long capacity_bits
Definition Permutohedral.h:324
int size() const
Definition Permutohedral.h:207
HashTablePermutohedral & operator=(const HashTablePermutohedral &)=delete
~HashTablePermutohedral()
Definition Permutohedral.h:197
Value * values
Definition Permutohedral.h:321
HashTablePermutohedral()
Definition Permutohedral.h:185
Key * keys
Definition Permutohedral.h:320
size_t capacity
Definition Permutohedral.h:323
Entry * entries
Definition Permutohedral.h:322
size_t filled
Definition Permutohedral.h:323
size_t maxFill() const
Definition Permutohedral.h:212
Value * lookup(const Key &k, bool create=true)
Definition Permutohedral.h:268
void grow(int order=1)
Definition Permutohedral.h:275
Value * getValues() const
Definition Permutohedral.h:224
int lookupOffset(const Key &key, bool create=true)
Definition Permutohedral.h:234
Definition Permutohedral.h:336
void slice(float *col, size_t replay_index) const
Definition Permutohedral.h:564
struct PermutohedralLattice::ReplayEntry * replay
HashTable::Value Value
Definition Permutohedral.h:341
const float * scaleFactor
Definition Permutohedral.h:629
PermutohedralLattice(size_t nData_, int nThreads_=1)
Definition Permutohedral.h:349
int nThreads
Definition Permutohedral.h:628
PermutohedralLattice(const PermutohedralLattice &)=delete
void blur() const
Definition Permutohedral.h:576
~PermutohedralLattice()
Definition Permutohedral.h:394
void merge_splat_threads()
Definition Permutohedral.h:512
PermutohedralLattice & operator=(const PermutohedralLattice &)=delete
int nData
Definition Permutohedral.h:627
HashTable * hashTables
Definition Permutohedral.h:642
HashTablePermutohedral< D, VD > HashTable
Definition Permutohedral.h:339
HashTable::Key Key
Definition Permutohedral.h:340
const int * canonical
Definition Permutohedral.h:630
void splat(float *position, float *value, size_t replay_index, int thread_index=0) const
Definition Permutohedral.h:405
char * key
Definition common/metadata.c:40
static void weight(const float *c1, const float *c2, const float sharpen, dt_aligned_pixel_t weight)
Definition eaw.c:29
size_t size
Definition mipmap_cache.c:3
Definition Permutohedral.h:316
int keyIdx
Definition Permutohedral.h:317
Definition Permutohedral.h:59
Key(const Key &)=default
unsigned hash
Definition Permutohedral.h:99
Key & operator=(const Key &)=default
void setHash()
Definition Permutohedral.h:78
Key(const Key &origin, int dim, int direction)
Definition Permutohedral.h:62
bool operator==(const Key &other) const
Definition Permutohedral.h:89
short key[KD]
Definition Permutohedral.h:100
void setKey(int idx, short val)
Definition Permutohedral.h:73
Definition Permutohedral.h:106
void setValue(int idx, short val)
Definition Permutohedral.h:126
Value & operator+=(const Value &other)
Definition Permutohedral.h:168
void add(const Value &other)
Definition Permutohedral.h:136
Value(const Value &)=default
Value & operator=(const Value &)=default
static void clear(float *val)
Definition Permutohedral.h:121
float value[VD]
Definition Permutohedral.h:177
Value(int init)
Definition Permutohedral.h:109
void mix(const Value *left, const Value *center, const Value *right)
Definition Permutohedral.h:160
void addTo(float *dest, float weight) const
Definition Permutohedral.h:152
void addValue(int idx, short val)
Definition Permutohedral.h:131
void add(const float *other, float weight)
Definition Permutohedral.h:144
Definition Permutohedral.h:634
int table
Definition Permutohedral.h:637
float weight[D+1]
Definition Permutohedral.h:639
int offset[D+1]
Definition Permutohedral.h:638