LCOV - code coverage report
Current view: top level - src - ft_image.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 0.0 % 108 0
Test Date: 2025-10-15 21:43:52 Functions: 0.0 % 5 0

            Line data    Source code
       1              : /**
       2              :  * @file ft_image.c
       3              :  * @brief Image comparison functions using libpuzzle.
       4              :  * @ingroup ImageComparison
       5              :  */
       6              : /*
       7              :  * Copyright (C) 2007 François Pesce : francois.pesce (at) gmail (dot) com
       8              :  *
       9              :  * Licensed under the Apache License, Version 2.0 (the "License");
      10              :  * you may not use this file except in compliance with the License.
      11              :  * You may obtain a copy of the License at
      12              :  *
      13              :  *      http://www.apache.org/licenses/LICENSE-2.0
      14              :  *
      15              :  * Unless required by applicable law or agreed to in writing, software
      16              :  * distributed under the License is distributed on an "AS IS" BASIS,
      17              :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      18              :  * See the License for the specific language governing permissions and
      19              :  * limitations under the License.
      20              :  */
      21              : 
      22              : #include "ft_image.h"
      23              : 
      24              : #include <stdio.h>
      25              : #include <puzzle.h>
      26              : 
      27              : #include <apr_thread_mutex.h>
      28              : 
      29              : #include "config.h"
      30              : #include "debug.h"
      31              : #include "ft_config.h"
      32              : #include "ft_file.h"
      33              : #include "napr_threadpool.h"
      34              : #include "napr_heap.h"
      35              : 
      36              : static const int NB_WORKER = 4;
      37              : 
      38              : struct compute_vector_ctx_t
      39              : {
      40              :     apr_thread_mutex_t *mutex;
      41              :     PuzzleContext *contextp;
      42              :     ft_conf_t *conf;
      43              :     unsigned int heap_size;
      44              :     unsigned int nb_processed;
      45              : };
      46              : typedef struct compute_vector_ctx_t compute_vector_ctx_t;
      47              : 
      48              : struct compute_vector_task_t
      49              : {
      50              :     compute_vector_ctx_t *cv_ctx;
      51              :     ft_file_t *file;
      52              : };
      53              : typedef struct compute_vector_task_t compute_vector_task_t;
      54              : 
      55            0 : static apr_status_t compute_vector(void *ctx, void *data)
      56            0 : {
      57            0 :     char errbuf[ERROR_BUFFER_SIZE];
      58            0 :     compute_vector_ctx_t *cv_ctx = (compute_vector_ctx_t *) ctx;
      59            0 :     compute_vector_task_t *task = (compute_vector_task_t *) data;
      60            0 :     ft_file_t *file = task->file;
      61            0 :     apr_status_t status = APR_SUCCESS;
      62              : 
      63            0 :     memset(errbuf, 0, sizeof(errbuf));
      64            0 :     puzzle_init_cvec(cv_ctx->contextp, &(file->cvec));
      65            0 :     if (0 == puzzle_fill_cvec_from_file(cv_ctx->contextp, &(file->cvec), file->path)) {
      66            0 :         file->cvec_ok |= 0x1;
      67              :     }
      68              :     else {
      69            0 :         DEBUG_ERR("error calling puzzle_fill_cvec_from_file, ignoring file: %s", file->path);
      70              :     }
      71              : 
      72            0 :     status = apr_thread_mutex_lock(cv_ctx->mutex);
      73            0 :     if (APR_SUCCESS != status) {
      74            0 :         DEBUG_ERR("error calling apr_thread_mutex_lock: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
      75            0 :         return status;
      76              :     }
      77            0 :     if (is_option_set(cv_ctx->conf->mask, OPTION_VERBO)) {
      78            0 :         (void) fprintf(stderr, "\rProgress [%u/%u] %d%% ", cv_ctx->nb_processed, cv_ctx->heap_size,
      79            0 :                        (int) ((float) cv_ctx->nb_processed / (float) cv_ctx->heap_size * 100.0F));
      80              :     }
      81            0 :     cv_ctx->nb_processed += 1;
      82            0 :     status = apr_thread_mutex_unlock(cv_ctx->mutex);
      83            0 :     if (APR_SUCCESS != status) {
      84            0 :         DEBUG_ERR("error calling apr_thread_mutex_unlock: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
      85            0 :         return status;
      86              :     }
      87              : 
      88            0 :     return APR_SUCCESS;
      89              : }
      90              : 
      91              : static const int MAX_PUZZLE_WIDTH = 5000;
      92              : static const int MAX_PUZZLE_HEIGHT = 5000;
      93              : static const int PUZZLE_LAMBDAS = 13;
      94              : static const int FIX_FOR_CLUSTERING = 0;
      95              : static const int MAX_PERCENTAGE = 100;
      96              : 
      97              : static void initialize_puzzle_context(PuzzleContext * context);
      98              : static apr_status_t compute_image_vectors(ft_conf_t *conf, PuzzleContext * context);
      99              : static void compare_image_vectors(ft_conf_t *conf, PuzzleContext * context);
     100              : 
     101            0 : apr_status_t ft_image_twin_report(ft_conf_t *conf)
     102              : {
     103              :     PuzzleContext context;
     104            0 :     apr_status_t status = APR_SUCCESS;
     105              : 
     106            0 :     initialize_puzzle_context(&context);
     107              : 
     108            0 :     status = compute_image_vectors(conf, &context);
     109            0 :     if (status != APR_SUCCESS) {
     110            0 :         puzzle_free_context(&context);
     111            0 :         return status;
     112              :     }
     113              : 
     114            0 :     compare_image_vectors(conf, &context);
     115              : 
     116            0 :     puzzle_free_context(&context);
     117              : 
     118            0 :     return APR_SUCCESS;
     119              : }
     120              : 
     121            0 : static void initialize_puzzle_context(PuzzleContext * context)
     122              : {
     123            0 :     puzzle_init_context(context);
     124            0 :     puzzle_set_max_width(context, MAX_PUZZLE_WIDTH);
     125            0 :     puzzle_set_max_height(context, MAX_PUZZLE_HEIGHT);
     126            0 :     puzzle_set_lambdas(context, PUZZLE_LAMBDAS);
     127            0 : }
     128              : 
     129            0 : static apr_status_t compute_image_vectors(ft_conf_t *conf, PuzzleContext * context)
     130            0 : {
     131            0 :     char errbuf[ERROR_BUFFER_SIZE];
     132            0 :     apr_status_t status = APR_SUCCESS;
     133            0 :     napr_threadpool_t *threadpool = NULL;
     134              :     compute_vector_ctx_t cv_ctx;
     135            0 :     unsigned int heap_size = napr_heap_size(conf->heap);
     136              : 
     137            0 :     memset(errbuf, 0, sizeof(errbuf));
     138            0 :     cv_ctx.contextp = context;
     139            0 :     cv_ctx.heap_size = heap_size;
     140            0 :     cv_ctx.nb_processed = 0;
     141            0 :     cv_ctx.conf = conf;
     142              : 
     143            0 :     status = apr_thread_mutex_create(&cv_ctx.mutex, APR_THREAD_MUTEX_DEFAULT, conf->pool);
     144            0 :     if (APR_SUCCESS != status) {
     145            0 :         DEBUG_ERR("error calling apr_thread_mutex_create: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
     146            0 :         return status;
     147              :     }
     148              : 
     149            0 :     status = napr_threadpool_init(&threadpool, &cv_ctx, NB_WORKER, compute_vector, conf->pool);
     150            0 :     if (APR_SUCCESS != status) {
     151            0 :         DEBUG_ERR("error calling napr_threadpool_init: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
     152            0 :         return status;
     153              :     }
     154              : 
     155            0 :     for (unsigned int idx = 0; idx < heap_size; idx++) {
     156            0 :         compute_vector_task_t *task = apr_palloc(conf->pool, sizeof(compute_vector_task_t));
     157            0 :         task->cv_ctx = &cv_ctx;
     158            0 :         task->file = napr_heap_get_nth(conf->heap, idx);
     159            0 :         status = napr_threadpool_add(threadpool, task);
     160            0 :         if (APR_SUCCESS != status) {
     161            0 :             DEBUG_ERR("error calling napr_threadpool_add: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
     162            0 :             return status;
     163              :         }
     164              :     }
     165              : 
     166            0 :     napr_threadpool_wait(threadpool);
     167            0 :     status = apr_thread_mutex_destroy(cv_ctx.mutex);
     168            0 :     if (APR_SUCCESS != status) {
     169            0 :         DEBUG_ERR("error calling apr_thread_mutex_destroy: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
     170            0 :         return status;
     171              :     }
     172              : 
     173            0 :     if (is_option_set(conf->mask, OPTION_VERBO)) {
     174            0 :         (void) fprintf(stderr, "\rProgress [%u/%u] %d%% ", heap_size, heap_size, MAX_PERCENTAGE);
     175            0 :         (void) fprintf(stderr, "\n");
     176              :     }
     177              : 
     178            0 :     return APR_SUCCESS;
     179              : }
     180              : 
     181            0 : static void compare_image_vectors(ft_conf_t *conf, PuzzleContext * context)
     182              : {
     183            0 :     unsigned long nb_cmp = napr_heap_size(conf->heap) * (napr_heap_size(conf->heap) - 1) / 2;
     184            0 :     unsigned long cnt_cmp = 0;
     185            0 :     ft_file_t *file = NULL;
     186              : 
     187            0 :     while (NULL != (file = napr_heap_extract(conf->heap))) {
     188            0 :         if (!(file->cvec_ok & 0x1)) {
     189            0 :             continue;
     190              :         }
     191              : 
     192            0 :         unsigned char already_printed = 0;
     193            0 :         unsigned int heap_size = napr_heap_size(conf->heap);
     194            0 :         for (unsigned int idx = 0; idx < heap_size; idx++) {
     195            0 :             ft_file_t *file_cmp = napr_heap_get_nth(conf->heap, idx);
     196            0 :             if (!(file_cmp->cvec_ok & 0x1)) {
     197            0 :                 continue;
     198              :             }
     199              : 
     200              :             double distance =
     201            0 :                 puzzle_vector_normalized_distance(context, &(file->cvec), &(file_cmp->cvec), FIX_FOR_CLUSTERING);
     202            0 :             if (distance < conf->threshold) {
     203            0 :                 if (!already_printed) {
     204            0 :                     (void) printf("%s%c", file->path, conf->sep);
     205            0 :                     already_printed = 1;
     206              :                 }
     207              :                 else {
     208            0 :                     (void) printf("%c", conf->sep);
     209              :                 }
     210            0 :                 (void) printf("%s", file_cmp->path);
     211              :             }
     212            0 :             if (is_option_set(conf->mask, OPTION_VERBO)) {
     213            0 :                 (void) fprintf(stderr, "\rCompare progress [%10lu/%10lu] %02.2f%% ", cnt_cmp, nb_cmp,
     214            0 :                                (double) ((double) cnt_cmp / (double) nb_cmp * 100.0F));
     215              :             }
     216            0 :             cnt_cmp++;
     217              :         }
     218              : 
     219            0 :         if (already_printed) {
     220            0 :             (void) printf("\n\n");
     221              :         }
     222              : 
     223            0 :         puzzle_free_cvec(context, &(file->cvec));
     224              :     }
     225              : 
     226            0 :     if (is_option_set(conf->mask, OPTION_VERBO)) {
     227            0 :         (void) fprintf(stderr, "\rCompare progress [%10lu/%10lu] %02.2f%% ", cnt_cmp, nb_cmp, 100.0);
     228            0 :         (void) fprintf(stderr, "\n");
     229              :     }
     230            0 : }
        

Generated by: LCOV version 2.0-1