diff --git a/main.c b/main.c index af034e2..38ff3b2 100644 --- a/main.c +++ b/main.c @@ -14,7 +14,7 @@ static float reference[DATA_LEN] = { static float data[DATA_LEN] = { //0, 10, 20, 30, 40, 50, 50, 35, 15, 15, 0 - 0, 15.7, 0, 0, 0.1, 0.2, 0.1, 10, 24, 6, 3, 2, 0.2, 0.4, 0.5, 0 + 0, 13, 16, 0, 0.1, 0.2, 10, 0, 24, 0, 0, 7, 12, 0.4, 0.5, 0 }; static float ref_p[REF_LEN]; @@ -75,37 +75,39 @@ int main(void) //return 0; - vec_match_cfg_t cfg = { + vec_fuzzymatch_cfg_t cfg = { .length = DATA_LEN, .drift_x = 1, .offset_y = 1, .abs_threshold = 0.1 }; + + vec_hausdorff_cfg_t cfg_hdf = { + .length = DATA_LEN, + .max_drift_x = 10, + .cost_x = 10, + .cost_y = 4 + }; + + // example printf("REF: "); for (int i = 0; i < DATA_LEN; i++) { - printf("%.1f, ", reference[i]); + printf("%5.1f, ", reference[i]); } printf("\n"); printf("MEAS: "); for (int i = 0; i < DATA_LEN; i++) { - printf("%.1f, ", data[i]); + printf("%5.1f, ", data[i]); } printf("\n"); - // error metric fields - float env_e, abs_e; - - bool ok = vec_match(data, reference, &cfg, &env_e, &abs_e); - printf("%s", ok ? "MATCH OK" : "MATCH FAILED"); - printf("\n"); - - printf("Error rate: ENV %.2f, ABS %.2f\n", env_e, abs_e); + // --- PACK REFERENCE VECTOR --- float thr; int ref_pack_len = vec_pack_auto(ref_p, REF_LEN, reference, DATA_LEN, &thr); @@ -113,21 +115,36 @@ int main(void) printf("Reference packed with zero threshold %.1f to %d items.\n", thr, ref_pack_len); printf("REF packed: "); for (int i = 0; i < ref_pack_len; i++) { - printf("%.1f, ", ref_p[i]); + printf("%5.1f, ", ref_p[i]); } printf("\n"); - pack_walker_t pw; - pw_init(&pw, ref_p, ref_pack_len); - printf("Value #8 = %.1f\n", pw_get(&pw, 8)); - printf("Value #7 = %.1f\n", pw_get(&pw, 7)); - printf("Value #15 = %.1f\n", pw_get(&pw, 15)); - printf("Trying packed match\n"); - ok = vec_match_packed(data, ref_p, ref_pack_len, &cfg, &env_e, &abs_e); - printf("%s", ok ? "MATCH OK" : "MATCH FAILED"); + // error metric fields + float env_e, abs_e; + bool ok = vec_fuzzymatch(data, reference, &cfg, &env_e, &abs_e); + printf("FUZZY: %s", ok ? "MATCH OK" : "MATCH FAILED"); printf("\n"); printf("Error rate: ENV %.2f, ABS %.2f\n", env_e, abs_e); + + ok = vec_fuzzymatch_packed(data, ref_p, ref_pack_len, &cfg, &env_e, &abs_e); + printf("PACKED FUZZY: %s", ok ? "MATCH OK" : "MATCH FAILED"); + printf("\n"); + printf("Error rate: ENV %.2f, ABS %.2f\n", env_e, abs_e); + + + + + + + + // ---- HAUSDORFF ---- + + float hdif = vec_hausdorff(data, reference, &cfg_hdf); + printf("HAUSDORFF dist: %.2f\n", hdif); + + float hdif_p = vec_hausdorff_packed(data, ref_p, ref_pack_len, &cfg_hdf); + printf("HAUSDORFF dist packed: %.2f\n", hdif_p); } diff --git a/main.d b/main.d index 266d428..ac0365f 100644 --- a/main.d +++ b/main.d @@ -24,4 +24,9 @@ main.out: src/vec_match.c /usr/include/stdc-predef.h \ /usr/include/bits/huge_valf.h /usr/include/bits/huge_vall.h \ /usr/include/bits/inf.h /usr/include/bits/nan.h \ /usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \ - /usr/include/string.h /usr/include/xlocale.h src/vec_match.h + /usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/include-fixed/limits.h \ + /usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/include-fixed/syslimits.h \ + /usr/include/limits.h /usr/include/bits/posix1_lim.h \ + /usr/include/bits/local_lim.h /usr/include/linux/limits.h \ + /usr/include/bits/posix2_lim.h /usr/include/string.h \ + /usr/include/xlocale.h src/vec_match.h diff --git a/src/vec_match.c b/src/vec_match.c index 19e950b..3d0715d 100644 --- a/src/vec_match.c +++ b/src/vec_match.c @@ -4,21 +4,98 @@ #include #include #include +#include #include #include "vec_match.h" -#define SQ(a) ((a)*(a)) +#define SQ(a) ((a) * (a)) #define IS_FZERO(f) ((f) < 0.0f) #define F2ZERO(f) roundf(-(f)) #define ZERO2F(z) (0.0f - z) +#define NUM_DIST(a,b) ((a) > (b) ? (a) - (b) : (b) - (a)) -bool vec_match_do(const float *data, const float *ref, uint32_t ref_p_len, - const vec_match_cfg_t *cfg, - float *fuzzy_match_error, float *abs_match_error, bool packed) +// ---- HAUSDORFF ---- + +// real hausdorf should just return the max distance, we sum them all +float vec_hausdorff_do(const float *data, + const float *ref, uint32_t ref_p_len, + const vec_hausdorff_cfg_t *cfg, bool packed) +{ + uint32_t a, b; + float f, g; // tmp float + + pack_walker_t w; // walker for packed reference + + if (packed) { + pw_init(&w, ref, ref_p_len); + } + + float dist_sum = 0; + + + for (uint32_t i = 0; i < cfg->length; i++) { + + // bounds for base and peak search + if (i < cfg->max_drift_x) { + a = 0; + } else { + a = i - cfg->max_drift_x; + } + + if (i + cfg->max_drift_x >= cfg->length) { + b = cfg->length - 1; + } else { + b = i + cfg->max_drift_x; + } + + // get metric for point + float dist = FLT_MAX; + + for (uint32_t j = a; j <= b; j++) { + f = packed ? pw_get(&w, j) : ref[j]; + + // get X dist + g = sqrtf(SQ((f - data[i]) * cfg->cost_y) + + SQ(NUM_DIST(i, j) * cfg->cost_x)); + + if (dist > g) dist = g; + } + + dist_sum += dist; + } + + return dist_sum; +} + + + +float vec_hausdorff(const float *data, + const float *ref, + const vec_hausdorff_cfg_t *cfg) +{ + return vec_hausdorff_do(data, ref, cfg->length, cfg, false); +} + + + +float vec_hausdorff_packed(const float *data, + const float *ref, uint32_t ref_p_len, + const vec_hausdorff_cfg_t *cfg) +{ + return vec_hausdorff_do(data, ref, ref_p_len, cfg, true); +} + + + +// ---- FUZZY MATCH ---- + +bool vec_fuzzymatch_do(const float *data, const float *ref, uint32_t ref_p_len, + const vec_fuzzymatch_cfg_t *cfg, + float *fuzzy_match_error, float *abs_match_error, bool packed) { uint32_t a, b; float f; // tmp float @@ -94,24 +171,24 @@ bool vec_match_do(const float *data, const float *ref, uint32_t ref_p_len, -bool vec_match(const float *data, const float *ref, - const vec_match_cfg_t *cfg, - float *fuzzy_match_error, float *abs_match_error) +bool vec_fuzzymatch(const float *data, const float *ref, + const vec_fuzzymatch_cfg_t *cfg, + float *fuzzy_match_error, float *abs_match_error) { - return vec_match_do(data, ref, cfg->length, cfg, fuzzy_match_error, abs_match_error, false); + return vec_fuzzymatch_do(data, ref, cfg->length, cfg, fuzzy_match_error, abs_match_error, false); } - -bool vec_match_packed(const float *data, const float *ref, uint32_t ref_p_len, - const vec_match_cfg_t *cfg, - float *fuzzy_match_error, float *abs_match_error) +bool vec_fuzzymatch_packed(const float *data, const float *ref, uint32_t ref_p_len, + const vec_fuzzymatch_cfg_t *cfg, + float *fuzzy_match_error, float *abs_match_error) { - return vec_match_do(data, ref, ref_p_len, cfg, fuzzy_match_error, abs_match_error, true); + return vec_fuzzymatch_do(data, ref, ref_p_len, cfg, fuzzy_match_error, abs_match_error, true); } + // ---- PACKING UTILS ---- diff --git a/src/vec_match.h b/src/vec_match.h index 26d80c6..d5983a4 100644 --- a/src/vec_match.h +++ b/src/vec_match.h @@ -3,13 +3,21 @@ #include #include +typedef struct { + uint32_t length; // data length (data & ref length must be equal) + uint32_t max_drift_x; + float cost_x; // must be > 1 + float cost_y; +} vec_hausdorff_cfg_t; + + /* Example: drift_x 1, offset_y 10, abs_threshold 0.1 */ typedef struct { uint32_t length; // data length (data & ref length must be equal) uint32_t drift_x; // allowed horizontal drift (Hz drift if values are 1 Hz FFT bins) float offset_y; // allowed vertical offset (bin amplitude, positive or negative) float abs_threshold; // absolute threshold (to fix preccision errors, also added to offset_y) -} vec_match_cfg_t; +} vec_fuzzymatch_cfg_t; typedef struct { @@ -44,6 +52,24 @@ void pw_init(pack_walker_t *wlkr, const float *packed_vec, uint32_t packed_len); float pw_get(pack_walker_t *w, uint32_t idx); +/** + * Get vector's Hausdorff distance from reference + * + * @param data matched data + * @param ref reference data + * @param cfg config struct + * @return distance metric + */ +float vec_hausdorff(const float *data, const float *ref, const vec_hausdorff_cfg_t *cfg); + + +/** + * Same as vec_hausdorff(), except the reference vector is packed. + */ +float vec_hausdorff_packed(const float *data, const float *ref_packed, + uint32_t ref_p_len, const vec_hausdorff_cfg_t *cfg); + + /** * Match signal to reference, allowing for some offser and noise * @@ -54,16 +80,16 @@ float pw_get(pack_walker_t *w, uint32_t idx); * @param abs_match_error error metric calculated from raw data (can be used if envelope match passes) * @return envelope match status (match using drift and offset) */ -bool vec_match(const float *data, const float *ref, const vec_match_cfg_t *cfg, +bool vec_fuzzymatch(const float *data, const float *ref, const vec_fuzzymatch_cfg_t *cfg, float *fuzzy_match_error, float *abs_match_error); /** * Match vector against a packed reference vector (without unpacking). - * Params otherwise the same as vec_match() + * Params otherwise the same as vec_fuzzymatch() */ -bool vec_match_packed(const float *data, const float *ref_packed, uint32_t ref_p_len, - const vec_match_cfg_t *cfg, float *fuzzy_match_error, float *abs_match_error); +bool vec_fuzzymatch_packed(const float *data, const float *ref_packed, uint32_t ref_p_len, + const vec_fuzzymatch_cfg_t *cfg, float *fuzzy_match_error, float *abs_match_error); /**