2x box-filter downsample for multi-channel interleaved data.
Each output pixel is the per-channel average of the corresponding 2x2 block. Output dimensions are floor(w/2) x floor(h/2).
534{
535 if (channels == 1) {
537 return;
538 }
539
540 new_w = w / 2;
542 const uint32_t ch = channels;
543 const uint32_t row_stride = w * ch;
544 const uint32_t out_stride = new_w * ch;
545
546 P::for_each(P::par_unseq,
547 std::views::iota(uint32_t { 0 }, new_h).begin(),
548 std::views::iota(uint32_t { 0 }, new_h).end(),
549 [&](uint32_t oy) {
550 const float* row0 = src.data() + static_cast<size_t>(oy * 2) * row_stride;
551 const float* row1 = src.data() + static_cast<size_t>(oy * 2 + 1) * row_stride;
552 float* drow = dst.data() + static_cast<size_t>(oy) * out_stride;
553
554 uint32_t ox = 0;
555
556#ifdef MAYAFLUX_ARCH_X64
557 if (ch == 4) {
558 const __m256 quarter = _mm256_set1_ps(0.25F);
559 for (; ox + 1 <= new_w; ++ox) {
560 const float* p00 = row0 + ox * 8;
561 const float* p01 = row0 + ox * 8 + 4;
562 const float* p10 = row1 + ox * 8;
563 const float* p11 = row1 + ox * 8 + 4;
564
565 __m128 v00 = _mm_loadu_ps(p00);
566 __m128 v01 = _mm_loadu_ps(p01);
567 __m128 v10 = _mm_loadu_ps(p10);
568 __m128 v11 = _mm_loadu_ps(p11);
569
570 __m128 sum = _mm_add_ps(_mm_add_ps(v00, v01), _mm_add_ps(v10, v11));
571 __m128 res = _mm_mul_ps(sum, _mm_set1_ps(0.25F));
572 _mm_storeu_ps(drow + ox * 4, res);
573 }
574 return;
575 }
576#elif defined(MAYAFLUX_ARCH_ARM64)
577 if (ch == 4) {
578 for (; ox + 1 <= new_w; ++ox) {
579 const float* p00 = row0 + ox * 8;
580 const float* p01 = row0 + ox * 8 + 4;
581 const float* p10 = row1 + ox * 8;
582 const float* p11 = row1 + ox * 8 + 4;
583
584 float32x4_t v00 = vld1q_f32(p00);
585 float32x4_t v01 = vld1q_f32(p01);
586 float32x4_t v10 = vld1q_f32(p10);
587 float32x4_t v11 = vld1q_f32(p11);
588
589 float32x4_t
sum = vaddq_f32(vaddq_f32(v00, v01), vaddq_f32(v10, v11));
590 vst1q_f32(drow + ox * 4, vmulq_n_f32(sum, 0.25F));
591 }
592 return;
593 }
594#endif
595
596 for (; ox < new_w; ++ox) {
597 const float* p00 = row0 + ox * 2 * ch;
598 const float* p01 = row0 + (ox * 2 + 1) * ch;
599 const float* p10 = row1 + ox * 2 * ch;
600 const float* p11 = row1 + (ox * 2 + 1) * ch;
601 float* out = drow + ox * ch;
602 for (uint32_t c = 0; c < ch; ++c)
603 out[c] = (p00[c] + p01[c] + p10[c] + p11[c]) * 0.25F;
604 }
605 });
606}
std::vector< double > sum(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Sum per window.
void downsample_2x(std::span< const float > src, std::span< float > dst, uint32_t w, uint32_t h, uint32_t &new_w, uint32_t &new_h)
2x box-filter downsample writing into a caller-supplied buffer.