blind-spatial-mean.c - blind - suckless command-line video editing utility
(HTM) git clone git://git.suckless.org/blind
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
blind-spatial-mean.c (4867B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include "common.h"
3
4 USAGE("[-d | -g | -h | -l power-stream | -p power-stream | -v]")
5 /* TODO add [-w weight-stream] for [-ghlpv] */
6
7 /* Because the syntax for a function returning a function pointer is disgusting. */
8 typedef void (*process_func)(struct stream *stream);
9
10 #define C (j & 3)
11 /*
12 * X-parameter 1: method enum value
13 * X-parameter 2: identifier-friendly name
14 * X-parameter 3: initial assignments
15 * X-parameter 4: initial value
16 * X-parameter 5: subcell processing
17 * X-parameter 6: subcell finalisation
18 */
19 #define LIST_MEANS(TYPE)\
20 /* [default] arithmetic mean */\
21 X(ARITHMETIC, arithmetic,, 0, img[C] += *buf, img[C] /= pixels)\
22 /* standard deviation */\
23 X(STANDARD_DEVIATION, sd,, 0, (img[C] += *buf * *buf, aux[C] += *buf),\
24 img[C] = nnpow((img[C] - aux[C] * aux[C] / pixels) / pixels, (TYPE)0.5)) \
25 /* geometric mean */\
26 X(GEOMETRIC, geometric,, 1, img[C] *= *buf, img[C] = nnpow(img[C], 1 / pixels))\
27 /* harmonic mean */\
28 X(HARMONIC, harmonic,, 0, img[C] += (TYPE)1 / *buf, img[C] = pixels / img[C])\
29 /* Lehmer mean */\
30 X(LEHMER, lehmer, (a[0] = powers[0] - (TYPE)1, a[1] = powers[1] - (TYPE)1,\
31 a[2] = powers[2] - (TYPE)1, a[3] = powers[3] - (TYPE)1), 0,\
32 (img[C] += nnpow(*buf, powers[C]), aux[C] += nnpow(*buf, a[C])), img[C] /= aux[C])\
33 /* power mean (Hölder mean) (m = 2 for root square mean; m = 3 for cubic mean) */\
34 X(POWER, power,, 0, img[C] += nnpow(*buf, powers[C]),\
35 img[C] = nnpow(img[C], (TYPE)1 / powers[C]) / pixels)\
36 /* variance */\
37 X(VARIANCE, variance,, 0, (img[C] += *buf * *buf, aux[C] += *buf),\
38 img[C] = (img[C] - aux[C] * aux[C] / pixels) / pixels)
39
40 #define X(V, ...) V,
41 enum method { LIST_MEANS() };
42 #undef X
43
44 static struct stream power;
45 static const char *power_file = NULL;
46
47 #define MAKE_PROCESS(PIXFMT, TYPE,\
48 _1, NAME, INIT, INITIAL, PROCESS_SUBCELL, FINALISE_SUBCELL)\
49 static void\
50 process_##PIXFMT##_##NAME(struct stream *stream)\
51 {\
52 TYPE img[4], aux[4], *buf, a[4], powers[4];\
53 TYPE pixels = (TYPE)(stream->frame_size / sizeof(img));\
54 size_t i, n, j = 0, m = stream->frame_size / sizeof(*img);\
55 int first = 1;\
56 do {\
57 n = stream->ptr / stream->pixel_size * stream->n_chan;\
58 buf = (TYPE *)(stream->buf);\
59 for (i = 0; i < n; i++, buf++, j++, j %= m) {\
60 if (!j) {\
61 if (!first) {\
62 for (j = 0; j < ELEMENTSOF(img); j++)\
63 FINALISE_SUBCELL;\
64 j = 0;\
65 ewriteall(STDOUT_FILENO, img, sizeof(img), "<stdout>");\
66 }\
67 first = 0;\
68 if (power_file && !eread_frame(&power, powers))\
69 return;\
70 INIT;\
71 img[0] = aux[0] = INITIAL;\
72 img[1] = aux[1] = INITIAL;\
73 img[2] = aux[2] = INITIAL;\
74 img[3] = aux[3] = INITIAL;\
75 }\
76 PROCESS_SUBCELL;\
77 }\
78 n *= sizeof(TYPE);\
79 memmove(stream->buf, stream->buf + n, stream->ptr -= n);\
80 } while (eread_stream(stream, SIZE_MAX));\
81 if (!first) {\
82 for (j = 0; j < ELEMENTSOF(img); j++)\
83 FINALISE_SUBCELL;\
84 ewriteall(STDOUT_FILENO, img, sizeof(img), "<stdout>");\
85 }\
86 (void) aux, (void) a, (void) powers, (void) pixels;\
87 }
88 #define X(...) MAKE_PROCESS(lf, double, __VA_ARGS__)
89 LIST_MEANS(double)
90 #undef X
91 #define X(...) MAKE_PROCESS(f, float, __VA_ARGS__)
92 LIST_MEANS(float)
93 #undef X
94 #undef MAKE_PROCESS
95 #undef C
96
97 #define X(ID, NAME, ...) [ID] = process_lf_##NAME,
98 static const process_func process_functions_lf[] = { LIST_MEANS() };
99 #undef X
100
101 #define X(ID, NAME, ...) [ID] = process_f_##NAME,
102 static const process_func process_functions_f[] = { LIST_MEANS() };
103 #undef X
104
105 int
106 main(int argc, char *argv[])
107 {
108 struct stream stream;
109 process_func process;
110 enum method method = ARITHMETIC;
111
112 ARGBEGIN {
113 case 'd':
114 method = STANDARD_DEVIATION;
115 break;
116 case 'g':
117 method = GEOMETRIC;
118 break;
119 case 'h':
120 method = HARMONIC;
121 break;
122 case 'l':
123 method = LEHMER;
124 power_file = UARGF();
125 break;
126 case 'p':
127 method = POWER;
128 power_file = UARGF();
129 break;
130 case 'v':
131 method = VARIANCE;
132 break;
133 default:
134 usage();
135 } ARGEND;
136
137 if (argc)
138 usage();
139
140 eopen_stream(&stream, NULL);
141 if (power_file != NULL) {
142 eopen_stream(&power, power_file);
143 if (power.width != 1 || power.height != 1)
144 eprintf("%s: videos do not have the 1x1 geometry\n", power_file);
145 if (strcmp(power.pixfmt, stream.pixfmt))
146 eprintf("videos use incompatible pixel formats\n");
147 }
148
149 CHECK_N_CHAN(&stream, 4, 4);
150 if (stream.encoding == DOUBLE)
151 process = process_functions_lf[method];
152 else if (stream.encoding == FLOAT)
153 process = process_functions_f[method];
154 else
155 eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
156
157 if (DPRINTF_HEAD(STDOUT_FILENO, stream.frames, 1, 1, stream.pixfmt) < 0)
158 eprintf("dprintf:");
159 process(&stream);
160 if (stream.ptr)
161 eprintf("%s: incomplete frame\n", stream.file);
162 return 0;
163 }