00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #include <biosphere_module.h>
00036 #include "util.h"
00037
00038 #include <string.h>
00039 #include <stdio.h>
00040 #include <stdlib.h>
00041 #include <assert.h>
00042 #include <math.h>
00043
00050 #define NORM_METHOD_NO 0
00051 #define NORM_METHOD_MM 1
00052 #define NORM_METHOD_ZS 2
00053 #define NORM_METHOD_TH 3
00054 #define NORM_METHOD_QQ 4
00055 #define NORM_METHOD_QLQ 5
00061 bs_module bsmod_symtable;
00062
00063
00067 static bs_status bsmod_fusion_init(void)
00068 {
00069 return BS_OK;
00070 }
00071
00072
00076 static bs_status bsmod_fusion_cleanup(void)
00077 {
00078 return BS_OK;
00079 }
00080
00081
00082 static bs_service_response *create_biometric_decision_response(
00083 const bs_service_request *request,
00084 bs_bool decision)
00085 {
00086 bs_service_response *resp;
00087 bs_message_instance *output;
00088 bs_part_instance *part;
00089 bs_data_type *type;
00090 unsigned i;
00091
00092 resp = (bs_service_response *) calloc(1, sizeof(bs_service_response));
00093 for (i = 0; i < 16; i++)
00094 resp->uuid[i] = request->uuid[i];
00095 resp->service = (char *) malloc(len(request->service) + 1);
00096 resp->port = (char *) malloc(len(request->port) + 1);
00097 resp->operation = (char *) malloc(len(request->operation) + 1);
00098 strcpy(resp->service, request->service);
00099 strcpy(resp->port, request->port);
00100 strcpy(resp->operation, request->operation);
00101
00102 output = (bs_message_instance *) calloc(1, sizeof(bs_service_response));
00103 output->name = (char *) malloc(len("biometric_decision") + 1);
00104 strcpy(output->name, "biometric_decision");
00105 output->num_parts = 1;
00106 output->parts = (bs_part_instance **) calloc(1, sizeof(bs_part_instance *));
00107 resp->output = output;
00108
00109 part = (bs_part_instance *) calloc(1, sizeof(bs_part_instance));
00110 part->name = (char *) malloc(len("") + 1);
00111 strcpy(part->name, "decision");
00112 part->data = (char *) calloc(1, 20);
00113 if (decision) sprintf(part->data, "ACCEPT");
00114 else sprintf(part->data, "REJECT");
00115 part->size = len(part->data);
00116 output->parts[0] = part;
00117
00118 type = (bs_data_type *) calloc(1, sizeof(bs_data_type));
00119 type->name = (char *) malloc(len("bs_string") + 1);
00120 strcpy(type->name, "bs_string");
00121 type->builtin = TRUE;
00122 type->from_mp = FALSE;
00123 part->type = type;
00124
00125 return resp;
00126 }
00127
00128
00129 static bs_service_response *create_biometric_response(
00130 const bs_service_request *request,
00131 bs_double score)
00132 {
00133 bs_service_response *resp;
00134 bs_message_instance *output;
00135 bs_part_instance *part;
00136 bs_data_type *type;
00137 unsigned i;
00138
00139 resp = (bs_service_response *) calloc(1, sizeof(bs_service_response));
00140 for (i = 0; i < 16; i++)
00141 resp->uuid[i] = request->uuid[i];
00142 resp->service = (char *) malloc(len(request->service) + 1);
00143 resp->port = (char *) malloc(len(request->port) + 1);
00144 resp->operation = (char *) malloc(len(request->operation) + 1);
00145 strcpy(resp->service, request->service);
00146 strcpy(resp->port, request->port);
00147 strcpy(resp->operation, request->operation);
00148
00149 output = (bs_message_instance *) calloc(1, sizeof(bs_service_response));
00150 output->name = (char *) malloc(len("biometric_score") + 1);
00151 strcpy(output->name, "biometric_score");
00152 output->num_parts = 1;
00153 output->parts = (bs_part_instance **) calloc(1, sizeof(bs_part_instance *));
00154 resp->output = output;
00155
00156 part = (bs_part_instance *) calloc(1, sizeof(bs_part_instance));
00157 part->name = (char *) malloc(len("") + 1);
00158 strcpy(part->name, "score");
00159 part->data = (char *) calloc(1, 20);
00160 sprintf(part->data, "%lf", score);
00161 part->size = len(part->data);
00162 output->parts[0] = part;
00163
00164 type = (bs_data_type *) calloc(1, sizeof(bs_data_type));
00165 type->name = (char *) malloc(len("bs_double") + 1);
00166 strcpy(type->name, "bs_double");
00167 type->builtin = TRUE;
00168 type->from_mp = FALSE;
00169 part->type = type;
00170
00171 return resp;
00172 }
00173
00174
00182 static int get_normalization(const char *norm)
00183 {
00184 if (strlen(norm) < 2) return NORM_METHOD_NO;
00185 if (norm[0] == 'M' && norm[1] == 'M') return NORM_METHOD_MM;
00186 if (norm[0] == 'Z' && norm[1] == 'S') return NORM_METHOD_ZS;
00187 if (norm[0] == 'T' && norm[1] == 'H') return NORM_METHOD_TH;
00188 if (norm[0] == 'Q' && norm[1] == 'Q') return NORM_METHOD_QQ;
00189 if (norm[0] == 'Q' && norm[1] == 'L' && norm[2] == 'Q')
00190 return NORM_METHOD_QLQ;
00191 else return NORM_METHOD_NO;
00192 }
00193
00194 #define IMPOSSIBLE 1000000000.0
00195
00205 static double normalize(double s, char *norm)
00206 {
00207 int normtype = get_normalization(norm);
00208 double p1 = IMPOSSIBLE, p2 = IMPOSSIBLE;
00209 double c = IMPOSSIBLE, w = IMPOSSIBLE, Nmm;
00210 unsigned i = 1;
00211
00212 if (normtype == NORM_METHOD_NO) return s;
00213
00214
00215 while (norm[i] != '\0') {
00216 if (norm[i-1] == ' ' && norm[i] != ' ') {
00217 char *n = norm + i;
00218 p1 = atof(n);
00219 i++;
00220 break;
00221 }
00222 i++;
00223 }
00224 while (norm[i] != '\0') {
00225 if (norm[i-1] == ' ' && norm[i] != ' ') {
00226 char *n = norm + i;
00227 p2 = atof(n);
00228 i++;
00229 break;
00230 }
00231 i++;
00232 }
00233 while (norm[i] != '\0') {
00234 if (norm[i-1] == ' ' && norm[i] != ' ') {
00235 char *n = norm + i;
00236 c = atof(n);
00237 i++;
00238 break;
00239 }
00240 i++;
00241 }
00242 while (norm[i] != '\0') {
00243 if (norm[i-1] == ' ' && norm[i] != ' ') {
00244 char *n = norm + i;
00245 w = atof(n);
00246 i++;
00247 break;
00248 }
00249 i++;
00250 }
00251 if (p1 == IMPOSSIBLE || p2 == IMPOSSIBLE) return -1.0;
00252
00253
00254 switch(normtype) {
00255 case NORM_METHOD_MM:
00256
00257 return (s - p1) / (p2 - p1);
00258 case NORM_METHOD_ZS:
00259
00260 return (s - p1) / p2;
00261 case NORM_METHOD_TH:
00262
00263 return 0.5 * (tanh(0.01 * (s - p1) / p2) + 1);
00264 case NORM_METHOD_QQ:
00265
00266 if (c == IMPOSSIBLE) return -1.0;
00267 Nmm = (s - p1) / (p2 - p1);
00268 if (Nmm <= c) return Nmm * Nmm / c;
00269 else return c + sqrt((1 - c) * (Nmm - c));
00270 case NORM_METHOD_QLQ:
00271
00272 if (c == IMPOSSIBLE || w == IMPOSSIBLE) return -1.0;
00273 Nmm = (s - p1) / (p2 - p1);
00274 if (Nmm <= c - w/2) return Nmm * Nmm / (c - w/2);
00275 else if (c - w/2 < Nmm && Nmm <= c + w/2) return Nmm;
00276 else return c + w/2 + sqrt((1 - c - w/2) * (Nmm - c - w/2));
00277 }
00278 return s;
00279 }
00280
00281
00282 static double fuse_mw(unsigned ns, double s1, double s2, double s3,
00283 char *meth)
00284 {
00285 double w1 = IMPOSSIBLE, w2 = IMPOSSIBLE, w3 = IMPOSSIBLE;
00286 unsigned i = 1;
00287
00288 while (meth[i] != '\0') {
00289 if (meth[i-1] == ' ' && meth[i] != ' ') {
00290 char *n = meth + i;
00291 w1 = atof(n);
00292 i++;
00293 break;
00294 }
00295 i++;
00296 }
00297 while (meth[i] != '\0') {
00298 if (meth[i-1] == ' ' && meth[i] != ' ') {
00299 char *n = meth + i;
00300 w2 = atof(n);
00301 i++;
00302 break;
00303 }
00304 i++;
00305 }
00306 while (meth[i] != '\0') {
00307 if (meth[i-1] == ' ' && meth[i] != ' ') {
00308 char *n = meth + i;
00309 w3 = atof(n);
00310 i++;
00311 break;
00312 }
00313 i++;
00314 }
00315
00316 if (ns == 2 && w1 != IMPOSSIBLE && w2 != IMPOSSIBLE) {
00317 double mw1, mw2;
00318 mw1 = 1.0 / (w1 * (1/w1 + 1/w2));
00319 mw2 = 1.0 / (w2 * (1/w1 + 1/w2));
00320 return s1 * mw1 + s2 * mw2;
00321 }
00322 else if (ns == 3 && w1 != IMPOSSIBLE && w2 != IMPOSSIBLE &&
00323 w3 != IMPOSSIBLE) {
00324 double mw1, mw2, mw3;
00325 mw1 = 1.0 / (w1 * (1/w1 + 1/w2 + 1/w3));
00326 mw2 = 1.0 / (w2 * (1/w1 + 1/w2 + 1/w3));
00327 mw3 = 1.0 / (w3 * (1/w1 + 1/w2 + 1/w3));
00328 return s1 * mw1 + s2 * mw2 + s3 * mw3;
00329 }
00330 return -1.0;
00331 }
00332
00333 static bs_bool fuse_dw(unsigned ns, bs_bool s1a, bs_bool s2a, bs_bool s3a,
00334 char *meth)
00335 {
00336 double s1 = s1a ? 1.0: 0.0;
00337 double s2 = s2a ? 1.0: 0.0;
00338 double s3 = s3a ? 1.0: 0.0;
00339 double threshold;
00340
00341 double w1 = IMPOSSIBLE, w2 = IMPOSSIBLE, w3 = IMPOSSIBLE;
00342 unsigned i = 1;
00343
00344
00345 while (meth[i] != '\0') {
00346 if (meth[i-1] == ' ' && meth[i] != ' ') {
00347 char *n = meth + i;
00348 threshold = atof(n);
00349 i++;
00350 break;
00351 }
00352 i++;
00353 }
00354 while (meth[i] != '\0') {
00355 if (meth[i-1] == ' ' && meth[i] != ' ') {
00356 char *n = meth + i;
00357 w1 = atof(n);
00358 i++;
00359 break;
00360 }
00361 i++;
00362 }
00363 while (meth[i] != '\0') {
00364 if (meth[i-1] == ' ' && meth[i] != ' ') {
00365 char *n = meth + i;
00366 w2 = atof(n);
00367 i++;
00368 break;
00369 }
00370 i++;
00371 }
00372 while (meth[i] != '\0') {
00373 if (meth[i-1] == ' ' && meth[i] != ' ') {
00374 char *n = meth + i;
00375 w3 = atof(n);
00376 i++;
00377 break;
00378 }
00379 i++;
00380 }
00381
00382 if (ns == 2 && w1 != IMPOSSIBLE && w2 != IMPOSSIBLE) {
00383 double mw1, mw2;
00384 mw1 = 1.0 / (w1 * (1/w1 + 1/w2));
00385 mw2 = 1.0 / (w2 * (1/w1 + 1/w2));
00386 return s1 * mw1 + s2 * mw2 >= threshold;
00387 }
00388 else if (ns == 3 && w1 != IMPOSSIBLE && w2 != IMPOSSIBLE &&
00389 w3 != IMPOSSIBLE) {
00390 double mw1, mw2, mw3;
00391 mw1 = 1.0 / (w1 * (1/w1 + 1/w2 + 1/w3));
00392 mw2 = 1.0 / (w2 * (1/w1 + 1/w2 + 1/w3));
00393 mw3 = 1.0 / (w3 * (1/w1 + 1/w2 + 1/w3));
00394 return s1 * mw1 + s2 * mw2 + s3 * mw3 >= threshold;
00395 }
00396 return FALSE;
00397 }
00398
00399
00404 static bs_bool majority_vote(unsigned ns, double s1, double s2, double s3,
00405 char *meth)
00406 {
00407 double threshold = 1.5;
00408 unsigned i = 1, num_accept = 0;
00409
00410
00411 while (meth[i] != '\0') {
00412 if (meth[i-1] == ' ' && meth[i] != ' ') {
00413 char *n = meth + i;
00414 threshold = atof(n);
00415 i++;
00416 break;
00417 }
00418 i++;
00419 }
00420
00421
00422 if (s1) num_accept++;
00423 if (s2) num_accept++;
00424 if (ns > 2 && s3) num_accept++;
00425 return num_accept >= threshold;
00426 }
00427
00428
00435 static bs_status handle_fuse2(const bs_service_request *request,
00436 bs_service_response **response)
00437 {
00438 char *meth, *norm1, *norm2;
00439 double s1, s2, result = -1.0;
00440
00441
00442 if (request->input->num_parts != 5) return BS_SERVICE_NUM_PARTS_MISMATCH;
00443 meth = request->input->parts[0]->data;
00444 if (strlen(meth) < 2) return BS_SERVICE_PART_INCORRECT;
00445 norm1 = request->input->parts[1]->data;
00446 norm2 = request->input->parts[2]->data;
00447 s1 = atof(request->input->parts[3]->data);
00448 s2 = atof(request->input->parts[4]->data);
00449
00450
00451 s1 = normalize(s1, norm1);
00452 s2 = normalize(s2, norm2);
00453
00454
00455 if (meth[0] == 'S' && meth[1] == 'S')
00456 result = s1 + s2;
00457 else if (meth[0] == 'M' && meth[1] == 'I')
00458 result = min(s1, s2, 10000000.0);
00459 else if (meth[0] == 'M' && meth[1] == 'A')
00460 result = max(s1, s2, -10000000.0);
00461 else if (meth[0] == 'M' && meth[1] == 'W')
00462 result = fuse_mw(2, s1, s2, 0.0, meth);
00463
00464
00465 *response = create_biometric_response(request, result);
00466 return BS_OK;
00467 }
00468
00469
00476 static bs_status handle_fuse3(const bs_service_request *request,
00477 bs_service_response **response)
00478 {
00479 char *meth, *norm1, *norm2, *norm3;
00480 double s1, s2, s3, result = -1.0;
00481
00482
00483 if (request->input->num_parts != 7) return BS_SERVICE_NUM_PARTS_MISMATCH;
00484 meth = request->input->parts[0]->data;
00485 if (strlen(meth) < 2) return BS_SERVICE_PART_INCORRECT;
00486 norm1 = request->input->parts[1]->data;
00487 norm2 = request->input->parts[2]->data;
00488 norm3 = request->input->parts[3]->data;
00489 s1 = atof(request->input->parts[4]->data);
00490 s2 = atof(request->input->parts[5]->data);
00491 s3 = atof(request->input->parts[6]->data);
00492
00493
00494 s1 = normalize(s1, norm1);
00495 s2 = normalize(s2, norm2);
00496 s3 = normalize(s3, norm3);
00497
00498
00499 if (meth[0] == 'S' && meth[1] == 'S')
00500 result = s1 + s2 + s3;
00501 else if (meth[0] == 'M' && meth[1] == 'I')
00502 result = min(s1, s2, s3);
00503 else if (meth[0] == 'M' && meth[1] == 'A')
00504 result = max(s1, s2, s3);
00505 else if (meth[0] == 'M' && meth[1] == 'W')
00506 result = fuse_mw(3, s1, s2, s3, meth);
00507
00508
00509 *response = create_biometric_response(request, result);
00510 return BS_OK;
00511 }
00512
00513
00520 static bs_status handle_fuse2_decision(const bs_service_request *request,
00521 bs_service_response **response)
00522 {
00523 char *meth;
00524 bs_bool s1, s2, result = FALSE;
00525
00526
00527 if (request->input->num_parts != 3) return BS_SERVICE_NUM_PARTS_MISMATCH;
00528 meth = request->input->parts[0]->data;
00529 if (strlen(meth) < 2) return BS_SERVICE_PART_INCORRECT;
00530 s1 = decision_to_bool(request->input->parts[1]->data);
00531 s2 = decision_to_bool(request->input->parts[2]->data);
00532
00533
00534
00535 if (meth[0] == 'B' && meth[1] == 'A') {
00536 if (s1 && s2) result = TRUE;
00537 }
00538 else if (meth[0] == 'B' && meth[1] == 'O') {
00539 if (s1 || s2) result = TRUE;
00540 }
00541 else if (meth[0] == 'M' && meth[1] == 'V')
00542 result = majority_vote(2, s1, s2, FALSE, meth);
00543 else if (meth[0] == 'D' && meth[1] == 'W')
00544 result = fuse_dw(2, s1, s2, FALSE, meth);
00545 else return BS_ERROR;
00546
00547
00548 *response = create_biometric_decision_response(request, result);
00549 return BS_OK;
00550 }
00551
00552
00559 static bs_status handle_fuse3_decision(const bs_service_request *request,
00560 bs_service_response **response)
00561 {
00562 char *meth;
00563 bs_bool s1, s2, s3, result = FALSE;
00564
00565
00566 if (request->input->num_parts != 4) return BS_SERVICE_NUM_PARTS_MISMATCH;
00567 meth = request->input->parts[0]->data;
00568 if (strlen(meth) < 2) return BS_SERVICE_PART_INCORRECT;
00569 s1 = decision_to_bool(request->input->parts[1]->data);
00570 s2 = decision_to_bool(request->input->parts[2]->data);
00571 s3 = decision_to_bool(request->input->parts[3]->data);
00572
00573
00574
00575 if (meth[0] == 'B' && meth[1] == 'A') {
00576 if (s1 && s2 && s3) result = TRUE;
00577 }
00578 else if (meth[0] == 'B' && meth[1] == 'O') {
00579 if (s1 || s2 || s3) result = TRUE;
00580 }
00581 else if (meth[0] == 'M' && meth[1] == 'V')
00582 result = majority_vote(3, s1, s2, s3, meth);
00583 else if (meth[0] == 'D' && meth[1] == 'W')
00584 result = fuse_dw(3, s1, s2, s3, meth);
00585 else return BS_ERROR;
00586
00587
00588 *response = create_biometric_decision_response(request, result);
00589 return BS_OK;
00590 }
00591
00592
00597 static bs_status bsmod_fusion_handle_service(
00598 const bs_service_request *request,
00599 bs_service_response **response, void *extra)
00600 {
00601 if (!streq(request->service, "BSModFusionServices"))
00602 return BS_NO_SERVICE;
00603
00604 if (streq(request->port, "MatchLevel")) {
00605 if (streq(request->operation, "fuse2"))
00606 return handle_fuse2(request, response);
00607 else if (streq(request->operation, "fuse3"))
00608 return handle_fuse3(request, response);
00609 }
00610 else if (streq(request->port, "DecisionLevel")) {
00611 if (streq(request->operation, "fuse2_decision"))
00612 return handle_fuse2_decision(request, response);
00613 else if (streq(request->operation, "fuse3_decision"))
00614 return handle_fuse3_decision(request, response);
00615 }
00616
00617 return BS_NO_SERVICE;
00618 }
00619
00620
00621
00622
00623
00624 bs_module bsmod_symtable = {
00625
00626 0,
00627 0,
00628
00629
00630 0,
00631 1,
00632
00633
00634 "multimodal fusion module",
00635 "M.A.Hartman",
00636 "may 5, 2007",
00637 "(c) 2007, M.A.Hartman. See distributed LICENSE file for more information.",
00638 "A module which offers various biometric fusion functions and normalization "
00639 "methods. Fusion methods are: Simple Sum (SS), Minimum Score (MI), "
00640 "Maximum Score (MA) and Matcher Weighting (MW). Available normalization "
00641 "that can be applied to the score before fusion are: Min-Max (MM), Z-score (ZS), "
00642 "Tanh (TH) and Adaptive (QQ and QLQ). Some of the normalization functions "
00643 "require extra parameters. These should be provided after the name of the "
00644 "normalization, separated by spaces, in the 'norm' part.",
00645
00646
00647 bsmod_fusion_init,
00648 bsmod_fusion_cleanup,
00649
00650
00651 bsmod_fusion_handle_service,
00652 NULL,
00653 "bsmod_fusion.wsdl"
00654 };
00655