19#ifndef PACS_ENCODING_SIMD_PHOTOMETRIC_HPP
20#define PACS_ENCODING_SIMD_PHOTOMETRIC_HPP
33 size_t pixel_count)
noexcept;
35 size_t pixel_count, uint16_t max_value)
noexcept;
37 size_t pixel_count)
noexcept;
39 size_t pixel_count)
noexcept;
51 size_t pixel_count)
noexcept {
52 for (
size_t i = 0; i < pixel_count; ++i) {
53 dst[i] = 255 - src[i];
66 uint16_t max_value)
noexcept {
67 for (
size_t i = 0; i < pixel_count; ++i) {
68 dst[i] = max_value - src[i];
80 size_t pixel_count)
noexcept {
81 for (
size_t i = 0; i < pixel_count; ++i) {
82 const int r = src[i * 3];
83 const int g = src[i * 3 + 1];
84 const int b = src[i * 3 + 2];
88 const int y = (19595 * r + 38470 * g + 7471 * b + 32768) >> 16;
89 const int cb = (-11056 * r - 21712 * g + 32768 * b + 32768) >> 16;
90 const int cr = (32768 * r - 27440 * g - 5328 * b + 32768) >> 16;
92 dst[i * 3] =
static_cast<uint8_t
>(y < 0 ? 0 : (y > 255 ? 255 : y));
93 dst[i * 3 + 1] =
static_cast<uint8_t
>(cb + 128);
94 dst[i * 3 + 2] =
static_cast<uint8_t
>(cr + 128);
106 size_t pixel_count)
noexcept {
107 for (
size_t i = 0; i < pixel_count; ++i) {
108 const int y = src[i * 3];
109 const int cb = src[i * 3 + 1] - 128;
110 const int cr = src[i * 3 + 2] - 128;
113 const int r = y + ((91881 * cr + 32768) >> 16);
114 const int g = y - ((22554 * cb + 46802 * cr + 32768) >> 16);
115 const int b = y + ((116130 * cb + 32768) >> 16);
117 dst[i * 3] =
static_cast<uint8_t
>(r < 0 ? 0 : (r > 255 ? 255 : r));
118 dst[i * 3 + 1] =
static_cast<uint8_t
>(g < 0 ? 0 : (g > 255 ? 255 : g));
119 dst[i * 3 + 2] =
static_cast<uint8_t
>(b < 0 ? 0 : (b > 255 ? 255 : b));
127#if defined(PACS_SIMD_SSE2)
133inline void invert_monochrome_8bit_sse2(
const uint8_t* src, uint8_t* dst,
134 size_t pixel_count)
noexcept {
135 const __m128i all_ones = _mm_set1_epi8(
static_cast<char>(0xFF));
136 const size_t simd_count = (pixel_count / 16) * 16;
139 for (; i < simd_count; i += 16) {
140 __m128i v = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(src + i));
141 v = _mm_xor_si128(v, all_ones);
142 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(dst + i), v);
153inline void invert_monochrome_16bit_sse2(
const uint16_t* src, uint16_t* dst,
155 uint16_t max_value)
noexcept {
156 const __m128i max_vec = _mm_set1_epi16(
static_cast<int16_t
>(max_value));
157 const size_t simd_count = (pixel_count / 8) * 8;
160 for (; i < simd_count; i += 8) {
161 __m128i v = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(src + i));
162 v = _mm_sub_epi16(max_vec, v);
163 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(dst + i), v);
176#if defined(PACS_SIMD_AVX2)
182inline void invert_monochrome_8bit_avx2(
const uint8_t* src, uint8_t* dst,
183 size_t pixel_count)
noexcept {
184 const __m256i all_ones = _mm256_set1_epi8(
static_cast<char>(0xFF));
185 const size_t simd_count = (pixel_count / 32) * 32;
188 for (; i < simd_count; i += 32) {
189 __m256i v = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(src + i));
190 v = _mm256_xor_si256(v, all_ones);
191 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(dst + i), v);
195#if defined(PACS_SIMD_SSE2)
196 if (pixel_count - i >= 16) {
197 invert_monochrome_8bit_sse2(src + i, dst + i, pixel_count - i);
209inline void invert_monochrome_16bit_avx2(
const uint16_t* src, uint16_t* dst,
211 uint16_t max_value)
noexcept {
212 const __m256i max_vec = _mm256_set1_epi16(
static_cast<int16_t
>(max_value));
213 const size_t simd_count = (pixel_count / 16) * 16;
216 for (; i < simd_count; i += 16) {
217 __m256i v = _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(src + i));
218 v = _mm256_sub_epi16(max_vec, v);
219 _mm256_storeu_si256(
reinterpret_cast<__m256i*
>(dst + i), v);
223#if defined(PACS_SIMD_SSE2)
224 if (pixel_count - i >= 8) {
225 invert_monochrome_16bit_sse2(src + i, dst + i, pixel_count - i, max_value);
239inline void rgb_to_ycbcr_8bit_avx2(
const uint8_t* src, uint8_t* dst,
240 size_t pixel_count)
noexcept {
245 const __m256i coeff_y_r = _mm256_set1_epi16(77);
246 const __m256i coeff_y_g = _mm256_set1_epi16(150);
247 const __m256i coeff_y_b = _mm256_set1_epi16(29);
249 const __m256i coeff_cb_r = _mm256_set1_epi16(-43);
250 const __m256i coeff_cb_g = _mm256_set1_epi16(-85);
251 const __m256i coeff_cb_b = _mm256_set1_epi16(128);
253 const __m256i coeff_cr_r = _mm256_set1_epi16(128);
254 const __m256i coeff_cr_g = _mm256_set1_epi16(-107);
255 const __m256i coeff_cr_b = _mm256_set1_epi16(-21);
257 const __m256i offset_128 = _mm256_set1_epi16(128);
258 const __m256i round_add = _mm256_set1_epi16(128);
260 const size_t simd_count = (pixel_count / 8) * 8;
263 for (; i < simd_count; i += 8) {
266 alignas(32) int16_t r[8], g[8], b[8];
267 for (
int j = 0; j < 8; ++j) {
268 r[j] = src[(i + j) * 3];
269 g[j] = src[(i + j) * 3 + 1];
270 b[j] = src[(i + j) * 3 + 2];
273 __m256i r_vec = _mm256_load_si256(
reinterpret_cast<const __m256i*
>(r));
274 __m256i g_vec = _mm256_load_si256(
reinterpret_cast<const __m256i*
>(g));
275 __m256i b_vec = _mm256_load_si256(
reinterpret_cast<const __m256i*
>(b));
278 __m256i y = _mm256_add_epi16(
279 _mm256_add_epi16(_mm256_mullo_epi16(r_vec, coeff_y_r),
280 _mm256_mullo_epi16(g_vec, coeff_y_g)),
281 _mm256_mullo_epi16(b_vec, coeff_y_b));
282 y = _mm256_add_epi16(y, round_add);
283 y = _mm256_srai_epi16(y, 8);
286 __m256i cb = _mm256_add_epi16(
287 _mm256_add_epi16(_mm256_mullo_epi16(r_vec, coeff_cb_r),
288 _mm256_mullo_epi16(g_vec, coeff_cb_g)),
289 _mm256_mullo_epi16(b_vec, coeff_cb_b));
290 cb = _mm256_add_epi16(cb, round_add);
291 cb = _mm256_srai_epi16(cb, 8);
292 cb = _mm256_add_epi16(cb, offset_128);
295 __m256i
cr = _mm256_add_epi16(
296 _mm256_add_epi16(_mm256_mullo_epi16(r_vec, coeff_cr_r),
297 _mm256_mullo_epi16(g_vec, coeff_cr_g)),
298 _mm256_mullo_epi16(b_vec, coeff_cr_b));
299 cr = _mm256_add_epi16(cr, round_add);
300 cr = _mm256_srai_epi16(cr, 8);
301 cr = _mm256_add_epi16(cr, offset_128);
304 alignas(32) int16_t y_out[8], cb_out[8], cr_out[8];
305 _mm256_store_si256(
reinterpret_cast<__m256i*
>(y_out), y);
306 _mm256_store_si256(
reinterpret_cast<__m256i*
>(cb_out), cb);
307 _mm256_store_si256(
reinterpret_cast<__m256i*
>(cr_out), cr);
309 for (
int j = 0; j < 8; ++j) {
310 dst[(i + j) * 3] =
static_cast<uint8_t
>(
311 y_out[j] < 0 ? 0 : (y_out[j] > 255 ? 255 : y_out[j]));
312 dst[(i + j) * 3 + 1] =
static_cast<uint8_t
>(
313 cb_out[j] < 0 ? 0 : (cb_out[j] > 255 ? 255 : cb_out[j]));
314 dst[(i + j) * 3 + 2] =
static_cast<uint8_t
>(
315 cr_out[j] < 0 ? 0 : (cr_out[j] > 255 ? 255 : cr_out[j]));
326inline void ycbcr_to_rgb_8bit_avx2(
const uint8_t* src, uint8_t* dst,
327 size_t pixel_count)
noexcept {
332 const __m256i coeff_r_cr = _mm256_set1_epi16(359);
333 const __m256i coeff_g_cb = _mm256_set1_epi16(-88);
334 const __m256i coeff_g_cr = _mm256_set1_epi16(-183);
335 const __m256i coeff_b_cb = _mm256_set1_epi16(454);
337 const __m256i round_add = _mm256_set1_epi16(128);
338 const __m256i zero = _mm256_setzero_si256();
339 const __m256i max_255 = _mm256_set1_epi16(255);
341 const size_t simd_count = (pixel_count / 8) * 8;
344 for (; i < simd_count; i += 8) {
346 alignas(32) int16_t y[8], cb[8], cr[8];
347 for (
int j = 0; j < 8; ++j) {
348 y[j] = src[(i + j) * 3];
349 cb[j] = src[(i + j) * 3 + 1] - 128;
350 cr[j] = src[(i + j) * 3 + 2] - 128;
353 __m256i y_vec = _mm256_load_si256(
reinterpret_cast<const __m256i*
>(y));
354 __m256i cb_vec = _mm256_load_si256(
reinterpret_cast<const __m256i*
>(cb));
355 __m256i cr_vec = _mm256_load_si256(
reinterpret_cast<const __m256i*
>(cr));
358 __m256i r = _mm256_add_epi16(
360 _mm256_add_epi16(_mm256_mullo_epi16(cr_vec, coeff_r_cr), round_add),
365 __m256i g = _mm256_add_epi16(
368 _mm256_add_epi16(_mm256_mullo_epi16(cb_vec, coeff_g_cb),
369 _mm256_mullo_epi16(cr_vec, coeff_g_cr)),
375 __m256i b = _mm256_add_epi16(
377 _mm256_add_epi16(_mm256_mullo_epi16(cb_vec, coeff_b_cb), round_add),
382 r = _mm256_max_epi16(_mm256_min_epi16(r, max_255), zero);
383 g = _mm256_max_epi16(_mm256_min_epi16(g, max_255), zero);
384 b = _mm256_max_epi16(_mm256_min_epi16(b, max_255), zero);
387 alignas(32) int16_t r_out[8], g_out[8], b_out[8];
388 _mm256_store_si256(
reinterpret_cast<__m256i*
>(r_out), r);
389 _mm256_store_si256(
reinterpret_cast<__m256i*
>(g_out), g);
390 _mm256_store_si256(
reinterpret_cast<__m256i*
>(b_out), b);
392 for (
int j = 0; j < 8; ++j) {
393 dst[(i + j) * 3] =
static_cast<uint8_t
>(r_out[j]);
394 dst[(i + j) * 3 + 1] =
static_cast<uint8_t
>(g_out[j]);
395 dst[(i + j) * 3 + 2] =
static_cast<uint8_t
>(b_out[j]);
409#if defined(PACS_SIMD_NEON)
415inline void invert_monochrome_8bit_neon(
const uint8_t* src, uint8_t* dst,
416 size_t pixel_count)
noexcept {
417 const uint8x16_t all_ones = vdupq_n_u8(0xFF);
418 const size_t simd_count = (pixel_count / 16) * 16;
421 for (; i < simd_count; i += 16) {
422 uint8x16_t v = vld1q_u8(src + i);
423 v = veorq_u8(v, all_ones);
424 vst1q_u8(dst + i, v);
435inline void invert_monochrome_16bit_neon(
const uint16_t* src, uint16_t* dst,
437 uint16_t max_value)
noexcept {
438 const uint16x8_t max_vec = vdupq_n_u16(max_value);
439 const size_t simd_count = (pixel_count / 8) * 8;
442 for (; i < simd_count; i += 8) {
443 uint16x8_t v = vld1q_u16(src + i);
444 v = vsubq_u16(max_vec, v);
445 vst1q_u16(dst + i, v);
456inline void rgb_to_ycbcr_8bit_neon(
const uint8_t* src, uint8_t* dst,
457 size_t pixel_count)
noexcept {
459 const int16x8_t coeff_y_r = vdupq_n_s16(77);
460 const int16x8_t coeff_y_g = vdupq_n_s16(150);
461 const int16x8_t coeff_y_b = vdupq_n_s16(29);
463 const int16x8_t coeff_cb_r = vdupq_n_s16(-43);
464 const int16x8_t coeff_cb_g = vdupq_n_s16(-85);
465 const int16x8_t coeff_cb_b = vdupq_n_s16(128);
467 const int16x8_t coeff_cr_r = vdupq_n_s16(128);
468 const int16x8_t coeff_cr_g = vdupq_n_s16(-107);
469 const int16x8_t coeff_cr_b = vdupq_n_s16(-21);
471 const int16x8_t offset_128 = vdupq_n_s16(128);
473 const size_t simd_count = (pixel_count / 8) * 8;
476 for (; i < simd_count; i += 8) {
478 uint8x8x3_t
rgb = vld3_u8(src + i * 3);
481 int16x8_t r = vreinterpretq_s16_u16(vmovl_u8(
rgb.val[0]));
482 int16x8_t g = vreinterpretq_s16_u16(vmovl_u8(
rgb.val[1]));
483 int16x8_t b = vreinterpretq_s16_u16(vmovl_u8(
rgb.val[2]));
486 int16x8_t y = vaddq_s16(vaddq_s16(vmulq_s16(r, coeff_y_r),
487 vmulq_s16(g, coeff_y_g)),
488 vmulq_s16(b, coeff_y_b));
489 y = vaddq_s16(y, offset_128);
490 y = vshrq_n_s16(y, 8);
493 int16x8_t cb = vaddq_s16(vaddq_s16(vmulq_s16(r, coeff_cb_r),
494 vmulq_s16(g, coeff_cb_g)),
495 vmulq_s16(b, coeff_cb_b));
496 cb = vaddq_s16(cb, offset_128);
497 cb = vshrq_n_s16(cb, 8);
498 cb = vaddq_s16(cb, offset_128);
501 int16x8_t
cr = vaddq_s16(vaddq_s16(vmulq_s16(r, coeff_cr_r),
502 vmulq_s16(g, coeff_cr_g)),
503 vmulq_s16(b, coeff_cr_b));
504 cr = vaddq_s16(cr, offset_128);
505 cr = vshrq_n_s16(cr, 8);
506 cr = vaddq_s16(cr, offset_128);
510 ycbcr.val[0] = vqmovun_s16(y);
511 ycbcr.val[1] = vqmovun_s16(cb);
512 ycbcr.val[2] = vqmovun_s16(cr);
515 vst3_u8(dst + i * 3, ycbcr);
525inline void ycbcr_to_rgb_8bit_neon(
const uint8_t* src, uint8_t* dst,
526 size_t pixel_count)
noexcept {
527 const int16x8_t coeff_r_cr = vdupq_n_s16(359);
528 const int16x8_t coeff_g_cb = vdupq_n_s16(-88);
529 const int16x8_t coeff_g_cr = vdupq_n_s16(-183);
530 const int16x8_t coeff_b_cb = vdupq_n_s16(454);
531 const int16x8_t offset_128 = vdupq_n_s16(128);
533 const size_t simd_count = (pixel_count / 8) * 8;
536 for (; i < simd_count; i += 8) {
538 uint8x8x3_t ycbcr = vld3_u8(src + i * 3);
541 int16x8_t y = vreinterpretq_s16_u16(vmovl_u8(ycbcr.val[0]));
542 int16x8_t cb = vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(ycbcr.val[1])),
544 int16x8_t
cr = vsubq_s16(vreinterpretq_s16_u16(vmovl_u8(ycbcr.val[2])),
548 int16x8_t r = vaddq_s16(y, vshrq_n_s16(vaddq_s16(vmulq_s16(cr, coeff_r_cr),
552 int16x8_t g = vaddq_s16(y, vshrq_n_s16(
553 vaddq_s16(vaddq_s16(vmulq_s16(cb, coeff_g_cb),
554 vmulq_s16(cr, coeff_g_cr)),
558 int16x8_t bl = vaddq_s16(y, vshrq_n_s16(vaddq_s16(vmulq_s16(cb, coeff_b_cb),
563 rgb.val[0] = vqmovun_s16(r);
564 rgb.val[1] = vqmovun_s16(g);
565 rgb.val[2] = vqmovun_s16(bl);
568 vst3_u8(dst + i * 3, rgb);
594 size_t pixel_count)
noexcept {
595#if defined(PACS_SIMD_AVX2)
597 detail::invert_monochrome_8bit_avx2(src, dst, pixel_count);
601#if defined(PACS_SIMD_SSE2)
603 detail::invert_monochrome_8bit_sse2(src, dst, pixel_count);
607#if defined(PACS_SIMD_NEON)
609 detail::invert_monochrome_8bit_neon(src, dst, pixel_count);
626 uint16_t max_value)
noexcept {
627#if defined(PACS_SIMD_AVX2)
629 detail::invert_monochrome_16bit_avx2(src, dst, pixel_count, max_value);
633#if defined(PACS_SIMD_SSE2)
635 detail::invert_monochrome_16bit_sse2(src, dst, pixel_count, max_value);
639#if defined(PACS_SIMD_NEON)
641 detail::invert_monochrome_16bit_neon(src, dst, pixel_count, max_value);
656 size_t pixel_count)
noexcept {
657#if defined(PACS_SIMD_AVX2)
659 detail::rgb_to_ycbcr_8bit_avx2(src, dst, pixel_count);
663#if defined(PACS_SIMD_NEON)
665 detail::rgb_to_ycbcr_8bit_neon(src, dst, pixel_count);
680 size_t pixel_count)
noexcept {
681#if defined(PACS_SIMD_AVX2)
683 detail::ycbcr_to_rgb_8bit_avx2(src, dst, pixel_count);
687#if defined(PACS_SIMD_NEON)
689 detail::ycbcr_to_rgb_8bit_neon(src, dst, pixel_count);
@ rgb
Red, Green, Blue color model.
void invert_monochrome_8bit_scalar(const uint8_t *src, uint8_t *dst, size_t pixel_count) noexcept
Scalar 8-bit monochrome inversion (MONOCHROME1 <-> MONOCHROME2)
void ycbcr_to_rgb_8bit_scalar(const uint8_t *src, uint8_t *dst, size_t pixel_count) noexcept
Scalar YCbCr to RGB conversion (ITU-R BT.601)
void invert_monochrome_16bit_scalar(const uint16_t *src, uint16_t *dst, size_t pixel_count, uint16_t max_value) noexcept
Scalar 16-bit monochrome inversion.
void rgb_to_ycbcr_8bit_scalar(const uint8_t *src, uint8_t *dst, size_t pixel_count) noexcept
Scalar RGB to YCbCr conversion (ITU-R BT.601)
void invert_monochrome_8bit(const uint8_t *src, uint8_t *dst, size_t pixel_count) noexcept
Invert 8-bit monochrome pixels (MONOCHROME1 <-> MONOCHROME2)
bool has_neon() noexcept
Check if NEON is available.
bool has_sse2() noexcept
Check if SSE2 is available.
void invert_monochrome_16bit(const uint16_t *src, uint16_t *dst, size_t pixel_count, uint16_t max_value) noexcept
Invert 16-bit monochrome pixels.
bool has_avx2() noexcept
Check if AVX2 is available.
void ycbcr_to_rgb_8bit(const uint8_t *src, uint8_t *dst, size_t pixel_count) noexcept
Convert YCbCr to RGB color space (ITU-R BT.601)
void rgb_to_ycbcr_8bit(const uint8_t *src, uint8_t *dst, size_t pixel_count) noexcept
Convert RGB to YCbCr color space (ITU-R BT.601)
@ cr
Computed Radiography.
SIMD configuration and CPU feature detection.
Platform-specific SIMD type definitions and wrappers.