Blender V5.0
keying_screen.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include <cstdint>
6#include <memory>
7#include <string>
8
9#include "BLI_hash.hh"
10#include "BLI_listbase.h"
11#include "BLI_math_base.hh"
12#include "BLI_math_color.h"
13#include "BLI_math_vector.hh"
15#include "BLI_vector.hh"
16
17#include "IMB_imbuf.hh"
18
19#include "DNA_defaults.h"
20#include "DNA_movieclip_types.h"
21#include "DNA_tracking_types.h"
22
23#include "GPU_shader.hh"
24#include "GPU_storage_buffer.hh"
25
26#include "BKE_movieclip.h"
27#include "BKE_tracking.h"
28
29#include "COM_context.hh"
30#include "COM_keying_screen.hh"
31#include "COM_result.hh"
32#include "COM_utilities.hh"
33
34namespace blender::compositor {
35
36/* --------------------------------------------------------------------
37 * Keying Screen Key.
38 */
39
44
49
51{
52 return a.frame == b.frame && a.smoothness == b.smoothness;
53}
54
55/* --------------------------------------------------------------------
56 * Keying Screen.
57 */
58
59/* Computes the color and normalized positions of the keying screen markers in the given movie
60 * tracking object. The color is computed as the mean color of the search pattern of the marker. */
61static void compute_marker_points(MovieClip *movie_clip,
62 MovieClipUser &movie_clip_user,
63 MovieTrackingObject *movie_tracking_object,
64 Vector<float2> &marker_positions,
65 Vector<float4> &marker_colors)
66{
67 BLI_assert(marker_positions.is_empty());
68 BLI_assert(marker_colors.is_empty());
69
70 ImBuf *image_buffer = BKE_movieclip_get_ibuf(movie_clip, &movie_clip_user);
71 if (!image_buffer) {
72 return;
73 }
74
75 LISTBASE_FOREACH (MovieTrackingTrack *, track, &movie_tracking_object->tracks) {
76 const MovieTrackingMarker *marker = BKE_tracking_marker_get(track, movie_clip_user.framenr);
77 if (marker->flag & MARKER_DISABLED) {
78 continue;
79 }
80
81 /* Skip out of bound markers since they have no corresponding color. */
82 const float2 position = float2(marker->pos) + float2(track->offset);
83 if (math::clamp(position, float2(0.0f), float2(1.0f)) != position) {
84 continue;
85 }
86
87 ImBuf *pattern_image_buffer = BKE_tracking_get_pattern_imbuf(
88 image_buffer, track, marker, true, false);
89 if (!pattern_image_buffer) {
90 continue;
91 }
92
93 /* Find the mean color of the rectangular search pattern of the marker. */
94 float4 mean_color = float4(0.0f);
95 for (int i = 0; i < pattern_image_buffer->x * pattern_image_buffer->y; i++) {
96 if (pattern_image_buffer->float_buffer.data) {
97 mean_color += float4(&pattern_image_buffer->float_buffer.data[i * 4]);
98 }
99 else {
100 float4 linear_color;
101 uchar4 srgb_color = uchar4(&pattern_image_buffer->byte_buffer.data[i * 4]);
102 srgb_to_linearrgb_uchar4(linear_color, srgb_color);
103 mean_color += linear_color;
104 }
105 }
106 mean_color /= pattern_image_buffer->x * pattern_image_buffer->y;
107
108 marker_colors.append(mean_color);
109 marker_positions.append(position);
110
111 IMB_freeImBuf(pattern_image_buffer);
112 }
113
114 IMB_freeImBuf(image_buffer);
115}
116
117/* Get a MovieClipUser with an initialized clip frame number. */
119{
121 const int scene_frame = context.get_frame_number();
122 const int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movie_clip, scene_frame);
123 BKE_movieclip_user_set_frame(&movie_clip_user, clip_frame);
124 return movie_clip_user;
125}
126
128 MovieClip *movie_clip,
129 MovieTrackingObject *movie_tracking_object,
130 const float smoothness)
131 : result(context.create_result(ResultType::Color))
132{
133 int2 size;
134 MovieClipUser movie_clip_user = get_movie_clip_user(context, movie_clip);
135 BKE_movieclip_get_size(movie_clip, &movie_clip_user, &size.x, &size.y);
136
137 Vector<float2> marker_positions;
138 Vector<float4> marker_colors;
140 movie_clip, movie_clip_user, movie_tracking_object, marker_positions, marker_colors);
141
142 if (marker_positions.is_empty()) {
143 return;
144 }
145
146 this->result.allocate_texture(Domain(size), false);
147 if (context.use_gpu()) {
148 this->compute_gpu(context, smoothness, marker_positions, marker_colors);
149 }
150 else {
151 this->compute_cpu(smoothness, marker_positions, marker_colors);
152 }
153}
154
155void KeyingScreen::compute_gpu(Context &context,
156 const float smoothness,
157 Vector<float2> &marker_positions,
158 const Vector<float4> &marker_colors)
159{
160 gpu::Shader *shader = context.get_shader("compositor_keying_screen");
161 GPU_shader_bind(shader);
162
163 GPU_shader_uniform_1f(shader, "smoothness", smoothness);
164 GPU_shader_uniform_1i(shader, "number_of_markers", marker_positions.size());
165
166 /* SSBO needs to be aligned to 16 bytes, and since sizeof(float2) is only 8 bytes, we need to add
167 * a dummy element at the end for odd sizes to satisfy the alignment requirement. Notice that the
168 * number_of_markers uniform was already assigned above to the original size, so the dummy
169 * element has no effect in the shader. Also notice that the marker colors are always 16 byte
170 * aligned since sizeof(float4) is 16 bytes, so not need to add anything there. */
171 if (marker_positions.size() % 2 == 1) {
172 marker_positions.append(float2(0.0f));
173 }
174
175 gpu::StorageBuf *positions_ssbo = GPU_storagebuf_create_ex(marker_positions.size() *
176 sizeof(float2),
177 marker_positions.data(),
179 "Marker Positions");
180 const int positions_ssbo_location = GPU_shader_get_ssbo_binding(shader, "marker_positions");
181 GPU_storagebuf_bind(positions_ssbo, positions_ssbo_location);
182
183 gpu::StorageBuf *colors_ssbo = GPU_storagebuf_create_ex(marker_colors.size() * sizeof(float4),
184 marker_colors.data(),
186 "Marker Colors");
187 const int colors_ssbo_location = GPU_shader_get_ssbo_binding(shader, "marker_colors");
188 GPU_storagebuf_bind(colors_ssbo, colors_ssbo_location);
189
190 this->result.bind_as_image(shader, "output_img");
191
192 compute_dispatch_threads_at_least(shader, this->result.domain().size);
193
194 this->result.unbind_as_image();
195 GPU_storagebuf_unbind(positions_ssbo);
196 GPU_storagebuf_unbind(colors_ssbo);
198
199 GPU_storagebuf_free(positions_ssbo);
200 GPU_storagebuf_free(colors_ssbo);
201}
202
203void KeyingScreen::compute_cpu(const float smoothness,
204 const Vector<float2> &marker_positions,
205 const Vector<float4> &marker_colors)
206{
207 float squared_shape_parameter = math::square(1.0f / smoothness);
208 const int2 size = this->result.domain().size;
209 parallel_for(size, [&](const int2 texel) {
210 float2 normalized_pixel_location = (float2(texel) + float2(0.5f)) / float2(size);
211
212 /* Interpolate the markers using a Gaussian Radial Basis Function Interpolation with the
213 * reciprocal of the smoothness as the shaping parameter. Equal weights are assigned to all
214 * markers, so no RBF fitting is required. */
215 float sum_of_weights = 0.0f;
216 float4 weighted_sum = float4(0.0f);
217 for (const int64_t i : marker_positions.index_range()) {
218 float2 marker_position = marker_positions[i];
219 float2 difference = normalized_pixel_location - marker_position;
220 float squared_distance = math::dot(difference, difference);
221 float gaussian = math::exp(-squared_distance * squared_shape_parameter);
222
223 float4 marker_color = marker_colors[i];
224 weighted_sum += marker_color * gaussian;
225 sum_of_weights += gaussian;
226 }
227 weighted_sum /= sum_of_weights;
228
229 this->result.store_pixel(texel, weighted_sum);
230 });
231}
232
234{
235 this->result.release();
236}
237
238/* --------------------------------------------------------------------
239 * Keying Screen Container.
240 */
241
243{
244 /* First, delete all cached keying screens that are no longer needed. */
245 for (auto &cached_keying_screens_for_id : map_.values()) {
246 cached_keying_screens_for_id.remove_if([](auto item) { return !item.value->needed; });
247 }
248 map_.remove_if([](auto item) { return item.value.is_empty(); });
249
250 /* Second, reset the needed status of the remaining cached keying screens to false to ready them
251 * to track their needed status for the next evaluation. */
252 for (auto &cached_keying_screens_for_id : map_.values()) {
253 for (auto &value : cached_keying_screens_for_id.values()) {
254 value->needed = false;
255 }
256 }
257}
258
260 MovieClip *movie_clip,
261 MovieTrackingObject *movie_tracking_object,
262 float smoothness)
263{
264 const KeyingScreenKey key(context.get_frame_number(), smoothness);
265
266 /* We concatenate the movie clip ID name with the tracking object name to cache multiple tracking
267 * objects per movie clip. */
268 const std::string library_key = movie_clip->id.lib ? movie_clip->id.lib->id.name : "";
269 const std::string id_key = std::string(movie_clip->id.name) + library_key;
270 const std::string object_key = id_key + movie_tracking_object->name;
271 auto &cached_keying_screens_for_id = map_.lookup_or_add_default(object_key);
272
273 /* Invalidate the cache for that movie clip if it was changed since it was cached. */
274 if (!cached_keying_screens_for_id.is_empty() &&
275 movie_clip->runtime.last_update != update_counts_.lookup(id_key))
276 {
277 cached_keying_screens_for_id.clear();
278 }
279
280 auto &keying_screen = *cached_keying_screens_for_id.lookup_or_add_cb(key, [&]() {
281 return std::make_unique<KeyingScreen>(context, movie_clip, movie_tracking_object, smoothness);
282 });
283
284 /* Store the current update count to later compare to and check if the movie clip changed. */
285 update_counts_.add_overwrite(id_key, movie_clip->runtime.last_update);
286
287 keying_screen.needed = true;
288 return keying_screen.result;
289}
290
291} // namespace blender::compositor
float BKE_movieclip_remap_scene_to_clip_frame(const struct MovieClip *clip, float framenr)
void BKE_movieclip_user_set_frame(struct MovieClipUser *user, int framenr)
void BKE_movieclip_get_size(struct MovieClip *clip, const struct MovieClipUser *user, int *r_width, int *r_height)
struct ImBuf * BKE_movieclip_get_ibuf(struct MovieClip *clip, const struct MovieClipUser *user)
struct MovieTrackingMarker * BKE_tracking_marker_get(struct MovieTrackingTrack *track, int framenr)
Definition tracking.cc:1358
struct ImBuf * BKE_tracking_get_pattern_imbuf(const struct ImBuf *ibuf, const struct MovieTrackingTrack *track, const struct MovieTrackingMarker *marker, bool anchored, bool disable_channels)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
MINLINE void srgb_to_linearrgb_uchar4(float linear[4], const unsigned char srgb[4])
#define DNA_struct_default_get(struct_name)
@ MARKER_DISABLED
void GPU_shader_uniform_1f(blender::gpu::Shader *sh, const char *name, float value)
void GPU_shader_bind(blender::gpu::Shader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
int GPU_shader_get_ssbo_binding(blender::gpu::Shader *shader, const char *name)
void GPU_shader_uniform_1i(blender::gpu::Shader *sh, const char *name, int value)
void GPU_shader_unbind()
void GPU_storagebuf_free(blender::gpu::StorageBuf *ssbo)
blender::gpu::StorageBuf * GPU_storagebuf_create_ex(size_t size, const void *data, GPUUsageType usage, const char *name)
void GPU_storagebuf_bind(blender::gpu::StorageBuf *ssbo, int slot)
void GPU_storagebuf_unbind(blender::gpu::StorageBuf *ssbo)
@ GPU_USAGE_STATIC
void IMB_freeImBuf(ImBuf *ibuf)
long long int int64_t
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
IndexRange index_range() const
int64_t size() const
void append(const T &value)
bool is_empty() const
Result & get(Context &context, MovieClip *movie_clip, MovieTrackingObject *movie_tracking_object, float smoothness)
KeyingScreenKey(int frame, float smoothness)
KeyingScreen(Context &context, MovieClip *movie_clip, MovieTrackingObject *movie_tracking_object, const float smoothness)
void compute_dispatch_threads_at_least(gpu::Shader *shader, int2 threads_range, int2 local_size=int2(16))
Definition utilities.cc:196
static MovieClipUser get_movie_clip_user(Context &context, MovieClip *movie_clip)
bool operator==(const BokehKernelKey &a, const BokehKernelKey &b)
static void compute_marker_points(MovieClip *movie_clip, MovieClipUser &movie_clip_user, MovieTrackingObject *movie_tracking_object, Vector< float2 > &marker_positions, Vector< float4 > &marker_colors)
void parallel_for(const int2 range, const Function &function)
T clamp(const T &a, const T &min, const T &max)
T exp(const T &x)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T square(const T &a)
blender::VecBase< uint8_t, 4 > uchar4
VecBase< float, 4 > float4
uint64_t get_default_hash(const T &v, const Args &...args)
Definition BLI_hash.hh:233
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
struct Library * lib
Definition DNA_ID.h:420
char name[258]
Definition DNA_ID.h:432
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
ID id
Definition DNA_ID.h:550
struct MovieClip_Runtime runtime
i
Definition text_draw.cc:230