Mercurial > hg > audiodb
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, × | |
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(×, &(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 { |