/usr/include/movit/deinterlace_effect.h is in libmovit-dev 1.6.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 | #ifndef _MOVIT_DEINTERLACE_EFFECT_H
#define _MOVIT_DEINTERLACE_EFFECT_H 1
// YADIF deinterlacing filter (original by Michael Niedermayer, in MPlayer).
//
// Good deinterlacing is very hard. YADIF, despite its innocious-sounding
// name (Yet Another DeInterlacing Filter) is probably the most commonly
// used (non-trivial) deinterlacing filter in the open-source world.
// It works by trying to fill in the missing lines from neighboring ones
// (spatial interpolation), and then constrains that estimate within an
// interval found from previous and next frames (temporal interpolation).
// It's not very fast, even in GPU implementation, but 1080i60 -> 1080p60
// realtime conversion is well within range for a mid-range GPU.
//
// The inner workings of YADIF are poorly documented; implementation details
// are generally explained the .frag file. However, a few things should be
// mentioned here: YADIF has two modes, with and without a “spatial interlacing
// check” which basically allows more temporal change in areas of high detail.
// (The variant with the check corresponds to the original's modes 0 and 1, and
// the variant without to modes 2 and 3. The remaining difference is whether it
// is frame-doubling or not, which in Movit is up to the driver, not the
// filter.)
//
// Neither mode is perfect by any means. If the spatial check is off, the
// filter possesses the potentially nice quality that a static picture
// deinterlaces exactly to itself. (If it's on, there's some flickering
// on very fine vertical detail. The picture is nice and stable if no such
// detail is present, though.) But then, certain patterns, like horizontally
// scrolling text, leaves residues. Both have issues with diagonal lines at
// certain angles leaving stray pixels, although in practical applications,
// YADIF is pretty good.
//
// In general, having the spatial check on (the default) is the safe choice.
// However, if you are reasonably certain that the image comes from a video source
// (ie., no graphical overlays), or if the case of still images is particularly
// important for you (e.g., slides from a laptop), you could turn it off.
// It is slightly faster, although in practice, it does not mean all that much.
// You need to decide before finalize(), as the choice gets compiled into the shader.
//
// YADIF needs five fields as input; the previous two, the current one, and
// then the two next ones. (By convention, they come in that order, although if
// you reverse them, it doesn't matter, as the filter is symmetric. It _does_
// matter if you change the ordering in any other way, though.) They need to be
// of the same resolution, or the effect will assert-fail. If you cannot supply
// this, you could simply reuse the current field for previous/next as
// required; it won't be optimal in any way, but it also won't blow up on you.
//
// This requirement to “see the future” will mean you have an extra full frame
// of delay (33.3 ms at 60i, 40 ms at 50i). You will also need to tell the
// filter for each and every invocation if the current field (ie., the one in
// the middle input) is a top or bottom field (neighboring fields have opposite
// parity, so all the others are implicit).
#include <epoxy/gl.h>
#include <memory>
#include <string>
#include "effect.h"
namespace movit {
class DeinterlaceComputeEffect;
class DeinterlaceEffect : public Effect {
public:
DeinterlaceEffect();
std::string effect_type_id() const override { return "DeinterlaceEffect"; }
std::string output_fragment_shader() override;
// Replaces itself with DeinterlaceComputeEffect if compute shaders are supported.
// Otherwise, does nothing.
void rewrite_graph(EffectChain *graph, Node *self) override;
bool set_int(const std::string &key, int value) override;
void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) override;
// First = before previous, second = previous, third = current,
// fourth = next, fifth = after next. These are treated symmetrically,
// though.
//
// Note that if you have interlaced _frames_ and not _fields_, you will
// need to pull them apart first, for instance with SliceEffect.
unsigned num_inputs() const override { return 5; }
bool needs_texture_bounce() const override { return true; }
bool changes_output_size() const override { return true; }
AlphaHandling alpha_handling() const override { return INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK; }
void inform_input_size(unsigned input_num, unsigned width, unsigned height) override;
void get_output_size(unsigned *width, unsigned *height,
unsigned *virtual_width, unsigned *virtual_height) const override;
enum FieldPosition { TOP = 0, BOTTOM = 1 };
private:
// If compute shaders are supported, contains the actual effect.
// If not, nullptr.
std::unique_ptr<DeinterlaceComputeEffect> compute_effect_owner;
DeinterlaceComputeEffect *compute_effect = nullptr;
unsigned widths[5], heights[5];
// See file-level comment for explanation of this option.
bool enable_spatial_interlacing_check;
// Which field the current input (the middle one) is.
FieldPosition current_field_position;
// Offset for one pixel in the horizontal direction (1/width).
float inv_width;
// Vertical resolution of the output.
float num_lines;
// All of these offsets are vertical texel offsets; they are needed to adjust
// for the changed texel center as the number of lines double, and depend on
// <current_field_position>.
// For sampling unchanged lines from the current field.
float self_offset;
// For evaluating the low-pass filter (in the current field). Four taps.
float current_offset[2];
// For evaluating the high-pass filter (in the previous and next fields).
// Five taps, but evaluated twice since there are two fields.
float other_offset[3];
};
// A compute shader implementation of DeinterlaceEffect. It saves a bunch of loads
// since it can share them between neighboring pixels (and also does not need
// texture bounce), so it has the potential to be faster, although exactly how
// much depends on your chain and other factors. DeinterlaceEffect will
// automatically become a proxy to DeinterlaceComputeEffect if your system
// supports compute shaders.
class DeinterlaceComputeEffect : public Effect {
public:
DeinterlaceComputeEffect();
std::string effect_type_id() const override { return "DeinterlaceComputeEffect"; }
std::string output_fragment_shader() override;
void set_gl_state(GLuint glsl_program_num, const std::string &prefix, unsigned *sampler_num) override;
unsigned num_inputs() const override { return 5; }
bool changes_output_size() const override { return true; }
bool is_compute_shader() const override { return true; }
void get_compute_dimensions(unsigned output_width, unsigned output_height,
unsigned *x, unsigned *y, unsigned *z) const override;
AlphaHandling alpha_handling() const override { return INPUT_PREMULTIPLIED_ALPHA_KEEP_BLANK; }
void inform_input_size(unsigned input_num, unsigned width, unsigned height) override;
void get_output_size(unsigned *width, unsigned *height,
unsigned *virtual_width, unsigned *virtual_height) const override;
enum FieldPosition { TOP = 0, BOTTOM = 1 };
private:
unsigned widths[5], heights[5];
// See file-level comment for explanation of this option.
bool enable_spatial_interlacing_check;
// Which field the current input (the middle one) is.
FieldPosition current_field_position;
// Offset for one pixel in the horizontal and verticla direction (1/width, 1/height).
float inv_width, inv_height;
// For evaluating the low-pass filter (in the current field). Four taps.
float current_field_vertical_offset;
};
} // namespace movit
#endif // !defined(_MOVIT_DEINTERLACE_EFFECT_H)
|