Начало работы
API процессинг-блоков (блоков обработки) является альтернативным масштабируемым интерфейсом, замещающим существующие API, для более легкой интеграции возможностей Face SDK в ваше приложение.
Требования
- Операционная система Windows x86 64-bit или Linux x86 64-bit.
- Установлен пакет Face SDK windows_x86_64 или linux_x86_64 (см. Начало работы).
Основные возможности
- Объединение многочисленных компонентов в единую интеграцию
- Простота и легкость изучения
- Быстрое внедрение
- Долгосрочная поддержка и обновления
API процессинг-блоков является частью будущих решений 3DiVi. Для получения более подробной информации обратитесь к торговому представителю 3DiVi.
Типы процессинг-блоков, модификации и доступные версии
Примеры использования API процессинг-блоков демонстрируются в:
- Примерах examples/python/processing_blocks/ на Python
- Сэмпле processing_block_demo на Flutter
Тип блока | Описание | Модификация | Версии |
---|---|---|---|
FACE_DETECTOR | Используется для обнаружения человеческих лиц на изображении. Результатом обнаружения является ограничивающий прямоугольник (рамка) вокруг обнаруженного лица. | ssyv | [1, 3] |
uld | [1] | ||
blf_back | [1] | ||
blf_front | [1] | ||
HUMAN_BODY_DETECTOR | Используется для обнаружения человеческих тел на изображении. Результатом обнаружения является ограничивающий прямоугольник (рамка) вокруг обнаруженного лица. | ssyv | [1] |
OBJECT_DETECTOR | Используется для обнаружения объектов разных типов на изображении. Результатом обнаружения является ограничивающий прямоугольник (рамка) вокруг обнаруженного объекта с классификационным именем. | ssyx | [1] |
HUMAN_POSE_ESTIMATOR | Используется для вычисления ключевых точек скелета человеческого тела на изображении. Результатом обнаружения является список ключевых точек с их координатами и оценкой достоверности обнаруженного человеческого тела. | heavy | [1] |
EMOTION_ESTIMATOR | Используется для оценки эмоций человека по кадрированному изображению лица. Результатом оценки является числовое значения степени проявления для каждой оцениваемой эмоции. | heavy | [1] |
AGE_ESTIMATOR | Используется для оценки возраста человека по кадрированному изображению с лицом. Результатом оценки является возраст человека. | heavy | [1, 2] |
light | [1, 2] | ||
GENDER_ESTIMATOR | Используется для оценки пола человека по кадрированному изображению с лицом. Результат оценки — вердикт о половой идентичности. | heavy | [1, 2] |
light | [1, 2] | ||
MASK_ESTIMATOR | Используется для определения наличия или отсутствия медицинской маски на лице человека с изображения. Результат оценки — вердикт о наличии маски. | light | [1, 2] |
LIVENESS_ESTIMATOR | Используется для оценки принадлежности лица на RGB-изображении реальному человеку. Результатом обнаружения является ограничивающий прямоугольник (рамка) вокруг обнаруженного лица, вердикт о принадлежности реальному человеку и числовое значение вероятности. | [1] | |
Используется для оценки принадлежности лица на RGB-изображении реальному человеку. Результат оценки - вердикт о принадлежности реальному человеку и числовое значение вероятности. | v4 | [1] | |
QUALITY_ASSESSMENT_ESTIMATOR | Используется для оценки качества фотографии лица для задач идентификации на одном цветном изображении. Результат оценки - подробный анализ качества. | assessment | [1] |
estimation | [1] | ||
FACE_FITTER | Используется для вычисления ключевых точек человеческого лица на изображении. Результатом обнаружения является список ключевых точек с их координатами. | tddfa_faster | [1] |
tddfa | [1] | ||
mesh | [1] | ||
FACE_RECOGNIZER | Используется для вычисления шаблонов человеческого лица на изображении. Результатом обнаружения является шаблон человеческого лица. | 1000 | [12] |
MATCHER_MODULE | Используется для сравнения шаблонов человеческих лиц. Результатом является вердикт схожести и дистанция между шаблонами. | [1] |
- Первое значение модификации для блока является значением по умолчанию.
- Минимальное значение версии для модификации является значением по умолчанию.
Context
Ключевой концепцией API процессинг-блоков является использование контейнеров Context.
Context - это гетерогенный контейнер, состоящий из набора иерархически организованных данных, представленных как пары ключ-значение. Ближайший аналог Context - объект JSON. Каждый объект Context может содержать: скалярный объект (integer, real, boolean, string), указатель на область памяти, массив объектов Context, ассоциативный контейнер пар <string, Context> с неограниченной вложенностью.
Как создать и использовать Context
Создайте Context-контейнер:
- C++
- Python
- Flutter
auto array_elem0 = service->createContext();
array_elem0 = service.createContext({})
Context array_elem0 = service.createContext({});
- Общий набор операций с Context:
- создание ассоциативного контейнера по ключу
["key"]
на пустом контейнере Context:
- C++
- Python
- Flutter
array_elem0["name"] = "Julius Zeleny"; // передать string
array_elem0["phone"] = 11111111111l; // передать integer (long)
array_elem0["social_score"] = 0.999; // передать double
array_elem0["verified"] = true; // передать bool
array_elem0["name"] = "Julius Zeleny" # передать string
array_elem0["phone"] = 11111111111 # передать integer
array_elem0["social_score"] = 0.999 # передать double
array_elem0["verified"] = True # передать bool
array_elem0["name"] = "Julius Zeleny"; // передать string
array_elem0["phone"] = 11111111111; // передать integer
array_elem0["social_score"] = 0.999; // передать double
array_elem0["verified"] = true; // передать bool
- геттеры:
- C++
- Python
- Flutter
ASSERT_EQ( array_elem0["name"].getString(), "Julius Zeleny" );
ASSERT_EQ( array_elem0["phone"].getLong(), 11111111111l );
ASSERT_EQ( array_elem0["social_score"].getDouble(), 0.999 );
ASSERT_EQ( array_elem0["verified"].getBool(), true );
assert array_elem0["name"].get_value(), "Julius Zeleny"
assert array_elem0["phone"].get_value() == 11111111111
assert array_elem0["social_score"].get_value() == 0.999
assert array_elem0["verified"].get_value() == True
assert (array_elem0["name"].get_value() == "Julius Zeleny");
assert (array_elem0["phone"].get_value() == 11111111111);
assert (array_elem0["social_score"].get_value() == 0.999);
assert (array_elem0["verified"].get_value() == true);
- создание массива вызовом метода
push_back
на пустом контейнере Context:
- C++
- Python
- Flutter
auto array == service->createContext();
array.push_back(array_elem0);
array = service.create_context([])
array.push_back(array_elem0)
Context array = service.createContext([]);
array.pushBack(array_elem0);
- итерации по массиву:
- C++
- Python
- Flutter
// получить значение по индексу
ASSERT_EQ( array[0]["phone"].getLong(), 11111111111l );
// выполнить итерацию с индексом
size_t array_sz = array.size();
for(size_t i = 0; i < array_sz; ++i)
array[i]["phone"];
// или с итераторами
for(auto iter = array.begin(); iter != array.end(); ++iter)
(*iter)["phone"]; // возвращается вложенный Context
// или с foreach
for(auto val : array)
val["phone"];
# получить значение по индексу
assert array[0]["phone"].get_value() == 11111111111
# выполнить итерацию с индексом
for i in range(len(array)):
array[i]["phone"]
# или с итераторами
for elem in array:
elem["phone"]
// получить значение по индексу
assert (array[0]["phone"].get_value() == 11111111111);
// выполнить итерацию с индексом
for (int i = 0; i < array.len(); i++)
array[i]["phone"];
- операции со вложенными ассоциативными контейнерами:
- C++
- Python
- Flutter
auto full = service->createContext();
full["friends"] = std::move(array); // использование семантики перемещения без копирования
// доступ к вложенному объекту
ASSERT_EQ( full["friends"][0]["social_score"].getDouble(), 0.999 );
// перебор значений ассоциативного контейнера
for(auto iter = full.begin(); iter != full.end(); ++iter) {
iter.key(); // получить значение ключа из итератора
(*iter)[0]["social_score"].getDouble(); // получить значение
}
// с foreach
for(auto val : full)
val[0]["social_score"].getDouble();
full = service.create_context({})
full["friends"] = array.to_dict()
# доступ к вложенному объекту
assert full["friends"][0]["social_score"].get_value() == 0.999
# перебор значений ассоциативного контейнера
for key in full.keys():
full[key][0]["social_score"].get_value()
Context full = service.createContext({});
full["friends"] = array.toMap();
// доступ к вложенному объекту
assert (full["friends"][0]["social_score"].get_value() == 0.999);
// перебор значений ассоциативного контейнера
for (var key in full.getKeys())
full[key][0]["social_score"].get_value();
- другие удобные методы Context:
- C++
- Python
- Flutter
void clear()
bool contains(const std::string& key) // для ассоциативного контейнера
Context operator[](size_t index) // для последовательного массива, элемент с указанием доступа и проверкой границ
Context operator[](const std::string& key) // для ассоциативного контейнера, доступ или вставка данных
Context at(const std::string& key) // для ассоциативного контейнера, с проверкой границ
size_t size() // возвращается число элементов контейнера
bool isNone() // проверка на пустоту
bool isArray() // проверка на последовательный массив
bool isObject() // проверка на ассоциативный контейнер
bool isLong(), isDouble(), isString(), isBool() // проверка на содержание скалярного типа данных
def to_dict(self) -> dict # преобразует context в словарь
def is_none(self) -> bool # проверка на пустоту
def is_array(self) -> bool # проверка на последовательный массив
def is_object(self) -> bool # проверка на ассоциативный контейнер
def is_long, is_double, is_string, is_bool -> bool # проверка на содержание скалярного типа данных
Context operator[](int index) // для последовательного массива, элемент с указанием доступа и проверкой границ
Context operator[](String key) // для ассоциативного контейнера, доступ или вставка данных
int len() // возвращается число элементов контейнера
bool is_none() // проверка на пустоту
bool is_array() // проверка на последовательный массив
bool is_object() // проверка на ассоциативный контейнер
bool is_long(), is_double(), is_string(), is_bool() // проверка на содержание скалярного типа данных
Бинарный формат изображений
Большинство процессинг-блоков выполняют операции с контейнерами Context, содержащими изображения в бинарном формате:
/*
{
"image" : { "format": "NDARRAY",
"blob": <data pointer>,
"dtype": "uint8_t",
"shape": [height, width, channels] }
}
*/
Ключ "blob"
содержит умный указатель на данные. Указатель устанавливается функцией void Context::setDataPtr(void* ptr, int copy_sz)
,
где copy_sz
- размер памяти в байтах, который будет скопирован, и затем автоматически освобождается, когда заканчивается время жизни объекта Context.
Копирование не будет выполнено, если в качестве аргумента copy_sz
будет передан 0
. В этом случае объект Context не контролирует время жизни объекта, на который он указывает.
Ключ "blob"
содержит умный указатель на данные. Указатель устанавливается функцией void Context::setDataPtr(void* ptr, int copy_sz)
,
где copy_sz
- это размер памяти в байтах, которая будет скопирована и далее автоматически освобождена.
Копирование не выполняется, если в качестве аргумента copy_sz
передан 0.
Вы также можете аллоцировать "сырую" память, например, чтобы скопировать данные позже, передавая nullptr и размер в качестве аргументов setDataPtr
.
Ключ "dtype"
может содержать одно из следующих значений: "uint8_t"
, "int8_t"
, "uint16_t"
, "int16_t"
, "int32_t"
, "float"
, "double"
.
Каждому значению соответствует определенный тип OpenCV: CV_8U
, CV_8S
, CV_16U
, CV_16S
, CV_32S
, CV_32F
, CV_64F
.
Создание контейнера Context c RGB-изображением
- C++
- Python
- Flutter
- Создайте контейнер Context для изображения используя метод
createContext()
:
auto imgCtx = service->createContext();
- Прочитайте RGB-изображение из файла:
// read the image from file
std::string input_image_path = "<path_to_image>";
cv::Mat image = cv::imread(input_image_path, cv::IMREAD_COLOR);
cv::Mat input_image;
cv::cvtColor(image, input_image, cv::COLOR_BGR2RGB);
4.a Поместите изображение в контейнер ИЛИ
// using pbio::context_utils::putImage(Context& ctx, unsigned char* data, size_t height, size_t width, pbio::IRawImage::Format format, bool copy)
pbio::context_utils::putImage(imgCtx, input_image.data, input_image.rows, input_image.cols, pbio::IRawImage::FORMAT_RGB, true);
4.b ИЛИ скопируйте изображение из pbio::RawImage
, pbio::CVRawImage
, pbio::InternalImageBuffer
в бинарный формат и положите его в контейнер Context:
// constructing pbio::RawImage
pbio::RawImage input_rawimg(input_image.cols, input_image.rows, pbio::RawImage::Format::FORMAT_RGB, input_image.data);
// using void putImage(Context& ctx, const RawImage& raw_image)
pbio::context_utils::putImage(imgCtx, input_rawimg);
- Прочитайте RGB-изображение из файла:
input_image_path = "<path_to_image>"
image = cv2.imread(input_image_path, cv2.IMREAD_COLOR)
input_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
- Создайте словарь в бинарном формате изображения:
imgDict = {
"blob": input_image.tobytes(),
"dtype": "uint8_t",
"format": "NDARRAY",
"shape": [dim for dim in input_image.shape]
}
- Создайте контейнер Context для изображения используя метод
createContext()
:
imgCtx = service.create_context(imgDict)
- Прочитайте RGB-изображение из файла:
File file = File(<"imagePath">);
final Uint8List bytes = await file.readAsBytes();
final ImageDescriptor descriptor = await ImageDescriptor.encoded(await ImmutableBuffer.fromUint8List(bytes));
- Создайте словарь в бинарном формате изображения:
Map<String, dynamic> imageContext = {
"blob": bytes,
"dtype": "uint8_t",
"format": "NDARRAY",
"shape": [descriptor.height, descriptor.width, 3]
};
- Создайте контейнер Context для изображения используя метод
createContext()
:
imgCtx = service.createContext(imageContext);
Создание процессинг-блока
В этом разделе описаны шаги по созданию любого процессинг блока.
Для создания блока необходимо указать соответствующие значения для ключей unit_type
, modification
и version
(См. описание выбранного процессинг-блока).
- C++
- Python
- Flutter
- Создайте контейнер Context:
auto configCtx = service->createContext();
- Определите значения ключей контейнера Context специфичных для выбранного процессинг-блока:
// обязательно, укажите имя процессинг-блока
configCtx["unit_type"] = "<name_of_processing_block>";
// если не указать, то будет использоваться значение по умолчанию
configCtx["modification"] = "<modification>";
// если не указать, то будет использоваться первая версия модификации
configCtx["version"] = "<version>";
// модели по умолчанию расположены в директории дистрибутива Face SDK: share/processing_block/<modification>/(<version>/ или <version>.enc)
// вы можете задать ваш собственный путь до модели
configCtx["model_path"] = "<path_to_model_file>";
// библиотека onnxruntime по умолчанию расположена в директории дистрибутива Face SDK: "lib" для платформы Linux или "bin" для платформы Windows
// вы можете задать ваш собственный путь до библиотеки onnxruntime
// если путь до библиотеки не указан, то будет использоваться стандартный порядок поиска, используемый в данной ОС
configCtx["ONNXRuntime"]["library_path"] = "../lib"; // для Linux
configCtx["ONNXRuntime"]["library_path"] = "../bin"; // для Windows
// опционально, значение "true" для ускорения процессинг-блоков на GPU (только для блоков с поддержкой CUDA)
configCtx["use_cuda"] = false;
- Создание процессинг-блока:
pbio::ProcessingBlock processing_block = service->createProcessingBlock(configCtx);
- Создайте контейнер Dict:
configDict = {};
- Определите значения ключей контейнера Context, специфичных для выбранного процессинг-блока:
# обязательно, укажите имя процессинг-блока
configDict["unit_type"] = "<name_of_processing_block>"
# если не указать, то будет использоваться значение по умолчанию
configDict["modification"] = "<modification>"
# если не указать, то будет использоваться первая версия модификации
configDict["version"] = "<version>"
# модели по умолчанию расположены в директории дистрибутива Face SDK: share/processing_block/<modification>/(<version>/ или <version>.enc)
# вы можете задать ваш собственный путь до модели
configDict["model_path"] = "<path_to_model_file>"
# библиотека onnxruntime по умолчанию расположена в директории дистрибутива Face SDK: "lib" для платформы Linux или "bin" для платформы Windows
# вы можете задать ваш собственный путь до библиотеки onnxruntime
# если путь до библиотеки не указан, то будет использоваться стандартный порядок поиска, используемый в данной ОС
configDict["ONNXRuntime"]["library_path"] = "../lib" # для Linux
configDict["ONNXRuntime"]["library_path"] = "../bin" # для Windows
# опционально, значение "true" для ускорения процессинг-блоков на GPU (только для блоков с поддержкой CUDA)
configDict["use_cuda"] = False
- Создание процессинг-блока:
processing_block = service.create_processing_block(configDict);
- Создайте контейнер Map:
Map<String, dynamic> configMap = {};
- Определите значения ключей контейнера Context, специфичных для выбранного процессинг-блока:
// обязательно, укажите имя процессинг-блока
configMap["unit_type"] = "<name_of_processing_block>";
// если не указать, то будет использоваться значение по умолчанию
configMap["modification"] = "<modification>";
// если не указать, то будет использоваться первая версия модификации
configMap["version"] = "<version>";
// модели по умолчанию расположены в директории дистрибутива Face SDK: share/processing_block/<modification>/(<version>/ или <version>.enc)
// вы можете задать ваш собственный путь до модели
configMap["model_path"] = "<path_to_model_file>";
- Создание процессинг-блока:
processing_block = service.createProcessingBlock(configMap);
Ускорение на GPU
Процессинг-блоки могут быть использованы с ускорением на GPU. Для этого необходимо определить ключ "use_cuda"
со значением true
для Context-контейнера блока обработчика. Для запуска процессинг блоков на cuda-10.1 необходимо определить ключ "use_legacy"
со значением true
для Context-контейнера блока обработчика
(См. Создание процессинг-блока).