blind

suckless command-line video editing utility
git clone git://git.suckless.org/blind
Log | Files | Refs | README | 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 }