lavfi: add Perlin noise generator
This commit is contained in:
		
							parent
							
								
									c9151ea507
								
							
						
					
					
						commit
						3764b8ecdb
					
				| @ -14,6 +14,7 @@ version <next>: | |||||||
| - xHE-AAC decoder | - xHE-AAC decoder | ||||||
| - removed DEC Alpha DSP and support code | - removed DEC Alpha DSP and support code | ||||||
| - VVC encoding support via libvvenc | - VVC encoding support via libvvenc | ||||||
|  | - perlin video source | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| version 7.0: | version 7.0: | ||||||
|  | |||||||
							
								
								
									
										100
									
								
								doc/filters.texi
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								doc/filters.texi
									
									
									
									
									
								
							| @ -17290,6 +17290,9 @@ The command accepts the same syntax of the corresponding option. | |||||||
| If the specified expression is not valid, it is kept at its current | If the specified expression is not valid, it is kept at its current | ||||||
| value. | value. | ||||||
| 
 | 
 | ||||||
|  | @anchor{lutrgb} | ||||||
|  | @anchor{lutyuv} | ||||||
|  | @anchor{lut} | ||||||
| @section lut, lutrgb, lutyuv | @section lut, lutrgb, lutyuv | ||||||
| 
 | 
 | ||||||
| Compute a look-up table for binding each pixel component input value | Compute a look-up table for binding each pixel component input value | ||||||
| @ -29281,6 +29284,103 @@ ffplay -f lavfi life=s=300x200:mold=10:r=60:ratio=0.1:death_color=#C83232:life_c | |||||||
| @end example | @end example | ||||||
| @end itemize | @end itemize | ||||||
| 
 | 
 | ||||||
|  | @section perlin | ||||||
|  | Generate Perlin noise. | ||||||
|  | 
 | ||||||
|  | Perlin noise is a kind of noise with local continuity in space. This | ||||||
|  | can be used to generate patterns with continuity in space and time, | ||||||
|  | e.g. to simulate smoke, fluids, or terrain. | ||||||
|  | 
 | ||||||
|  | In case more than one octave is specified through the @option{octaves} | ||||||
|  | option, Perlin noise is generated as a sum of components, each one | ||||||
|  | with doubled frequency. In this case the @option{persistence} option | ||||||
|  | specify the ratio of the amplitude with respect to the previous | ||||||
|  | component. More octave components enable to specify more high | ||||||
|  | frequency details in the generated noise (e.g. small size variations | ||||||
|  | due to boulders in a generated terrain). | ||||||
|  | 
 | ||||||
|  | @subsection Options | ||||||
|  | @table @option | ||||||
|  | 
 | ||||||
|  | @item size, s | ||||||
|  | Specify the size (width and height) of the buffered video frames. For the | ||||||
|  | syntax of this option, check the | ||||||
|  | @ref{video size syntax,,"Video size" section in the ffmpeg-utils manual,ffmpeg-utils}. | ||||||
|  | 
 | ||||||
|  | @item rate, r | ||||||
|  | Specify the frame rate expected for the video stream, expressed as a | ||||||
|  | number of frames per second. | ||||||
|  | 
 | ||||||
|  | @item octaves | ||||||
|  | Specify the total number of components making up the noise, each one | ||||||
|  | with doubled frequency. | ||||||
|  | 
 | ||||||
|  | @item persistence | ||||||
|  | Set the ratio used to compute the amplitude of the next octave | ||||||
|  | component with respect to the previous component amplitude. | ||||||
|  | 
 | ||||||
|  | @item xscale | ||||||
|  | @item yscale | ||||||
|  | Define a scale factor used to multiple the x, y coordinates. This can | ||||||
|  | be useful to define an effect with a pattern stretched along the x or | ||||||
|  | y axis. | ||||||
|  | 
 | ||||||
|  | @item tscale | ||||||
|  | Define a scale factor used to multiple the time coordinate. This can | ||||||
|  | be useful to change the time variation speed. | ||||||
|  | 
 | ||||||
|  | @item random_mode | ||||||
|  | Set random mode used to compute initial pattern. | ||||||
|  | 
 | ||||||
|  | Supported values are: | ||||||
|  | @table @option | ||||||
|  | @item random | ||||||
|  | Compute and use random seed. | ||||||
|  | 
 | ||||||
