LCOV - code coverage report
Current view: top level - src/decode - decoder.c (source / functions) Coverage Total Hit
Test: lierre Coverage Report Lines: 89.6 % 182 163
Test Date: 2026-03-06 08:40:14 Functions: 100.0 % 8 8
Legend: Lines: hit not hit

            Line data    Source code
       1              : /*
       2              :  * liblierre - decoder.c
       3              :  *
       4              :  * This file is part of liblierre.
       5              :  *
       6              :  * Author: Go Kudo <zeriyoshi@gmail.com>
       7              :  * SPDX-License-Identifier: MIT AND ISC
       8              :  *
       9              :  * This file contains code derived from quirc (https://github.com/dlbeer/quirc).
      10              :  * Copyright (C) 2010-2012 Daniel Beer <dlbeer@gmail.com>
      11              :  * Licensed under the ISC License.
      12              :  */
      13              : 
      14              : #include "../internal/decoder.h"
      15              : 
      16              : #if LIERRE_USE_SIMD
      17              : #include "../internal/simd.h"
      18              : #endif
      19              : 
      20              : #define LIERRE_HISTOGRAM_SIZE 256
      21              : 
      22          121 : static inline uint8_t compute_otsu_threshold(const decoder_t *decoder)
      23              : {
      24              :     const uint8_t *image_ptr;
      25              :     uint64_t total_sum_int;
      26          121 :     uint32_t histogram[LIERRE_HISTOGRAM_SIZE] = {0}, total_pixels, foreground_count, background_count,
      27              :              optimal_threshold, i;
      28              :     uint8_t pixel_value;
      29              :     size_t remaining;
      30              :     double total_sum, foreground_sum, foreground_mean, background_mean, between_class_variance, max_variance;
      31              : 
      32          121 :     total_pixels = (uint32_t)(decoder->w * decoder->h);
      33              : 
      34          121 :     image_ptr = decoder->image;
      35          121 :     remaining = (size_t)total_pixels;
      36          121 :     total_sum_int = 0;
      37    123405702 :     while (remaining--) {
      38    123405581 :         pixel_value = *image_ptr++;
      39    123405581 :         histogram[pixel_value]++;
      40    123405581 :         total_sum_int += pixel_value;
      41              :     }
      42          121 :     total_sum = (double)total_sum_int;
      43              : 
      44          121 :     foreground_sum = 0.0;
      45          121 :     foreground_count = 0;
      46          121 :     max_variance = 0.0;
      47          121 :     optimal_threshold = 0;
      48              : 
      49        29921 :     for (i = 0; i < LIERRE_HISTOGRAM_SIZE; i++) {
      50        29921 :         foreground_count += histogram[i];
      51        29921 :         if (foreground_count == 0) {
      52         1583 :             continue;
      53              :         }
      54              : 
      55        28338 :         background_count = total_pixels - foreground_count;
      56        28338 :         if (background_count == 0) {
      57          121 :             break;
      58              :         }
      59              : 
      60        28217 :         foreground_sum += (double)i * histogram[i];
      61        28217 :         foreground_mean = foreground_sum / foreground_count;
      62        28217 :         background_mean = (total_sum - foreground_sum) / background_count;
      63        28217 :         between_class_variance = (foreground_mean - background_mean) * (foreground_mean - background_mean) *
      64        28217 :                                  foreground_count * background_count;
      65              : 
      66        28217 :         if (between_class_variance >= max_variance) {
      67        26895 :             optimal_threshold = i;
      68        26895 :             max_variance = between_class_variance;
      69              :         }
      70              :     }
      71              : 
      72          121 :     return (uint8_t)optimal_threshold;
      73              : }
      74              : 
      75          121 : static inline void binarize_image(decoder_t *decoder, uint8_t threshold)
      76              : {
      77              :     int32_t x, y;
      78              :     uint8_t *row, pixel_value;
      79              :     size_t total;
      80              : 
      81          121 :     total = (size_t)decoder->w * (size_t)decoder->h;
      82              : 
      83              : #if LIERRE_USE_SIMD && defined(LIERRE_SIMD_AVX2)
      84              :     {
      85              :         __m256i thresh_vec, data;
      86              :         __m256i black_lo, white_lo, result_lo;
      87              :         __m128i data128;
      88              :         size_t i, simd_count;
      89              : 
      90          121 :         simd_count = total & ~15ULL;
      91              : 
      92      7712950 :         for (i = 0; i < simd_count; i += 16) {
      93     15425658 :             data128 = _mm_loadu_si128((const __m128i *)(decoder->image + i));
      94      7712829 :             data = _mm256_cvtepu8_epi16(data128);
      95              : 
      96     15425658 :             thresh_vec = _mm256_set1_epi16((int16_t)threshold);
      97      7712829 :             black_lo = _mm256_set1_epi16((int16_t)LIERRE_PIXEL_BLACK);
      98      7712829 :             white_lo = _mm256_set1_epi16((int16_t)LIERRE_PIXEL_WHITE);
      99              : 
     100      7712829 :             result_lo = _mm256_blendv_epi8(white_lo, black_lo, _mm256_cmpgt_epi16(thresh_vec, data));
     101              : 
     102      7712829 :             _mm256_storeu_si256((__m256i *)(decoder->pixels + i), result_lo);
     103              :         }
     104              : 
     105          438 :         for (i = simd_count; i < total; i++) {
     106          317 :             decoder->pixels[i] = (decoder->image[i] < threshold) ? LIERRE_PIXEL_BLACK : LIERRE_PIXEL_WHITE;
     107              :         }
     108              :     }
     109              : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_NEON)
     110              :     {
     111              :         uint8x8_t data8, thresh_vec8;
     112              :         uint16x8_t data16, mask16, black_vec, white_vec, result;
     113              :         size_t i, simd_count;
     114              : 
     115              :         black_vec = vdupq_n_u16(LIERRE_PIXEL_BLACK);
     116              :         white_vec = vdupq_n_u16(LIERRE_PIXEL_WHITE);
     117              : 
     118              :         simd_count = total & ~7ULL;
     119              : 
     120              :         for (i = 0; i < simd_count; i += 8) {
     121              :             data8 = vld1_u8(decoder->image + i);
     122              :             data16 = vmovl_u8(data8);
     123              :             mask16 = vcltq_u16(data16, vdupq_n_u16(threshold));
     124              :             result = vbslq_u16(mask16, black_vec, white_vec);
     125              :             vst1q_u16(decoder->pixels + i, result);
     126              :         }
     127              : 
     128              :         for (i = simd_count; i < total; i++) {
     129              :             decoder->pixels[i] = (decoder->image[i] < threshold) ? LIERRE_PIXEL_BLACK : LIERRE_PIXEL_WHITE;
     130              :         }
     131              :     }
     132              : #elif LIERRE_USE_SIMD && defined(LIERRE_SIMD_WASM)
     133              :     {
     134              :         v128_t data8, data16_lo, data16_hi, thresh_vec, black_vec, white_vec, mask_lo, mask_hi, result_lo, result_hi;
     135              :         size_t i, simd_count;
     136              : 
     137              :         thresh_vec = wasm_u16x8_splat(threshold);
     138              :         black_vec = wasm_u16x8_splat(LIERRE_PIXEL_BLACK);
     139              :         white_vec = wasm_u16x8_splat(LIERRE_PIXEL_WHITE);
     140              : 
     141              :         simd_count = total & ~15ULL;
     142              : 
     143              :         for (i = 0; i < simd_count; i += 16) {
     144              :             data8 = wasm_v128_load(decoder->image + i);
     145              :             data16_lo = wasm_u16x8_extend_low_u8x16(data8);
     146              :             data16_hi = wasm_u16x8_extend_high_u8x16(data8);
     147              : 
     148              :             mask_lo = wasm_u16x8_lt(data16_lo, thresh_vec);
     149              :             mask_hi = wasm_u16x8_lt(data16_hi, thresh_vec);
     150              : 
     151              :             result_lo = wasm_v128_bitselect(black_vec, white_vec, mask_lo);
     152              :             result_hi = wasm_v128_bitselect(black_vec, white_vec, mask_hi);
     153              : 
     154              :             wasm_v128_store(decoder->pixels + i, result_lo);
     155              :             wasm_v128_store(decoder->pixels + i + 8, result_hi);
     156              :         }
     157              : 
     158              :         for (i = simd_count; i < total; i++) {
     159              :             decoder->pixels[i] = (decoder->image[i] < threshold) ? LIERRE_PIXEL_BLACK : LIERRE_PIXEL_WHITE;
     160              :         }
     161              :     }
     162              : #else
     163              :     for (y = 0; y < decoder->h; y++) {
     164              :         row = decoder->image + y * decoder->w;
     165              :         for (x = 0; x < decoder->w; x++) {
     166              :             pixel_value = row[x];
     167              :             decoder->pixels[y * decoder->w + x] = (pixel_value < threshold) ? LIERRE_PIXEL_BLACK : LIERRE_PIXEL_WHITE;
     168              :         }
     169              :     }
     170              : 
     171              :     (void)total;
     172              : #endif
     173          121 : }
     174              : 
     175          121 : static inline int32_t decoder_resize(decoder_t *decoder, int32_t width, int32_t height)
     176              : {
     177              :     lierre_pixel_t *pixels;
     178              :     flood_fill_vars_t *vars;
     179              :     uint8_t *image;
     180              :     size_t num_vars, vars_byte_size;
     181              : 
     182          121 :     image = NULL;
     183          121 :     pixels = NULL;
     184          121 :     vars = NULL;
     185              : 
     186          121 :     if (width < 0 || height < 0) {
     187            0 :         return -1;
     188              :     }
     189              : 
     190          121 :     image = lcalloc((size_t)width, (size_t)height);
     191          121 :     if (!image) {
     192            0 :         return -1;
     193              :     }
     194              : 
     195          121 :     pixels = lcalloc((size_t)width * (size_t)height, sizeof(lierre_pixel_t));
     196          121 :     if (!pixels) {
     197            0 :         lfree(image);
     198              : 
     199            0 :         return -1;
     200              :     }
     201              : 
     202          121 :     num_vars = (size_t)height * 2 / 3;
     203          121 :     if (num_vars == 0) {
     204            0 :         num_vars = 1;
     205              :     }
     206              : 
     207          121 :     vars_byte_size = sizeof(flood_fill_vars_t) * num_vars;
     208          121 :     vars = lmalloc(vars_byte_size);
     209          121 :     if (!vars) {
     210            0 :         lfree(image);
     211            0 :         lfree(pixels);
     212              : 
     213            0 :         return -1;
     214              :     }
     215              : 
     216          121 :     decoder->w = width;
     217          121 :     decoder->h = height;
     218              : 
     219          121 :     if (decoder->image) {
     220           12 :         lfree(decoder->image);
     221              :     }
     222              : 
     223          121 :     decoder->image = image;
     224              : 
     225          121 :     if (decoder->pixels) {
     226           12 :         lfree(decoder->pixels);
     227              :     }
     228              : 
     229          121 :     decoder->pixels = pixels;
     230              : 
     231          121 :     if (decoder->flood_fill_vars) {
     232           12 :         lfree(decoder->flood_fill_vars);
     233              :     }
     234              : 
     235          121 :     decoder->flood_fill_vars = vars;
     236          121 :     decoder->num_flood_fill_vars = num_vars;
     237              : 
     238          121 :     return 0;
     239              : }
     240              : 
     241           45 : static inline void *decode_qr_thread(void *arg)
     242              : {
     243           45 :     decode_thread_ctx_t *ctx = (decode_thread_ctx_t *)arg;
     244              : 
     245           45 :     extract_qr_code(ctx->decoder, ctx->grid_index, &ctx->code);
     246           45 :     ctx->err = decode_qr(&ctx->code, &ctx->data);
     247              : 
     248           45 :     return NULL;
     249              : }
     250              : 
     251          109 : extern decoder_t *lierre_decoder_create(void)
     252              : {
     253              :     decoder_t *decoder;
     254              : 
     255          109 :     decoder = lcalloc(1, sizeof(decoder_t));
     256          109 :     if (!decoder) {
     257            0 :         return NULL;
     258              :     }
     259              : 
     260          109 :     return decoder;
     261              : }
     262              : 
     263          109 : extern void lierre_decoder_destroy(decoder_t *decoder)
     264              : {
     265          109 :     if (!decoder) {
     266            0 :         return;
     267              :     }
     268              : 
     269          109 :     if (decoder->image) {
     270          109 :         lfree(decoder->image);
     271              :     }
     272              : 
     273          109 :     if (decoder->pixels) {
     274          109 :         lfree(decoder->pixels);
     275              :     }
     276              : 
     277          109 :     if (decoder->flood_fill_vars) {
     278          109 :         lfree(decoder->flood_fill_vars);
     279              :     }
     280              : 
     281          109 :     lfree(decoder);
     282              : }
     283              : 
     284           73 : extern lierre_error_t lierre_decoder_process(decoder_t *decoder, const uint8_t *gray_image, int32_t width,
     285              :                                              int32_t height, decoder_result_t *result)
     286              : {
     287              :     qr_code_t code;
     288              :     qr_data_t data;
     289              :     lierre_error_t err;
     290              :     uint8_t threshold;
     291              :     int32_t row, i;
     292              : 
     293           73 :     if (!decoder || !gray_image || !result || width <= 0 || height <= 0) {
     294            0 :         return LIERRE_ERROR_INVALID_PARAMS;
     295              :     }
     296              : 
     297           73 :     if (decoder_resize(decoder, width, height) < 0) {
     298            0 :         return LIERRE_ERROR_DATA_OVERFLOW;
     299              :     }
     300              : 
     301           73 :     lmemcpy(decoder->image, gray_image, (size_t)width * (size_t)height);
     302              : 
     303           73 :     decoder->num_regions = LIERRE_PIXEL_REGION;
     304           73 :     decoder->num_capstones = 0;
     305           73 :     decoder->num_grids = 0;
     306              : 
     307           73 :     threshold = compute_otsu_threshold(decoder);
     308           73 :     decoder->threshold = threshold;
     309           73 :     binarize_image(decoder, threshold);
     310              : 
     311        48069 :     for (row = 0; row < height; row++) {
     312        47996 :         scan_finder_patterns(decoder, (uint32_t)row);
     313              :     }
     314              : 
     315          291 :     for (i = 0; i < decoder->num_capstones; i++) {
     316          218 :         find_capstone_groups(decoder, i);
     317              :     }
     318              : 
     319           73 :     result->count = 0;
     320              : 
     321          157 :     for (i = 0; i < decoder->num_grids && result->count < LIERRE_DECODER_MAX_GRIDS; i++) {
     322           84 :         extract_qr_code(decoder, i, &code);
     323           84 :         err = decode_qr(&code, &data);
     324              : 
     325           84 :         if (err == LIERRE_ERROR_SUCCESS) {
     326           62 :             result->codes[result->count].corners[0] = code.corners[0];
     327           62 :             result->codes[result->count].corners[1] = code.corners[1];
     328           62 :             result->codes[result->count].corners[2] = code.corners[2];
     329           62 :             result->codes[result->count].corners[3] = code.corners[3];
     330           62 :             lmemcpy(result->codes[result->count].payload, data.payload, (size_t)data.payload_len);
     331           62 :             result->codes[result->count].payload_len = data.payload_len;
     332           62 :             result->count++;
     333              :         }
     334              :     }
     335              : 
     336           73 :     return LIERRE_ERROR_SUCCESS;
     337              : }
     338              : 
     339           48 : extern lierre_error_t lierre_decoder_process_mt(decoder_t *decoder, const uint8_t *gray_image, int32_t width,
     340              :                                                 int32_t height, decoder_result_t *result, uint32_t num_threads)
     341              : {
     342              :     lierre_thread_t *threads;
     343              :     decode_thread_ctx_t *contexts;
     344              :     uint32_t active_threads, t;
     345              :     uint8_t threshold;
     346              :     int32_t row, i;
     347              : 
     348           48 :     if (!decoder || !gray_image || !result || width <= 0 || height <= 0) {
     349            0 :         return LIERRE_ERROR_INVALID_PARAMS;
     350              :     }
     351              : 
     352           48 :     if (num_threads < 1) {
     353            0 :         num_threads = 1;
     354              :     }
     355           48 :     if (num_threads > LIERRE_DECODER_MT_MAX_THREADS) {
     356            0 :         num_threads = LIERRE_DECODER_MT_MAX_THREADS;
     357              :     }
     358              : 
     359           48 :     if (decoder_resize(decoder, width, height) < 0) {
     360            0 :         return LIERRE_ERROR_DATA_OVERFLOW;
     361              :     }
     362              : 
     363           48 :     lmemcpy(decoder->image, gray_image, (size_t)width * (size_t)height);
     364              : 
     365           48 :     decoder->num_regions = LIERRE_PIXEL_REGION;
     366           48 :     decoder->num_capstones = 0;
     367           48 :     decoder->num_grids = 0;
     368              : 
     369           48 :     threshold = compute_otsu_threshold(decoder);
     370           48 :     decoder->threshold = threshold;
     371           48 :     binarize_image(decoder, threshold);
     372              : 
     373        40993 :     for (row = 0; row < height; row++) {
     374        40945 :         scan_finder_patterns(decoder, (uint32_t)row);
     375              :     }
     376              : 
     377          196 :     for (i = 0; i < decoder->num_capstones; i++) {
     378          148 :         find_capstone_groups(decoder, i);
     379              :     }
     380              : 
     381           48 :     result->count = 0;
     382              : 
     383           48 :     if (decoder->num_grids == 0) {
     384            6 :         return LIERRE_ERROR_SUCCESS;
     385              :     }
     386              : 
     387           42 :     threads = lmalloc(sizeof(lierre_thread_t) * num_threads);
     388           42 :     contexts = lmalloc(sizeof(decode_thread_ctx_t) * decoder->num_grids);
     389           42 :     if (!threads || !contexts) {
     390            0 :         lfree(threads);
     391            0 :         lfree(contexts);
     392            0 :         return LIERRE_ERROR_DATA_OVERFLOW;
     393              :     }
     394              : 
     395           87 :     for (i = 0; i < decoder->num_grids; i++) {
     396           45 :         contexts[i].decoder = decoder;
     397           45 :         contexts[i].grid_index = i;
     398           45 :         contexts[i].err = LIERRE_ERROR_INVALID_PARAMS;
     399              :     }
     400              : 
     401           42 :     i = 0;
     402           84 :     while (i < decoder->num_grids) {
     403           42 :         active_threads = 0;
     404              : 
     405           87 :         for (t = 0; t < num_threads && i < decoder->num_grids; t++, i++) {
     406           45 :             lierre_thread_create(&threads[t], decode_qr_thread, &contexts[i]);
     407           45 :             active_threads++;
     408              :         }
     409              : 
     410           87 :         for (t = 0; t < active_threads; t++) {
     411           45 :             lierre_thread_join(threads[t], NULL);
     412              :         }
     413              :     }
     414              : 
     415           87 :     for (i = 0; i < decoder->num_grids && result->count < LIERRE_DECODER_MAX_GRIDS; i++) {
     416           45 :         if (contexts[i].err == LIERRE_ERROR_SUCCESS) {
     417           42 :             result->codes[result->count].corners[0] = contexts[i].code.corners[0];
     418           42 :             result->codes[result->count].corners[1] = contexts[i].code.corners[1];
     419           42 :             result->codes[result->count].corners[2] = contexts[i].code.corners[2];
     420           42 :             result->codes[result->count].corners[3] = contexts[i].code.corners[3];
     421           42 :             lmemcpy(result->codes[result->count].payload, contexts[i].data.payload,
     422              :                     (size_t)contexts[i].data.payload_len);
     423           42 :             result->codes[result->count].payload_len = contexts[i].data.payload_len;
     424           42 :             result->count++;
     425              :         }
     426              :     }
     427              : 
     428           42 :     lfree(threads);
     429           42 :     lfree(contexts);
     430              : 
     431           42 :     return LIERRE_ERROR_SUCCESS;
     432              : }
        

Generated by: LCOV version 2.0-1