Commit 2f33ea8cce30ccffa747f44c1e71b6f4e592f453
1 parent
16363df8
Generalized EBTS support
Showing
1 changed file
with
108 additions
and
42 deletions
openbr/plugins/format.cpp
| ... | ... | @@ -780,22 +780,26 @@ BR_REGISTER(Format, scoresFormat) |
| 780 | 780 | class ebtsFormat : public Format |
| 781 | 781 | { |
| 782 | 782 | Q_OBJECT |
| 783 | -; | |
| 784 | - QString textFieldValue(const QByteArray &byteArray, const QString &fieldNumber, int from = 0) const | |
| 785 | - { | |
| 786 | - // Find the field, skip the number bytes, and account for the semicolon | |
| 787 | - int fieldPosition = byteArray.indexOf(fieldNumber, from) + fieldNumber.size() + 1; | |
| 788 | - int sepPosition = byteArray.indexOf(QChar(0x1D),fieldPosition); | |
| 789 | 783 | |
| 790 | - return byteArray.mid(fieldPosition,sepPosition-fieldPosition); | |
| 791 | - } | |
| 784 | + struct Field { | |
| 785 | + float type; | |
| 786 | + QList<QByteArray> data; | |
| 787 | + }; | |
| 788 | + | |
| 789 | + struct Record { | |
| 790 | + int type; | |
| 791 | + quint32 bytes; | |
| 792 | + int position; // Starting position of record | |
| 792 | 793 | |
| 793 | - int recordBytes(const QByteArray &byteArray, const float fieldNumber, int from = 0) const | |
| 794 | + QList<Field> fields; | |
| 795 | + }; | |
| 796 | + | |
| 797 | + quint32 recordBytes(const QByteArray &byteArray, const float recordType, int from) const | |
| 794 | 798 | { |
| 795 | 799 | bool ok; |
| 796 | - int size; | |
| 800 | + quint32 size; | |
| 797 | 801 | |
| 798 | - if (fieldNumber == 4 || fieldNumber == 7) { | |
| 802 | + if (recordType == 4 || recordType == 7) { | |
| 799 | 803 | // read first four bytes |
| 800 | 804 | ok = true; |
| 801 | 805 | size = qFromBigEndian<quint32>((const uchar*)byteArray.mid(from,4).constData()); |
| ... | ... | @@ -807,16 +811,47 @@ class ebtsFormat : public Format |
| 807 | 811 | return ok ? size : -1; |
| 808 | 812 | } |
| 809 | 813 | |
| 810 | - QList<QByteArray> parseRecord(const QByteArray &byteArray, const int from, const int bytes) const | |
| 814 | + void parseRecord(const QByteArray &byteArray, Record &record) const | |
| 811 | 815 | { |
| 812 | - return byteArray.mid(from,bytes).split(QChar(0x1D).toLatin1()); | |
| 816 | + if (record.type == 4 || record.type == 7) { | |
| 817 | + // Just a binary blob | |
| 818 | + // Read everything after the first four bytes | |
| 819 | + // Not current supported | |
| 820 | + } else { | |
| 821 | + // Continue reading fields until we get all the data | |
| 822 | + int position = record.position; | |
| 823 | + float dataField = .999; | |
| 824 | + while (position < record.position + record.bytes) { | |
| 825 | + int index = byteArray.indexOf(QChar(0x1D), position); | |
| 826 | + Field field = parseField(byteArray.mid(position, index-position),QChar(0x1F)); | |
| 827 | + if (field.type - dataField == record.type ) { | |
| 828 | + // Data begin after the field identifier and the colon | |
| 829 | + int dataBegin = byteArray.indexOf(':', position)+1; | |
| 830 | + field.data.clear(); | |
| 831 | + field.data.append(byteArray.mid(dataBegin, record.bytes-(dataBegin-record.position))); | |
| 832 | + | |
| 833 | + // Data fields are always last in the record | |
| 834 | + record.fields.append(field); | |
| 835 | + break; | |
| 836 | + } | |
| 837 | + // Advance the position accounting for the separator | |
| 838 | + position += index-position+1; | |
| 839 | + record.fields.append(field); | |
| 840 | + } | |
| 841 | + } | |
| 813 | 842 | } |
| 814 | 843 | |
| 815 | - QList<QByteArray> parseField(const QByteArray &byteArray, const QChar &sep) const | |
| 844 | + Field parseField(const QByteArray &byteArray, const QChar &sep) const | |
| 816 | 845 | { |
| 817 | - QByteArray data = byteArray.split(':').last(); | |
| 846 | + bool ok; | |
| 847 | + Field f; | |
| 818 | 848 | |
| 819 | - return data.split(sep.toLatin1()); | |
| 849 | + QList<QByteArray> data = byteArray.split(':'); | |
| 850 | + | |
| 851 | + f.type = data.first().toFloat(&ok); | |
| 852 | + f.data = data.last().split(sep.toLatin1()); | |
| 853 | + | |
| 854 | + return f; | |
| 820 | 855 | } |
| 821 | 856 | |
| 822 | 857 | Template read() const |
| ... | ... | @@ -828,40 +863,73 @@ class ebtsFormat : public Format |
| 828 | 863 | |
| 829 | 864 | Mat m; |
| 830 | 865 | |
| 866 | + QList<Record> records; | |
| 867 | + | |
| 831 | 868 | // Read the type one record (every EBTS file will have one of these) |
| 832 | - int recordOneBytes = recordBytes(byteArray,1); | |
| 833 | - QList<QByteArray> recordOne = parseRecord(byteArray,0,recordOneBytes); | |
| 869 | + Record r1; | |
| 870 | + r1.type = 1; | |
| 871 | + r1.position = 0; | |
| 872 | + r1.bytes = recordBytes(byteArray,r1.type,r1.position); | |
| 873 | + | |
| 874 | + // The fields in a type 1 record are strictly defined | |
| 875 | + QList<QByteArray> data = byteArray.mid(r1.position,r1.bytes).split(QChar(0x1D).toLatin1()); | |
| 876 | + foreach (const QByteArray &datum, data) { | |
| 877 | + Field f = parseField(datum,QChar(0x1F)); | |
| 878 | + r1.fields.append(f); | |
| 879 | + } | |
| 834 | 880 | |
| 835 | - // The third field (1.03/1.003) will have record identifiers for the remaining | |
| 836 | - // portion of the transaction | |
| 837 | - QList<QByteArray> recordOneFieldThree = parseField(recordOne.at(2),QChar(0x1F)); | |
| 881 | + records.append(r1); | |
| 838 | 882 | |
| 839 | - QList<int> recordTypes; | |
| 883 | + // Read the type two record (every EBTS file will have one of these) | |
| 884 | + Record r2; | |
| 885 | + r2.type = 2; | |
| 886 | + r2.position = r1.bytes; | |
| 887 | + r2.bytes = recordBytes(byteArray,r2.type,r2.position); | |
| 840 | 888 | |
| 841 | - for (int i=2; i<recordOneFieldThree.size()-1; i++) /* We don't care about the first two and the final items */ { | |
| 842 | - // The first two bytes indicate the record index (and we don't want the separator), but we only care about the type | |
| 843 | - QByteArray fieldNumber = recordOneFieldThree[i].mid(3); | |
| 844 | - recordTypes.push_back(fieldNumber.toInt()); | |
| 889 | + // The fields in a type 2 record are strictly defined | |
| 890 | + data = byteArray.mid(r2.position,r2.bytes).split(QChar(0x1D).toLatin1()); | |
| 891 | + foreach (const QByteArray &datum, data) { | |
| 892 | + Field f = parseField(datum,QChar(0x1F)); | |
| 893 | + r2.fields.append(f); | |
| 845 | 894 | } |
| 846 | 895 | |
| 847 | - int recordTwoBytes = recordBytes(byteArray,2,recordOneBytes); | |
| 848 | - QList<QByteArray> recordTwo = parseRecord(byteArray,recordOneBytes,recordTwoBytes); | |
| 849 | - | |
| 850 | - QList<int> recordSizes; | |
| 851 | - int startingPosition = recordOneBytes + recordTwoBytes; | |
| 852 | - foreach(int i, recordTypes) { | |
| 853 | - // Maybe switch to position | |
| 854 | - int recordSize = recordBytes(byteArray,i,startingPosition); | |
| 855 | - qDebug() << i << recordSize; | |
| 856 | - recordSizes.append(recordSize); | |
| 857 | - startingPosition += recordSize; | |
| 896 | + | |
| 897 | + records.append(r2); | |
| 898 | + | |
| 899 | + // The third field of the first record contains informations about all the remaining records in the transaction | |
| 900 | + // We don't care about the first two and the final items | |
| 901 | + for (int i=2; i<r1.fields.at(2).data.size()-1; i++) { | |
| 902 | + // The first two bytes indicate the record index (and we don't want the separator), but we only care about the type | |
| 903 | + QByteArray recordType = r1.fields.at(2).data[i].mid(3); | |
| 904 | + Record r; | |
| 905 | + r.type = recordType.toInt(); | |
| 906 | + records.append(r); | |
| 858 | 907 | } |
| 859 | 908 | |
| 860 | - // Parse the remaining records knowing that the first four bytes of types 4 and 7 are sizes | |
| 909 | + QList<int> frontalIdxs; | |
| 910 | + int position = r1.bytes + r2.bytes; | |
| 911 | + for (int i=2; i<records.size(); i++) { | |
| 912 | + records[i].position = position; | |
| 913 | + records[i].bytes = recordBytes(byteArray,records[i].type,position); | |
| 861 | 914 | |
| 862 | - // Win at everything | |
| 915 | + parseRecord(byteArray, records[i]); | |
| 916 | + if (records[i].type == 10) frontalIdxs.append(i); | |
| 917 | + position += records[i].bytes; | |
| 918 | + } | |
| 919 | + | |
| 920 | + if (!frontalIdxs.isEmpty()) { | |
| 921 | + // We use the first type 10 record | |
| 922 | + // Its last field contains the mugshot data (and it will only have one subfield) | |
| 923 | + m = imdecode(Mat(3, records[frontalIdxs.first()].fields.last().data.first().size(), CV_8UC3, records[frontalIdxs.first()].fields.last().data.first().data()), CV_LOAD_IMAGE_COLOR); | |
| 924 | + if (!m.data) qWarning("ebtsFormat::read failed to decode image data."); | |
| 925 | + return Template(m); | |
| 926 | + } else { | |
| 927 | + qWarning("ebtsFormat::cannot find image data within file."); | |
| 928 | + return Template(); | |
| 929 | + } | |
| 863 | 930 | |
| 864 | 931 | // Demographics |
| 932 | + /* | |
| 865 | 933 | { |
| 866 | 934 | int recordOneSize = recordBytes(byteArray,1); |
| 867 | 935 | qDebug() << recordBytes(byteArray,1); |
| ... | ... | @@ -906,9 +974,7 @@ class ebtsFormat : public Format |
| 906 | 974 | int age = current.year() - dob.year(); |
| 907 | 975 | if (current.month() < dob.month()) age--; |
| 908 | 976 | t.file.set("Age", age); |
| 909 | - } | |
| 910 | - | |
| 911 | - return t; | |
| 977 | + }*/ | |
| 912 | 978 | } |
| 913 | 979 | |
| 914 | 980 | void write(const Template &t) const | ... | ... |