How to write an interface - rusbiometrics/IRPV GitHub Wiki

Минимальным набором инструментов для создания совместимого с API интерфейса является C++ компилятор (более точно, система сборки) и текстовый редактор. При помощи этих инструментов Вам необходимо создать так называемый shared object (*.so), если испытания будут проводиться на UNIX-совместимых системах, или dynamic linked library (*.dll) если это будет MS Windows.

Давайте рассмотрим конкретный пример создания такого интерфейса. Перед этим, если вы не знаете какие инструменты Вам понадобятся, изучите руководство по настройке тестового стенда.

Первое, с чего мы начнём это выберем имя для нашего алгоритма. Имя должно соответствовать следующему шаблону: irpv_11_(name)_(version)_(cpu/gpu). Префикс irpv является обязательной частью и сообщает нам, что алгоритм участвует в испытаниях с IRPV-совместимым интерфейсом. Следующая обязательная часть 11 говорит о том что алгоритм испытывается по схеме простой верификации (один к одному). Далее следует имя алгоритма, которое выбираете Вы. Для примера я буду использовать имя null, т.к. мой алгоритм будет генерировать случайные числа никак не связанные с входными данными. Далее, я должен буду указать версию алгоритма, в моём случае это очевидно будет 0. И последнее я должен указать устройство на котором будут производиться вычисления: cpu обозначает центральный процессор, gpu графический ускоритель (видеокарту). Причём, Вы должны понять, что выберите Вы сейчас cpu или gpu, это лишь облегчит работу по учёту результатов испытаний, но не укажет испытательному СПО на чём ваш алгоритм должен быть запущен. Это просто договорённость. Настоящая реализация будет предоставлена вашим *.so (или *.dll) файлом. Итак, мой алгоритм называется irpv_11_null_0_cpu, значит в конце этого руководства мы должны получить файл irpv_11_null_0_cpu.so.

Мы готовы к созданию файлов исходного кода, которые будут собраны в тот самый бинарный *.so (или *.dll) файл. Я создам два файла: nullimplirpv11.h и nullimplirpv11.cpp (их имена не имеют значения и выбраны произвольно). Первый из них, с расширением *.h я использую для объявления класса NullImplIRPV11:

#ifndef NULLIMPLIRPV11_H_
#define NULLIMPLIRPV11_H_

#include "irpv.h"

/*
 * Declare the implementation class of the IRPV VERIF (1:1) Interface
 */
namespace IRPV {
	
    class NullImplIRPV11 : public IRPV::VerifInterface {
public:

    NullImplIRPV11();
    ~NullImplIRPV11() override;

    // Здесь нужно загружать ресурсы алгоритма
    ReturnStatus
    initialize(const std::string &configDir) override; 

    // Здесь нужно вычислять биометрический шаблон
    ReturnStatus
    createTemplate(
            const Image &image,
            TemplateRole role,
            std::vector<uint8_t> &templ) override;

    // Здесь нужно сравнивать биометрические шаблоны
    ReturnStatus
    matchTemplates(
            const std::vector<uint8_t> &verifTemplate,
            const std::vector<uint8_t> &enrollTemplate,
            double &similarity) override; 

private:
    // Здесь можно добавить необходимые для работы алгоритма
    // переменные, функции, и вообще всё, что Вам нужно
};
}

#endif /* NULLIMPLIRPV11_H_ */

Наличие такого класса, который будет выступать обёрткой моего алгоритма распознавания, это требование API. Как Вы можете заметить, мой класс наследует от класса VerifInterface - базового абстрактного класса интерфейса API. Определение класса VerifInterface находится в заголовочном файле irpv.h, он определяет наличие трёх чистых виртуальных методов: initialize, сreateTemplate и matchTemplates а также одного статического метода getImplementation. Позже мы определим каждый из них, а пока я хочу указать, что мой класс NullImplIRPV11 переопределяет виртуальные методы своего предка и уже не является абстрактным. Это нам и нужно, поскольку тестировать мы собираемся конкретный алгоритм со стандартным для условий теста интерфейсом.

Определение методов своего класса я вынесу в *.cpp файл:

#include <cstring>
#include "nullimplirpv11.h"

using namespace std;
using namespace IRPV;

NullImplIRPV11::NullImplIRPV11() {}

NullImplIRPV11::~NullImplIRPV11() {}

ReturnStatus
NullImplIRPV11::initialize(const std::string &configDir)
{
    // Наш алгоритм является пустышкой, поэтому я ничего не буду
    // загружать с диска и просто верну код успешного выполнения задачи
    return ReturnStatus(ReturnCode::Success); 
}

