Loading...
Searching...
No Matches
Bayer.hpp
1#pragma once
3#include <Gfx/Graph/decoders/GPUVideoDecoder.hpp>
4
5#include <QString>
6
7extern "C" {
8#include <libavformat/avformat.h>
9}
10
11namespace score::gfx
12{
40{
43 enum class Phase
44 {
45 RGGB,
46 GRBG,
47 GBRG,
48 BGGR,
49 };
50
51 // %1 = user filter, %2/%3 = red-site offset, %4 = sample scale
52 static const constexpr auto frag = R"_(#version 450
53
55
56layout(binding=3) uniform sampler2D u_tex;
57
58layout(location = 0) in vec2 v_texcoord;
59layout(location = 0) out vec4 fragColor;
60
61vec4 processTexture(vec4 tex) {
62 vec4 processed = tex;
63 { %1 }
64 return processed;
65}
66
67float smp(ivec2 q, ivec2 lim) {
68 return texelFetch(u_tex, clamp(q, ivec2(0), lim), 0).r;
69}
70
71void main() {
72 ivec2 sz = textureSize(u_tex, 0);
73 ivec2 lim = sz - ivec2(1);
74 ivec2 ip = clamp(ivec2(floor(v_texcoord * vec2(sz))), ivec2(0), lim);
75
76 // (0,0) marks the red site; the other three cells follow from its parity.
77 ivec2 c = (ip + ivec2(%2, %3)) & 1;
78
79 float ctr = smp(ip, lim);
80 float n = smp(ip + ivec2( 0,-1), lim);
81 float s = smp(ip + ivec2( 0, 1), lim);
82 float w = smp(ip + ivec2(-1, 0), lim);
83 float e = smp(ip + ivec2( 1, 0), lim);
84 float nw = smp(ip + ivec2(-1,-1), lim);
85 float ne = smp(ip + ivec2( 1,-1), lim);
86 float sw = smp(ip + ivec2(-1, 1), lim);
87 float se = smp(ip + ivec2( 1, 1), lim);
88
89 float cross4 = (n + s + w + e) * 0.25;
90 float diag4 = (nw + ne + sw + se) * 0.25;
91 float horz2 = (w + e) * 0.5;
92 float vert2 = (n + s) * 0.5;
93
94 vec3 rgb;
95 if(c.x == 0 && c.y == 0)
96 rgb = vec3(ctr, cross4, diag4); // red site
97 else if(c.x == 1 && c.y == 1)
98 rgb = vec3(diag4, cross4, ctr); // blue site
99 else if(c.x == 1 && c.y == 0)
100 rgb = vec3(horz2, ctr, vert2); // green, red neighbours across
101 else
102 rgb = vec3(vert2, ctr, horz2); // green, blue neighbours across
103
104 fragColor = processTexture(vec4(adjustCapture(rgb * %4), 1.0));
105}
106)_";
107
109 QRhiTexture::Format fmt, int bytesPerSample, Video::ImageFormat& d,
110 Phase phase, double sampleScale = 1.0)
111 : format{fmt}
112 , bytesPerSample{bytesPerSample}
113 , decoder{d}
114 , phase{phase}
115 , sampleScale{sampleScale}
116 {
117 }
118
119 QRhiTexture::Format format;
120 int bytesPerSample{};
121 Video::ImageFormat& decoder;
122 Phase phase{};
123 double sampleScale{1.0};
124
125 std::pair<QShader, QShader> init(RenderList& r) override
126 {
127 auto& rhi = *r.state.rhi;
128 const auto w = decoder.width, h = decoder.height;
129
130 {
131 const bool supported = rhi.isTextureFormatSupported(format);
132 auto tex = rhi.newTexture(format, QSize{w, h}, 1, QRhiTexture::Flag{});
133 const bool created = tex->create();
134 if(!supported || !created)
135 qWarning() << "BayerDecoder: the backend will not give us a" << int(format)
136 << "texture at" << w << "x" << h
137 << "(supported:" << supported << "created:" << created
138 << ") -- the mosaic has nowhere to land";
139 else
140 qDebug() << "BayerDecoder:" << w << "x" << h << "format" << int(format)
141 << "ok";
142
143 // The mosaic is reconstructed per texel: linear filtering would blend
144 // neighbouring colour sites together before the demosaic can separate
145 // them, which is the same hazard the byte-reassembling decoders have.
146 auto sampler = rhi.newSampler(
147 QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
148 QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
149 sampler->create();
150
151 samplers.push_back({sampler, tex});
152 }
153
154 int px = 0, py = 0;
155 switch(phase)
156 {
157 case Phase::RGGB: px = 0; py = 0; break;
158 case Phase::GRBG: px = 1; py = 0; break;
159 case Phase::GBRG: px = 0; py = 1; break;
160 case Phase::BGGR: px = 1; py = 1; break;
161 }
162
164 r.state, score::gfx::captureVertexShader,
165 QString(frag)
166 .arg("")
167 .arg(px)
168 .arg(py)
169 .arg(QString::number(sampleScale, 'f', 6)));
170 }
171
172 void exec(RenderList&, QRhiResourceUpdateBatch& res, AVFrame& frame) override
173 {
174 auto tex = samplers[0].texture;
175 QRhiTextureUploadEntry entry{
176 0, 0,
178 frame.data[0], decoder.width, decoder.height, bytesPerSample,
179 frame.linesize[0])};
180 QRhiTextureUploadDescription desc{entry};
181 res.uploadTexture(tex, desc);
182 }
183};
184
185} // namespace score::gfx
The sensor corrections, as shader source shared by every demosaicer.
#define SCORE_GFX_CAPTURE_ADJUST_FN
Definition CaptureAdjustGLSL.hpp:48
#define SCORE_GFX_CAPTURE_UNIFORMS
Definition CaptureAdjustGLSL.hpp:30
Processes and renders a video frame on the GPU.
Definition GPUVideoDecoder.hpp:90
static QRhiTextureSubresourceUploadDescription createTextureUpload(uint8_t *pixels, int w, int h, int bytesPerPixel, int stride)
Utility method to create a QRhiTextureSubresourceUploadDescription.
Definition GPUVideoDecoder.cpp:22
List of nodes to be rendered to an output.
Definition RenderList.hpp:30
RenderState & state
RenderState corresponding to this RenderList.
Definition RenderList.hpp:169
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
std::pair< QShader, QShader > makeShaders(const RenderState &v, QString vert, QString frag, int multiViewCount)
Get a pair of compiled vertex / fragment shaders from GLSL 4.5 sources.
Definition score-plugin-gfx/Gfx/Graph/Utils.cpp:1238
Definition VideoInterface.hpp:26
Demosaics a single-channel colour-filter-array frame into RGB.
Definition Bayer.hpp:40
Phase
Definition Bayer.hpp:44
std::pair< QShader, QShader > init(RenderList &r) override
Initialize a GPUVideoDecoder.
Definition Bayer.hpp:125
void exec(RenderList &, QRhiResourceUpdateBatch &res, AVFrame &frame) override
Decode and upload a video frame to the GPU.
Definition Bayer.hpp:172