Line data Source code
1 : /*
2 : * liblierre - image.c
3 : *
4 : * This file is part of liblierre.
5 : *
6 : * Author: Go Kudo <zeriyoshi@gmail.com>
7 : * SPDX-License-Identifier: MIT
8 : */
9 :
10 : #include "internal/image.h"
11 :
12 : #include <string.h>
13 :
14 : #include "internal/memory.h"
15 :
16 : #if LIERRE_USE_SIMD
17 : #include "internal/simd.h"
18 : #endif
19 :
20 : #define LIERRE_PIXEL_VALUE_MIN 0
21 : #define LIERRE_PIXEL_VALUE_MAX 255
22 : #define LIERRE_MIN_QR_SIZE 21
23 : #define LIERRE_CONTRAST_FACTOR 128
24 : #define LIERRE_CONTRAST_DIVISOR 100
25 : #define LIERRE_FILTER_KERNEL_SIZE 3
26 : #define LIERRE_FILTER_KERNEL_ELEMS 9
27 : #define LIERRE_SHARPEN_CENTER_COEF 5
28 :
29 : typedef struct {
30 : const uint8_t *src;
31 : uint8_t *dst;
32 : size_t src_width;
33 : size_t dst_width;
34 : size_t start_row;
35 : size_t end_row;
36 : } lierre_image_mt_minimize_ctx_t;
37 :
38 : typedef struct {
39 : uint8_t *image;
40 : uint8_t *temp;
41 : size_t width;
42 : size_t height;
43 : size_t start_row;
44 : size_t end_row;
45 : } lierre_image_mt_filter_ctx_t;
46 :
47 : #define LIERRE_IMAGE_MT_MAX_THREADS 64
48 : #define LIERRE_IMAGE_MINIMIZE_MAX_ITERATIONS 4
49 :
50 : static inline void lierre_minimize_row(const uint8_t *src_row0, const uint8_t *src_row1, uint8_t *dst, size_t dst_width)
51 : {
52 : #if LIERRE_USE_SIMD && defined(LIERRE_SIMD_AVX2)
53 : __m256i data0, data1, row0_even, row0_odd, row1_even, row1_odd, sum, avg;
54 : __m128i packed;
55 : size_t x, simd_count, sx;
56 : int32_t sum_scalar;
57 :
58 : simd_count = dst_width & ~15ULL;
59 :
60 : for (x = 0; x < simd_count; x += 16) {
61 : data0 = _mm256_loadu_si256((const __m256i *)(src_row0 + x * 2));
62 : data1 = _mm256_loadu_si256((const __m256i *)(src_row1 + x * 2));
63 :
64 : row0_even = _mm256_and_si256(data0, _mm256_set1_epi16(0x00FF));
65 : row0_odd = _mm256_srli_epi16(data0, 8);
66 : row1_even = _mm256_and_si256(data1, _mm256_set1_epi16(0x00FF));
67 : row1_odd = _mm256_srli_epi16(data1, 8);
68 :
69 : sum = _mm256_add_epi16(row0_even, row0_odd);
70 : sum = _mm256_add_epi16(sum, row1_even);
71 : sum = _mm256_add_epi16(sum, row1_odd);
72 :
73 : avg = _mm256_srli_epi16(sum, 2);
74 :
75 : packed = _mm_packus_epi16(_mm256_castsi256_si128(avg), _mm256_extracti128_si256(avg, 1));
76 : _mm_storeu_si128((__m128i *)(dst + x), packed);
77 : }
78 :
79 : for (x = simd_count; x < dst_width; x++) {
80 : sx = x * 2;
81 : sum_scalar =
82 : (int32_t)src_row0[sx] + (int32_t)src_row0[sx + 1] + (int32_t)src_row1[sx] + (int32_t)src_row1[sx + 1];
83 : dst[x] = (uint8_t)(sum_scalar >> 2);
84 : }
85 : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_NEON)
86 : uint8x16x2_t row0, row1;
87 : uint16x8_t sum_lo, sum_hi;
88 : uint8x8_t avg_lo, avg_hi;
89 : uint8x16_t result;
90 : size_t x, simd_count, sx;
91 : int32_t sum_scalar;
92 :
93 : simd_count = dst_width & ~15ULL;
94 :
95 : for (x = 0; x < simd_count; x += 16) {
96 : row0 = vld2q_u8(src_row0 + x * 2);
97 : row1 = vld2q_u8(src_row1 + x * 2);
98 :
99 : sum_lo = vaddl_u8(vget_low_u8(row0.val[0]), vget_low_u8(row0.val[1]));
100 : sum_lo = vaddw_u8(sum_lo, vget_low_u8(row1.val[0]));
101 : sum_lo = vaddw_u8(sum_lo, vget_low_u8(row1.val[1]));
102 :
103 : sum_hi = vaddl_u8(vget_high_u8(row0.val[0]), vget_high_u8(row0.val[1]));
104 : sum_hi = vaddw_u8(sum_hi, vget_high_u8(row1.val[0]));
105 : sum_hi = vaddw_u8(sum_hi, vget_high_u8(row1.val[1]));
106 :
107 : avg_lo = vshrn_n_u16(sum_lo, 2);
108 : avg_hi = vshrn_n_u16(sum_hi, 2);
109 : result = vcombine_u8(avg_lo, avg_hi);
110 :
111 : vst1q_u8(dst + x, result);
112 : }
113 :
114 : for (x = simd_count; x < dst_width; x++) {
115 : sx = x * 2;
116 : sum_scalar =
117 : (int32_t)src_row0[sx] + (int32_t)src_row0[sx + 1] + (int32_t)src_row1[sx] + (int32_t)src_row1[sx + 1];
118 : dst[x] = (uint8_t)(sum_scalar >> 2);
119 : }
120 : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_WASM)
121 : v128_t data0, data1, row0_even, row0_odd, row1_even, row1_odd, sum, avg, mask = wasm_i16x8_splat(0x00FF);
122 : size_t x, simd_count = dst_width & ~15ULL;
123 :
124 : for (x = 0; x < simd_count; x += 16) {
125 : data0 = wasm_v128_load(src_row0 + x * 2);
126 : data1 = wasm_v128_load(src_row1 + x * 2);
127 :
128 : row0_even = wasm_v128_and(data0, mask);
129 : row0_odd = wasm_u16x8_shr(data0, 8);
130 : row1_even = wasm_v128_and(data1, mask);
131 : row1_odd = wasm_u16x8_shr(data1, 8);
132 :
133 : sum = wasm_i16x8_add(row0_even, row0_odd);
134 : sum = wasm_i16x8_add(sum, row1_even);
135 : sum = wasm_i16x8_add(sum, row1_odd);
136 :
137 : avg = wasm_u16x8_shr(sum, 2);
138 :
139 : data0 = wasm_v128_load(src_row0 + x * 2 + 16);
140 : data1 = wasm_v128_load(src_row1 + x * 2 + 16);
141 :
142 : row0_even = wasm_v128_and(data0, mask);
143 : row0_odd = wasm_u16x8_shr(data0, 8);
144 : row1_even = wasm_v128_and(data1, mask);
145 : row1_odd = wasm_u16x8_shr(data1, 8);
146 :
147 : sum = wasm_i16x8_add(row0_even, row0_odd);
148 : sum = wasm_i16x8_add(sum, row1_even);
149 : sum = wasm_i16x8_add(sum, row1_odd);
150 : sum = wasm_u16x8_shr(sum, 2);
151 :
152 : wasm_v128_store(dst + x, wasm_u8x16_narrow_i16x8(avg, sum));
153 : }
154 :
155 : {
156 : size_t sx;
157 : int32_t sum_scalar;
158 : for (x = simd_count; x < dst_width; x++) {
159 : sx = x * 2;
160 : sum_scalar =
161 : (int32_t)src_row0[sx] + (int32_t)src_row0[sx + 1] + (int32_t)src_row1[sx] + (int32_t)src_row1[sx + 1];
162 : dst[x] = (uint8_t)(sum_scalar >> 2);
163 : }
164 : }
165 : #else
166 : size_t x, sx;
167 : int32_t sum_scalar;
168 :
169 : for (x = 0; x < dst_width; x++) {
170 : sx = x * 2;
171 : sum_scalar =
172 : (int32_t)src_row0[sx] + (int32_t)src_row0[sx + 1] + (int32_t)src_row1[sx] + (int32_t)src_row1[sx + 1];
173 : dst[x] = (uint8_t)(sum_scalar >> 2);
174 : }
175 : #endif
176 : }
177 :
178 1 : static inline void lierre_find_minmax(const uint8_t *image, size_t total, uint8_t *out_min, uint8_t *out_max)
179 : {
180 : #if LIERRE_USE_SIMD && defined(LIERRE_SIMD_AVX2)
181 1 : __m256i min_vec = _mm256_set1_epi8((char)255), max_vec = _mm256_setzero_si256(), data;
182 : __m128i min128, max128;
183 : uint8_t min_arr[32], max_arr[32], min_val, max_val;
184 1 : size_t i, simd_count = total & ~31ULL;
185 :
186 1251 : for (i = 0; i < simd_count; i += 32) {
187 2500 : data = _mm256_loadu_si256((const __m256i *)(image + i));
188 1250 : min_vec = _mm256_min_epu8(min_vec, data);
189 1250 : max_vec = _mm256_max_epu8(max_vec, data);
190 : }
191 :
192 2 : min128 = _mm_min_epu8(_mm256_castsi256_si128(min_vec), _mm256_extracti128_si256(min_vec, 1));
193 3 : max128 = _mm_max_epu8(_mm256_castsi256_si128(max_vec), _mm256_extracti128_si256(max_vec, 1));
194 :
195 : _mm_storeu_si128((__m128i *)min_arr, min128);
196 : _mm_storeu_si128((__m128i *)max_arr, max128);
197 :
198 1 : min_val = 255;
199 1 : max_val = 0;
200 17 : for (i = 0; i < 16; i++) {
201 16 : if (min_arr[i] < min_val) {
202 1 : min_val = min_arr[i];
203 : }
204 16 : if (max_arr[i] > max_val) {
205 1 : max_val = max_arr[i];
206 : }
207 : }
208 :
209 1 : for (i = simd_count; i < total; i++) {
210 0 : if (image[i] < min_val) {
211 0 : min_val = image[i];
212 : }
213 0 : if (image[i] > max_val) {
214 0 : max_val = image[i];
215 : }
216 : }
217 :
218 1 : *out_min = min_val;
219 1 : *out_max = max_val;
220 : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_NEON)
221 : uint8x16_t min_vec, max_vec, data;
222 : uint8_t min_val, max_val;
223 : size_t i, simd_count;
224 :
225 : min_vec = vdupq_n_u8(255);
226 : max_vec = vdupq_n_u8(0);
227 :
228 : simd_count = total & ~15ULL;
229 :
230 : for (i = 0; i < simd_count; i += 16) {
231 : data = vld1q_u8(image + i);
232 : min_vec = vminq_u8(min_vec, data);
233 : max_vec = vmaxq_u8(max_vec, data);
234 : }
235 :
236 : min_val = vminvq_u8(min_vec);
237 : max_val = vmaxvq_u8(max_vec);
238 :
239 : for (i = simd_count; i < total; i++) {
240 : if (image[i] < min_val) {
241 : min_val = image[i];
242 : }
243 : if (image[i] > max_val) {
244 : max_val = image[i];
245 : }
246 : }
247 :
248 : *out_min = min_val;
249 : *out_max = max_val;
250 : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_WASM)
251 : v128_t min_vec, max_vec, data;
252 : uint8_t min_arr[16], max_arr[16], min_val, max_val;
253 : size_t i, simd_count;
254 :
255 : min_vec = wasm_u8x16_splat(255);
256 : max_vec = wasm_u8x16_splat(0);
257 :
258 : simd_count = total & ~15ULL;
259 :
260 : for (i = 0; i < simd_count; i += 16) {
261 : data = wasm_v128_load(image + i);
262 : min_vec = wasm_u8x16_min(min_vec, data);
263 : max_vec = wasm_u8x16_max(max_vec, data);
264 : }
265 :
266 : wasm_v128_store(min_arr, min_vec);
267 : wasm_v128_store(max_arr, max_vec);
268 :
269 : min_val = 255;
270 : max_val = 0;
271 : for (i = 0; i < 16; i++) {
272 : if (min_arr[i] < min_val) {
273 : min_val = min_arr[i];
274 : }
275 : if (max_arr[i] > max_val) {
276 : max_val = max_arr[i];
277 : }
278 : }
279 :
280 : for (i = simd_count; i < total; i++) {
281 : if (image[i] < min_val) {
282 : min_val = image[i];
283 : }
284 : if (image[i] > max_val) {
285 : max_val = image[i];
286 : }
287 : }
288 :
289 : *out_min = min_val;
290 : *out_max = max_val;
291 : #else
292 : uint8_t min_val, max_val;
293 : size_t i;
294 :
295 : min_val = 255;
296 : max_val = 0;
297 :
298 : for (i = 0; i < total; i++) {
299 : if (image[i] < min_val) {
300 : min_val = image[i];
301 : }
302 : if (image[i] > max_val) {
303 : max_val = image[i];
304 : }
305 : }
306 :
307 : *out_min = min_val;
308 : *out_max = max_val;
309 : #endif
310 1 : }
311 :
312 1 : static inline int64_t lierre_sum(const uint8_t *image, size_t total)
313 : {
314 : #if LIERRE_USE_SIMD && defined(LIERRE_SIMD_AVX2)
315 : __m256i sum_vec, data, zero;
316 : int64_t sum;
317 : uint64_t partial[4];
318 : size_t i, simd_count;
319 :
320 1 : sum_vec = _mm256_setzero_si256();
321 1 : zero = _mm256_setzero_si256();
322 :
323 1 : simd_count = total & ~31ULL;
324 :
325 1251 : for (i = 0; i < simd_count; i += 32) {
326 2500 : data = _mm256_loadu_si256((const __m256i *)(image + i));
327 2500 : sum_vec = _mm256_add_epi64(sum_vec, _mm256_sad_epu8(data, zero));
328 : }
329 :
330 : _mm256_storeu_si256((__m256i *)partial, sum_vec);
331 1 : sum = (int64_t)(partial[0] + partial[1] + partial[2] + partial[3]);
332 :
333 1 : for (i = simd_count; i < total; i++) {
334 0 : sum += image[i];
335 : }
336 :
337 1 : return sum;
338 : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_NEON)
339 : uint32x4_t sum_vec;
340 : uint8x16_t data;
341 : uint16x8_t sum16;
342 : int64_t sum;
343 : size_t i, simd_count;
344 :
345 : sum_vec = vdupq_n_u32(0);
346 :
347 : simd_count = total & ~15ULL;
348 :
349 : for (i = 0; i < simd_count; i += 16) {
350 : data = vld1q_u8(image + i);
351 : sum16 = vpaddlq_u8(data);
352 : sum_vec = vpadalq_u16(sum_vec, sum16);
353 : }
354 :
355 : sum = (int64_t)(vgetq_lane_u32(sum_vec, 0) + vgetq_lane_u32(sum_vec, 1) + vgetq_lane_u32(sum_vec, 2) +
356 : vgetq_lane_u32(sum_vec, 3));
357 :
358 : for (i = simd_count; i < total; i++) {
359 : sum += image[i];
360 : }
361 :
362 : return sum;
363 : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_WASM)
364 : v128_t sum_vec = wasm_i64x2_splat(0), data, lo, hi, sum16, lo32, hi32, sum32, lo64, hi64;
365 : int64_t sum;
366 : uint64_t partial[2];
367 : size_t i, simd_count = total & ~15ULL;
368 :
369 : for (i = 0; i < simd_count; i += 16) {
370 : data = wasm_v128_load(image + i);
371 : lo = wasm_u16x8_extend_low_u8x16(data);
372 : hi = wasm_u16x8_extend_high_u8x16(data);
373 : sum16 = wasm_i16x8_add(lo, hi);
374 : lo32 = wasm_u32x4_extend_low_u16x8(sum16);
375 : hi32 = wasm_u32x4_extend_high_u16x8(sum16);
376 : sum32 = wasm_i32x4_add(lo32, hi32);
377 : lo64 = wasm_u64x2_extend_low_u32x4(sum32);
378 : hi64 = wasm_u64x2_extend_high_u32x4(sum32);
379 : sum_vec = wasm_i64x2_add(sum_vec, wasm_i64x2_add(lo64, hi64));
380 : }
381 :
382 : wasm_v128_store(partial, sum_vec);
383 : sum = (int64_t)(partial[0] + partial[1]);
384 :
385 : for (i = simd_count; i < total; i++) {
386 : sum += image[i];
387 : }
388 :
389 : return sum;
390 : #else
391 : int64_t sum;
392 : size_t i;
393 :
394 : sum = 0;
395 : for (i = 0; i < total; i++) {
396 : sum += image[i];
397 : }
398 :
399 : return sum;
400 : #endif
401 : }
402 :
403 : static inline void *minimize_thread(void *arg)
404 : {
405 : lierre_image_mt_minimize_ctx_t *ctx;
406 : size_t y, src_y;
407 :
408 : ctx = (lierre_image_mt_minimize_ctx_t *)arg;
409 :
410 : for (y = ctx->start_row; y < ctx->end_row; y++) {
411 : src_y = y * 2;
412 : lierre_minimize_row(ctx->src + src_y * ctx->src_width, ctx->src + (src_y + 1) * ctx->src_width,
413 : ctx->dst + y * ctx->dst_width, ctx->dst_width);
414 : }
415 :
416 : return NULL;
417 : }
418 :
419 : static inline uint8_t *apply_minimize_once(const uint8_t *image, size_t width, size_t height, size_t *out_width,
420 : size_t *out_height)
421 : {
422 : uint8_t *result;
423 : size_t new_width, new_height, y, src_y;
424 :
425 : new_width = width >> 1;
426 : new_height = height >> 1;
427 :
428 : if (new_width < 21 || new_height < 21) {
429 : *out_width = width;
430 : *out_height = height;
431 : return NULL;
432 : }
433 :
434 : result = lmalloc(new_width * new_height);
435 : if (!result) {
436 : return NULL;
437 : }
438 :
439 : for (y = 0; y < new_height; y++) {
440 : src_y = y * 2;
441 : lierre_minimize_row(image + src_y * width, image + (src_y + 1) * width, result + y * new_width, new_width);
442 : }
443 :
444 : *out_width = new_width;
445 : *out_height = new_height;
446 :
447 : return result;
448 : }
449 :
450 : static inline uint8_t *apply_minimize_once_mt(const uint8_t *image, size_t width, size_t height, size_t *out_width,
451 : size_t *out_height, uint32_t num_threads)
452 : {
453 : lierre_thread_t *threads;
454 : lierre_image_mt_minimize_ctx_t *contexts;
455 : uint32_t i;
456 : uint8_t *result;
457 : size_t new_width, new_height, rows_per_thread;
458 :
459 : new_width = width >> 1;
460 : new_height = height >> 1;
461 :
462 : if (new_width < 21 || new_height < 21) {
463 : *out_width = width;
464 : *out_height = height;
465 : return NULL;
466 : }
467 :
468 : if (num_threads > LIERRE_IMAGE_MT_MAX_THREADS) {
469 : num_threads = LIERRE_IMAGE_MT_MAX_THREADS;
470 : }
471 : if (num_threads > new_height) {
472 : num_threads = (uint32_t)new_height;
473 : }
474 : if (num_threads < 1) {
475 : num_threads = 1;
476 : }
477 :
478 : result = lmalloc(new_width * new_height);
479 : if (!result) {
480 : return NULL;
481 : }
482 :
483 : threads = lmalloc(sizeof(lierre_thread_t) * num_threads);
484 : contexts = lmalloc(sizeof(lierre_image_mt_minimize_ctx_t) * num_threads);
485 : if (!threads || !contexts) {
486 : lfree(result);
487 : lfree(threads);
488 : lfree(contexts);
489 : return NULL;
490 : }
491 :
492 : rows_per_thread = new_height / num_threads;
493 :
494 : for (i = 0; i < num_threads; i++) {
495 : contexts[i].src = image;
496 : contexts[i].dst = result;
497 : contexts[i].src_width = width;
498 : contexts[i].dst_width = new_width;
499 : contexts[i].start_row = i * rows_per_thread;
500 : contexts[i].end_row = (i == num_threads - 1) ? new_height : (i + 1) * rows_per_thread;
501 : lierre_thread_create(&threads[i], minimize_thread, &contexts[i]);
502 : }
503 :
504 : for (i = 0; i < num_threads; i++) {
505 : lierre_thread_join(threads[i], NULL);
506 : }
507 :
508 : lfree(threads);
509 : lfree(contexts);
510 :
511 : *out_width = new_width;
512 : *out_height = new_height;
513 :
514 : return result;
515 : }
516 :
517 : static inline uint8_t *lierre_image_minimize(const uint8_t *image, size_t width, size_t height, size_t *out_width,
518 : size_t *out_height, bool use_mt, uint32_t num_threads)
519 : {
520 : uint32_t iter;
521 : uint8_t *current, *next;
522 : size_t cur_width, cur_height, new_width, new_height;
523 :
524 : current = lmalloc(width * height);
525 : if (!current) {
526 : return NULL;
527 : }
528 :
529 : lmemcpy(current, image, width * height);
530 : cur_width = width;
531 : cur_height = height;
532 :
533 : for (iter = 0; iter < LIERRE_IMAGE_MINIMIZE_MAX_ITERATIONS; iter++) {
534 : if (use_mt) {
535 : next = apply_minimize_once_mt(current, cur_width, cur_height, &new_width, &new_height, num_threads);
536 : } else {
537 : next = apply_minimize_once(current, cur_width, cur_height, &new_width, &new_height);
538 : }
539 :
540 : if (!next) {
541 : break;
542 : }
543 :
544 : lfree(current);
545 : current = next;
546 : cur_width = new_width;
547 : cur_height = new_height;
548 : }
549 :
550 : *out_width = cur_width;
551 : *out_height = cur_height;
552 :
553 : return current;
554 : }
555 :
556 8 : static inline void *denoise_filter_thread(void *arg)
557 : {
558 : lierre_image_mt_filter_ctx_t *ctx;
559 : int32_t sum;
560 : size_t x, y, i, j, idx;
561 :
562 8 : ctx = (lierre_image_mt_filter_ctx_t *)arg;
563 :
564 1288 : for (y = ctx->start_row; y < ctx->end_row; y++) {
565 1280 : if (y == 0 || y >= ctx->height - 1) {
566 4 : continue;
567 : }
568 1533505 : for (x = 1; x < ctx->width - 1; x++) {
569 1532229 : sum = 0;
570 4702457 : for (j = y - 1; j <= y + 1; j++) {
571 8237723 : for (i = x - 1; i <= x + 1; i++) {
572 5067495 : sum += ctx->temp[j * ctx->width + i];
573 : }
574 : }
575 1532229 : idx = y * ctx->width + x;
576 1532229 : ctx->image[idx] = (uint8_t)(sum / LIERRE_FILTER_KERNEL_ELEMS);
577 : }
578 : }
579 :
580 8 : return NULL;
581 : }
582 :
583 8 : static inline void *sharpening_filter_thread(void *arg)
584 : {
585 : lierre_image_mt_filter_ctx_t *ctx;
586 : int32_t val;
587 : size_t x, y, idx;
588 :
589 8 : ctx = (lierre_image_mt_filter_ctx_t *)arg;
590 :
591 1286 : for (y = ctx->start_row; y < ctx->end_row; y++) {
592 1278 : if (y == 0 || y >= ctx->height - 1) {
593 4 : continue;
594 : }
595 536965 : for (x = 1; x < ctx->width - 1; x++) {
596 535691 : idx = y * ctx->width + x;
597 535691 : val = LIERRE_SHARPEN_CENTER_COEF * (int32_t)ctx->temp[idx] - (int32_t)ctx->temp[(y - 1) * ctx->width + x] -
598 535691 : (int32_t)ctx->temp[(y + 1) * ctx->width + x] - (int32_t)ctx->temp[y * ctx->width + (x - 1)] -
599 535691 : (int32_t)ctx->temp[y * ctx->width + (x + 1)];
600 535691 : if (val < LIERRE_PIXEL_VALUE_MIN) {
601 17554 : val = LIERRE_PIXEL_VALUE_MIN;
602 : }
603 535691 : if (val > LIERRE_PIXEL_VALUE_MAX) {
604 43304 : val = LIERRE_PIXEL_VALUE_MAX;
605 : }
606 535691 : ctx->image[idx] = (uint8_t)val;
607 : }
608 : }
609 :
610 8 : return NULL;
611 : }
612 :
613 1 : extern void image_brightness_normalize(uint8_t *image, size_t width, size_t height)
614 : {
615 : size_t i, total;
616 : int32_t range;
617 : uint8_t min_val, max_val;
618 :
619 1 : total = width * height;
620 1 : if (total == 0) {
621 1 : return;
622 : }
623 :
624 1 : lierre_find_minmax(image, total, &min_val, &max_val);
625 :
626 1 : range = (int32_t)max_val - (int32_t)min_val;
627 1 : if (range <= 0) {
628 1 : return;
629 : }
630 :
631 0 : for (i = 0; i < total; i++) {
632 0 : image[i] = (uint8_t)(((int32_t)(image[i] - min_val) * LIERRE_PIXEL_VALUE_MAX) / range);
633 : }
634 : }
635 :
636 1 : extern void image_contrast_normalize(uint8_t *image, size_t width, size_t height)
637 : {
638 : size_t i, total;
639 : int64_t sum64;
640 : int32_t mean, val, new_val, factor;
641 :
642 1 : total = width * height;
643 1 : if (total == 0) {
644 0 : return;
645 : }
646 :
647 1 : sum64 = lierre_sum(image, total);
648 :
649 1 : mean = (int32_t)(sum64 / (int64_t)total);
650 1 : factor = LIERRE_CONTRAST_FACTOR;
651 :
652 40001 : for (i = 0; i < total; i++) {
653 40000 : val = (int32_t)image[i];
654 40000 : new_val = mean + ((val - mean) * factor) / LIERRE_CONTRAST_DIVISOR;
655 :
656 40000 : if (new_val < LIERRE_PIXEL_VALUE_MIN) {
657 0 : new_val = LIERRE_PIXEL_VALUE_MIN;
658 : }
659 :
660 40000 : if (new_val > LIERRE_PIXEL_VALUE_MAX) {
661 0 : new_val = LIERRE_PIXEL_VALUE_MAX;
662 : }
663 :
664 40000 : image[i] = (uint8_t)new_val;
665 : }
666 : }
667 :
668 2 : extern void image_denoise_mt(uint8_t *image, size_t width, size_t height, uint32_t num_threads)
669 : {
670 : lierre_thread_t *threads;
671 : lierre_image_mt_filter_ctx_t *contexts;
672 : uint32_t i;
673 : uint8_t *temp;
674 : size_t rows_per_thread;
675 :
676 2 : if (width < 3 || height < 3) {
677 0 : return;
678 : }
679 :
680 2 : if (num_threads > LIERRE_IMAGE_MT_MAX_THREADS) {
681 0 : num_threads = LIERRE_IMAGE_MT_MAX_THREADS;
682 : }
683 2 : if (num_threads > height) {
684 0 : num_threads = (uint32_t)height;
685 : }
686 2 : if (num_threads < 1) {
687 0 : num_threads = 1;
688 : }
689 :
690 2 : temp = lmalloc(width * height);
691 2 : if (!temp) {
692 0 : return;
693 : }
694 :
695 2 : threads = lmalloc(sizeof(lierre_thread_t) * num_threads);
696 2 : contexts = lmalloc(sizeof(lierre_image_mt_filter_ctx_t) * num_threads);
697 2 : if (!threads || !contexts) {
698 0 : lfree(temp);
699 0 : lfree(threads);
700 0 : lfree(contexts);
701 0 : return;
702 : }
703 :
704 2 : lmemcpy(temp, image, width * height);
705 :
706 2 : rows_per_thread = height / num_threads;
707 :
708 10 : for (i = 0; i < num_threads; i++) {
709 8 : contexts[i].image = image;
710 8 : contexts[i].temp = temp;
711 8 : contexts[i].width = width;
712 8 : contexts[i].height = height;
713 8 : contexts[i].start_row = i * rows_per_thread;
714 8 : contexts[i].end_row = (i == num_threads - 1) ? height : (i + 1) * rows_per_thread;
715 8 : lierre_thread_create(&threads[i], denoise_filter_thread, &contexts[i]);
716 : }
717 :
718 10 : for (i = 0; i < num_threads; i++) {
719 8 : lierre_thread_join(threads[i], NULL);
720 : }
721 :
722 2 : lfree(threads);
723 2 : lfree(contexts);
724 2 : lfree(temp);
725 : }
726 :
727 2 : extern void image_denoise(uint8_t *image, size_t width, size_t height)
728 : {
729 : int32_t sum;
730 : uint8_t *temp;
731 : size_t x, y, i, j, idx;
732 :
733 2 : if (width < 3 || height < 3) {
734 0 : return;
735 : }
736 :
737 2 : temp = lmalloc(width * height);
738 2 : if (!temp) {
739 0 : return;
740 : }
741 :
742 2 : lmemcpy(temp, image, width * height);
743 :
744 798 : for (y = 1; y < height - 1; y++) {
745 517204 : for (x = 1; x < width - 1; x++) {
746 516408 : sum = 0;
747 2065632 : for (j = y - 1; j <= y + 1; j++) {
748 6196896 : for (i = x - 1; i <= x + 1; i++) {
749 4647672 : sum += temp[j * width + i];
750 : }
751 : }
752 516408 : idx = y * width + x;
753 516408 : image[idx] = (uint8_t)(sum / LIERRE_FILTER_KERNEL_ELEMS);
754 : }
755 : }
756 :
757 2 : lfree(temp);
758 : }
759 :
760 2 : extern void image_sharpen_mt(uint8_t *image, size_t width, size_t height, uint32_t num_threads)
761 : {
762 : lierre_thread_t *threads;
763 : lierre_image_mt_filter_ctx_t *contexts;
764 : uint32_t i;
765 : uint8_t *temp;
766 : size_t rows_per_thread;
767 :
768 2 : if (width < 3 || height < 3) {
769 0 : return;
770 : }
771 :
772 2 : if (num_threads > LIERRE_IMAGE_MT_MAX_THREADS) {
773 0 : num_threads = LIERRE_IMAGE_MT_MAX_THREADS;
774 : }
775 2 : if (num_threads > height) {
776 0 : num_threads = (uint32_t)height;
777 : }
778 2 : if (num_threads < 1) {
779 0 : num_threads = 1;
780 : }
781 :
782 2 : temp = lmalloc(width * height);
783 2 : if (!temp) {
784 0 : return;
785 : }
786 :
787 2 : threads = lmalloc(sizeof(lierre_thread_t) * num_threads);
788 2 : contexts = lmalloc(sizeof(lierre_image_mt_filter_ctx_t) * num_threads);
789 2 : if (!threads || !contexts) {
790 0 : lfree(temp);
791 0 : lfree(threads);
792 0 : lfree(contexts);
793 0 : return;
794 : }
795 :
796 2 : lmemcpy(temp, image, width * height);
797 :
798 2 : rows_per_thread = height / num_threads;
799 :
800 10 : for (i = 0; i < num_threads; i++) {
801 8 : contexts[i].image = image;
802 8 : contexts[i].temp = temp;
803 8 : contexts[i].width = width;
804 8 : contexts[i].height = height;
805 8 : contexts[i].start_row = i * rows_per_thread;
806 8 : contexts[i].end_row = (i == num_threads - 1) ? height : (i + 1) * rows_per_thread;
807 8 : lierre_thread_create(&threads[i], sharpening_filter_thread, &contexts[i]);
808 : }
809 :
810 10 : for (i = 0; i < num_threads; i++) {
811 8 : lierre_thread_join(threads[i], NULL);
812 : }
813 :
814 2 : lfree(threads);
815 2 : lfree(contexts);
816 2 : lfree(temp);
817 : }
818 :
819 1 : extern void image_sharpen(uint8_t *image, size_t width, size_t height)
820 : {
821 : int32_t val;
822 : uint8_t *temp;
823 : size_t x, y, idx;
824 :
825 1 : if (width < 3 || height < 3) {
826 0 : return;
827 : }
828 :
829 1 : temp = lmalloc(width * height);
830 1 : if (!temp) {
831 0 : return;
832 : }
833 :
834 1 : lmemcpy(temp, image, width * height);
835 :
836 199 : for (y = 1; y < height - 1; y++) {
837 39402 : for (x = 1; x < width - 1; x++) {
838 39204 : idx = y * width + x;
839 39204 : val = LIERRE_SHARPEN_CENTER_COEF * (int32_t)temp[idx] - (int32_t)temp[(y - 1) * width + x] -
840 39204 : (int32_t)temp[(y + 1) * width + x] - (int32_t)temp[y * width + (x - 1)] -
841 39204 : (int32_t)temp[y * width + (x + 1)];
842 39204 : if (val < LIERRE_PIXEL_VALUE_MIN) {
843 0 : val = LIERRE_PIXEL_VALUE_MIN;
844 : }
845 39204 : if (val > LIERRE_PIXEL_VALUE_MAX) {
846 0 : val = LIERRE_PIXEL_VALUE_MAX;
847 : }
848 39204 : image[idx] = (uint8_t)val;
849 : }
850 : }
851 :
852 1 : lfree(temp);
853 : }
|