18void rgba_to_gray(std::span<const float> rgba, std::span<float>
dst, uint32_t w, uint32_t
h)
20 const size_t n =
static_cast<size_t>(w) *
h;
22 std::for_each(std::execution::par_unseq,
23 std::views::iota(uint32_t { 0 },
h).begin(),
24 std::views::iota(uint32_t { 0 },
h).end(),
26 const float* src_row = rgba.data() +
static_cast<size_t>(row) * w * 4;
27 float* dst_row =
dst.data() +
static_cast<size_t>(row) * w;
29#ifdef MAYAFLUX_ARCH_X64
31 for (;
px + 8 <= w;
px += 8) {
32 __m128 a0 = _mm_loadu_ps(src_row +
px * 4);
33 __m128 a1 = _mm_loadu_ps(src_row +
px * 4 + 4);
34 __m128 a2 = _mm_loadu_ps(src_row +
px * 4 + 8);
35 __m128 a3 = _mm_loadu_ps(src_row +
px * 4 + 12);
36 _MM_TRANSPOSE4_PS(a0, a1, a2, a3);
37 __m128 lo = _mm_fmadd_ps(a0, _mm_set1_ps(0.299F),
38 _mm_fmadd_ps(a1, _mm_set1_ps(0.587F),
39 _mm_mul_ps(a2, _mm_set1_ps(0.114F))));
41 __m128 b0 = _mm_loadu_ps(src_row +
px * 4 + 16);
42 __m128 b1 = _mm_loadu_ps(src_row +
px * 4 + 20);
43 __m128 b2 = _mm_loadu_ps(src_row +
px * 4 + 24);
44 __m128 b3 = _mm_loadu_ps(src_row +
px * 4 + 28);
45 _MM_TRANSPOSE4_PS(b0, b1, b2, b3);
46 __m128 hi = _mm_fmadd_ps(b0, _mm_set1_ps(0.299F),
47 _mm_fmadd_ps(b1, _mm_set1_ps(0.587F),
48 _mm_mul_ps(b2, _mm_set1_ps(0.114F))));
50 _mm256_storeu_ps(dst_row +
px, _mm256_set_m128(hi, lo));
52 for (;
px < w; ++
px) {
53 const size_t p =
px * 4;
54 dst_row[
px] = src_row[p] * 0.299F
55 + src_row[p + 1] * 0.587F
56 + src_row[p + 2] * 0.114F;
59#elif defined(MAYAFLUX_ARCH_ARM64)
60 const float32x4_t k_r = vdupq_n_f32(0.299F);
61 const float32x4_t k_g = vdupq_n_f32(0.587F);
62 const float32x4_t k_b = vdupq_n_f32(0.114F);
64 for (;
px + 4 <= w;
px += 4) {
65 float32x4x4_t p = vld4q_f32(src_row +
px * 4);
66 vst1q_f32(dst_row +
px,
67 vmlaq_f32(vmlaq_f32(vmulq_f32(p.val[0], k_r), p.val[1], k_g), p.val[2], k_b));
69 for (;
px < w; ++
px) {
70 const size_t p =
px * 4;
71 dst_row[
px] = src_row[p] * 0.299F
72 + src_row[p + 1] * 0.587F
73 + src_row[p + 2] * 0.114F;
77 for (uint32_t
px = 0;
px < w; ++
px) {
78 const size_t p =
px * 4;
79 dst_row[
px] = src_row[p] * 0.299F
80 + src_row[p + 1] * 0.587F
81 + src_row[p + 2] * 0.114F;
118void gray_to_rgba(std::span<const float> gray, std::span<float>
dst, uint32_t w, uint32_t
h)
120 const size_t n =
static_cast<size_t>(w) *
h;
122 std::for_each(std::execution::par_unseq,
123 std::views::iota(uint32_t { 0 },
h).begin(),
124 std::views::iota(uint32_t { 0 },
h).end(),
126 const float* src_row = gray.data() +
static_cast<size_t>(row) * w;
127 float* dst_row =
dst.data() +
static_cast<size_t>(row) * w * 4;
129#ifdef MAYAFLUX_ARCH_X64
132 const __m256 alpha_mask = _mm256_set_ps(1.0F, 0.0F, 0.0F, 0.0F,
133 1.0F, 0.0F, 0.0F, 0.0F);
135 for (;
px + 2 <= w;
px += 2) {
136 const float g0 = src_row[
px];
137 const float g1 = src_row[
px + 1];
139 __m256 v = _mm256_set_ps(1.0F, g1, g1, g1, 1.0F, g0, g0, g0);
140 _mm256_storeu_ps(dst_row +
px * 4, v);
142 for (;
px < w; ++
px) {
143 const float v = src_row[
px];
144 dst_row[
px * 4] = dst_row[
px * 4 + 1] = dst_row[
px * 4 + 2] = v;
145 dst_row[
px * 4 + 3] = 1.0F;
148#elif defined(MAYAFLUX_ARCH_ARM64)
149 const float32x4_t
alpha = vdupq_n_f32(1.0F);
151 for (;
px + 4 <= w;
px += 4) {
152 float32x4_t g = vld1q_f32(src_row +
px);
153 float32x4_t lo = vzip1q_f32(g, g);
154 float32x4_t hi = vzip2q_f32(g, g);
155 float32x4x2_t p01 = vzipq_f32(lo, vdupq_lane_f32(vget_low_f32(
alpha), 0));
162 vst4q_f32(dst_row +
px * 4, rgba);
167 for (;
px < w; ++
px) {
168 const float v = src_row[
px];
170 dst_row[
px * 4 + 1] = v;
171 dst_row[
px * 4 + 2] = v;
172 dst_row[
px * 4 + 3] = 1.0F;
176 for (uint32_t
px = 0;
px < w; ++
px) {
177 const float v = src_row[
px];
179 dst_row[
px * 4 + 1] = v;
180 dst_row[
px * 4 + 2] = v;
181 dst_row[
px * 4 + 3] = 1.0F;
196 const size_t n = gray.size();
197 const float*
src = gray.data();
200#ifdef MAYAFLUX_ARCH_X64
201 const __m256 thresh = _mm256_set1_ps(
value);
202 const __m256 one = _mm256_set1_ps(1.0F);
204 for (; i + 8 <= n; i += 8) {
205 __m256 v = _mm256_loadu_ps(
src + i);
206 __m256 mask = _mm256_cmp_ps(v, thresh, _CMP_GE_OS);
207 _mm256_storeu_ps(
out + i, _mm256_and_ps(mask, one));
212#elif defined(MAYAFLUX_ARCH_ARM64)
213 const float32x4_t thresh = vdupq_n_f32(
value);
214 const float32x4_t one = vdupq_n_f32(1.0F);
216 for (; i + 4 <= n; i += 4) {
217 float32x4_t v = vld1q_f32(
src + i);
218 uint32x4_t mask = vcgeq_f32(v, thresh);
220 vreinterpretq_f32_u32(vandq_u32(mask, vreinterpretq_u32_f32(one))));
226 std::transform(std::execution::par_unseq, gray.begin(), gray.end(),
dst.begin(),
227 [
value](
float v) { return v >= value ? 1.0F : 0.0F; });
281 constexpr int32_t bins = 256;
282 std::array<float, bins> hist {};
285 hist[
static_cast<int32_t
>(std::clamp(v, 0.0F, 1.0F) * (bins - 1))]++;
287 const auto total =
static_cast<float>(gray.size());
291 float sum_all = 0.0F;
292 for (int32_t i = 0; i < bins; ++i)
293 sum_all +=
static_cast<float>(i) * hist[i];
297 float best_var = 0.0F;
300 for (int32_t t = 0; t < bins; ++t) {
305 const float w_fg = 1.0F - w_bg;
309 sum_bg +=
static_cast<float>(t) * hist[t];
311 const float mean_bg = sum_bg / w_bg;
312 const float mean_fg = (sum_all - sum_bg) / w_fg;
313 const float diff = mean_fg - mean_bg;
314 const float var = w_bg * w_fg * diff * diff;
316 if (var > best_var) {
322 const float t_norm =
static_cast<float>(best_t) /
static_cast<float>(bins - 1);
338 const Eigen::Index en =
static_cast<Eigen::Index
>(data.size());
339 auto m = Eigen::Map<Eigen::ArrayXf>(data.data(), en);
340 const float mn = m.minCoeff();
341 const float mx = m.maxCoeff();
342 const float range = mx - mn;
346 const float inv = 1.0F / range;
347 float*
out = data.data();
348 const size_t n = data.size();
350#ifdef MAYAFLUX_ARCH_X64
351 const __m256 v_mn = _mm256_set1_ps(mn);
352 const __m256 v_inv = _mm256_set1_ps(inv);
354 for (; i + 8 <= n; i += 8) {
355 __m256 v = _mm256_loadu_ps(
out + i);
356 _mm256_storeu_ps(
out + i, _mm256_mul_ps(_mm256_sub_ps(v, v_mn), v_inv));
359 out[i] = (
out[i] - mn) * inv;
361#elif defined(MAYAFLUX_ARCH_ARM64)
362 const float32x4_t v_mn = vdupq_n_f32(mn);
363 const float32x4_t v_inv = vdupq_n_f32(inv);
365 for (; i + 4 <= n; i += 4) {
366 float32x4_t v = vld1q_f32(
out + i);
367 vst1q_f32(
out + i, vmulq_f32(vsubq_f32(v, v_mn), v_inv));
370 out[i] = (
out[i] - mn) * inv;
373 std::transform(std::execution::par_unseq, data.begin(), data.end(), data.begin(),
374 [mn, inv](
float v) { return (v - mn) * inv; });
383 const float inv = 1.0F / (hi - lo);
384 float*
out = data.data();
385 const size_t n = data.size();
387#ifdef MAYAFLUX_ARCH_X64
388 const __m256 v_lo = _mm256_set1_ps(lo);
389 const __m256 v_inv = _mm256_set1_ps(inv);
390 const __m256 v_zero = _mm256_setzero_ps();
391 const __m256 v_one = _mm256_set1_ps(1.0F);
393 for (; i + 8 <= n; i += 8) {
394 __m256 v = _mm256_loadu_ps(
out + i);
395 v = _mm256_mul_ps(_mm256_sub_ps(v, v_lo), v_inv);
396 v = _mm256_min_ps(_mm256_max_ps(v, v_zero), v_one);
397 _mm256_storeu_ps(
out + i, v);
400 out[i] = std::clamp((
out[i] - lo) * inv, 0.0F, 1.0F);
402#elif defined(MAYAFLUX_ARCH_ARM64)
403 const float32x4_t v_lo = vdupq_n_f32(lo);
404 const float32x4_t v_inv = vdupq_n_f32(inv);
405 const float32x4_t v_zero = vdupq_n_f32(0.0F);
406 const float32x4_t v_one = vdupq_n_f32(1.0F);
408 for (; i + 4 <= n; i += 4) {
409 float32x4_t v = vld1q_f32(
out + i);
410 v = vmulq_f32(vsubq_f32(v, v_lo), v_inv);
411 v = vminq_f32(vmaxq_f32(v, v_zero), v_one);
412 vst1q_f32(
out + i, v);
415 out[i] = std::clamp((
out[i] - lo) * inv, 0.0F, 1.0F);
418 std::transform(std::execution::par_unseq, data.begin(), data.end(), data.begin(),
419 [lo, inv](
float v) { return std::clamp((v - lo) * inv, 0.0F, 1.0F); });
424 std::span<const float>
src,
425 std::span<float>
dst,
426 uint32_t w, uint32_t
h,
427 uint32_t& new_w, uint32_t& new_h)
432 std::for_each(std::execution::par_unseq,
433 std::views::iota(uint32_t { 0 }, new_h).begin(),
434 std::views::iota(uint32_t { 0 }, new_h).end(),
436 const float* row0 =
src.data() +
static_cast<size_t>(oy * 2) * w;
437 const float* row1 =
src.data() +
static_cast<size_t>(oy * 2 + 1) * w;
438 float* drow =
dst.data() +
static_cast<size_t>(oy) * new_w;
440#ifdef MAYAFLUX_ARCH_X64
441 const __m256 quarter = _mm256_set1_ps(0.25F);
443 for (; ox + 8 <= new_w; ox += 8) {
445 __m256 r0a = _mm256_loadu_ps(row0 + ox * 2);
446 __m256 r0b = _mm256_loadu_ps(row0 + ox * 2 + 8);
447 __m256 r1a = _mm256_loadu_ps(row1 + ox * 2);
448 __m256 r1b = _mm256_loadu_ps(row1 + ox * 2 + 8);
451 __m256 sum0 = _mm256_hadd_ps(r0a, r0b);
452 __m256 sum1 = _mm256_hadd_ps(r1a, r1b);
453 __m256 sum = _mm256_add_ps(sum0, sum1);
455 __m256d perm_d = _mm256_permute4x64_pd(_mm256_castps_pd(sum), _MM_SHUFFLE(3, 1, 2, 0));
456 __m256 result = _mm256_mul_ps(_mm256_castpd_ps(perm_d), quarter);
457 _mm256_storeu_ps(drow + ox, result);
459 for (; ox < new_w; ++ox) {
460 drow[ox] = (row0[ox * 2] + row0[ox * 2 + 1]
461 + row1[ox * 2] + row1[ox * 2 + 1])
465#elif defined(MAYAFLUX_ARCH_ARM64)
466 const float32x4_t quarter = vdupq_n_f32(0.25F);
468 for (; ox + 4 <= new_w; ox += 4) {
471 float32x4x2_t r0 = vld2q_f32(row0 + ox * 2);
472 float32x4x2_t r1 = vld2q_f32(row1 + ox * 2);
475 float32x4_t sum = vaddq_f32(
476 vaddq_f32(r0.val[0], r0.val[1]),
477 vaddq_f32(r1.val[0], r1.val[1]));
478 vst1q_f32(drow + ox, vmulq_f32(sum, quarter));
480 for (; ox < new_w; ++ox) {
481 drow[ox] = (row0[ox * 2] + row0[ox * 2 + 1]
482 + row1[ox * 2] + row1[ox * 2 + 1])
487 for (uint32_t ox = 0; ox < new_w; ++ox) {
488 drow[ox] = (row0[ox * 2] + row0[ox * 2 + 1]
489 + row1[ox * 2] + row1[ox * 2 + 1])
509 std::span<const float>
src,
510 std::span<float>
dst,
511 uint32_t w, uint32_t
h,
513 uint32_t& new_w, uint32_t& new_h)
523 const uint32_t row_stride = w * ch;
524 const uint32_t out_stride = new_w * ch;
526 std::for_each(std::execution::par_unseq,
527 std::views::iota(uint32_t { 0 }, new_h).begin(),
528 std::views::iota(uint32_t { 0 }, new_h).end(),
530 const float* row0 =
src.data() +
static_cast<size_t>(oy * 2) * row_stride;
531 const float* row1 =
src.data() +
static_cast<size_t>(oy * 2 + 1) * row_stride;
532 float* drow =
dst.data() +
static_cast<size_t>(oy) * out_stride;
536#ifdef MAYAFLUX_ARCH_X64
538 const __m256 quarter = _mm256_set1_ps(0.25F);
539 for (; ox + 1 <= new_w; ++ox) {
540 const float* p00 = row0 + ox * 8;
541 const float* p01 = row0 + ox * 8 + 4;
542 const float* p10 = row1 + ox * 8;
543 const float* p11 = row1 + ox * 8 + 4;
545 __m128 v00 = _mm_loadu_ps(p00);
546 __m128 v01 = _mm_loadu_ps(p01);
547 __m128 v10 = _mm_loadu_ps(p10);
548 __m128 v11 = _mm_loadu_ps(p11);
550 __m128 sum = _mm_add_ps(_mm_add_ps(v00, v01), _mm_add_ps(v10, v11));
551 __m128 res = _mm_mul_ps(sum, _mm_set1_ps(0.25F));
552 _mm_storeu_ps(drow + ox * 4, res);
556#elif defined(MAYAFLUX_ARCH_ARM64)
558 for (; ox + 1 <= new_w; ++ox) {
559 const float* p00 = row0 + ox * 8;
560 const float* p01 = row0 + ox * 8 + 4;
561 const float* p10 = row1 + ox * 8;
562 const float* p11 = row1 + ox * 8 + 4;
564 float32x4_t v00 = vld1q_f32(p00);
565 float32x4_t v01 = vld1q_f32(p01);
566 float32x4_t v10 = vld1q_f32(p10);
567 float32x4_t v11 = vld1q_f32(p11);
569 float32x4_t sum = vaddq_f32(vaddq_f32(v00, v01), vaddq_f32(v10, v11));
570 vst1q_f32(drow + ox * 4, vmulq_n_f32(sum, 0.25F));
576 for (; ox < new_w; ++ox) {
577 const float* p00 = row0 + ox * 2 * ch;
578 const float* p01 = row0 + (ox * 2 + 1) * ch;
579 const float* p10 = row1 + ox * 2 * ch;
580 const float* p11 = row1 + (ox * 2 + 1) * ch;
581 float*
out = drow + ox * ch;
582 for (uint32_t c = 0; c < ch; ++c)
583 out[c] = (p00[c] + p01[c] + p10[c] + p11[c]) * 0.25F;
void threshold_adaptive(std::span< const float > gray, std::span< float > dst, uint32_t w, uint32_t h, uint32_t block_size, float offset)
Adaptive threshold writing into caller-supplied buffer.
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.