Mercurial > hg > audiodb
changeset 20:0519fc406b29
New major version, mostly tested: both sequence queries (-Q seq --pointnn N and -Q seq --radius R) now work, all reported distances are Euclidean.
author | mas01mc |
---|---|
date | Mon, 13 Aug 2007 23:25:16 +0000 |
parents | 0c5884204732 |
children | 95f1f4a42257 |
files | audioDB.cpp audioDB.h docs/TODO.txt |
diffstat | 3 files changed, 115 insertions(+), 80 deletions(-) [+] |
line wrap: on
line diff
--- a/audioDB.cpp Mon Aug 13 20:19:45 2007 +0000 +++ b/audioDB.cpp Mon Aug 13 23:25:16 2007 +0000 @@ -945,9 +945,9 @@ break; case O2_FLAG_SEQUENCE_QUERY: if(radius==0) - trackSequenceQuery(dbName, inFile, adbQueryResult); + trackSequenceQueryNN(dbName, inFile, adbQueryResult); else - trackSequenceQueryEuc(dbName, inFile, adbQueryResult); + trackSequenceQueryRad(dbName, inFile, adbQueryResult); break; case O2_FLAG_TRACK_QUERY: trackPointQuery(dbName, inFile, adbQueryResult); @@ -1140,10 +1140,6 @@ delete dbdurs; } -void audioDB::sequenceQuery(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult){ - -} - // trackPointQuery // return the trackNN closest tracks to the query track // uses average of pointNN points per track @@ -1391,15 +1387,13 @@ delete meanDBdur; } - -void audioDB::deleteDB(const char* dbName, const char* inFile){ -} -// NBest matched filter distance between query and target tracks -// efficient implementation -// outputs average of N minimum matched filter distances -void audioDB::trackSequenceQuery(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult){ +// k nearest-neighbor (k-NN) search between query and target tracks +// efficient implementation based on matched filter +// assumes normed shingles +// outputs distances of retrieved shingles, max retreived = pointNN shingles per per track +void audioDB::trackSequenceQueryNN(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult){ initTables(dbName, inFile); @@ -1425,6 +1419,7 @@ if(verbosity>1) cerr << "performing norms ... "; cerr.flush(); unsigned dbVectors = dbH->length/(sizeof(double)*dbH->dim); + // Make a copy of the query queryCopy = new double[numVectors*dbH->dim]; memcpy(queryCopy, query, numVectors*dbH->dim*sizeof(double)); @@ -1434,16 +1429,18 @@ assert(qNorm&&sNorm&&queryCopy&&sMeanL2&&sequenceLength); unitNorm(queryCopy, dbH->dim, numVectors, qNorm); query = queryCopy; + // Make norm measurements relative to sequenceLength unsigned w = sequenceLength-1; unsigned i,j; double* ps; double tmp1,tmp2; + // Copy the L2 norm values to core to avoid disk random access later on memcpy(sNorm, l2normTable, dbVectors*sizeof(double)); double* snPtr = sNorm; for(i=0; i<dbH->numFiles; i++){ - if(trackTable[i]>sequenceLength){ + if(trackTable[i]>=sequenceLength){ tmp1=*snPtr; j=1; w=sequenceLength-1; @@ -1453,10 +1450,16 @@ w=trackTable[i]-sequenceLength; // +1 - 1 while(w--){ tmp2=*ps; - *ps=*(ps-1)-tmp1+*(ps+sequenceLength); + *ps=*(ps-1)-tmp1+*(ps+sequenceLength-1); tmp1=tmp2; ps++; } + ps = snPtr; + w=trackTable[i]-sequenceLength+1; + while(w--){ + *ps=sqrt(*ps); + ps++; + } } snPtr+=trackTable[i]; } @@ -1469,11 +1472,13 @@ unsigned processedTracks=0; for(i=0; i<dbH->numFiles; i++){ if(trackTable[i]>sequenceLength-1){ - w = trackTable[i]-sequenceLength+1; + w = trackTable[i]-sequenceLength; pn = sMeanL2+i; + *pn=0; while(w--) - *pn+=*ps++; - *pn/=trackTable[i]-sequenceLength+1; + if(*ps>0) + *pn+=*ps++; + *pn/=trackTable[i]-sequenceLength; SILENCE_THRESH+=*pn; processedTracks++; } @@ -1481,27 +1486,36 @@ } if(verbosity>1) cerr << "processedTracks: " << processedTracks << endl; + + SILENCE_THRESH/=processedTracks; USE_THRESH=1; // Turn thresholding on - DIFF_THRESH=SILENCE_THRESH/2; // 50% of the mean shingle power - SILENCE_THRESH/=10; // 10% of the mean shingle power is SILENCE - + DIFF_THRESH=SILENCE_THRESH; // mean shingle power + SILENCE_THRESH/=5; // 20% of the mean shingle power is SILENCE + if(verbosity>4) + cerr << "silence thresh: " << SILENCE_THRESH; w=sequenceLength-1; i=1; tmp1=*qNorm; while(w--) *qNorm+=qNorm[i++]; ps = qNorm+1; - qMeanL2 = *qNorm; - w=numVectors-sequenceLength; + w=numVectors-sequenceLength; // +1 -1 while(w--){ tmp2=*ps; - *ps=*(ps-1)-tmp1+*(ps+sequenceLength); + *ps=*(ps-1)-tmp1+*(ps+sequenceLength-1); tmp1=tmp2; - qMeanL2+=*ps; - *ps++; + ps++; + } + ps = qNorm; + qMeanL2 = 0; + w=numVectors-sequenceLength+1; + while(w--){ + *ps=sqrt(*ps); + qMeanL2+=*ps++; } qMeanL2 /= numVectors-sequenceLength+1; + if(verbosity>1) cerr << "done." << endl; @@ -1528,13 +1542,13 @@ double oneOverWL=1.0/wL; for(k=0; k<pointNN; k++){ - distances[k]=0.0; + distances[k]=1.0e6; qIndexes[k]=~0; sIndexes[k]=~0; } for(k=0; k<trackNN; k++){ - trackDistances[k]=0.0; + trackDistances[k]=1.0e6; trackQIndexes[k]=~0; trackSIndexes[k]=~0; trackIDs[k]=~0; @@ -1586,7 +1600,7 @@ numVectors=wL; } - double ** D = 0; // Cross-correlation between query and target + double ** D = 0; // Differences query and target double ** DD = 0; // Matched filter distance D = new double*[numVectors]; @@ -1613,6 +1627,15 @@ } char nextKey [MAXSTR]; + + // chi^2 statistics + double sampleCount = 0; + double sampleSum = 0; + double logSampleSum = 0; + double minSample = 1e9; + double maxSample = 0; + + // Track loop for(processedTracks=0, track=0 ; processedTracks < dbH->numFiles ; track++, processedTracks++){ // get trackID from file if using a control file @@ -1633,7 +1656,7 @@ if(verbosity>7) cerr << track << "." << trackIndexOffset << "." << trackTable[track] << " | ";cerr.flush(); - // Cross-correlation matrix + // Sum products matrix for(j=0; j<numVectors;j++){ D[j]=new double[trackTable[track]]; assert(D[j]); @@ -1646,7 +1669,8 @@ assert(DD[j]); } - // Cross Correlation + double tmp; + // Dot product for(j=0; j<numVectors; j++) for(k=0; k<trackTable[track]; k++){ qp=query+j*dbH->dim; @@ -1672,6 +1696,7 @@ *sp+++=*spd++; } } + else{ // HOP_SIZE != 1 for(w=0; w<wL; w++) for(j=0; j<numVectors-w; j+=HOP_SIZE){ @@ -1700,23 +1725,38 @@ } // Search for minimum distance by shingles (concatenated vectors) - for(j=0;j<numVectors-wL+1;j+=HOP_SIZE) - for(k=0;k<trackTable[track]-wL+1;k+=HOP_SIZE){ - - diffL2 = fabs(qNorm[j] - sNorm[k]); + for(j=0;j<numVectors-wL;j+=HOP_SIZE) + for(k=0;k<trackTable[track]-wL;k+=HOP_SIZE){ + thisDist=2-(2/(qNorm[j]*sNorm[trackIndexOffset+k]))*DD[j][k]; + if(verbosity>10) + cerr << thisDist << " " << qNorm[j] << " " << sNorm[trackIndexOffset+k] << endl; + // Gather chi^2 statistics + if(thisDist<minSample) + minSample=thisDist; + else if(thisDist>maxSample) + maxSample=thisDist; + if(thisDist>1e-9){ + sampleCount++; + sampleSum+=thisDist; + logSampleSum+=log(thisDist); + } + + // diffL2 = fabs(qNorm[j] - sNorm[trackIndexOffset+k]); // Power test if(!USE_THRESH || // Threshold on mean L2 of Q and S sequences - (USE_THRESH && qNorm[j]>SILENCE_THRESH && sNorm[k]>SILENCE_THRESH && + (USE_THRESH && qNorm[j]>SILENCE_THRESH && sNorm[trackIndexOffset+k]>SILENCE_THRESH && // Are both query and target windows above mean energy? - (qNorm[j]>qMeanL2 && sNorm[k]>sMeanL2[track] && diffL2 < DIFF_THRESH ))) - thisDist=DD[j][k]*oneOverWL; + (qNorm[j]>qMeanL2*.25 && sNorm[trackIndexOffset+k]>sMeanL2[track]*.25))) // && diffL2 < DIFF_THRESH ))) + thisDist=thisDist; // Computed above else - thisDist=0.0; - - // NBest match algorithm - for(m=0; m<pointNN; m++){ - if(thisDist>=distances[m]){ + thisDist=1000000.0; + + // k-NN match algorithm + m=pointNN; + while(m--){ + if(thisDist<=distances[m]) + if(m==0 || thisDist>=distances[m-1]){ // Shuffle distances up the list for(l=pointNN-1; l>m; l--){ distances[l]=distances[l-1]; @@ -1730,29 +1770,25 @@ qIndexes[m]=j; sIndexes[m]=k; break; - } + } } } // Calculate the mean of the N-Best matches thisDist=0.0; for(m=0; m<pointNN; m++) - if(distances[m]<0.000001){ // Stop rubbish songs getting good scores - thisDist=0.0; - break; - } - else - thisDist+=distances[m]; + thisDist+=distances[m]; thisDist/=pointNN; // Let's see the distances then... if(verbosity>3) cerr << fileTable+track*O2_FILETABLESIZE << " " << thisDist << endl; + // All the track stuff goes here n=trackNN; while(n--){ - if(thisDist>=trackDistances[n]){ - if((n==0 || thisDist<=trackDistances[n-1])){ + if(thisDist<=trackDistances[n]){ + if((n==0 || thisDist>=trackDistances[n-1])){ // Copy all values above up the queue for( l=trackNN-1 ; l > n ; l--){ trackDistances[l]=trackDistances[l-1]; @@ -1772,14 +1808,7 @@ break; } } // Duration match - - // per-track reset array values - for(unsigned k=0; k<pointNN; k++){ - distances[k]=0.0; - qIndexes[k]=~0; - sIndexes[k]=~0; - } - + // Clean up current track if(D!=NULL){ for(j=0; j<numVectors; j++) @@ -1791,20 +1820,29 @@ delete[] DD[j]; } } + // per-track reset array values + for(unsigned k=0; k<pointNN; k++){ + distances[k]=1.0e6; + qIndexes[k]=~0; + sIndexes[k]=~0; + } } gettimeofday(&tv2,NULL); - if(verbosity>1) + if(verbosity>1){ cerr << endl << "processed tracks :" << processedTracks << " matched tracks: " << successfulTracks << " elapsed time:" << ( tv2.tv_sec*1000 + tv2.tv_usec/1000 ) - ( tv1.tv_sec*1000+tv1.tv_usec/1000 ) << " msec" << endl; - + cerr << "sampleCount: " << sampleCount << " sampleSum: " << sampleSum << " logSampleSum: " << logSampleSum + << " minSample: " << minSample << " maxSample: " << maxSample << endl; + } if(adbQueryResult==0){ if(verbosity>1) cerr<<endl; // Output answer // Loop over nearest neighbours for(k=0; k < min(trackNN,successfulTracks); k++) - cout << fileTable+trackIDs[k]*O2_FILETABLESIZE << " " << trackDistances[k] << " " << trackQIndexes[k] << " " << trackSIndexes[k] << endl; + cout << fileTable+trackIDs[k]*O2_FILETABLESIZE << " " << trackDistances[k] << " " + << trackQIndexes[k] << " " << trackSIndexes[k] << endl; } else{ // Process Web Services Query int listLen = min(trackNN, processedTracks); @@ -1828,9 +1866,9 @@ // Clean up if(trackOffsetTable) - delete trackOffsetTable; + delete[] trackOffsetTable; if(queryCopy) - delete queryCopy; + delete[] queryCopy; //if(qNorm) //delete qNorm; if(D) @@ -1838,17 +1876,18 @@ if(DD) delete[] DD; if(timesdata) - delete timesdata; + delete[] timesdata; if(meanDBdur) - delete meanDBdur; + delete[] meanDBdur; } -// NBest matched filter distance between query and target tracks -// efficient implementation -// outputs average of N minimum matched filter distances -void audioDB::trackSequenceQueryEuc(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult){ +// Radius search between query and target tracks +// efficient implementation based on matched filter +// assumes normed shingles +// outputs count of retrieved shingles, max retreived = one shingle per query shingle per track +void audioDB::trackSequenceQueryRad(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult){ initTables(dbName, inFile);
--- a/audioDB.h Mon Aug 13 20:19:45 2007 +0000 +++ b/audioDB.h Mon Aug 13 23:25:16 2007 +0000 @@ -217,13 +217,11 @@ // private methods void error(const char* a, const char* b = ""); void pointQuery(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult=0); - void sequenceQuery(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult=0); void trackPointQuery(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult=0); - void trackSequenceQuery(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult=0); - void trackSequenceQueryEuc(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult=0); + void trackSequenceQueryNN(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult=0); + void trackSequenceQueryRad(const char* dbName, const char* inFile, adb__queryResult *adbQueryResult=0); void initTables(const char* dbName, const char* inFile); - void NBestMatchedFilter(); void unitNorm(double* X, unsigned d, unsigned n, double* qNorm); void unitNormAndInsertL2(double* X, unsigned dim, unsigned n, unsigned append); void normalize(double* X, int dim, int n); @@ -245,7 +243,6 @@ void ws_query(const char*dbName, const char *trackKey, const char* hostport); void l2norm(const char* dbName); void dump(const char* dbName); - void deleteDB(const char* dbName, const char* inFile); // web services void startServer();
--- a/docs/TODO.txt Mon Aug 13 20:19:45 2007 +0000 +++ b/docs/TODO.txt Mon Aug 13 23:25:16 2007 +0000 @@ -2,13 +2,12 @@ audioDB FIXME: o fix segfault when query is zero-length -o use periodic memunmap on batch insert +:-) DONE use periodic memunmap on batch insert o allow keys to be passed as queries -o rename 'segments' in help to 'files' or 'keys' ? +:-) DONE rename 'segments' to 'tracks' in code and help files. o test suite o SOAP to serialize queryFile and keyList o SOAP to serialize files on insert / batch insert ? -M. Casey - 24/7/7 +M. Casey 13/08/07 -