ReturnStatus
NullImplIRPV11::createTemplate(const Image &image,
        TemplateRole role,
        std::vector<uint8_t> &templ)
{
    // Здесь наш алгоритм должен создать биометрический шаблон, для
    // имитации мы будем просто копировать в качестве шаблона вот такую строку:
    string blurb{"Long time ago in a galaxy far far away...\n"};
    templ.resize(blurb.size());
    memcpy(templ.data(), blurb.c_str(), blurb.size());
    return ReturnStatus(ReturnCode::Success);
}

ReturnStatus
NullImplIRPV11::matchTemplates(
        const std::vector<uint8_t> &verifTemplate,
        const std::vector<uint8_t> &enrollTemplate,
        double &similarity)
{
    // Генерируем случайную меру близости (от 0 до RND_MAX)
    similarity = std::rand(); 
    return ReturnStatus(ReturnCode::Success);
}

// Ключевой момент, определяем статический метод базового абстрактного класса
std::shared_ptr<VerifInterface>
VerifInterface::getImplementation() 
{
    // Возвращаем указатель на экземпляр нашего (неабстрактного) класса
    return std::make_shared<NullImplIRPV11>(); 
}

Определение говорит само за себя, здесь нечего добавить. Позже, программа, которая будет выполнять тестирование нашего алгоритма, создаст (при помощи метода getImplementation) и будет работать с экземпляром класса VerifInterface. Но так как во время её сборки мы будем линковаться к нашей динамической библиотеке, то, в результате, в тесте будет работать уже наша реализация методов initialize, createTemplate и matchTemplates.

Таким образом, мы подготовили исходники для сборки нашего *.so (или *.dll) файла. Далее будм исходить из того, что мы работаем в ОС Linux Ubuntu. В этом случае, для сборки нашей библиотеки необходимо сделать следующее:

  1. Загрузить исходники проекта IRPV:
git clone https://github.com/rusbiometrics/IRPV.git
  1. Собрать нашу динамическую библиотеку (пусть исходники хранятся в директории nullImpl):
g++ -c -Wall -fPIC -DBUILD_SHARED_LIBRARY -InullImpl -IIRPV
-o nullimplirpv11.o /nullImpl/nullimplirpv11.cpp
g++ -shared -o irpv_11_null_0_cpu.so nullimplirpv11.o
  1. Переместить *.so файл в директорию /IRPV/API_bin/irpv_11_null_0_cpu (путь определяется именем нашей библиотеки):
mkdir /IRPV/API_bin/irpv_11_null_0_cpu
mv irpv_11_null_0_cpu.so /IRPV/API_bin/irpv_11_null_0_cpu/ 

Если вы знакомы с системой сборки qmake, то указанные выше шаги можно выполнить автоматически, предварительно подготовив соответствующий *.pro файл. Пример такого файла, вместе с файлами исходного кода которые мы описали выше, можно найти здесь. В этом случае команды будут такими:

mkdir build
cd build
qmake ../nullImpl/nullimplirpv11.pro
make
  1. Добавить путь /IRPV/API_bin/irpv_11_null_0_cpu в переменную среды LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/IRPV/API_bin/irpv_11_null_0_cpu

Вкратце, это всё что нужно для подготовки библиотеки к тестированию при помощи API IRPV. Естественно, когда Вы будете оборачивать алгоритмы в совместимый с API IRPV интерфейс, то кол-во дополнительных действий может сильно увеличиться. В самом простом случае ваш алгоритм уже написан на С++ и, возможно, использует дополнительные 3rd-parties библиотеки. Если это так, то на этапе сборки интерфейса (а также во время сборки теста) нужно будет дополнительно указать компилятору где нужно искать ваши заголовочные файлы и сообщить линкеру где брать дополнительные 3rd-parties библиотеки. В более сложных случаях, ваш алгоритм может быть написан на каком-нибудь экзотическом языке или, возможно, работать как web-api. В этих случаях, Вам придётся самостоятельно реализовать обмен между алгоритмом и интерфейсом API IRPV. Используйте для этого сокеты, процессы, файлы, любую совместимую технологию. Однако, поскольку различных случаев интеграции может быть очень много, то здесь мы не будем на них останавливаться.

Далее мы рассмотрим процесс запуска процедуры тестирования. Перейти.

⚠️ **GitHub.com Fallback** ⚠️