comparison bindings/python/pyadbmodule.c @ 750:d93292ae7c1b

Fixed accumulation and distance options (strcmp==0) and added times. Added queryFromData
author mas01mc
date Wed, 24 Nov 2010 19:29:52 +0000
parents dd4b9fec8d85
children 98d2e1de11bb
comparison
equal deleted inserted replaced
749:dd4b9fec8d85 750:d93292ae7c1b
405 if (falsePositives){ 405 if (falsePositives){
406 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES; 406 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES;
407 } 407 }
408 408
409 //set up spec->params 409 //set up spec->params
410 if (strcmp(accuMode,"db")){ 410 if (strcmp(accuMode,"db")==0){
411 spec->params.accumulation = ADB_ACCUMULATION_DB; 411 spec->params.accumulation = ADB_ACCUMULATION_DB;
412 } else if (strcmp(accuMode,"track")){ 412 } else if (strcmp(accuMode,"track")==0){
413 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK; 413 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
414 } else if (strcmp(accuMode,"one2one")){ 414 } else if (strcmp(accuMode,"one2one")==0){
415 spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE; 415 spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE;
416 } else{ 416 } else{
417 PyErr_SetString(PyExc_ValueError, 417 PyErr_SetString(PyExc_ValueError,
418 "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n"); 418 "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n");
419 return NULL; 419 return NULL;
420 } 420 }
421 if (strcmp(distMode, "dot")){ 421 if (strcmp(distMode, "dot")==0){
422 spec->params.distance = ADB_DISTANCE_DOT_PRODUCT; 422 spec->params.distance = ADB_DISTANCE_DOT_PRODUCT;
423 }else if (strcmp(distMode, "eucNorm")){ 423 }else if (strcmp(distMode, "eucNorm")==0){
424 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED; 424 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
425 }else if (strcmp(distMode, "euclidean")){ 425 }else if (strcmp(distMode, "euclidean")==0){
426 spec->params.distance = ADB_DISTANCE_EUCLIDEAN; 426 spec->params.distance = ADB_DISTANCE_EUCLIDEAN;
427 }else{ 427 }else{
428 PyErr_SetString(PyExc_ValueError, 428 PyErr_SetString(PyExc_ValueError,
429 "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' or \'euclidean\'.\n"); 429 "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' or \'euclidean\'.\n");
430 return NULL; 430 return NULL;
498 if (ok != 0){ 498 if (ok != 0){
499 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while trying to retrieve the data associated with the passed key.\n"); 499 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while trying to retrieve the data associated with the passed key.\n");
500 return NULL; 500 return NULL;
501 } 501 }
502 result = audiodb_query_spec(current_db, spec); 502 result = audiodb_query_spec(current_db, spec);
503 if (result == NULL){
504 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n");
505 return NULL;
506 }
507 if(strcmp(resFmt, "dict")==0){
508 outgoing = PyDict_New();
509 for (i=0;i<result->nresults;i++){
510 thisKey = PyString_FromString(result->results[i].ikey);
511 if (!PyDict_Contains(outgoing, thisKey)){
512 newBits = Py_BuildValue("[(dII)]",
513 result->results[i].dist,
514 result->results[i].qpos,
515 result->results[i].ipos);
516 if (PyDict_SetItem(outgoing, thisKey,newBits)){
517 printf("key : %s\ndist : %f\nqpos : %i\nipos : %i\n", result->results[i].ikey, result->results[i].dist, result->results[i].qpos, result->results[i].ipos);
518 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
519 Py_XDECREF(newBits);
520 return NULL;
521 }
522 Py_DECREF(newBits);
523 }else {
524 //the key already has a value, so we need to fetch the value, confirm it's a list and append another tuple to it.
525 currentValue = PyDict_GetItem(outgoing, thisKey);
526 if (!PyList_Check(currentValue)){
527 PyErr_SetString(PyExc_TypeError, "The result dictionary appears to be malformed.\n");
528 return NULL;
529 }
530 newBits = Py_BuildValue("dII",result->results[i].dist,
531 result->results[i].qpos,
532 result->results[i].ipos);
533 if (PyList_Append(currentValue, newBits)){
534 //error msg here
535 Py_XDECREF(newBits);
536 return NULL;
537 }
538 if (PyDict_SetItem(outgoing, thisKey, newBits)){
539 PyErr_SetString(PyExc_AttributeError, "Error adding a tuple to the result dict\n");
540 Py_XDECREF(newBits);
541 return NULL;
542 }
543 Py_DECREF(newBits);
544
545 }
546 }
547 }else if(strcmp(resFmt, "list")==0){
548 outgoing = PyList_New((Py_ssize_t)0);
549 for (i=0;i<result->nresults;i++){
550 newBits = Py_BuildValue("sdII",result->results[i].ikey,
551 result->results[i].dist,
552 result->results[i].qpos,
553 result->results[i].ipos);
554 if (PyList_Append(outgoing, newBits)){
555 //error msg here
556 Py_XDECREF(newBits);
557 return NULL;
558 }
559 Py_DECREF(newBits);
560 }
561 if(PyList_Reverse(outgoing)){//need to do this as things come off the accumulator backward.
562 PyErr_SetString(PyExc_RuntimeError,
563 "the reverse failed, hopefully a sensable error will follow.\nIf not, fix it.\n");
564 return NULL;
565 }
566 }else{
567 PyErr_SetString(PyExc_ValueError,
568 "Poorly specified result mode. Result must be either \'dist\' or \'list\'.\n");
569 return NULL;
570 }
571 if (audiodb_query_free_results(current_db, spec, result)){
572 printf("bit of trouble freeing the result and spec...\ncheck for leaks.");
573 }
574
575 return outgoing;
576
577
578
579 }
580
581 /* Data query.
582 * Returns a dict that is result ordered and key = result key
583 * value is a list of tuples one per result associated with that key, of the form:
584 * (dist, qpos, ipos)
585 * api call:
586 * adb_query_results_t *audiodb_query_spec(adb_t *, const adb_query_spec_t *);
587 ***/
588 PyObject * _pyadb_queryFromData(PyObject *self, PyObject *args, PyObject *keywds)
589 {
590 adb_t *current_db;
591 adb_query_spec_t *spec;
592 adb_query_results_t *result;
593 int ok, exhaustive, falsePositives;
594 uint32_t i;
595 const char *accuMode = "db";
596 const char *distMode = "dot";
597 const char *resFmt = "dict";
598 uint32_t hop = 0;
599 double radius = 0;
600 double absThres = 0;
601 double relThres = 0;
602 double durRatio = 0;
603 PyObject *includeKeys = NULL;
604 PyObject *excludeKeys = NULL;
605 PyObject *incoming = NULL;
606 PyObject *outgoing = NULL;
607 PyObject *thisKey = NULL;
608 PyObject *currentValue = NULL;
609 PyObject *newBits = NULL;
610 npy_intp dims[2];
611 unsigned int nDims = 0;
612 unsigned int nVect = 0;
613 PyArrayObject *features = NULL;
614 PyArrayObject *power = NULL;
615 PyArrayObject *times = NULL;
616 PyArray_Descr *descr;
617 adb_status_t *status;
618
619 static char *kwlist[] = { "db", "features",
620 "seqLength",
621 "seqStart",
622 "exhaustive",
623 "falsePositives",
624 "accumulation",
625 "distance",
626 "npoints",//nearest neighbor points per track
627 "ntracks",
628 "includeKeys",
629 "excludeKeys",
630 "radius",
631 "absThres",
632 "relThres",
633 "durRatio",
634 "hopSize",
635 "resFmt",
636 "power",
637 "times",
638 NULL
639 };
640
641 spec = (adb_query_spec_t *)malloc(sizeof(adb_query_spec_t));
642 spec->qid.datum = (adb_datum_t *)malloc(sizeof(adb_datum_t));
643 result = (adb_query_results_t *)malloc(sizeof(adb_query_results_t));
644
645 spec->qid.sequence_length = 16;
646 spec->qid.sequence_start = 0;
647 spec->qid.flags = 0;
648 spec->params.npoints = 1;
649 spec->params.ntracks = 100;//number of results returned in db mode
650 spec->refine.flags = 0;
651
652 ok = PyArg_ParseTupleAndKeywords(args, keywds, "OO!|iiiissIIOOddddIsO!O!", kwlist,
653 &incoming, &PyArray_Type, &features,
654 &spec->qid.sequence_length,
655 &spec->qid.sequence_start,
656 &exhaustive, &falsePositives,
657 &accuMode,&distMode,
658 &spec->params.npoints,
659 &spec->params.ntracks,
660 &includeKeys, &excludeKeys,
661 &radius, &absThres, &relThres, &durRatio, &hop,
662 &resFmt,
663 &PyArray_Type, &power, &PyArray_Type, &times
664 );
665
666 if (!ok) {return NULL;}
667 current_db = (adb_t *)PyCObject_AsVoidPtr(incoming);
668
669 if (!features){ /* Sanity Check */
670 PyErr_SetString(PyExc_ValueError,
671 "queryFromData: function requires feature data as numpy ndarray. PythonC required keyword check failed.\n");
672 return NULL;
673 }
674
675 /* Check the dimensionality of passed data agrees with the passed database */
676 if(PyArray_NDIM(features)!=2){
677 PyErr_SetString(PyExc_ValueError,
678 "queryFromData: passed features have incorrect shape, should be (nVecs, nDims).\n");
679 return NULL;
680 }
681
682
683 if(power && PyArray_NDIM(power)!=1){
684 PyErr_SetString(PyExc_ValueError,
685 "queryFromData: passed power have incorrect shape, should be (nVecs,).\n");
686 return NULL;
687 }
688
689 if(times && PyArray_NDIM(times)!=1){
690 PyErr_SetString(PyExc_ValueError,
691 "queryFromData: passed times have incorrect shape, should be (nVecs,).\n");
692 return NULL;
693 }
694
695 status = (adb_status_t*) malloc(sizeof(adb_status_t));
696 int errtest = audiodb_status(current_db, status);
697 if(errtest){
698 PyErr_SetString(PyExc_TypeError, "queryFromData failed: could not get status of passed ADB database");
699 free(status);
700 return NULL;
701 }
702
703 if(!PyArray_DIMS(features)[1]==status->dim){
704 PyErr_SetString(PyExc_ValueError,
705 "queryFromData: passed features have incorrect dimensionality.\n");
706 free(status);
707 return NULL;
708 }
709
710 if(power && PyArray_DIMS(power)[0] != PyArray_DIMS(features)[0]){
711 PyErr_SetString(PyExc_ValueError,
712 "queryFromData: passed power and features have incompatible nVecs dimension.\n");
713 free(status);
714 return NULL;
715 }
716
717 if(times && PyArray_DIMS(times)[0] != PyArray_DIMS(features)[0]){
718 PyErr_SetString(PyExc_ValueError,
719 "queryFromData: passed times and features have incompatible nVecs dimension.\n");
720 free(status);
721 return NULL;
722 }
723
724 free(status);
725
726
727 if (exhaustive){
728 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_EXHAUSTIVE;
729 }
730 if (falsePositives){
731 spec->qid.flags = spec->qid.flags | ADB_QID_FLAG_ALLOW_FALSE_POSITIVES;
732 }
733
734 //set up spec->params
735 if (strcmp(accuMode,"db")==0){
736 spec->params.accumulation = ADB_ACCUMULATION_DB;
737 } else if (strcmp(accuMode,"track")==0){
738 spec->params.accumulation = ADB_ACCUMULATION_PER_TRACK;
739 } else if (strcmp(accuMode,"one2one")==0){
740 spec->params.accumulation = ADB_ACCUMULATION_ONE_TO_ONE;
741 } else{
742 PyErr_SetString(PyExc_ValueError,
743 "Poorly specified distance mode. distance must either be \'db\', \'track\' or \'one2one\'.\n");
744 return NULL;
745 }
746 if (strcmp(distMode, "dot")==0){
747 spec->params.distance = ADB_DISTANCE_DOT_PRODUCT;
748 }else if (strcmp(distMode, "eucNorm")==0){
749 spec->params.distance = ADB_DISTANCE_EUCLIDEAN_NORMED;
750 }else if (strcmp(distMode, "euclidean")==0){
751 spec->params.distance = ADB_DISTANCE_EUCLIDEAN;
752 }else{
753 PyErr_SetString(PyExc_ValueError,
754 "Poorly specified distance mode. distance must either be \'dot\', \'eucNorm\' or \'euclidean\'.\n");
755 return NULL;
756 }
757
758 //set up spec->refine
759 //include/exclude keys
760 if (includeKeys){
761 if (!PyList_Check(includeKeys)){
762 PyErr_SetString(PyExc_TypeError, "Include keys must be specified as a list of strings.\n");
763 return NULL;
764 }
765 spec->refine.flags = spec->refine.flags | ADB_REFINE_INCLUDE_KEYLIST;
766 spec->refine.include.nkeys = (uint32_t)PyList_Size(includeKeys);
767 spec->refine.include.keys = (const char **)calloc(sizeof(const char *), spec->refine.include.nkeys);
768 for (i=0;i<spec->refine.include.nkeys;i++){
769 if (PyString_Check(PyList_GetItem(includeKeys, (Py_ssize_t)i))){
770 spec->refine.include.keys[i] = PyString_AsString(PyList_GetItem(includeKeys, (Py_ssize_t)i));
771 }else{
772 PyErr_SetString(PyExc_TypeError, "Include keys must each be specified as a string.\nFound one that was not.\n");
773 return NULL;
774 }
775 }
776 }
777 if (excludeKeys){
778 if (!PyList_Check(excludeKeys)){
779 PyErr_SetString(PyExc_TypeError, "Exclude keys must be specified as a list of strings.\n");
780 return NULL;
781 }
782 spec->refine.flags = spec->refine.flags | ADB_REFINE_EXCLUDE_KEYLIST;
783 spec->refine.exclude.nkeys = (uint32_t)PyList_Size(excludeKeys);
784 spec->refine.exclude.keys = (const char **)calloc(sizeof(const char *), spec->refine.exclude.nkeys);
785 for (i=0;i<spec->refine.exclude.nkeys;i++){
786 if (PyString_Check(PyList_GetItem(excludeKeys, (Py_ssize_t)i))){
787 spec->refine.exclude.keys[i] = PyString_AsString(PyList_GetItem(excludeKeys, (Py_ssize_t)i));
788 }else{
789 PyErr_SetString(PyExc_TypeError, "Exclude keys must each be specified as a string.\nFound one that was not.\n");
790 return NULL;
791 }
792 }
793 }
794 //the rest of spec->refine
795 if (radius){
796 spec->refine.flags = spec->refine.flags | ADB_REFINE_RADIUS;
797 spec->refine.radius = radius;
798 }
799 if (absThres){
800 spec->refine.flags = spec->refine.flags | ADB_REFINE_ABSOLUTE_THRESHOLD;
801 spec->refine.absolute_threshold = absThres;
802 }
803 if (relThres){
804 spec->refine.flags = spec->refine.flags | ADB_REFINE_RELATIVE_THRESHOLD;
805 spec->refine.relative_threshold = relThres;
806 }
807 if (durRatio){
808 spec->refine.flags = spec->refine.flags | ADB_REFINE_DURATION_RATIO;
809 spec->refine.duration_ratio = durRatio;
810 }
811 if (hop){
812 spec->refine.flags = spec->refine.flags | ADB_REFINE_HOP_SIZE;
813 /* not ideal but a temporary bandage fix */
814 spec->refine.qhopsize = hop;
815 spec->refine.ihopsize = hop;
816 }
817
818 descr = PyArray_DescrFromType(NPY_DOUBLE);
819
820 if (PyArray_AsCArray(&features, &(spec->qid.datum->data), dims, 2, descr)){
821 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the feature np array as a C array.");
822 return NULL;
823 }
824
825 if (power){
826 if (PyArray_AsCArray(&power, &(spec->qid.datum->power), dims, 1, descr)){
827 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the power np array as a C array.");
828 return NULL;
829 }
830 }else{
831 spec->qid.datum->power=NULL;
832 }
833
834 if (times){
835 if (PyArray_AsCArray(&times, &(spec->qid.datum->times), dims, 1, descr)){
836 PyErr_SetString(PyExc_RuntimeError, "Trouble expressing the times np array as a C array.");
837 return NULL;
838 }
839 }else{
840 spec->qid.datum->times=NULL;
841 }
842
843 nVect = PyArray_DIMS(features)[0];
844 nDims = PyArray_DIMS(features)[1];
845 spec->qid.datum->nvectors = (uint32_t)nVect;
846 spec->qid.datum->dim = (uint32_t)nDims;
847
848 result = audiodb_query_spec(current_db, spec);
849
503 if (result == NULL){ 850 if (result == NULL){
504 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n"); 851 PyErr_SetString(PyExc_RuntimeError, "Encountered an error while running the actual query, or there was nothing returned.\n");
505 return NULL; 852 return NULL;
506 } 853 }
507 if(strcmp(resFmt, "dict")==0){ 854 if(strcmp(resFmt, "dict")==0){
770 absThres = double absolute power threshold (db must have power),\n\ 1117 absThres = double absolute power threshold (db must have power),\n\
771 relThres = double relative power threshold (db must have power),\n\ 1118 relThres = double relative power threshold (db must have power),\n\
772 durRatio = double time expansion/compresion ratio,\n\ 1119 durRatio = double time expansion/compresion ratio,\n\
773 hopSize = int hopsize (1 by default)])->resultDict\n\ 1120 hopSize = int hopsize (1 by default)])->resultDict\n\
774 resFmt = [\"list\"|\"dict\"](\"dict\" by default)"}, 1121 resFmt = [\"list\"|\"dict\"](\"dict\" by default)"},
1122 {"_pyadb_queryFromData", (PyCFunction)_pyadb_queryFromData, METH_VARARGS | METH_KEYWORDS,
1123 "data query. Required features=F (numpy ndarray). Optional: power=P (numpy 1d array), times=T (numpy 1d array)"},
775 {NULL,NULL, 0, NULL} 1124 {NULL,NULL, 0, NULL}
776 }; 1125 };
777 1126
778 void init_pyadb() 1127 void init_pyadb()
779 { 1128 {