MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ filter_horizontal_planes()

MAYAFLUX_API void MayaFlux::Kinesis::Vision::filter_horizontal_planes ( std::span< const float *const >  src,
std::span< float *const >  dst,
uint32_t  w,
uint32_t  h,
std::span< const float >  kernel 
)

Fused horizontal separable filter over N planes.

Applies kernel horizontally to all source planes simultaneously. One row scan reads all N sources and writes all N destinations. N is src.size(); src.size() must equal dst.size().

No aliasing between any src pointer and any dst pointer.

Parameters
srcSource plane pointers, each pointing to w * h floats.
dstDestination plane pointers, each pointing to w * h floats.
wImage width in pixels.
hImage height in pixels.
kernel1D kernel. Length must be odd.

Definition at line 201 of file ImageFilter.cpp.

206{
207 constexpr size_t Unroll = 4;
208
209 const auto half = static_cast<int32_t>(kernel.size() / 2);
210 const auto iw = static_cast<int32_t>(w);
211 const float* kdata = kernel.data();
212 const size_t n = src.size();
213
214 P::for_each(P::par_unseq,
215 std::views::iota(uint32_t { 0 }, h).begin(),
216 std::views::iota(uint32_t { 0 }, h).end(),
217 [&](uint32_t row) {
218 const size_t row_off = static_cast<size_t>(row) * w;
219
220 const float* s_ptr[32];
221 float* d_ptr[32];
222 for (size_t i = 0; i < n; ++i) {
223 s_ptr[i] = src[i] + row_off;
224 d_ptr[i] = dst[i] + row_off;
225 }
226
227 // left border
228 for (size_t i = 0; i < n; ++i) {
229 for (int32_t px = 0; px < std::min(half, iw); ++px) {
230 float acc = 0.0F;
231 for (int32_t k = -half; k <= half; ++k) {
232 acc += s_ptr[i][std::clamp(px + k, 0, iw - 1)]
233 * kdata[static_cast<size_t>(k + half)];
234 }
235 d_ptr[i][px] = acc;
236 }
237 }
238
239 const int32_t interior_end = iw - half;
240
241#ifdef MAYAFLUX_ARCH_X64
242 int32_t px = half;
243 for (; px + 8 <= interior_end; px += 8) {
244 size_t i = 0;
245 for (; i + Unroll - 1 < n; i += Unroll) {
246 __m256 acc0 = _mm256_setzero_ps();
247 __m256 acc1 = _mm256_setzero_ps();
248 __m256 acc2 = _mm256_setzero_ps();
249 __m256 acc3 = _mm256_setzero_ps();
250 for (int32_t k = -half; k <= half; ++k) {
251 const __m256 kv = _mm256_set1_ps(kdata[static_cast<size_t>(k + half)]);
252 acc0 = _mm256_fmadd_ps(_mm256_loadu_ps(s_ptr[i + 0] + px + k), kv, acc0);
253 acc1 = _mm256_fmadd_ps(_mm256_loadu_ps(s_ptr[i + 1] + px + k), kv, acc1);
254 acc2 = _mm256_fmadd_ps(_mm256_loadu_ps(s_ptr[i + 2] + px + k), kv, acc2);
255 acc3 = _mm256_fmadd_ps(_mm256_loadu_ps(s_ptr[i + 3] + px + k), kv, acc3);
256 }
257 _mm256_storeu_ps(d_ptr[i + 0] + px, acc0);
258 _mm256_storeu_ps(d_ptr[i + 1] + px, acc1);
259 _mm256_storeu_ps(d_ptr[i + 2] + px, acc2);
260 _mm256_storeu_ps(d_ptr[i + 3] + px, acc3);
261 }
262 for (; i < n; ++i) {
263 __m256 acc = _mm256_setzero_ps();
264 for (int32_t k = -half; k <= half; ++k) {
265 acc = _mm256_fmadd_ps(
266 _mm256_loadu_ps(s_ptr[i] + px + k),
267 _mm256_set1_ps(kdata[static_cast<size_t>(k + half)]),
268 acc);
269 }
270 _mm256_storeu_ps(d_ptr[i] + px, acc);
271 }
272 }
273 // right border + scalar tail
274 for (size_t i = 0; i < n; ++i) {
275 for (int32_t px2 = px; px2 < iw; ++px2) {
276 float acc = 0.0F;
277 for (int32_t k = -half; k <= half; ++k) {
278 acc += s_ptr[i][std::clamp(px2 + k, 0, iw - 1)]
279 * kdata[static_cast<size_t>(k + half)];
280 }
281 d_ptr[i][px2] = acc;
282 }
283 }
284
285#elif defined(MAYAFLUX_ARCH_ARM64)
286 int32_t px = half;
287 for (; px + 4 <= interior_end; px += 4) {
288 size_t i = 0;
289 for (; i + Unroll - 1 < n; i += Unroll) {
290 float32x4_t acc0 = vdupq_n_f32(0.0F);
291 float32x4_t acc1 = vdupq_n_f32(0.0F);
292 float32x4_t acc2 = vdupq_n_f32(0.0F);
293 float32x4_t acc3 = vdupq_n_f32(0.0F);
294 for (int32_t k = -half; k <= half; ++k) {
295 const float32x4_t kv = vdupq_n_f32(kdata[static_cast<size_t>(k + half)]);
296 acc0 = vmlaq_f32(acc0, vld1q_f32(s_ptr[i + 0] + px + k), kv);
297 acc1 = vmlaq_f32(acc1, vld1q_f32(s_ptr[i + 1] + px + k), kv);
298 acc2 = vmlaq_f32(acc2, vld1q_f32(s_ptr[i + 2] + px + k), kv);
299 acc3 = vmlaq_f32(acc3, vld1q_f32(s_ptr[i + 3] + px + k), kv);
300 }
301 vst1q_f32(d_ptr[i + 0] + px, acc0);
302 vst1q_f32(d_ptr[i + 1] + px, acc1);
303 vst1q_f32(d_ptr[i + 2] + px, acc2);
304 vst1q_f32(d_ptr[i + 3] + px, acc3);
305 }
306 for (; i < n; ++i) {
307 float32x4_t acc = vdupq_n_f32(0.0F);
308 for (int32_t k = -half; k <= half; ++k)
309 acc = vmlaq_f32(acc,
310 vld1q_f32(s_ptr[i] + px + k),
311 vdupq_n_f32(kdata[static_cast<size_t>(k + half)]));
312 vst1q_f32(d_ptr[i] + px, acc);
313 }
314 }
315 for (size_t i = 0; i < n; ++i) {
316 for (int32_t px2 = px; px2 < iw; ++px2) {
317 float acc = 0.0F;
318 for (int32_t k = -half; k <= half; ++k)
319 acc += s_ptr[i][std::clamp(px2 + k, 0, iw - 1)]
320 * kdata[static_cast<size_t>(k + half)];
321 d_ptr[i][px2] = acc;
322 }
323 }
324
325#else
326 for (size_t i = 0; i < n; ++i) {
327 for (int32_t px = half; px < interior_end; ++px) {
328 float acc = 0.0F;
329 for (int32_t k = -half; k <= half; ++k)
330 acc += s_ptr[i][px + k] * kdata[static_cast<size_t>(k + half)];
331 d_ptr[i][px] = acc;
332 }
333 for (int32_t px = interior_end; px < iw; ++px) {
334 float acc = 0.0F;
335 for (int32_t k = -half; k <= half; ++k)
336 acc += s_ptr[i][std::clamp(px + k, 0, iw - 1)]
337 * kdata[static_cast<size_t>(k + half)];
338 d_ptr[i][px] = acc;
339 }
340 }
341#endif
342 });
343}
uint32_t h
Definition InkPress.cpp:28
float k

References h, and k.

Referenced by harris_response().

+ Here is the caller graph for this function: