Commit f1f3728b72f9d6853b90595eb94b75e9952a430f
Merge branch 'fbi_ebts'
Showing
1 changed file
with
86 additions
and
1 deletions
openbr/plugins/format.cpp
| @@ -16,6 +16,7 @@ | @@ -16,6 +16,7 @@ | ||
| 16 | 16 | ||
| 17 | #include <QDate> | 17 | #include <QDate> |
| 18 | #include <QSize> | 18 | #include <QSize> |
| 19 | +#include <QChar> | ||
| 19 | #ifndef BR_EMBEDDED | 20 | #ifndef BR_EMBEDDED |
| 20 | #include <QtXml> | 21 | #include <QtXml> |
| 21 | #endif // BR_EMBEDDED | 22 | #endif // BR_EMBEDDED |
| @@ -720,7 +721,7 @@ BR_REGISTER(Format, xmlFormat) | @@ -720,7 +721,7 @@ BR_REGISTER(Format, xmlFormat) | ||
| 720 | /*! | 721 | /*! |
| 721 | * \ingroup formats | 722 | * \ingroup formats |
| 722 | * \brief Reads in scores or ground truth from a text table. | 723 | * \brief Reads in scores or ground truth from a text table. |
| 723 | - * \author Josh Klontz | 724 | + * \author Josh Klontz \cite jklontz |
| 724 | * | 725 | * |
| 725 | * Example of the format: | 726 | * Example of the format: |
| 726 | * \code | 727 | * \code |
| @@ -769,6 +770,90 @@ class scoresFormat : public Format | @@ -769,6 +770,90 @@ class scoresFormat : public Format | ||
| 769 | 770 | ||
| 770 | BR_REGISTER(Format, scoresFormat) | 771 | BR_REGISTER(Format, scoresFormat) |
| 771 | 772 | ||
| 773 | +/*! | ||
| 774 | + * \ingroup formats | ||
| 775 | + * \brief Reads FBI EBTS transactions. | ||
| 776 | + * \author Scott Klum \cite sklum | ||
| 777 | + * https://www.fbibiospecs.org/ebts.html | ||
| 778 | + * \note This will fail if a binary blob contains any of the fields attempt to locate within the file | ||
| 779 | + */ | ||
| 780 | +class ebtsFormat : public Format | ||
| 781 | +{ | ||
| 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 | + | ||
| 790 | + return byteArray.mid(fieldPosition,sepPosition-fieldPosition); | ||
| 791 | + } | ||
| 792 | + | ||
| 793 | + Template read() const | ||
| 794 | + { | ||
| 795 | + QByteArray byteArray; | ||
| 796 | + QtUtils::readFile(file, byteArray); | ||
| 797 | + | ||
| 798 | + Template t; | ||
| 799 | + | ||
| 800 | + Mat m; | ||
| 801 | + | ||
| 802 | + // Demographics | ||
| 803 | + { | ||
| 804 | + QString name = textFieldValue(byteArray, "2.018"); | ||
| 805 | + QStringList names = name.split(','); | ||
| 806 | + t.file.set("FIRSTNAME", names.at(1)); | ||
| 807 | + t.file.set("LASTNAME", names.at(0)); | ||
| 808 | + t.file.set("DOB", textFieldValue(byteArray, "2.022").toInt()); | ||
| 809 | + t.file.set("GENDER", textFieldValue(byteArray, "2.024")); | ||
| 810 | + t.file.set("RACE", textFieldValue(byteArray, "2.025")); | ||
| 811 | + } | ||
| 812 | + | ||
| 813 | + // Mugshot (first in file used) | ||
| 814 | + // Todo: Check for face designation | ||
| 815 | + { | ||
| 816 | + const QString imageRecord = "10.001:"; | ||
| 817 | + const QString imageDataRecord = "10.999:"; | ||
| 818 | + | ||
| 819 | + int fieldPosition = byteArray.indexOf(imageRecord); | ||
| 820 | + | ||
| 821 | + if (fieldPosition != -1) { | ||
| 822 | + int sepPosition = byteArray.indexOf(QChar(0x1D),fieldPosition); | ||
| 823 | + int dataPosition = byteArray.indexOf(imageDataRecord,sepPosition); | ||
| 824 | + | ||
| 825 | + int recordBytes = byteArray.mid(fieldPosition,sepPosition-fieldPosition).toInt(); | ||
| 826 | + int headerBytes = byteArray.mid(fieldPosition,dataPosition-fieldPosition).size() + imageDataRecord.size(); | ||
| 827 | + | ||
| 828 | + QByteArray data = byteArray.mid(dataPosition+imageRecord.size(),recordBytes-headerBytes); | ||
| 829 | + | ||
| 830 | + m = imdecode(Mat(3, data.size(), CV_8UC3, data.data()), CV_LOAD_IMAGE_COLOR); | ||
| 831 | + if (!m.data) qWarning("ebtsFormat::read failed to decode image data."); | ||
| 832 | + t.m() = m; | ||
| 833 | + } else qWarning("ebtsFormat::cannot find image data within file."); | ||
| 834 | + | ||
| 835 | + } | ||
| 836 | + | ||
| 837 | + if (t.file.contains("DOB")) { | ||
| 838 | + const QDate dob = QDate::fromString(t.file.get<QString>("DOB"), "yyyyMMdd"); | ||
| 839 | + const QDate current = QDate::currentDate(); | ||
| 840 | + int age = current.year() - dob.year(); | ||
| 841 | + if (current.month() < dob.month()) age--; | ||
| 842 | + t.file.set("Age", age); | ||
| 843 | + } | ||
| 844 | + | ||
| 845 | + return t; | ||
| 846 | + } | ||
| 847 | + | ||
| 848 | + void write(const Template &t) const | ||
| 849 | + { | ||
| 850 | + (void) t; | ||
| 851 | + qFatal("Writing EBTS files is not supported."); | ||
| 852 | + } | ||
| 853 | +}; | ||
| 854 | + | ||
| 855 | +BR_REGISTER(Format, ebtsFormat) | ||
| 856 | + | ||
| 772 | } // namespace br | 857 | } // namespace br |
| 773 | 858 | ||
| 774 | #include "format.moc" | 859 | #include "format.moc" |