|  | @item ken | ||||||
|  | Use the predefined initial pattern defined by Ken Perlin in the | ||||||
|  | original article, can be useful to compare the output with other | ||||||
|  | sources. | ||||||
|  | 
 | ||||||
|  | @item seed | ||||||
|  | Use the value specified by @option{random_seed} option. | ||||||
|  | @end table | ||||||
|  | 
 | ||||||
|  | @item random_seed, seed | ||||||
|  | When @option{random_mode} is set to @var{random_seed}, use this value | ||||||
|  | to compute the initial pattern. | ||||||
|  | @end table | ||||||
|  | 
 | ||||||
|  | @subsection Examples | ||||||
|  | @itemize | ||||||
|  | @item | ||||||
|  | Generate single component: | ||||||
|  | @example | ||||||
|  | perlin | ||||||
|  | @end example | ||||||
|  | 
 | ||||||
|  | @item | ||||||
|  | Use Perlin noise with 7 components, each one with a halved contribution | ||||||
|  | to total amplitude: | ||||||
|  | @example | ||||||
|  | perlin=octaves=7:persistence=0.5 | ||||||
|  | @end example | ||||||
|  | 
 | ||||||
|  | @item | ||||||
|  | Chain Perlin noise with the @ref{lutyuv} to generate a black&white | ||||||
|  | effect: | ||||||
|  | @example | ||||||
|  | perlin=octaves=3:tscale=0.3,lutyuv=y='if(lt(val\,128)\,255\,0)' | ||||||
|  | @end example | ||||||
|  | 
 | ||||||
|  | @item | ||||||
|  | Stretch noise along the y axis, and convert gray level to red-only | ||||||
|  | signal: | ||||||
|  | @example | ||||||
|  | perlin=octaves=7:tscale=0.4:yscale=0.3,lutrgb=r=val:b=0:g=0 | ||||||
|  | @end example | ||||||
|  | @end itemize | ||||||
|  | 
 | ||||||
| @section qrencodesrc | @section qrencodesrc | ||||||
| 
 | 
 | ||||||
