[JavaScript, Программирование, Node.JS, Rust] Обнаружение лиц в Node.js с использованием Rust и WebAssembly (перевод)
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Будущим учащимся на курсе "Node.js Developer" предлагаем записаться на открытый урок по теме "Докеризация node.js приложений".
А пока делимся традиционным переводом статьи.
В последней статье мы рассказывали, как вызывать функции Rust из Node.js. Сегодня мы расскажем, как написать приложение AIaaS (англ. Artificial Intelligence as a Service — «искусственный интеллект как услуга») на базе Node.js.Большинство приложений с искусственным интеллектом сейчас разрабатываются на языке Python, а главным языком программирования для веб-разработки является JavaScript. Для того чтобы реализовать возможности ИИ в вебе, нужно обернуть алгоритмы ИИ в JavaScript, а именно в Node.js.Однако ни Python, ни JavaScript сами по себе не подходят для разработки ИИ-приложений с большим объемом вычислений. Это высокоуровневые, медленные языки со сложной средой выполнения, в которых удобство использования достигается за счет снижения производительности. Для решения этой проблемы блоки интеллектуальных вычислений в Python оборачиваются в нативные C/C++-модули. Точно так же можно сделать и в Node.js, но мы нашли решение получше — WebAssembly.Виртуальные машины WebAssembly поддерживают тесную интеграцию с Node.js и другими средами выполнения JavaScript-кода. Они отличаются высокой производительностью, безопасны с точки зрения доступа к памяти, изначально защищены и совместимы с разными операционными системами. В нашем подходе сочетаются лучшие возможности WebAssembly и нативного кода.Как это работаетAIaaS-приложение на базе Node.js состоит из трех компонентов.
- Приложение на Node.js является веб-сервисом и вызывает функцию WebAssembly для выполнения интенсивных вычислений, таких как алгоритмы ИИ.
- Для подготовки, постобработки и интеграции данных с другими системами используется функция WebAssembly. Мы будем использовать язык Rust. Функцию должен написать разработчик приложения.
- Для запуска модели ИИ используется нативный код, что максимально повышает быстродействие. Эта часть кода очень короткая и должна проверяться с точки зрения безопасности и защищенности. Нужно просто вызвать эту нативную программу из функции WebAssembly — точно так же, как в Python и Node.js выполняется вызов нативных функций.
А теперь рассмотрим пример.Как можно реализовать функционал обнаружения лицВеб-сервис для обнаружения лиц позволяет пользователю загрузить фотографию и выделяет все обнаруженные лица зеленой рамкой.Исходный код Rust для реализации модели обнаружения лиц MTCNN создан по замечательной инструкции от Cetra: Обнаружение лиц с использованием библиотеки Tensorflow на Rust. Для того чтобы Tensorflow работала в WebAssembly, мы кое-что изменили.
Приложение на Node.js отвечает за загрузку файла и вывод результата.
app.post('/infer', function (req, res) {
let image_file = req.files.image_file;
var result_filename = uuidv4() + ".png";
// Call the infer() function from WebAssembly (SSVM)
var res = infer(req.body.detection_threshold, image_file.data);
fs.writeFileSync("public/" + result_filename, res);
res.send('<img src="' + result_filename + '"/>');
});
Как видите, JavaScript-приложение просто передает в функцию infer() графические данные и параметр detection_threshold, задающий минимальный размер лица, которое сможет обнаружить приложение, а затем сохраняет возвращаемое значение в графический файл на сервере. Функцияinfer() написана на языке Rust и скомпилирована в WebAssembly, поэтому ее можно вызвать из JavaScript.Сначала функция infer() преобразует исходные графические данные в плоскую форму и сохраняет их в массив. Она настраивает модель TensorFlow и использует плоские графические данные в качестве входных данных. После выполнения модели TensorFlow возвращается набор чисел — координаты четырех вершин рамки, выделяющей лицо. Затем функция infer() рисует рамку вокруг каждого лица и сохраняет измененное изображение в формате PNG на веб-сервере.
#[wasm_bindgen]
pub fn infer(detection_threshold: &str, image_data: &[u8]) -> Vec<u8> {
let mut dt = detection_threshold;
... ...
let mut img = image::load_from_memory(image_data).unwrap();
// Run the tensorflow model using the face_detection_mtcnn native wrapper
let mut cmd = Command::new("face_detection_mtcnn");
// Pass in some arguments
cmd.arg(img.width().to_string())
.arg(img.height().to_string())
.arg(dt);
// The image bytes data is passed in via STDIN
for (_x, _y, rgb) in img.pixels() {
cmd.stdin_u8(rgb[2] as u8)
.stdin_u8(rgb[1] as u8)
.stdin_u8(rgb[0] as u8);
}
let out = cmd.output();
// Draw boxes from the result JSON array
let line = Pixel::from_slice(&[0, 255, 0, 0]);
let stdout_json: Value = from_str(str::from_utf8(&out.stdout).expect("[]")).unwrap();
let stdout_vec = stdout_json.as_array().unwrap();
for i in 0..stdout_vec.len() {
let xy = stdout_vec[i].as_array().unwrap();
let x1: i32 = xy[0].as_f64().unwrap() as i32;
let y1: i32 = xy[1].as_f64().unwrap() as i32;
let x2: i32 = xy[2].as_f64().unwrap() as i32;
let y2: i32 = xy[3].as_f64().unwrap() as i32;
let rect = Rect::at(x1, y1).of_size((x2 - x1) as u32, (y2 - y1) as u32);
draw_hollow_rect_mut(&mut img, rect, *line);
}
let mut buf = Vec::new();
// Write the result image into STDOUT
img.write_to(&mut buf, image::ImageOutputFormat::Png).expect("Unable to write");
return buf;
}
Командаface_detection_mtcnn запускает модель TensorFlow в нативном коде. Она принимает три аргумента: ширину изображения, высоту изображения и минимальный размер лица для обнаружения. Фактические графические данные в виде плоских значений RGB передаются в программу из функции infer() WebAssembly через STDIN. Результат запуска модели кодируется в JSON и возвращается через STDOUT.Обратите внимание, мы передали параметр модели detectiont hreshold в тензор minsize, а затем использовали тензор input для передачи входных графических данных в программу. Тензор box используется для извлечения результатов из модели.
fn main() -> Result<(), Box<dyn Error>> {
// Get the arguments passed in from WebAssembly
let args: Vec<String> = env::args().collect();
let img_width: u64 = args[1].parse::<u64>().unwrap();
let img_height: u64 = args[2].parse::<u64>().unwrap();
let detection_threshold: f32 = args[3].parse::<f32>().unwrap();
let mut buffer: Vec<u8> = Vec::new();
let mut flattened: Vec<f32> = Vec::new();
// The image bytes are read from STDIN
io::stdin().read_to_end(&mut buffer)?;
for num in buffer {
flattened.push(num.into());
}
// Load up the graph as a byte array and create a tensorflow graph.
let model = include_bytes!("mtcnn.pb");
let mut graph = Graph::new();
graph.import_graph_def(&*model, &ImportGraphDefOptions::new())?;
let mut args = SessionRunArgs::new();
// The `input` tensor expects BGR pixel data from the input image
let input = Tensor::new(&[img_height, img_width, 3]).with_values(&flattened)?;
args.add_feed(&graph.operation_by_name_required("input")?, 0, &input);
// The `min_size` tensor takes the detection_threshold argument
let min_size = Tensor::new(&[]).with_values(&[detection_threshold])?;
args.add_feed(&graph.operation_by_name_required("min_size")?, 0, &min_size);
// Default input params for the model
let thresholds = Tensor::new(&[3]).with_values(&[0.6f32, 0.7f32, 0.7f32])?;
args.add_feed(&graph.operation_by_name_required("thresholds")?, 0, &thresholds);
let factor = Tensor::new(&[]).with_values(&[0.709f32])?;
args.add_feed(&graph.operation_by_name_required("factor")?, 0, &factor);
// Request the following outputs after the session runs.
let bbox = args.request_fetch(&graph.operation_by_name_required("box")?, 0);
let session = Session::new(&SessionOptions::new(), &graph)?;
session.run(&mut args)?;
// Get the bounding boxes
let bbox_res: Tensor<f32> = args.fetch(bbox)?;
let mut iter = 0;
let mut json_vec: Vec<[f32; 4]> = Vec::new();
while (iter * 4) < bbox_res.len() {
json_vec.push([
bbox_res[4 * iter + 1], // x1
bbox_res[4 * iter], // y1
bbox_res[4 * iter + 3], // x2
bbox_res[4 * iter + 2], // y2
]);
iter += 1;
}
let json_obj = json!(json_vec);
// Return result JSON in STDOUT
println!("{}", json_obj.to_string());
Ok(())
}
Наша цель — создать нативные обертки для выполнения типовых моделей ИИ, которые разработчики смогут использовать в качестве библиотек.Как развернуть программу обнаружения лицДля начала нужно установить Rust, Node.js, виртуальную машину Second State WebAssembly VM и инструмент ssvmup. Ознакомьтесь с инструкцией по настройке или воспользуйтесь Docker-образом. Вам также понадобится библиотека TensorFlow.
$ wget https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz
$ sudo tar -C /usr/ -xzf libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz
Для развертывания программы обнаружения лиц понадобится нативный драйвер для модели TensorFlow. Его можно скомпилировать из исходного кода на Rust, например из этого проекта.
# in the native_model_zoo/face_detection_mtcnn directory
$ cargo install --path .
Затем переходим к проекту веб-приложения. Запустите команду ssvmup для сборки функции WebAssembly из Rust.Эта функция WebAssembly осуществляет подготовку данных для веб-приложения.
# in the nodejs/face_detection_service directory
$ ssvmup build
Собрав функцию WebAssembly, можно запускать веб-приложение на Node.js.
$ npm i express express-fileupload uuid
$ cd node
$ node server.js
Теперь веб-сервис доступен на порту 8080 вашего компьютера. Попробуйте загрузить свои селфи, семейные или групповые фотографии!TensorFlow Model ZooПакет Rust facedetectionmtcnn — это обертка вокруг библиотеки TensorFlow. Она загружает обученную модель TensorFlow (так называемую замороженную сохраненную модель), передает входные данные в модель, выполняет модель и извлекает из нее выходные значения.В данном случае наша обертка извлекает только координаты рамок вокруг обнаруженных лиц. Модель также определяет уровни достоверности для каждого обнаруженного лица и положение глаз, рта и носа. Если изменить имена тензоров для извлечения данных, обертка получит и эту информацию и вернет ее в функцию WASM.Если вы захотите использовать другую модель, то создать для нее обертку по этому примеру будет несложно. Вам лишь нужно знать имена входного и выходного тензора и их типы данных.Для этого мы создали проект native model zoo, в рамках которого разрабатываем готовые к использованию обертки на языке Rust для самых разных моделей TensorFlow.Что дальшеВ этой статье мы разобрались, как создать AIaaS-приложение на Node.js с использованием Rust и WebAssembly для практического применения. Наш подход позволяет всем внести свой вклад в Model Zoo («зоопарк моделей»), который разработчики приложений смогут использовать в качестве ИИ-библиотеки.В следующей статье мы рассмотрим другую модель TensorFlow для классификации изображений и покажем, как добавить в обертку поддержку целого класса аналогичных моделей.
Узнать подробнее о курсе "Node.js Developer". Зарегистрироваться на открытый урок по теме "Докеризация node.js приложений" можно здесь.
===========
Источник:
habr.com
===========
===========
Автор оригинала: alabulei1
===========Похожие новости:
- [Тестирование IT-систем, Тестирование веб-сервисов, Тестирование мобильных приложений] Автоматизация тестирования приложений Salesforce (перевод)
- [JavaScript, Программирование, Учебный процесс в IT, Карьера в IT-индустрии] Веб-тренажёр Яндекс.Практикума. Как всё устроено
- [Промышленное программирование, Разработка робототехники, Программирование микроконтроллеров, Разработка под Arduino, Производство и разработка электроники] Портирование ModBus Slave RTU/ASCII на IAR AVR v3
- [Анализ и проектирование систем, Программирование микроконтроллеров, Управление проектами, Производство и разработка электроники, Транспорт] Опыт разработки системы управления для железнодорожной техники на отечественных микроконтроллерах
- [Программирование] Вы не знаете деструктуризацию, пока (перевод)
- [Ненормальное программирование, Занимательные задачки, Программирование, Искусственный интеллект] Russian AI Cup 2020 — новая игра-стратегия для разработчиков
- [Программирование микроконтроллеров] Полноценный трехпортовый USB-Serial адаптер на STM32 Blue Pill (STM32F103C8T6)
- [JavaScript, Программирование] Функции в JavaScript: секреты, о которых вы не слышали (перевод)
- [Python, Программирование, Машинное обучение] Определяем пол и возраст по фото
- [Разработка веб-сайтов, PHP, Программирование, Проектирование и рефакторинг] Сейчас я буду убеждать вас использовать статический анализ в PHP
Теги для поиска: #_javascript, #_programmirovanie (Программирование), #_node.js, #_rust, #_javascript, #_node.js, #_rust, #_webassembly, #_aiaas, #_blog_kompanii_otus._onlajnobrazovanie (
Блог компании OTUS. Онлайн-образование
), #_javascript, #_programmirovanie (
Программирование
), #_node.js, #_rust
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 17:15
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Будущим учащимся на курсе "Node.js Developer" предлагаем записаться на открытый урок по теме "Докеризация node.js приложений".
А пока делимся традиционным переводом статьи. В последней статье мы рассказывали, как вызывать функции Rust из Node.js. Сегодня мы расскажем, как написать приложение AIaaS (англ. Artificial Intelligence as a Service — «искусственный интеллект как услуга») на базе Node.js.Большинство приложений с искусственным интеллектом сейчас разрабатываются на языке Python, а главным языком программирования для веб-разработки является JavaScript. Для того чтобы реализовать возможности ИИ в вебе, нужно обернуть алгоритмы ИИ в JavaScript, а именно в Node.js.Однако ни Python, ни JavaScript сами по себе не подходят для разработки ИИ-приложений с большим объемом вычислений. Это высокоуровневые, медленные языки со сложной средой выполнения, в которых удобство использования достигается за счет снижения производительности. Для решения этой проблемы блоки интеллектуальных вычислений в Python оборачиваются в нативные C/C++-модули. Точно так же можно сделать и в Node.js, но мы нашли решение получше — WebAssembly.Виртуальные машины WebAssembly поддерживают тесную интеграцию с Node.js и другими средами выполнения JavaScript-кода. Они отличаются высокой производительностью, безопасны с точки зрения доступа к памяти, изначально защищены и совместимы с разными операционными системами. В нашем подходе сочетаются лучшие возможности WebAssembly и нативного кода.Как это работаетAIaaS-приложение на базе Node.js состоит из трех компонентов.
А теперь рассмотрим пример.Как можно реализовать функционал обнаружения лицВеб-сервис для обнаружения лиц позволяет пользователю загрузить фотографию и выделяет все обнаруженные лица зеленой рамкой.Исходный код Rust для реализации модели обнаружения лиц MTCNN создан по замечательной инструкции от Cetra: Обнаружение лиц с использованием библиотеки Tensorflow на Rust. Для того чтобы Tensorflow работала в WebAssembly, мы кое-что изменили. Приложение на Node.js отвечает за загрузку файла и вывод результата. app.post('/infer', function (req, res) {
let image_file = req.files.image_file; var result_filename = uuidv4() + ".png"; // Call the infer() function from WebAssembly (SSVM) var res = infer(req.body.detection_threshold, image_file.data); fs.writeFileSync("public/" + result_filename, res); res.send('<img src="' + result_filename + '"/>'); }); #[wasm_bindgen]
pub fn infer(detection_threshold: &str, image_data: &[u8]) -> Vec<u8> { let mut dt = detection_threshold; ... ... let mut img = image::load_from_memory(image_data).unwrap(); // Run the tensorflow model using the face_detection_mtcnn native wrapper let mut cmd = Command::new("face_detection_mtcnn"); // Pass in some arguments cmd.arg(img.width().to_string()) .arg(img.height().to_string()) .arg(dt); // The image bytes data is passed in via STDIN for (_x, _y, rgb) in img.pixels() { cmd.stdin_u8(rgb[2] as u8) .stdin_u8(rgb[1] as u8) .stdin_u8(rgb[0] as u8); } let out = cmd.output(); // Draw boxes from the result JSON array let line = Pixel::from_slice(&[0, 255, 0, 0]); let stdout_json: Value = from_str(str::from_utf8(&out.stdout).expect("[]")).unwrap(); let stdout_vec = stdout_json.as_array().unwrap(); for i in 0..stdout_vec.len() { let xy = stdout_vec[i].as_array().unwrap(); let x1: i32 = xy[0].as_f64().unwrap() as i32; let y1: i32 = xy[1].as_f64().unwrap() as i32; let x2: i32 = xy[2].as_f64().unwrap() as i32; let y2: i32 = xy[3].as_f64().unwrap() as i32; let rect = Rect::at(x1, y1).of_size((x2 - x1) as u32, (y2 - y1) as u32); draw_hollow_rect_mut(&mut img, rect, *line); } let mut buf = Vec::new(); // Write the result image into STDOUT img.write_to(&mut buf, image::ImageOutputFormat::Png).expect("Unable to write"); return buf; } fn main() -> Result<(), Box<dyn Error>> {
// Get the arguments passed in from WebAssembly let args: Vec<String> = env::args().collect(); let img_width: u64 = args[1].parse::<u64>().unwrap(); let img_height: u64 = args[2].parse::<u64>().unwrap(); let detection_threshold: f32 = args[3].parse::<f32>().unwrap(); let mut buffer: Vec<u8> = Vec::new(); let mut flattened: Vec<f32> = Vec::new(); // The image bytes are read from STDIN io::stdin().read_to_end(&mut buffer)?; for num in buffer { flattened.push(num.into()); } // Load up the graph as a byte array and create a tensorflow graph. let model = include_bytes!("mtcnn.pb"); let mut graph = Graph::new(); graph.import_graph_def(&*model, &ImportGraphDefOptions::new())?; let mut args = SessionRunArgs::new(); // The `input` tensor expects BGR pixel data from the input image let input = Tensor::new(&[img_height, img_width, 3]).with_values(&flattened)?; args.add_feed(&graph.operation_by_name_required("input")?, 0, &input); // The `min_size` tensor takes the detection_threshold argument let min_size = Tensor::new(&[]).with_values(&[detection_threshold])?; args.add_feed(&graph.operation_by_name_required("min_size")?, 0, &min_size); // Default input params for the model let thresholds = Tensor::new(&[3]).with_values(&[0.6f32, 0.7f32, 0.7f32])?; args.add_feed(&graph.operation_by_name_required("thresholds")?, 0, &thresholds); let factor = Tensor::new(&[]).with_values(&[0.709f32])?; args.add_feed(&graph.operation_by_name_required("factor")?, 0, &factor); // Request the following outputs after the session runs. let bbox = args.request_fetch(&graph.operation_by_name_required("box")?, 0); let session = Session::new(&SessionOptions::new(), &graph)?; session.run(&mut args)?; // Get the bounding boxes let bbox_res: Tensor<f32> = args.fetch(bbox)?; let mut iter = 0; let mut json_vec: Vec<[f32; 4]> = Vec::new(); while (iter * 4) < bbox_res.len() { json_vec.push([ bbox_res[4 * iter + 1], // x1 bbox_res[4 * iter], // y1 bbox_res[4 * iter + 3], // x2 bbox_res[4 * iter + 2], // y2 ]); iter += 1; } let json_obj = json!(json_vec); // Return result JSON in STDOUT println!("{}", json_obj.to_string()); Ok(()) } $ wget https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz
$ sudo tar -C /usr/ -xzf libtensorflow-gpu-linux-x86_64-1.15.0.tar.gz # in the native_model_zoo/face_detection_mtcnn directory
$ cargo install --path . # in the nodejs/face_detection_service directory
$ ssvmup build $ npm i express express-fileupload uuid
$ cd node $ node server.js Узнать подробнее о курсе "Node.js Developer". Зарегистрироваться на открытый урок по теме "Докеризация node.js приложений" можно здесь.
=========== Источник: habr.com =========== =========== Автор оригинала: alabulei1 ===========Похожие новости:
Блог компании OTUS. Онлайн-образование ), #_javascript, #_programmirovanie ( Программирование ), #_node.js, #_rust |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 17:15
Часовой пояс: UTC + 5