blind-temporal-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-temporal-mean.c (5440B)
---
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, void *buffer, void *image, size_t frame);
9
10 /*
11 * X-parameter 1: method enum value
12 * X-parameter 2: identifier-friendly name
13 * X-parameter 3: images
14 * X-parameter 4: action for first frame
15 * X-parameter 5: pre-process assignments
16 * X-parameter 6: subcell processing
17 * X-parameter 7: pre-finalise assignments
18 * X-parameter 8: subcell finalisation
19 */
20 #define LIST_MEANS(TYPE)\
21 /* [default] arithmetic mean */\
22 X(ARITHMETIC, arithmetic, 1, COPY_FRAME,, *img += *buf,\
23 a = (TYPE)1 / (TYPE)frame, *img *= a)\
24 /* standard deviation */\
25 X(STANDARD_DEVIATION, sd, 2, ZERO_AND_PROCESS_FRAME,, (*img += *buf * *buf, *aux += *buf),\
26 a = (TYPE)1 / (TYPE)frame, *img = nnpow((*img - *aux * *aux * a) * a, (TYPE)0.5))\
27 /* geometric mean */\
28 X(GEOMETRIC, geometric, 1, COPY_FRAME,, *img *= *buf,\
29 a = (TYPE)1 / (TYPE)frame, *img = nnpow(*img, a))\
30 /* harmonic mean */\
31 X(HARMONIC, harmonic, 1, ZERO_AND_PROCESS_FRAME,, *img += (TYPE)1 / *buf,\
32 a = (TYPE)frame, *img = a / *img)\
33 /* Lehmer mean */\
34 X(LEHMER, lehmer, 2, ZERO_AND_PROCESS_FRAME,,\
35 (*img += nnpow(*buf, *pows), *aux += nnpow(*buf, *pows - (TYPE)1)),, *img /= *aux)\
36 /* power mean (Hölder mean) (m = 2 for root square mean; m = 3 for cubic mean) */\
37 X(POWER, power, 1, ZERO_AND_PROCESS_FRAME,, *img += nnpow(*buf, *pows),\
38 a = (TYPE)1 / (TYPE)frame, *img = a * nnpow(*img, (TYPE)1 / *pows))\
39 /* variance */\
40 X(VARIANCE, variance, 2, ZERO_AND_PROCESS_FRAME,, (*img += *buf * *buf, *aux += *buf),\
41 a = (TYPE)1 / (TYPE)frame, *img = (*img - *aux * *aux * a) * a)
42
43 enum first_frame_action {
44 COPY_FRAME,
45 PROCESS_FRAME,
46 ZERO_AND_PROCESS_FRAME,
47 };
48
49 #define X(V, ...) V,
50 enum method { LIST_MEANS() };
51 #undef X
52
53 static void *powerbuf = NULL;
54
55 #define MAKE_PROCESS(PIXFMT, TYPE,\
56 _1, NAME, _3, _4, PRE_PROCESS, PROCESS_SUBCELL, PRE_FINALISE, FINALISE_SUBCELL)\
57 static void\
58 process_##PIXFMT##_##NAME(struct stream *stream, void *buffer, void *image, size_t frame)\
59 {\
60 TYPE *buf = buffer, *img = image, a, *pows = powerbuf;\
61 TYPE *aux = (TYPE *)(((char *)image) + stream->frame_size);\
62 size_t x, y, z;\
63 if (!buf) {\
64 PRE_FINALISE;\
65 for (z = 0; z < stream->n_chan; z++)\
66 for (y = 0; y < stream->height; y++)\
67 for (x = 0; x < stream->width; x++, img++, aux++, pows++)\
68 FINALISE_SUBCELL;\
69 } else {\
70 PRE_PROCESS;\
71 for (z = 0; z < stream->n_chan; z++)\
72 for (y = 0; y < stream->height; y++)\
73 for (x = 0; x < stream->width; x++, img++, aux++, pows++, buf++) { \
74 PROCESS_SUBCELL;\
75 }\
76 }\
77 (void) aux, (void) a, (void) pows, (void) frame;\
78 }
79 #define X(...) MAKE_PROCESS(lf, double, __VA_ARGS__)
80 LIST_MEANS(double)
81 #undef X
82 #define X(...) MAKE_PROCESS(f, float, __VA_ARGS__)
83 LIST_MEANS(float)
84 #undef X
85 #undef MAKE_PROCESS
86
87 #define X(ID, NAME, ...) [ID] = process_lf_##NAME,
88 static const process_func process_functions_lf[] = { LIST_MEANS() };
89 #undef X
90
91 #define X(ID, NAME, ...) [ID] = process_f_##NAME,
92 static const process_func process_functions_f[] = { LIST_MEANS() };
93 #undef X
94
95 int
96 main(int argc, char *argv[])
97 {
98 struct stream stream, power;
99 void *buf, *img;
100 process_func process;
101 size_t frames, images;
102 enum method method = ARITHMETIC;
103 enum first_frame_action first_frame_action;
104 const char *power_file = NULL;
105
106 ARGBEGIN {
107 case 'd':
108 method = STANDARD_DEVIATION;
109 break;
110 case 'g':
111 method = GEOMETRIC;
112 break;
113 case 'h':
114 method = HARMONIC;
115 break;
116 case 'l':
117 method = LEHMER;
118 power_file = UARGF();
119 break;
120 case 'p':
121 method = POWER;
122 power_file = UARGF();
123 break;
124 case 'v':
125 method = VARIANCE;
126 break;
127 default:
128 usage();
129 } ARGEND;
130
131 if (argc)
132 usage();
133
134 #define X(ID, _2, IMAGES, FIRST_FRAME_ACTION, ...)\
135 case ID:\
136 images = IMAGES;\
137 first_frame_action = FIRST_FRAME_ACTION;\
138 break;
139 switch (method) {
140 LIST_MEANS()
141 default:
142 abort();
143 }
144 #undef X
145
146 eopen_stream(&stream, NULL);
147 if (power_file != NULL) {
148 eopen_stream(&power, power_file);
149 echeck_compat(&stream, &power);
150 powerbuf = emalloc(power.frame_size);
151 if (!eread_frame(&power, powerbuf))
152 eprintf("%s is no frames\n", power_file);
153 }
154
155 if (stream.encoding == DOUBLE)
156 process = process_functions_lf[method];
157 else if (stream.encoding == FLOAT)
158 process = process_functions_f[method];
159 else
160 eprintf("pixel format %s is not supported, try xyza\n", stream.pixfmt);
161
162 stream.frames = 1;
163 echeck_dimensions(&stream, WIDTH | HEIGHT, NULL);
164 fprint_stream_head(stdout, &stream);
165 efflush(stdout, "<stdout>");
166 buf = emalloc(stream.frame_size);
167 if (first_frame_action == ZERO_AND_PROCESS_FRAME)
168 img = ecalloc(images, stream.frame_size);
169 else
170 img = emalloc2(images, stream.frame_size);
171
172 frames = 0;
173 if (first_frame_action == COPY_FRAME) {
174 if (!eread_frame(&stream, img))
175 eprintf("video is no frames\n");
176 frames++;
177 }
178 for (; eread_frame(&stream, buf); frames++)
179 process(&stream, buf, img, frames);
180 if (!frames)
181 eprintf("video has no frames\n");
182 process(&stream, NULL, img, frames);
183
184 ewriteall(STDOUT_FILENO, img, stream.frame_size, "<stdout>");
185 free(buf);
186 free(img);
187 free(powerbuf);
188 return 0;
189 }