| Generate a QR code using the libqrencode library (see | Generate a QR code using the libqrencode library (see | ||||||
|  | |||||||
| @ -603,6 +603,7 @@ OBJS-$(CONFIG_NULLSRC_FILTER)                += vsrc_testsrc.o | |||||||
| OBJS-$(CONFIG_OPENCLSRC_FILTER)              += vf_program_opencl.o opencl.o | OBJS-$(CONFIG_OPENCLSRC_FILTER)              += vf_program_opencl.o opencl.o | ||||||
| OBJS-$(CONFIG_PAL75BARS_FILTER)              += vsrc_testsrc.o | OBJS-$(CONFIG_PAL75BARS_FILTER)              += vsrc_testsrc.o | ||||||
| OBJS-$(CONFIG_PAL100BARS_FILTER)             += vsrc_testsrc.o | OBJS-$(CONFIG_PAL100BARS_FILTER)             += vsrc_testsrc.o | ||||||
|  | OBJS-$(CONFIG_PERLIN_FILTER)                 += vsrc_perlin.o perlin.o | ||||||
| OBJS-$(CONFIG_QRENCODE_FILTER)               += qrencode.o textutils.o | OBJS-$(CONFIG_QRENCODE_FILTER)               += qrencode.o textutils.o | ||||||
| OBJS-$(CONFIG_QRENCODESRC_FILTER)            += qrencode.o textutils.o | OBJS-$(CONFIG_QRENCODESRC_FILTER)            += qrencode.o textutils.o | ||||||
| OBJS-$(CONFIG_RGBTESTSRC_FILTER)             += vsrc_testsrc.o | OBJS-$(CONFIG_RGBTESTSRC_FILTER)             += vsrc_testsrc.o | ||||||
|  | |||||||
| @ -569,6 +569,7 @@ extern const AVFilter ff_vsrc_openclsrc; | |||||||
| extern const AVFilter ff_vsrc_qrencodesrc; | extern const AVFilter ff_vsrc_qrencodesrc; | ||||||
| extern const AVFilter ff_vsrc_pal75bars; | extern const AVFilter ff_vsrc_pal75bars; | ||||||
| extern const AVFilter ff_vsrc_pal100bars; | extern const AVFilter ff_vsrc_pal100bars; | ||||||
|  | extern const AVFilter ff_vsrc_perlin; | ||||||
| extern const AVFilter ff_vsrc_rgbtestsrc; | extern const AVFilter ff_vsrc_rgbtestsrc; | ||||||
| extern const AVFilter ff_vsrc_sierpinski; | extern const AVFilter ff_vsrc_sierpinski; | ||||||
| extern const AVFilter ff_vsrc_smptebars; | extern const AVFilter ff_vsrc_smptebars; | ||||||
|  | |||||||
							
								
								
									
										224
									
								
								libavfilter/perlin.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								libavfilter/perlin.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,224 @@ | |||||||
|  | /*
 | ||||||
|  |  * This file is part of FFmpeg. | ||||||
|  |  * | ||||||
|  |  * FFmpeg is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * FFmpeg is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License along | ||||||
|  |  * with FFmpeg; if not, write to the Free Software Foundation, Inc., | ||||||
|  |  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @file | ||||||
|  |  * Perlin Noise generator, based on code from: | ||||||
|  |  * https://adrianb.io/2014/08/09/perlinnoise.html
 | ||||||
|  |  * | ||||||
|  |  * Original article from Ken Perlin: | ||||||
|  |  * http://mrl.nyu.edu/~perlin/paper445.pdf
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <math.h> | ||||||
|  | 
 | ||||||
|  | #include "libavutil/lfg.h" | ||||||
|  | #include "libavutil/random_seed.h" | ||||||
|  | #include "perlin.h" | ||||||
|  | 
 | ||||||
|  | static inline int inc(int num, int period) | ||||||
|  | { | ||||||
|  |     num++; | ||||||
|  |     if (period > 0) | ||||||
|  |         num %= period; | ||||||
|  |     return num; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline double grad(int hash, double x, double y, double z) | ||||||
|  | { | ||||||
|  |     // Take the hashed value and take the first 4 bits of it (15 == 0b1111)
 | ||||||
|  |     int h = hash & 15; | ||||||
|  |     // If the most significant bit (MSB) of the hash is 0 then set u = x.  Otherwise y.
 | ||||||
|  |     double u = h < 8 /* 0b1000 */ ? x : y; | ||||||
|  |     double v; | ||||||
|  | 
 | ||||||
|  |     // In Ken Perlin's original implementation this was another
 | ||||||
|  |     // conditional operator (?:), then expanded for readability.
 | ||||||
|  |     if (h < 4 /* 0b0100 */) | ||||||
|  |         // If the first and second significant bits are 0 set v = y
 | ||||||
|  |         v = y; | ||||||
|  |     // If the first and second significant bits are 1 set v = x
 | ||||||
|  |     else if (h == 12 /* 0b1100 */ || h == 14 /* 0b1110 */) | ||||||
|  |         v = x; | ||||||
|  |     else | ||||||
|  |         // If the first and second significant bits are not equal (0/1, 1/0) set v = z
 | ||||||
|  |         v = z; | ||||||
|  | 
 | ||||||
|  |     // Use the last 2 bits to decide if u and v are positive or negative.  Then return their addition.
 | ||||||
|  |     return ((h&1) == 0 ? u : -u)+((h&2) == 0 ? v : -v); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline double fade(double t) | ||||||
|  | { | ||||||
|  |     // Fade function as defined by Ken Perlin. This eases coordinate values
 | ||||||
|  |     // so that they will "ease" towards integral values. This ends up smoothing
 | ||||||
|  |     // the final output.
 | ||||||
|  |     // use Horner method to compute: 6t^5 - 15t^4 + 10t^3
 | ||||||
|  |     return t * t * t * (t * (t * 6 - 15) + 10); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static double lerp(double a, double b, double x) | ||||||
|  | { | ||||||
|  |     return a + x * (b - a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Hash lookup table as defined by Ken Perlin.  This is a randomly
 | ||||||
|  | // arranged array of all numbers from 0-255 inclusive.
 | ||||||
|  | static uint8_t ken_permutations[] = { | ||||||
|  |     151, 160, 137,  91,  90,  15, 131,  13, 201,  95,  96,  53, 194, 233,   7, 225, | ||||||
|  |     140,  36, 103,  30,  69, 142,   8,  99,  37, 240,  21,  10,  23, 190,   6, 148, | ||||||
|  |     247, 120, 234,  75,   0,  26, 197,  62,  94, 252, 219, 203, 117,  35,  11,  32, | ||||||
|  |      57, 177,  33,  88, 237, 149,  56,  87, 174,  20, 125, 136, 171, 168,  68, 175, | ||||||
|  |      74, 165,  71, 134, 139,  48,  27, 166,  77, 146, 158, 231,  83, 111, 229, 122, | ||||||
|  |      60, 211, 133, 230, 220, 105,  92,  41,  55,  46, 245,  40, 244, 102, 143,  54, | ||||||
|  |      65,  25,  63, 161,   1, 216,  80,  73, 209,  76, 132, 187, 208,  89,  18, 169, | ||||||
|  |     200, 196, 135, 130, 116, 188, 159,  86, 164, 100, 109, 198, 173, 186,   3,  64, | ||||||
|  |      52, 217, 226, 250, 124, 123,   5, 202,  38, 147, 118, 126, 255,  82,  85, 212, | ||||||
|  |     207, 206,  59, 227,  47,  16,  58,  17, 182, 189,  28,  42, 223, 183, 170, 213, | ||||||
|  |     119, 248, 152,   2,  44, 154, 163,  70, 221, 153, 101, 155, 167,  43, 172,   9, | ||||||
|  |     129,  22,  39, 253,  19,  98, 108, 110,  79, 113, 224, 232, 178, 185, 112, 104, | ||||||
|  |     218, 246,  97, 228, 251,  34, 242, 193, 238, 210, 144,  12, 191, 179, 162, 241, | ||||||
|  |      81,  51, 145, 235, 249,  14, 239, 107,  49, 192, 214,  31, 181, 199, 106, 157, | ||||||
|  |     184,  84, 204, 176, 115, 121,  50,  45, 127,   4, 150, 254, 138, 236, 205,  93, | ||||||
|  |     222, 114,  67,  29,  24,  72, 243, 141, 128, 195,  78,  66, 215,  61, 156, 180 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | int ff_perlin_init(FFPerlin *perlin, double period, int octaves, double persistence, | ||||||
|  |                    enum FFPerlinRandomMode random_mode, unsigned int random_seed) | ||||||
|  | { | ||||||
|  |     int i; | ||||||
|  | 
 | ||||||
|  |     perlin->period = period; | ||||||
|  |     perlin->octaves = octaves; | ||||||
|  |     perlin->persistence = persistence; | ||||||
|  |     perlin->random_mode = random_mode; | ||||||
|  |     perlin->random_seed = random_seed; | ||||||
|  | 
 | ||||||
|  |     if (perlin->random_mode == FF_PERLIN_RANDOM_MODE_KEN) { | ||||||
|  |         for (i = 0; i < 512; i++) { | ||||||
|  |             perlin->permutations[i] = ken_permutations[i % 256]; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         AVLFG lfg; | ||||||
|  |         uint8_t random_permutations[256]; | ||||||
|  | 
 | ||||||
|  |         if (perlin->random_mode == FF_PERLIN_RANDOM_MODE_RANDOM) | ||||||
|  |             perlin->random_seed = av_get_random_seed(); | ||||||
|  | 
 | ||||||
|  |         av_lfg_init(&lfg, perlin->random_seed); | ||||||
|  | 
 | ||||||
|  |         for (i = 0; i < 256; i++) { | ||||||
|  |             random_permutations[i] = i; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (i = 0; i < 256; i++) { | ||||||
|  |             unsigned int random_idx = av_lfg_get(&lfg) % (256-i); | ||||||
|  |             uint8_t random_val = random_permutations[random_idx]; | ||||||
|  |             random_permutations[random_idx] = random_permutations[256-i]; | ||||||
|  | 
 | ||||||
|  |             perlin->permutations[i] = perlin->permutations[i+256] = random_val; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static double perlin_get(FFPerlin *perlin, double x, double y, double z) | ||||||
|  | { | ||||||
|  |     int xi, yi, zi; | ||||||
|  |     double xf, yf, zf; | ||||||
|  |     double u, v, w; | ||||||
|  |     const uint8_t *p = perlin->permutations; | ||||||
|  |     double period = perlin->period; | ||||||
|  |     int aaa, aba, aab, abb, baa, bba, bab, bbb; | ||||||
|  |     double x1, x2, y1, y2; | ||||||
|  | 
 | ||||||
|  |     if (perlin->period > 0) { | ||||||
|  |         // If we have any period on, change the coordinates to their "local" repetitions
 | ||||||
|  |         x = fmod(x, perlin->period); | ||||||
|  |         y = fmod(y, perlin->period); | ||||||
|  |         z = fmod(z, perlin->period); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Calculate the "unit cube" that the point asked will be located in
 | ||||||
|  |     // The left bound is ( |_x_|,|_y_|,|_z_| ) and the right bound is that
 | ||||||
|  |     // plus 1.  Next we calculate the location (from 0.0 to 1.0) in that cube.
 | ||||||
|  |     xi = (int)x & 255; | ||||||
|  |     yi = (int)y & 255; | ||||||
|  |     zi = (int)z & 255; | ||||||
|  | 
 | ||||||
|  |     xf = x - (int)x; | ||||||
|  |     yf = y - (int)y; | ||||||
|  |     zf = z - (int)z; | ||||||
|  | 
 | ||||||
|  |     // We also fade the location to smooth the result.
 | ||||||
|  |     u = fade(xf); | ||||||
|  |     v = fade(yf); | ||||||
|  |     w = fade(zf); | ||||||
|  | 
 | ||||||
|  |     aaa = p[p[p[    xi         ] +     yi         ] +     zi         ]; | ||||||
|  |     aba = p[p[p[    xi         ] + inc(yi, period)] +     zi         ]; | ||||||
|  |     aab = p[p[p[    xi         ] +     yi         ] + inc(zi, period)]; | ||||||
|  |     abb = p[p[p[    xi         ] + inc(yi, period)] + inc(zi, period)]; | ||||||
|  |     baa = p[p[p[inc(xi, period)] +     yi         ] +     zi         ]; | ||||||
|  |     bba = p[p[p[inc(xi, period)] + inc(yi, period)] +     zi         ]; | ||||||
|  |     bab = p[p[p[inc(xi, period)] +     yi         ] + inc(zi, period)]; | ||||||
|  |     bbb = p[p[p[inc(xi, period)] + inc(yi, period)] + inc(zi, period)]; | ||||||
|  | 
 | ||||||
|  |     // The gradient function calculates the dot product between a pseudorandom
 | ||||||
|  |     // gradient vector and the vector from the input coordinate to the 8
 | ||||||
|  |     // surrounding points in its unit cube.
 | ||||||
|  |     // This is all then lerped together as a sort of weighted average based on the faded (u,v,w)
 | ||||||
|  |     // values we made earlier.
 | ||||||
|  |     x1 = lerp(grad(aaa, xf  , yf  , zf), | ||||||
|  |               grad(baa, xf-1, yf  , zf), | ||||||
|  |               u); | ||||||
|  |     x2 = lerp(grad(aba, xf  , yf-1, zf), | ||||||
|  |               grad(bba, xf-1, yf-1, zf), | ||||||
|  |               u); | ||||||
|  |     y1 = lerp(x1, x2, v); | ||||||
|  | 
 | ||||||
|  |     x1 = lerp(grad(aab, xf  , yf  , zf-1), | ||||||
|  |               grad(bab, xf-1, yf  , zf-1), | ||||||
|  |               u); | ||||||
|  |     x2 = lerp(grad(abb, xf  , yf-1, zf-1), | ||||||
|  |               grad(bbb, xf-1, yf-1, zf-1), | ||||||
|  |                     u); | ||||||
|  |     y2 = lerp(x1, x2, v); | ||||||
|  | 
 | ||||||
|  |     // For convenience we bound it to 0 - 1 (theoretical min/max before is -1 - 1)
 | ||||||
|  |     return (lerp(y1, y2, w) + 1) / 2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | double ff_perlin_get(FFPerlin *perlin, double x, double y, double z) | ||||||
|  | { | ||||||
|  |     double total = 0; | ||||||
|  |     double frequency = 1; | ||||||
|  |     double amplitude = 1; | ||||||
|  |     double max_value = 0;                   // Used for normalizing result to 0.0 - 1.0
 | ||||||
|  | 
 | ||||||
|  |     for (int i = 0; i < perlin->octaves; i++) { | ||||||
|  |         total += perlin_get(perlin, x * frequency, y * frequency, z * frequency) * amplitude; | ||||||
|  |         max_value += amplitude; | ||||||
|  |         amplitude *= perlin->persistence; | ||||||
|  |         frequency *= 2; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return total / max_value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										101
									
								
								libavfilter/perlin.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								libavfilter/perlin.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,101 @@ | |||||||
|  | /*
 | ||||||
|  |  * Perlin noise generator | ||||||
|  |  * | ||||||
|  |  * This file is part of FFmpeg. | ||||||
|  |  * | ||||||
|  |  * FFmpeg is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  |  * License as published by the Free Software Foundation; either | ||||||
|  |  * version 2.1 of the License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * FFmpeg is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * Lesser General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Lesser General Public | ||||||
|  |  * License along with FFmpeg; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @file | ||||||
|  |  * Perlin Noise generator | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #ifndef AVFILTER_PERLIN_H | ||||||
|  | #define AVFILTER_PERLIN_H | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | enum FFPerlinRandomMode { | ||||||
|  |     FF_PERLIN_RANDOM_MODE_RANDOM, | ||||||
|  |     FF_PERLIN_RANDOM_MODE_KEN, | ||||||
|  |     FF_PERLIN_RANDOM_MODE_SEED, | ||||||
|  |     FF_PERLIN_RANDOM_MODE_NB | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Perlin generator context. This needs to be initialized with the | ||||||
|  |  * parameters used to generate the Perlin noise. | ||||||
|  |  */ | ||||||
|  | typedef struct FFPerlin { | ||||||
|  |     /**
 | ||||||
|  |      * spatial repeat period, if negative it is ignored | ||||||
|  |      */ | ||||||
|  |     double period; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * total number of components making up the noise, each one with | ||||||
|  |      * doubled frequency | ||||||
|  |      */ | ||||||
|  |     int octaves; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * ratio used to compute the amplitude of the next octave | ||||||
|  |      * component with respect to the previous component | ||||||
|  |      */ | ||||||
|  |     double persistence; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * permutations array used to compute the Perlin noise hash | ||||||
|  |      */ | ||||||
|  |     uint8_t permutations[512]; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * define how to compute the permutations array | ||||||
|  |      */ | ||||||
|  |     enum FFPerlinRandomMode random_mode; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * when random_mode is set FF_PERLIN_RANDOM_MODE_RANDOM, set random | ||||||
|  |      * seed used to compute the permutations array | ||||||
|  |      */ | ||||||
|  |     unsigned int random_seed; | ||||||
|  | } FFPerlin; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Initialize the Perlin noise generator with parameters. | ||||||
|  |  * | ||||||
|  |  * @param perlin Perlin noise generator context | ||||||
|  |  * @param period spatial repeat period, if negative it is ignored | ||||||
|  |  * @param octaves total number of components making up the noise, each one with doubled frequency | ||||||
|  |  * @param persistence define ratio used to compute the amplitude of the next octave | ||||||
|  |  *                    component with respect to the previous component | ||||||
|  |  * @param random_mode define how to compute the permutations array | ||||||
|  |  * @param random_seed when random_mode is set to FF_PERLIN_RANDOM_MODE_RANDOM, set random | ||||||
|  |  *                    seed used to compute the permutations array | ||||||
|  |  * @return a negative AVERROR code in case of error, a non negative value otherwise | ||||||
|  |  */ | ||||||
|  | int ff_perlin_init(FFPerlin *perlin, double period, int octaves, double persistence, | ||||||
|  |                    enum FFPerlinRandomMode random_mode, unsigned int random_seed); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Compute Perlin noise given the x, y, z coordinates. | ||||||
|  |  * | ||||||
|  |  * @param perlin Perlin noise generator context | ||||||
|  |  * @return normalized value for the perlin noise, in the range [0, 1] | ||||||
|  |  */ | ||||||
|  | double ff_perlin_get(FFPerlin *perlin, double x, double y, double z); | ||||||
|  | 
 | ||||||
|  | #endif  /* AVFILTER_PERLIN_H */ | ||||||
							
								
								
									
										169
									
								
								libavfilter/vsrc_perlin.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								libavfilter/vsrc_perlin.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,169 @@ | |||||||
|  | /*
 | ||||||
|  |  * This file is part of FFmpeg. | ||||||
|  |  * | ||||||
|  |  * FFmpeg is free software; you can redistribute it and/or | ||||||
|  |  * modify it under the terms of the GNU Lesser General Public | ||||||
|  |  * License as published by the Free Software Foundation; either | ||||||
|  |  * version 2.1 of the License, or (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * FFmpeg is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * Lesser General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU Lesser General Public | ||||||
|  |  * License along with FFmpeg; if not, write to the Free Software | ||||||
|  |  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * @file | ||||||
|  |  * Perlin noise generator | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <float.h> | ||||||
|  | 
 | ||||||
|  | #include "perlin.h" | ||||||
|  | #include "libavutil/lfg.h" | ||||||
|  | #include "libavutil/opt.h" | ||||||
|  | #include "avfilter.h" | ||||||
|  | #include "internal.h" | ||||||
|  | #include "formats.h" | ||||||
|  | #include "video.h" | ||||||
|  | 
 | ||||||
|  | typedef struct PerlinContext { | ||||||
|  |     const AVClass *class; | ||||||
|  | 
 | ||||||
|  |     int w, h; | ||||||
|  |     AVRational frame_rate; | ||||||
|  | 
 | ||||||
|  |     FFPerlin perlin; | ||||||
|  |     int octaves; | ||||||
|  |     double persistence; | ||||||
|  |     unsigned int random_seed; | ||||||
|  |     enum FFPerlinRandomMode random_mode; | ||||||
|  | 
 | ||||||
|  |     double xscale, yscale, tscale; | ||||||
|  |     uint64_t pts; | ||||||
|  | } PerlinContext; | ||||||
|  | 
 | ||||||
|  | #define OFFSET(x) offsetof(PerlinContext, x) | ||||||
|  | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM | ||||||
|  | 
 | ||||||
|  | static const AVOption perlin_options[] = { | ||||||
|  |     { "size",     "set video size", OFFSET(w),          AV_OPT_TYPE_IMAGE_SIZE, {.str="320x240"}, 0, 0, FLAGS }, | ||||||
|  |     { "s",        "set video size", OFFSET(w),          AV_OPT_TYPE_IMAGE_SIZE, {.str="320x240"}, 0, 0, FLAGS }, | ||||||
|  |     { "rate",     "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, | ||||||
|  |     { "r",        "set video rate", OFFSET(frame_rate), AV_OPT_TYPE_VIDEO_RATE, {.str="25"}, 0, INT_MAX, FLAGS }, | ||||||
|  |     { "octaves", "set the number of components to use to generate the noise", OFFSET(octaves), AV_OPT_TYPE_INT, {.i64=1}, 1, INT_MAX, FLAGS }, | ||||||
|  |     { "persistence", "set the octaves persistence", OFFSET(persistence), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.0, DBL_MAX, FLAGS }, | ||||||
|  | 
 | ||||||
|  |     { "xscale", "set x-scale factor", OFFSET(xscale), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.0, DBL_MAX, FLAGS }, | ||||||
|  |     { "yscale", "set y-scale factor", OFFSET(yscale), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.0, DBL_MAX, FLAGS }, | ||||||
|  |     { "tscale", "set t-scale factor", OFFSET(tscale), AV_OPT_TYPE_DOUBLE, {.dbl=1}, 0.0, DBL_MAX, FLAGS }, | ||||||
|  | 
 | ||||||
|  |     { "random_mode", "set random mode used to compute initial pattern", OFFSET(random_mode), AV_OPT_TYPE_INT, {.i64=FF_PERLIN_RANDOM_MODE_RANDOM}, 0, FF_PERLIN_RANDOM_MODE_NB-1, FLAGS, .unit = "random_mode" }, | ||||||
|  |     { "random", "compute and use random seed", 0, AV_OPT_TYPE_CONST, {.i64=FF_PERLIN_RANDOM_MODE_RANDOM},   0, 0, FLAGS, .unit = "random_mode" }, | ||||||
|  |     { "ken", "use the predefined initial pattern defined by Ken Perlin in the original article", 0, AV_OPT_TYPE_CONST, {.i64=FF_PERLIN_RANDOM_MODE_KEN}, 0, 0, FLAGS, .unit = "random_mode" }, | ||||||
|  |     { "seed", "use the value specified by random_seed", 0, AV_OPT_TYPE_CONST, {.i64=FF_PERLIN_RANDOM_MODE_SEED}, 0, 0, FLAGS, .unit="random_mode" }, | ||||||
|  | 
 | ||||||
|  |     { "random_seed", "set the seed for filling the initial pattern", OFFSET(random_seed), AV_OPT_TYPE_UINT, {.i64=0}, 0, UINT_MAX, FLAGS }, | ||||||
|  |     { "seed",        "set the seed for filling the initial pattern", OFFSET(random_seed), AV_OPT_TYPE_UINT, {.i64=0}, 0, UINT_MAX, FLAGS }, | ||||||
|  |     { NULL } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | AVFILTER_DEFINE_CLASS(perlin); | ||||||
|  | 
 | ||||||
|  | static av_cold int init(AVFilterContext *ctx) | ||||||
|  | { | ||||||
|  |     PerlinContext *perlin = ctx->priv; | ||||||
|  |     int ret; | ||||||
|  | 
 | ||||||
|  |     if (ret = ff_perlin_init(&perlin->perlin, -1, perlin->octaves, perlin->persistence, | ||||||
|  |                              perlin->random_mode, perlin->random_seed)) { | ||||||
|  |         return ret; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     av_log(ctx, AV_LOG_VERBOSE, | ||||||
|  |            "s:%dx%d r:%d/%d octaves:%d persistence:%f xscale:%f yscale:%f tscale:%f\n", | ||||||
|  |            perlin->w, perlin->h, perlin->frame_rate.num, perlin->frame_rate.den, | ||||||
|  |            perlin->octaves, perlin->persistence, | ||||||
|  |            perlin->xscale, perlin->yscale, perlin->tscale); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int config_props(AVFilterLink *outlink) | ||||||
|  | { | ||||||
|  |     PerlinContext *perlin = outlink->src->priv; | ||||||
|  | 
 | ||||||
|  |     outlink->w = perlin->w; | ||||||
|  |     outlink->h = perlin->h; | ||||||
|  |     outlink->time_base = av_inv_q(perlin->frame_rate); | ||||||
|  |     outlink->frame_rate = perlin->frame_rate; | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int request_frame(AVFilterLink *outlink) | ||||||
|  | { | ||||||
|  |     AVFilterContext *ctx = outlink->src; | ||||||
|  |     PerlinContext *perlin = ctx->priv; | ||||||
|  |     AVFrame *picref = ff_get_video_buffer(outlink, perlin->w, perlin->h); | ||||||
|  |     int i, j; | ||||||
|  |     uint8_t *data0, *data; | ||||||
|  |     double x, y, t; | ||||||
|  | 
 | ||||||
|  |     if (!picref) | ||||||
|  |         return AVERROR(ENOMEM); | ||||||
|  | 
 | ||||||
|  |     picref->sample_aspect_ratio = (AVRational) {1, 1}; | ||||||
|  |     picref->pts = perlin->pts++; | ||||||
|  |     picref->duration = 1; | ||||||
|  | 
 | ||||||
|  |     t = perlin->tscale * (perlin->pts * av_q2d(outlink->time_base)); | ||||||
|  |     data0 = picref->data[0]; | ||||||
|  | 
 | ||||||
|  |     for (i = 0; i < perlin->h; i++) { | ||||||
|  |         y = perlin->yscale * (double)i / perlin->h; | ||||||
|  | 
 | ||||||
|  |         data = data0; | ||||||
|  | 
 | ||||||
|  |         for (j = 0; j < perlin->w; j++) { | ||||||
|  |             double res; | ||||||
|  |             x = perlin->xscale * (double)j / perlin->w; | ||||||
|  |             res = ff_perlin_get(&perlin->perlin, x, y, t); | ||||||
|  |             av_log(ctx, AV_LOG_DEBUG, "x:%f y:%f t:%f => %f\n", x, y, t, res); | ||||||
|  |             *data++ = res * 255; | ||||||
|  |         } | ||||||
|  |         data0 += picref->linesize[0]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return ff_filter_frame(outlink, picref); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int query_formats(AVFilterContext *ctx) | ||||||
|  | { | ||||||
|  |     enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE }; | ||||||
|  | 
 | ||||||
|  |     return ff_set_common_formats_from_list(ctx, pix_fmts); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const AVFilterPad perlin_outputs[] = { | ||||||
|  |     { | ||||||
|  |         .name          = "default", | ||||||
|  |         .type          = AVMEDIA_TYPE_VIDEO, | ||||||
|  |         .request_frame = request_frame, | ||||||
|  |         .config_props  = config_props, | ||||||
|  |     }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const AVFilter ff_vsrc_perlin = { | ||||||
|  |     .name          = "perlin", | ||||||
|  |     .description   = NULL_IF_CONFIG_SMALL("Generate Perlin noise"), | ||||||
|  |     .priv_size     = sizeof(PerlinContext), | ||||||
|  |     .priv_class    = &perlin_class, | ||||||
|  |     .init          = init, | ||||||
|  |     .inputs        = NULL, | ||||||
|  |     FILTER_OUTPUTS(perlin_outputs), | ||||||
|  |     FILTER_QUERY_FUNC(query_formats), | ||||||
|  | }; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user