-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathplambda.c
2235 lines (2074 loc) · 65.2 KB
/
plambda.c
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
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// PLAMBDA(1) imscript PLAMBDA(1) {{{1
//
// NAME {{{2
// plambda - a RPN calculator for image pixels
//
// SYNOPSIS {{{2
// plambda a.pnb b.png c.png ... "lambda expression" > output
// plambda a.pnb b.png c.png ... "lambda expression" -o output.png
// plambda -c num1 num2 ... "lambda expression"
//
// DESCRIPTION {{{2
// Plambda applies an expression to all the pixels of a collection of
// images, and produces a single output image. Each input image
// corresponds to one of the variables of the expression (in alphabetical
// order). There are modifiers to the variables that allow access to the
// values of neighboring pixels, or to particular components of a pixel.
//
// LANGUAGE {{{2
// A "plambda" program is a sequence of tokens. Tokens may be constants,
// variables, or operators. Constants and variables get their value
// computed and pushed to the stack. Operators pop values from the stack,
// apply a function to them, and push back the results.
//
// CONSTANTS: numeric constants written in scientific notation, and "pi"
// OPERATORS: +, -, *, ^, /, and all the functions from math.h
// VARIABLES: anything not recognized as a constant or operator. There
// must be as many variables as input images, and they are assigned to
// images in alphabetical order.
//
// All operators (unary, binary and ternary) are vectorializable.
//
// Some "sugar" is added to the language:
//
// Predefined variables (always preceeded by a colon):
//
// TOKEN MEANING
//
// :i horizontal coordinate of the pixel
// :j vertical coordinate of the pixel
// :w width of the image
// :h heigth of the image
// :n number of pixels in the image
// :x relative horizontal coordinate of the pixel
// :y relative horizontal coordinate of the pixel
// :r relative distance to the center of the image
// :t relative angle from the center of the image
// :I horizontal coordinate of the pixel (centered)
// :J vertical coordinate of the pixel (centered)
// :W width of the image divided by 2*pi
// :H height of the image divided by 2*pi
//
//
// Variable modifiers acting on regular variables:
//
// TOKEN MEANING
//
// x value of pixel (i,j)
// x(0,0) value of pixel (i,j)
// x(1,0) value of pixel (i+1,j)
// x(0,-1) value of pixel (i,j-1)
// ...
//
// x value of pixel (i,j)
// x[0] value of first component of pixel (i,j)
// x[1] value of second component of pixel (i,j)
//
// x(1,-1)[2] value of third component of pixel (i+1,j-1)
//
//
// Stack operators (allow direct manipulation of the stack):
//
// TOKEN MEANING
//
// del remove the value at the top of the stack (ATTOTS)
// dup duplicate the value ATTOTS
// rot swap the two values ATTOTS
// split split the vector ATTOTS into scalar components
// join join the components of two vectors ATTOTS
// join3 join the components of three vectors ATTOTS
// n njoin join the components of n vectors ATTOTS
//
//
// Magic modifiers (which are not computed locally for each image):
//
// x%i value of the smallest sample of image "x"
// x%a value of the largest sample
// x%v average sample value
// x%m median sample value
// x%I value of the smallest pixel
// x%A value of the largest pixel
// x%V average pixel value
// x%M median pixel value (not implemented)
// x%qn nth sample percentile
// x%Qn nth pixel percentile (not implemented)
// x%r random sample of the image (not implemented)
// x%R random pixel of the image (not implemented)
//
// Notice that the scalar "magic" modifiers may act upon individual
// components, e.g. x[2]%i is the minimum value of the blue component.
//
//
// Vectorial operators (non-vectorializable):
//
// TOKEN MEANING
// randu push a random number U(0,1)
// randn push a random number N(0,1)
// randg push a random number N(0,1)
// randc push a random number C(0,1)
// randl push a random number L(0,1)
// rande push a random number E(1)
// randp push a random number P(1)
// rand push a random integer returned from "rand()"
// topolar convert a 2-vector from cartesian to polar
// frompolar
// cprod multiply two 2-vectors as complex numbers
// mprod multiply two vectors as matrices (4vector=2x2 matrix...)
// vprod vector product of two 3-vectors
// sprod scalar product of two n-vectors
// mdet determinant of a n-matrix (a n*n-vector)
// mtrans transpose of a matrix
// mtrace trace of a matrix
// minv inverse of a matrix
//
// OPTIONS {{{2
// -o file save output to named file
// -c act as a symbolic calculator
// -h print short help message
// --help print longer help message
// --man print manpage (requires help2man)
// --version print program version
//
// EXAMPLES {{{2
// Sum two images:
//
// plambda a.png b.png "a b +" > aplusb.png
//
// Add a gaussian to half of lena:
//
// plambda /tmp/lena.png "x 2 / :r :r * -1 * 40 * exp 200 * +"
//
// Forward differences to compute the derivative in horizontal direction:
//
// plambda lena.png "x(1,0) x -"
//
// Sobel edge detector:
// plambda lena.png "x(1,0) 2 * x(1,1) x(1,-1) + + x(-1,0) 2 * x(-1,1) x(-1,-1) + + - x(0,1) 2 * x(1,1) x(-1,1) + + x(0,-1) 2 * x(1,-1) x(-1,-1) + + - hypot"
//
// Color to gray:
// plambda lena.png "x[0] x[1] x[2] + + 3 /"
//
// Pick the blue channel of a RGB image:
// plambda lena.png "x[2]"
//
// Swap the blue an green channels of a RGB image (6 equivalent ways):
// plambda lena.png "x[0] x[2] x[1] join3"
// plambda lena.png "x[0] x[2] x[1] join join"
// plambda lena.png "x[0] x[1] x[2] rot join3"
// plambda lena.png "x[0] x[1] x[2] rot join join"
// plambda lena.png "x split rot join join"
// plambda lena.png "x split rot join3"
//
// Merge the two components of a vector field into a single file
// plambda x.tiff y.tiff "x y join" > xy.tiff
//
// Set to 0 the green component of a RGB image
// plambda lena.png "x[0] 0 x[2] join3"
//
// Naive Canny filter:
// cat lena.png | gblur 2 | plambda - "x(1,0) 2 * x(1,1) x(1,-1) + + x(-1,0) 2 * x(-1,1) x(-1,-1) + + - >1 x(0,1) 2 * x(1,1) x(-1,1) + + x(0,-1) 2 * x(1,-1) x(-1,-1) + + - >2 <1 <2 hypot <2 <1 atan2 join" | plambda - "x[0] 4 > >1 x[1] fabs pi 4 / > x[1] fabs pi 4 / 3 * < * >2 x[1] fabs pi 4 / < x[1] fabs pi 4 / 3 * > + >3 x[0] x[0](0,1) > x[0] x[0](0,-1) > * >4 x[0] x[0](1,0) > x[0] x[0](-1,0) > * >5 <1 <3 <5 * * <1 <2 <4 * * + x[0] *" | qauto | display
//
// Anti-Lalpacian (solve Poisson equation):
// cat lena.png | fft 1 | plambda - "x :I :I * :J :J * + / -1 *" | fft -1 | qauto | display
//
// Wiener Filter (for real kernels):
// PREC=0.01
// plambda kernel.fft image.fft "h[0] dup dup * $PREC + / y *"
//
// Deconvolution using max frequency cut (for real kernels):
// FCUT=80
// plambda kernel.fft image.fft ":I :J hypot $FCUT < y h[0] / 0 if"
//
// Generate a U(-1,1) scalar field with gaussian grain
// GRAINSIZE=7
// plambda zero:WxH "randn"|blur g $GRAINSIZE|plambda - "x $GRAINSIZE * pi sqrt * 2 * 2 sqrt / erf"
//
// Generate a N(0,1) scalar field with gaussian grain
// plambda zero:WxH "randn"|blur g $GRAINSIZE|plambda - "x $GRAINSIZE * pi sqrt * 2 *"
//
// Generate a L(0,sigma=1) scalar field with gaussian grain
// plambda zero:WxH "randn randn randn randn 4 njoin $GRAINSIZE * pi sqrt * 2 *"|blur g $GRAINSIZE|plambda - "x[0] x[1] * x[2] x[3] * - 2 sqrt /"
//
// Periodic component of an image
// cat image|fftper|fft|plambda - "x :I :I * :J :J * + *"|ifft|crop 0 0 `imprintf "%w %h" image`|fft|plambda - "x :I :I * :J :J * + / 4 /"|ifft >pcomponent
//
//
//
// AUTHOR {{{2
//
// Written by Enric Meinhardt-Llopis
//
//
// REPORTING BUGS {{{2
//
// Download last version from https://github.com/mnhrdt
// Report bugs to <[email protected]>
//
// TODO LIST {{{2
//
// * implement shunting-yard algorithm to admit infix notation
// * handle 3D and nD images
// * merge colonvars and magicvars (the only difficulty lies in naming)
// #includes {{{1
#include <assert.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "smapa.h"
#include "fail.c"
#include "xmalloc.c"
#include "random.c"
#include "parsenumbers.c"
#include "colorcoords.c"
// #defines {{{1
#define PLAMBDA_MAX_TOKENS 2049
#define PLAMBDA_MAX_VARLEN 0x100
#define PLAMBDA_MAX_PIXELDIM 0x100
#define PLAMBDA_MAX_MAGIC 42
#ifndef FORI
#define FORI(n) for(int i=0;i<(n);i++)
#endif
#ifndef FORJ
#define FORJ(n) for(int j=0;j<(n);j++)
#endif
#ifndef FORK
#define FORK(n) for(int k=0;k<(n);k++)
#endif
#ifndef FORL
#define FORL(n) for(int l=0;l<(n);l++)
#endif
#define PLAMBDA_CONSTANT 0 // numeric constant
#define PLAMBDA_SCALAR 1 // pixel component
#define PLAMBDA_VECTOR 2 // whole pixel
#define PLAMBDA_OPERATOR 3 // function
#define PLAMBDA_COLONVAR 4 // colon-type variable
#define PLAMBDA_STACKOP 5 // stack operator
#define PLAMBDA_VARDEF 6 // register variable definition (hacky)
#define PLAMBDA_MAGIC 7 // "magic" modifier (requiring cached global data)
// local functions {{{1
static double sum_two_doubles (double a, double b) { return a + b; }
static double substract_two_doubles(double a, double b) { return a - b; }
static double multiply_two_doubles (double a, double b) { return a * b; }
static double divide_two_doubles (double a, double b) {
if (!b && !a) return 0;
return a / b;
}
static double logic_g (double a, double b) { return a > b; }
static double logic_l (double a, double b) { return a < b; }
static double logic_e (double a, double b) { return a == b; }
static double logic_ge (double a, double b) { return a >= b; }
static double logic_le (double a, double b) { return a <= b; }
static double logic_ne (double a, double b) { return a != b; }
static double logic_if (double a, double b, double c) { return a ? b : c; }
static double logic_or (double a, double b) { return a || b; }
static double logic_and (double a, double b) { return a && b; }
static double logic_not (double a) { return !a; }
static double function_isfinite (double x) { return isfinite(x); }
static double function_isinf (double x) { return isinf(x); }
static double function_isnan (double x) { return isnan(x); }
static double function_isnormal (double x) { return isnormal(x); }
static double function_signbit (double x) { return signbit(x); }
static double inftozero (double x) { return isinf(x) ? 0 : x; }
static double nantozero (double x) { return isnan(x) ? 0 : x; }
static double notfintozero (double x) { return isfinite(x) ? x : 0; }
static double force_finite (double x) { return isfinite(x) ? x : 0; }
static double force_normal (double x) { return isnormal(x) ? x : 0; }
static double quantize_255 (double x)
{
int ix = x;
if (ix < 0) return 0;
if (ix > 255) return 255;
return ix;
}
static double quantize_easy(double x, double a, double b)
{
return quantize_255(255.0*(x-a)/(b-a));
}
static double range(double x, double a, double b)
{
return (x-a)/(b-a);
}
static double bound_double(double x, double a, double b)
{
if (x < a) return a;
if (x > b) return b;
return x;
}
static void from_cartesian_to_polar(float *y, float *x)
{
y[0] = hypot(x[0], x[1]);
y[1] = atan2(x[1], x[0]);
}
static void from_polar_to_cartesian(float *y, float *x)
{
y[0] = x[0] * cos(x[1]);
y[1] = x[0] * sin(x[1]);
}
static void complex_product(float *xy, float *x, float *y)
{
xy[0] = x[0]*y[0] - x[1]*y[1];
xy[1] = x[0]*y[1] + x[1]*y[0];
}
static void matrix_product_clean(
float *ab, int *ab_nrows, int *ab_ncols,
float *a, int a_nrows, int a_ncols,
float *b, int b_nrows, int b_ncols)
{
if (a_ncols != b_nrows)
fail("impossible matrix product (%d %d) * (%d %d)",
a_nrows, a_ncols, b_nrows, b_ncols);
*ab_nrows = a_nrows;
*ab_ncols = b_ncols;
float (*A)[a_ncols] = (void*)a;
float (*B)[b_ncols] = (void*)b;
float (*AB)[*ab_ncols] = (void*)ab;
for (int i = 0; i < *ab_nrows; i++)
for (int j = 0; j < *ab_ncols; j++)
{
AB[i][j] = 0;
for (int k = 0; k < a_ncols; k++)
AB[i][j] += A[i][k] * B[k][j];
}
}
// instance of "bivector_function"
static int matrix_product(float *ab, float *a, float *b, int na, int nb)
{
int a_nrows, a_ncols, b_nrows, b_ncols;
if (na == 4 && nb == 4) {
a_nrows = a_ncols = b_nrows = b_ncols = 2;
} else if (na == 9 && nb == 9) {
a_nrows = a_ncols = b_nrows = b_ncols = 3;
} else if (na == 9 && nb == 3) {
a_nrows = a_ncols = b_nrows = 3;
b_ncols = 1;
} else if (na == 4 && nb == 2) {
a_nrows = a_ncols = b_nrows = 2;
b_ncols = 1;
} else if (na == 1 && nb == 1) {
a_nrows = a_ncols = b_nrows = b_ncols = 1;
} else if (na == 6 && nb == 2) {
ab[0] = a[0]*b[0] + a[1]*b[1] + a[2];
ab[1] = a[3]*b[0] + a[4]*b[1] + a[5];
return 2;
} else fail("bad matrix product (%d %d)", na, nb);
assert(a_ncols == b_nrows);
int ab_nrows, ab_ncols;
matrix_product_clean(ab, &ab_nrows, &ab_ncols,
a, a_nrows, a_ncols, b, b_nrows, b_ncols);
return ab_nrows * ab_ncols;
}
// instance of "bivector_function"
static int vector_product(float *ab, float *a, float *b, int na, int nb)
{
if (na != 3 || nb != 3)
fail("bad vector product (%d %d)", na, nb);
ab[0] = a[1]*b[2] - a[2]*b[1];
ab[1] = a[2]*b[0] - a[0]*b[2];
ab[2] = a[0]*b[1] - a[1]*b[0];
return 3;
}
// instance of "bivector_function"
static int scalar_product(float *ab, float *a, float *b, int na, int nb)
{
if (na != nb)
fail("bad scalar product (%d %d)", na, nb);
*ab = 0;
for (int i = 0 ; i < na; i++)
*ab += a[i] * b[i];
return 1;
}
// instance of "univector_function"
static int matrix_determinant(float *r, float *a, int n)
{
switch(n) {
case 1: *r = *a; break;
case 4: *r = a[0]*a[3] - a[1]*a[2]; break;
case 6: *r = a[0]*a[4] - a[1]*a[3]; break;
case 9: *r = a[0]*a[4]*a[8] + a[2]*a[3]*a[7] + a[1]*a[5]*a[6]
- a[2]*a[4]*a[6] - a[1]*a[3]*a[8] - a[0]*a[5]*a[7]; break;
default: fail("can not compute determinant of object of size %d", n);
}
return 1;
}
// instance of "univector_function"
static int matrix_inverse(float *r, float *a, int n)
{
float det;
matrix_determinant(&det, a, n);
switch(n) {
case 1:
r[0] = 1/a[0];
return 1;
case 4:
r[0] = a[3]/det;
r[1] = -a[1]/det;
r[2] = -a[2]/det;
r[3] = a[0]/det;
return 4;
case 6:
r[0] = a[4]/det;
r[1] = -a[1]/det;
r[2] = (a[1]*a[5]-a[2]*a[4])/det;
r[3] = -a[3]/det;
r[4] = a[0]/det;
r[5] = (a[2]*a[3]-a[0]*a[5])/det;
return 6;
case 9:
r[0] = (a[4]*a[8]-a[5]*a[7])/det;
r[1] = (a[2]*a[7]-a[1]*a[8])/det;
r[2] = (a[1]*a[5]-a[2]*a[4])/det;
r[3] = (a[5]*a[6]-a[3]*a[8])/det;
r[4] = (a[0]*a[8]-a[2]*a[6])/det;
r[5] = (a[2]*a[3]-a[0]*a[5])/det;
r[6] = (a[3]*a[7]-a[4]*a[6])/det;
r[7] = (a[1]*a[6]-a[0]*a[7])/det;
r[8] = (a[0]*a[4]-a[1]*a[3])/det;
return 9;
default: fail("can not compute inversion of object of size %d", n);
}
}
// instance of "univector_function"
static int matrix_trace(float *r, float *a, int nn)
{
int n;
switch(nn) {
case 1: n = 1; break;
case 4: n = 2; break;
case 9: n = 3; break;
default: fail("can not compute trace of object of size %d", nn);
}
assert(n*n == nn);
*r = 0;
for (int i = 0; i < n; i++)
*r += a[i*n+i];
return 1;
}
// instance of "univector_function"
static int matrix_transpose(float *r, float *a, int nn)
{
int n;
switch(nn) {
case 1: n = 1; break;
case 4: n = 2; break;
case 9: n = 3; break;
default: fail("can not transpose object of size %d", nn);
}
assert(n*n == nn);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
r[i*n+j] = a[j*n+i];
return nn;
}
// table of all functions (local and from math.h) {{{1
struct predefined_function {
void (*f)(void);
char *name;
int nargs;
float value;
} global_table_of_predefined_functions[] = {
#define REGISTER_FUNCTION(x,n) {(void(*)(void))x, #x, n, 0}
#define REGISTER_FUNCTIONN(x,xn,n) {(void(*)(void))x, xn, n, 0}
REGISTER_FUNCTION(acos,1),
REGISTER_FUNCTION(acosh,1),
REGISTER_FUNCTION(asin,1),
REGISTER_FUNCTION(asinh,1),
REGISTER_FUNCTION(atan,1),
REGISTER_FUNCTION(atanh,1),
REGISTER_FUNCTION(cbrt,1),
REGISTER_FUNCTION(ceil,1),
REGISTER_FUNCTION(cos,1),
REGISTER_FUNCTION(cosh,1),
REGISTER_FUNCTION(erf,1),
REGISTER_FUNCTION(erfc,1),
REGISTER_FUNCTION(exp,1),
REGISTER_FUNCTION(exp2,1),
REGISTER_FUNCTION(expm1,1),
REGISTER_FUNCTION(fabs,1),
REGISTER_FUNCTION(floor,1),
REGISTER_FUNCTION(lgamma,1),
REGISTER_FUNCTION(log,1),
REGISTER_FUNCTION(log10,1),
REGISTER_FUNCTION(log1p,1),
REGISTER_FUNCTION(log2,1),
REGISTER_FUNCTION(logb,1),
REGISTER_FUNCTION(nearbyint,1),
REGISTER_FUNCTION(rint,1),
REGISTER_FUNCTION(round,1),
REGISTER_FUNCTION(sin,1),
REGISTER_FUNCTION(sinh,1),
REGISTER_FUNCTION(sqrt,1),
REGISTER_FUNCTION(tan,1),
REGISTER_FUNCTION(tanh,1),
REGISTER_FUNCTION(tgamma,1),
REGISTER_FUNCTION(trunc,1),
REGISTER_FUNCTION(inftozero,1),
REGISTER_FUNCTION(nantozero,1),
REGISTER_FUNCTION(notfintozero,1),
REGISTER_FUNCTION(force_finite,1),
REGISTER_FUNCTION(force_normal,1),
REGISTER_FUNCTION(atan2,2),
REGISTER_FUNCTION(copysign,2),
REGISTER_FUNCTION(fdim,2),
REGISTER_FUNCTION(fma,2),
REGISTER_FUNCTION(fmax,2),
REGISTER_FUNCTION(fmin,2),
REGISTER_FUNCTION(fmod,2),
REGISTER_FUNCTION(hypot,2),
REGISTER_FUNCTION(ldexp,2),
REGISTER_FUNCTION(nextafter,2),
REGISTER_FUNCTION(nexttoward,2),
REGISTER_FUNCTION(pow,2),
REGISTER_FUNCTION(remainder,2),
REGISTER_FUNCTIONN(quantize_255,"q255",1),
REGISTER_FUNCTIONN(quantize_easy,"qe",3),
REGISTER_FUNCTIONN(range,"range",3),
REGISTER_FUNCTIONN(bound_double,"bound",3),
REGISTER_FUNCTIONN(pow,"^",2),
REGISTER_FUNCTIONN(sum_two_doubles,"+",2),
REGISTER_FUNCTIONN(logic_g,">",2),
REGISTER_FUNCTIONN(logic_l,"<",2),
REGISTER_FUNCTIONN(logic_e,"=",2),
REGISTER_FUNCTIONN(logic_ge,">=",2),
REGISTER_FUNCTIONN(logic_le,"<=",2),
REGISTER_FUNCTIONN(logic_ne,"!=",2),
REGISTER_FUNCTIONN(logic_if,"if",3),
REGISTER_FUNCTIONN(logic_and,"and",2),
REGISTER_FUNCTIONN(logic_or,"or",2),
REGISTER_FUNCTIONN(logic_not,"not",1),
REGISTER_FUNCTIONN(function_isfinite,"isfinite",1),
REGISTER_FUNCTIONN(function_isinf,"isinf",1),
REGISTER_FUNCTIONN(function_isnan,"isnan",1),
REGISTER_FUNCTIONN(function_isnormal,"isnormal",1),
REGISTER_FUNCTIONN(function_signbit,"signbit",1),
REGISTER_FUNCTIONN(divide_two_doubles,"/",2),
REGISTER_FUNCTIONN(multiply_two_doubles,"*",2),
REGISTER_FUNCTIONN(substract_two_doubles,"-",2),
REGISTER_FUNCTIONN(random_uniform,"randu",-1),
REGISTER_FUNCTIONN(random_normal,"randn",-1),
REGISTER_FUNCTIONN(random_normal,"randg",-1),
REGISTER_FUNCTIONN(random_cauchy,"randc",-1),
REGISTER_FUNCTIONN(random_laplace,"randl",-1),
REGISTER_FUNCTIONN(random_exponential,"rande",-1),
REGISTER_FUNCTIONN(random_pareto,"randp",-1),
REGISTER_FUNCTIONN(random_raw,"rand",-1),
REGISTER_FUNCTIONN(from_cartesian_to_polar,"topolar", -2),
REGISTER_FUNCTIONN(from_polar_to_cartesian,"frompolar", -2),
REGISTER_FUNCTIONN(complex_product,"cprod", -3),
REGISTER_FUNCTIONN(matrix_product,"mprod",-5),
REGISTER_FUNCTIONN(vector_product,"vprod",-5),
REGISTER_FUNCTIONN(scalar_product,"sprod",-5),
REGISTER_FUNCTIONN(matrix_determinant,"mdet",-6),
REGISTER_FUNCTIONN(matrix_transpose,"mtrans",-6),
REGISTER_FUNCTIONN(matrix_inverse,"minv",-6),
REGISTER_FUNCTIONN(matrix_trace,"mtrace",-6),
//REGISTER_FUNCTIONN(rgb2hsv,"rgb2hsv",3),
//REGISTER_FUNCTIONN(hsv2rgb,"rgb2hsv",3),
#undef REGISTER_FUNCTION
#undef REGISTER_FUNCTIONN
{NULL, "pi", 0, M_PI},
#ifdef M_E
#define REGISTER_CONSTANT(x) {NULL, #x, 0, x}
REGISTER_CONSTANT(M_E),
REGISTER_CONSTANT(M_LOG2E),
REGISTER_CONSTANT(M_LOG10E),
REGISTER_CONSTANT(M_LN2),
REGISTER_CONSTANT(M_LN10),
//REGISTER_CONSTANT(M_PI),
REGISTER_CONSTANT(M_PI_2),
REGISTER_CONSTANT(M_PI_4),
REGISTER_CONSTANT(M_1_PI),
REGISTER_CONSTANT(M_2_PI),
REGISTER_CONSTANT(M_2_SQRTPI),
REGISTER_CONSTANT(M_SQRT2),
REGISTER_CONSTANT(M_SQRT1_2),
#undef REGISTER_CONSTANT
#endif
};
static float apply_function(struct predefined_function *f, float *v)
{
switch(f->nargs) {
case 0: return f->value;
case 1: return ((double(*)(double))(f->f))(v[0]);
case 2: return ((double(*)(double,double))f->f)(v[1], v[0]);
case 3: return ((double(*)(double,double,double))f->f)(v[2],v[1],v[0]);
case -1: return ((double(*)())(f->f))();
default: fail("bizarre{%d}", f->nargs);
}
//return 0;
}
static int symmetrize_index_inside(int i, int m)
{
assert( i >= 0 && i < m);
int r = 0;
if (i >= m/2) r = i-m;
if (i < m/2) r = i;
return r;
}
// the value of colon variables depends on the position within the image
static float eval_colonvar(int w, int h, int i, int j, int c)
{
switch(c) {
case 'i': return i;
case 'j': return j;
case 'w': return w;
case 'h': return h;
case 'n': return w*h;
case 'x': return (2.0/(w-1))*i - 1;
case 'y': return (2.0/(h-1))*j - 1;
case 'r': return hypot((2.0/(h-1))*j-1,(2.0/(w-1))*i-1);
case 't': return atan2((2.0/(h-1))*j-1,(2.0/(w-1))*i-1);
case 'I': return symmetrize_index_inside(i,w);
case 'J': return symmetrize_index_inside(j,h);
case 'W': return w/(2*M_PI);
case 'H': return h/(2*M_PI);
default: fail("unrecognized colonvar \":%c\"", c);
}
}
// struct plambda_program {{{1
struct plambda_token {
int type;
float value; // if type==constant, value
int index; // if type==variable, its index
// if type==operator, its index
int component; // if type==variable, index of selected component
int displacement[2]; // if type==variable, relative displacement
int colonvar; // if type==colon, the letter
char *tmphack; // temporary place for storing the unsorted index
};
struct collection_of_varnames {
int n;
char *t[PLAMBDA_MAX_TOKENS];
};
struct plambda_program {
int n;
struct plambda_token t[PLAMBDA_MAX_TOKENS];
struct collection_of_varnames var[1];
};
// image statistics {{{1
struct image_stats {
bool init_simple, init_vsimple, init_ordered, init_vordered;
float scalar_min, scalar_max, scalar_avg, scalar_med, scalar_sum;
//float vector_cmin[PLAMBDA_MAX_PIXELDIM]; // component-wise min
float vector_n1min[PLAMBDA_MAX_PIXELDIM]; // exemplar with min L1
float vector_n2min[PLAMBDA_MAX_PIXELDIM]; // exemplar with min L2
float vector_nimin[PLAMBDA_MAX_PIXELDIM]; // exemplar with min Linf
//float vector_cmax[PLAMBDA_MAX_PIXELDIM];
float vector_n1max[PLAMBDA_MAX_PIXELDIM];
float vector_n2max[PLAMBDA_MAX_PIXELDIM];
float vector_nimax[PLAMBDA_MAX_PIXELDIM];
float vector_avg[PLAMBDA_MAX_PIXELDIM];
float vector_med[PLAMBDA_MAX_PIXELDIM];
float vector_sum[PLAMBDA_MAX_PIXELDIM];
bool init_csimple, init_cordered;
float component_min[PLAMBDA_MAX_PIXELDIM];
float component_max[PLAMBDA_MAX_PIXELDIM];
float component_avg[PLAMBDA_MAX_PIXELDIM];
float component_med[PLAMBDA_MAX_PIXELDIM];
float component_sum[PLAMBDA_MAX_PIXELDIM];
float *sorted_samples, *sorted_components[PLAMBDA_MAX_PIXELDIM];
};
struct linear_statistics {
float min, max, avg, avgnz, sum;
int n, rns, rnz, nnan, ninf;
};
static void compute_linstats(struct linear_statistics *s,
float *x, int n, int stride, int offset)
{
int rns = 0, rnz = 0, nnan = 0, ninf = 0;
float min = INFINITY, max = -INFINITY;
long double avg = 0, avgnz = 0;
for (int i = 0; i < n; i++) {
float y = x[i*stride + offset];
if (isnan(y)) {
nnan += 1;
continue;
}
if (!isfinite(y)) ninf += 1;
if (y < min) min = y;
if (y > max) max = y;
avg += y;
rns += 1;
if (y) {
avgnz += y;
rnz += 1;
}
}
s->sum = avg;
avg /= rns; avgnz /= rnz;
s->min=min; s->max=max; s->avg=avg; s->avgnz=avgnz;
s->n=n; s->rns=rns; s->rnz=rnz; s->nnan=nnan; s->ninf=ninf;
}
static void compute_simple_sample_stats(struct image_stats *s,
float *x, int w, int h, int pd)
{
if (s->init_simple) return;
if (w*h > 1) s->init_simple = true;
struct linear_statistics ls[1];
compute_linstats(ls, x, w*h*pd, 1, 0);
s->scalar_min = ls->min;
s->scalar_max = ls->max;
s->scalar_avg = ls->avg;
s->scalar_sum = ls->sum;
}
static void compute_simple_component_stats(struct image_stats *s,
float *x, int w, int h, int pd)
{
if (s->init_csimple) return;
if (w*h > 1) s->init_csimple = true;
for (int l = 0; l < pd; l++)
{
struct linear_statistics ls[1];
compute_linstats(ls, x, w*h, pd, l);
s->component_min[l] = ls->min;
s->component_max[l] = ls->max;
s->component_avg[l] = ls->avg;
}
}
static float euclidean_norm_of_float_vector(const float *x, int n)
{
if (n == 1) return fabs(x[0]);
else {
float r = 0;
for (int i = 0; i < n; i++)
r = hypot(r, x[i]);
return r;
}
}
static void compute_simple_vector_stats(struct image_stats *s,
float *x, int w, int h, int pd)
{
if (s->init_vsimple) return;
if (w*h > 1) s->init_vsimple = true;
int np = w * h, rnp = 0;
float minpixel = INFINITY, maxpixel = -INFINITY;
long double avgpixel[PLAMBDA_MAX_PIXELDIM] = {0}, avgnorm = 0;
int minidx=-1, maxidx=-1;
for (int j = 0; j < pd; j++)
avgpixel[j] = 0;
for (int i = 0; i < np; i++)
{
float xnorm = euclidean_norm_of_float_vector(x + pd*i, pd);
if (isnan(xnorm)) continue;
if (xnorm < minpixel) { minidx = i; minpixel = xnorm; }
if (xnorm > maxpixel) { maxidx = i; maxpixel = xnorm; }
for (int j = 0; j < pd; j++)
avgpixel[j] += x[pd*i+j];
avgnorm += xnorm;
rnp += 1;
}
//assert(rnp);
FORI(pd) s->vector_sum[i] = avgpixel[i];
avgnorm /= rnp;
long double mipi[pd], mapi[pd];
for (int j = 0; j < pd; j++) {
mipi[j] = x[minidx*pd+j];
mapi[j] = x[maxidx*pd+j];
avgpixel[j] /= rnp;
}
FORI(pd) s->vector_n2min[i] = mipi[i];
FORI(pd) s->vector_n2max[i] = mapi[i];
FORI(pd) s->vector_avg[i] = avgpixel[i];
//setnumber(p, "error", avgnorm);
}
static int compare_floats(const void *aa, const void *bb)
{
const float *a = (const float *)aa;
const float *b = (const float *)bb;
return (*a > *b) - (*a < *b);
}
static void compute_ordered_sample_stats(struct image_stats *s,
float *x, int w, int h, int pd)
{
if (s->init_ordered) return;
if (w*h > 1) s->init_ordered = true;
int ns = w * h * pd;
s->sorted_samples = xmalloc(ns*sizeof(float));
FORI(ns) s->sorted_samples[i] = x[i];
qsort(s->sorted_samples, ns, sizeof(float), compare_floats);
s->scalar_med = s->sorted_samples[ns/2];
}
static void compute_ordered_component_stats(struct image_stats *s,
float *x, int w, int h, int pd)
{
if (s->init_cordered) return;
if (w*h > 1) s->init_cordered = true;
int ns = w * h;
float *t = xmalloc(pd*ns*sizeof(float));
for (int l = 0; l < pd; l++)
{
s->sorted_components[l] = t + l*ns;
FORI(ns) s->sorted_components[l][i] = x[i*pd+l];
qsort(s->sorted_components[l],ns,sizeof(float),compare_floats);
s->component_med[l] = s->sorted_components[l][ns/2];
}
}
static void compute_ordered_vector_stats(struct image_stats *s,
float *x, int w, int h, int pd)
{
(void)x;
(void)w;
(void)h;
(void)pd;
fail("ordered vector stats not implemented");
// there is some bizarre trickery waiting to be coded in here
//s->init_vordered = true;
}
static int bound(int a, int x, int b)
{
if (b < a) return bound(b, x, a);
if (x < a) return a;
if (x > b) return b;
return x;
}
// the value of magic variables depends on some globally cached data
static int eval_magicvar(float *out, int magic, int img_index, int comp, int qq,
float *x, int w, int h, int pd) // only needed on the first run
{
// XXX WARNING : global variables here (leading to non-re-entrant code)
static bool initt = false;
static struct image_stats *t = 0;
//static struct image_stats t[PLAMBDA_MAX_MAGIC];
if (!initt) {
t = xmalloc(PLAMBDA_MAX_MAGIC * sizeof*t);
for (int i = 0; i < PLAMBDA_MAX_MAGIC; i++) {
t[i].init_simple = false;
t[i].init_ordered = false;
t[i].init_vsimple = false;
t[i].init_vordered = false;
t[i].init_csimple = false;
t[i].init_cordered = false;
}
initt = true;
}
//fprintf(stderr, "magic=%c index=%d comp=%d\n",magic,img_index,comp);
if (img_index >= PLAMBDA_MAX_MAGIC)
fail("%d magic images is too much for me!", PLAMBDA_MAX_MAGIC);
struct image_stats *ti = t + img_index;
if (magic=='i' || magic=='a' || magic=='v' || magic=='s') {
if (comp < 0) { // use all samples
compute_simple_sample_stats(ti, x, w, h, pd);
switch(magic) {
case 'i': *out = ti->scalar_min; break;
case 'a': *out = ti->scalar_max; break;
case 'v': *out = ti->scalar_avg; break;
case 's': *out = ti->scalar_sum; break;
default: fail("this can not happen");
}
return 1;
} else { // use samples from the specified component
compute_simple_component_stats(ti, x, w, h, pd);
switch(magic) {
case 'i': *out = ti->component_min[comp]; break;
case 'a': *out = ti->component_max[comp]; break;
case 'v': *out = ti->component_avg[comp]; break;
case 's': *out = ti->component_sum[comp]; break;
default: fail("this can not happen");
}
return 1;
}
} else if (magic=='I' || magic=='A' || magic=='V' || magic=='S') {
compute_simple_vector_stats(ti, x, w, h, pd);
switch(magic) {
case 'I': FORI(pd) out[i] = ti->vector_n2min[i]; break;
case 'A': FORI(pd) out[i] = ti->vector_n2max[i]; break;
case 'V': FORI(pd) out[i] = ti->vector_avg[i]; break;
case 'S': FORI(pd) out[i] = ti->vector_sum[i]; break;
default: fail("this can not happen");
}
return pd;
} else if (magic=='Y' || magic=='E') {
compute_simple_component_stats(ti, x, w, h, pd);
switch(magic) {
case 'Y': FORI(pd) out[i] = ti->component_min[i]; break;
case 'E': FORI(pd) out[i] = ti->component_max[i]; break;
default: fail("this can not happen");
}
return pd;
} else if (magic == 'm' || magic == 'q') {
if (comp < 0) { // use all samples
compute_ordered_sample_stats(ti, x, w, h, pd);
if (magic == 'm') {
*out = ti->scalar_med;
return 1;
}
if (magic == 'q') {
int qpos = round(qq*w*h*pd/100.0);
qpos = bound(0, qpos, w*h*pd-1);
*out = ti->sorted_samples[qpos];
return 1;
}
} else {
compute_ordered_component_stats(ti, x, w, h, pd);
if (magic == 'm') {
*out = ti->component_med[comp];
return 1;
}
if (magic == 'q') {
int qpos = round(qq*w*h/100.0);
qpos = bound(0, qpos, w*h-1);
*out = ti->sorted_components[comp][qpos];
return 1;
}
}
} else if (magic == 'O') {
compute_ordered_component_stats(ti, x, w, h, pd);
FORI(pd) {
int qposi = round(qq*w*h/100.0);
qposi = bound(0, qposi, w*h-1);
out[i] = ti->sorted_components[i][qposi];
}
return pd;
} else if (magic == 'W') {
compute_ordered_component_stats(ti, x, w, h, pd);
FORI(pd) {
int qposi = round(qq*w*h/1000000.0);
qposi = bound(0, qposi, w*h-1);
out[i] = ti->sorted_components[i][qposi];
}
return pd;
} else if (magic == '0') {
compute_ordered_component_stats(ti, x, w, h, pd);
FORI(pd) {
int qposi = qq;//round(qq*w*h/1000000.0);
qposi = bound(0, qposi, w*h-1);