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 : }
|