27#include <apr_thread_mutex.h>
36static const int NB_WORKER = 4;
38struct compute_vector_ctx_t
40 apr_thread_mutex_t *mutex;
41 PuzzleContext *contextp;
43 unsigned int heap_size;
44 unsigned int nb_processed;
46typedef struct compute_vector_ctx_t compute_vector_ctx_t;
48struct compute_vector_task_t
50 compute_vector_ctx_t *cv_ctx;
53typedef struct compute_vector_task_t compute_vector_task_t;
55static apr_status_t compute_vector(
void *ctx,
void *data)
57 char errbuf[ERROR_BUFFER_SIZE];
58 compute_vector_ctx_t *cv_ctx = (compute_vector_ctx_t *) ctx;
59 compute_vector_task_t *task = (compute_vector_task_t *) data;
60 ft_file_t *file = task->file;
61 apr_status_t status = APR_SUCCESS;
63 memset(errbuf, 0,
sizeof(errbuf));
64 puzzle_init_cvec(cv_ctx->contextp, &(file->cvec));
65 if (0 == puzzle_fill_cvec_from_file(cv_ctx->contextp, &(file->cvec), file->path)) {
69 DEBUG_ERR(
"error calling puzzle_fill_cvec_from_file, ignoring file: %s", file->path);
72 status = apr_thread_mutex_lock(cv_ctx->mutex);
73 if (APR_SUCCESS != status) {
74 DEBUG_ERR(
"error calling apr_thread_mutex_lock: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
77 if (is_option_set(cv_ctx->conf->mask, OPTION_VERBO)) {
78 (void) fprintf(stderr,
"\rProgress [%u/%u] %d%% ", cv_ctx->nb_processed, cv_ctx->heap_size,
79 (
int) ((float) cv_ctx->nb_processed / (
float) cv_ctx->heap_size * 100.0F));
81 cv_ctx->nb_processed += 1;
82 status = apr_thread_mutex_unlock(cv_ctx->mutex);
83 if (APR_SUCCESS != status) {
84 DEBUG_ERR(
"error calling apr_thread_mutex_unlock: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
91static const int MAX_PUZZLE_WIDTH = 5000;
92static const int MAX_PUZZLE_HEIGHT = 5000;
93static const int PUZZLE_LAMBDAS = 13;
94static const int FIX_FOR_CLUSTERING = 0;
95static const int MAX_PERCENTAGE = 100;
97static void initialize_puzzle_context(PuzzleContext * context);
98static apr_status_t compute_image_vectors(
ft_conf_t *conf, PuzzleContext * context);
99static void compare_image_vectors(
ft_conf_t *conf, PuzzleContext * context);
103 PuzzleContext context;
104 apr_status_t status = APR_SUCCESS;
106 initialize_puzzle_context(&context);
108 status = compute_image_vectors(conf, &context);
109 if (status != APR_SUCCESS) {
110 puzzle_free_context(&context);
114 compare_image_vectors(conf, &context);
116 puzzle_free_context(&context);
121static void initialize_puzzle_context(PuzzleContext * context)
123 puzzle_init_context(context);
124 puzzle_set_max_width(context, MAX_PUZZLE_WIDTH);
125 puzzle_set_max_height(context, MAX_PUZZLE_HEIGHT);
126 puzzle_set_lambdas(context, PUZZLE_LAMBDAS);
129static apr_status_t compute_image_vectors(
ft_conf_t *conf, PuzzleContext * context)
131 char errbuf[ERROR_BUFFER_SIZE];
132 apr_status_t status = APR_SUCCESS;
134 compute_vector_ctx_t cv_ctx;
137 memset(errbuf, 0,
sizeof(errbuf));
138 cv_ctx.contextp = context;
139 cv_ctx.heap_size = heap_size;
140 cv_ctx.nb_processed = 0;
143 status = apr_thread_mutex_create(&cv_ctx.mutex, APR_THREAD_MUTEX_DEFAULT, conf->pool);
144 if (APR_SUCCESS != status) {
145 DEBUG_ERR(
"error calling apr_thread_mutex_create: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
150 if (APR_SUCCESS != status) {
151 DEBUG_ERR(
"error calling napr_threadpool_init: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
155 for (
unsigned int idx = 0; idx < heap_size; idx++) {
156 compute_vector_task_t *task = apr_palloc(conf->pool,
sizeof(compute_vector_task_t));
157 task->cv_ctx = &cv_ctx;
160 if (APR_SUCCESS != status) {
161 DEBUG_ERR(
"error calling napr_threadpool_add: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
167 status = apr_thread_mutex_destroy(cv_ctx.mutex);
168 if (APR_SUCCESS != status) {
169 DEBUG_ERR(
"error calling apr_thread_mutex_destroy: %s", apr_strerror(status, errbuf, ERROR_BUFFER_SIZE));
173 if (is_option_set(conf->mask, OPTION_VERBO)) {
174 (void) fprintf(stderr,
"\rProgress [%u/%u] %d%% ", heap_size, heap_size, MAX_PERCENTAGE);
175 (void) fprintf(stderr,
"\n");
181static void compare_image_vectors(
ft_conf_t *conf, PuzzleContext * context)
184 unsigned long cnt_cmp = 0;
185 ft_file_t *file = NULL;
188 if (!(file->cvec_ok & 0x1)) {
192 unsigned char already_printed = 0;
194 for (
unsigned int idx = 0; idx < heap_size; idx++) {
196 if (!(file_cmp->cvec_ok & 0x1)) {
201 puzzle_vector_normalized_distance(context, &(file->cvec), &(file_cmp->cvec), FIX_FOR_CLUSTERING);
202 if (distance < conf->threshold) {
203 if (!already_printed) {
204 (void) printf(
"%s%c", file->path, conf->sep);
208 (void) printf(
"%c", conf->sep);
210 (void) printf(
"%s", file_cmp->path);
212 if (is_option_set(conf->mask, OPTION_VERBO)) {
213 (void) fprintf(stderr,
"\rCompare progress [%10lu/%10lu] %02.2f%% ", cnt_cmp, nb_cmp,
214 (
double) ((double) cnt_cmp / (
double) nb_cmp * 100.0F));
219 if (already_printed) {
220 (void) printf(
"\n\n");
223 puzzle_free_cvec(context, &(file->cvec));
226 if (is_option_set(conf->mask, OPTION_VERBO)) {
227 (void) fprintf(stderr,
"\rCompare progress [%10lu/%10lu] %02.2f%% ", cnt_cmp, nb_cmp, 100.0);
228 (void) fprintf(stderr,
"\n");
UTIL debug output macros.
#define DEBUG_ERR(str, arg...)
Display error message at the level error.
Interface for file comparison and checksum calculation.
apr_status_t ft_image_twin_report(ft_conf_t *conf)
Compares images using libpuzzle and reports similar images.
void * napr_heap_extract(napr_heap_t *heap)
Removes and returns the element at the top of the heap (the min or max element).
unsigned int napr_heap_size(const napr_heap_t *heap)
Gets the current number of elements in the heap.
void * napr_heap_get_nth(const napr_heap_t *heap, unsigned int n)
Gets the element at a specific index in the heap's internal array.
A generic binary heap implementation (min-heap or max-heap).
apr_status_t napr_threadpool_wait(napr_threadpool_t *threadpool)
Waits until all tasks currently in the queue have been processed.
apr_status_t napr_threadpool_init(napr_threadpool_t **threadpool, void *ctx, unsigned long nb_thread, threadpool_process_data_callback_fn_t *process_data, apr_pool_t *pool)
Initializes a thread pool.
apr_status_t napr_threadpool_add(napr_threadpool_t *threadpool, void *data)
Adds a task (a data item) to the thread pool's processing queue.
A simple fixed-size thread pool for concurrent task processing.
struct napr_threadpool_t napr_threadpool_t
Opaque thread pool structure.
Main configuration structure for the ftwin application.