Назад | Далее | Содержание
Приложение
Эта страница охватывает множество функций управления форматами изображений, конвертации и предварительной/последующей обработки, реализованных в jetson-utils с использованием CUDA:
Управление изображениями
CUDA процедуры
cuda-examples.py
в дополнение к псевдокоду ниже. Перед тем как углубиться в эту тему, рекомендуется ознакомиться с предыдущей страницей о потоковой передаче видеосигнала и мультимедиа для получения информации о захвате и выводе видео, загрузке/сохранении изображений и т. д.## Форматы изображенийХотя API потоковой передачи видео и объекты DNN (например, imageNet
, detectNet
и segNet
) ожидают изображения в формате RGB/RGBA, определены различные другие форматы для приёма данных с датчиков и низкоуровневого ввода-вывода:Формат строки | imageFormat enum |
Тип данных | Глубина цвета |
---|---|---|---|
RGB/RGBA | rgb8 |
uchar3 |
24 |
rgba8 |
uchar4 |
32 | |
rgb32f |
float3 |
96 | |
rgba32f |
float4 |
128 | |
BGR/BGRA | bgr8 |
uchar3 |
24 |
bgra8 |
uchar4 |
32 | |
bgr32f |
float3 |
96 | |
bgra32f |
float4 |
128 | |
YUV (4:2:2) | yuyv |
uint8 |
16 |
yuy2 |
uint8 |
16 | |
yvyu |
uint8 |
16 | |
uyvy |
uint8 |
16 | |
YUV (4:2:0) | i420 |
uint8 |
12 |
yv12 |
uint8 |
12 | |
nv12 |
uint8 |
12 | |
Bayer | bayer-bggr |
uint8 |
8 |
bayer-gbrg |
uint8 |
8 | |
bayer-grbg |
uint8 |
8 | |
bayer-rggb |
uint8 |
8 | |
Grayscale | gray8 |
uint8 |
8 |
Примечание: в C++, форматы RGB/RGBA являются единственными, которые следует использовать с векторными типами
uchar3
/uchar4
/float3
/float4
. Предполагается, что при использовании этих типов изображения находятся в формате RGB/RGBA.Для конвертации изображений между форматами данных и/или цветовыми пространствами, см. раздел Конвертация цвета ниже.
Для выделения пустой памяти GPU для хранения промежуточных/выходных изображений (то есть рабочая память во время обработки), используйте одну из функций cudaAllocMapped()
на C++ или Python. Обратите внимание, что входные потоки videoSource
автоматически выделяют свою собственную память GPU и возвращают вам последнее изображение, поэтому вам не нужно выделять свою собственную память для этих потоков.
Память, выделенная с помощью функции cudaAllocMapped()
, находится в общей области памяти CPU/GPU, поэтому она доступна как для CPU, так и для GPU без необходимости выполнения копирования памяти между ними (поэтому она также называется памятью без копирования).
Однако требуется синхронизация — поэтому, если вы хотите получить доступ к изображению с CPU после обработки GPU, сначала вызовите функцию cudaDeviceSynchronize()
. Для освобождения памяти на C++ используйте функцию cudaFreeHost()
. В Python память будет автоматически освобождена сборщиком мусора, но вы можете явно освободить её с помощью оператора del
.Ниже приведен псевдокод на Python и C++ для выделения/синхронизации/освобождения памяти без копирования:
import jetson.utils
# выделение изображения размером 1920x1080 в формате rgb8
img = jetson.utils.cudaAllocMapped(width=1920, height=1080, format='rgb8')
# выполнение обработки на GPU здесь
...
# ожидание завершения обработки GPU
jetson.utils.cudaDeviceSynchronize()
# Python автоматически освободит память, но вы можете явно это сделать с помощью 'del'
del img
#include <jetson-utils/cudaMappedMemory.h>
void* img = NULL;
// Выделяем память для изображения размером 1920x1080 в формате rgb8
if( !cudaAllocMapped(&img, 1920, 1080, IMAGE_RGB8) )
return false; // ошибка памяти
// выполняем обработку на GPU здесь
...
// ждем завершения обработки на GPU
CUDA(cudaDeviceSynchronize());
// освобождаем память
CUDA(cudaFreeHost(img));
В C++ можно часто опускать явное указание перечисления imageFormat
, если указатели имеют тип uchar3/uchar4/float3/float4
. Ниже приведен эквивалентный по функциональности пример выделения памяти:
uchar3* img = NULL; // может быть uchar3 (rgb8), uchar4 (rgba8), float3 (rgb32f), float4 (rgba32f)
if( !cudaAllocMapped(&img, 1920, 1080) )
return false;
Примечание: при использовании этих векторных типов, изображения будут считаться в соответствующем цветовом пространстве RGB/RGBA. Если вы используете
uchar3/uchar4/float3/float4
для представления изображения, содержащего данные BGR/BGRA, некоторые функции обработки могут интерпретировать его как RGB/RGBA, если не указать правильный формат изображения явно.## Копирование изображений
cudaMemcpy()
можно использовать для копирования памяти между изображениями одного и того же формата и размера. cudaMemcpy()
— это стандартная функция CUDA в C++, и есть аналогичная версия для Python в библиотеке jetson.utils:
import jetson.utils
# Загружаем изображение и выделяем память для копирования
img_a = jetson.utils.loadImage("my_image.jpg")
img_b = jetson.utils.cudaAllocMapped(width=img_a.width, height=img_a.height, format=img_a.format)
# Копируем изображение (dst, src)
jetson.utils.cudaMemcpy(img_b, img_a)
# или можно использовать этот сокращенный вариант, который создаст дубликат
img_c = jetson.utils.cudaMemcpy(img_a)
#include <jetson-utils/cudaMappedMemory.h>
#include <jetson-utils/imageIO.h>
uchar3* img_a = NULL;
uchar3* img_b = NULL;
int width = 0;
int height = 0;
// Загружаем пример изображения
if( !loadImage("my_image.jpg", &img_a, &width, &height) )
return false; // ошибка загрузки
// Выделяем память для копирования
if( !cudaAllocMapped(&img_b, width, height) )
return false; // ошибка памяти
// Копируем изображение (dst, src)
if( CUDA_FAILED(cudaMemcpy(img_b, img_a, width * height * sizeof(uchar3), cudaMemcpyDeviceToDevice)) )
return false; // ошибка копирования
Когда вы выделяете изображение в Python или захватываете изображение из потока видео с помощью функции videoSource.Capture()
, она возвращает самодостаточный объект капсулы памяти (типа <jetson.utils.cudaImage>
) который можно передавать без необходимости копирования подлежащей памяти. Объект cudaImage
имеет следующие члены:```python
<jetson.utils.cudaImage>
.ptr # адрес памяти (не используется обычно)
.size # размер в байтах
.shape # кортеж (высота, ширина, каналы)
.width # ширина в пикселях
.height # высота в пикселях
.channels # количество цветовых каналов
.format # строка формата
.mapped # true, если используется ZeroCopy
Таким образом, вы можете выполнять действия, такие как `img.width` и `img.height`, чтобы получить доступ к свойствам изображения.
### Доступ к данным изображения в Python
CUDA изображения также являются индексируемыми, что позволяет вам индексировать их для прямого доступа к данным пикселей с помощью ЦПУ:
```python
for y in range(img.height):
for x in range(img.width):
pixel = img[y, x] # возвращает кортеж, например (r, g, b) для форматов RGB или (r, g, b, a) для форматов RGBA
img[y, x] = pixel # устанавливает пиксель из кортежа (длина кортежа должна соответствовать количеству каналов)
Примечание: оператор индексации Python доступен только если изображение было выделено в отображаемой памяти ZeroCopy (то есть с помощью функции
cudaAllocMapped()
). В противном случае данные недоступны для ЦПУ, и будет выброшено исключение.
Используемый для доступа к изображению индексируемый кортеж может принимать следующие формы:* img[y,x]
- обратите внимание на порядок кортежа (y,x), такой же как в numpy
img[y,x,channel]
- доступ к определенному каналу (например, 0 для красного, 1 для зеленого, 2 для синего, 3 для альфа)img[y*img.width+x]
- плоский одномерный индекс, доступ к всем каналам в данном пикселеХотя поддержка индексации изображений предусмотрена, индивидуальный доступ к каждому пикселю большого изображения не рекомендуется выполнять из Python, так как это значительно замедлит приложение. Предполагая, что реализация на GPU недоступна, лучшим альтернативным вариантом является использование NumPy.Вы можете получить доступ к капсуле памяти cudaImage
из NumPy, вызвав функцию jetson.utils.cudaToNumpy()
перед этим. Подлежащая память не копируется, и NumPy будет иметь прямой доступ к ней — поэтому будьте внимательны, если вы изменяете данные на месте через NumPy, они будут изменены и в капсуле cudaImage
. Для примера использования cudaToNumpy()
, см. образец cuda-to-numpy.py
из jetson-utils.
Обратите внимание, что OpenCV ожидает изображения в цветовом пространстве BGR, поэтому, если вы планируете использовать изображение с OpenCV, вам следует вызвать cv2.cvtColor()
с cv2.COLOR_RGB2BGR
перед использованием его в OpenCV.
Предположим, у вас есть изображение в массиве NumPy ndarray, возможно, предоставленное OpenCV. Как массив NumPy, оно будет доступно только с CPU. Вы можете использовать jetson.utils.cudaFromNumpy()
для копирования его на GPU (в общедоступную память CPU/GPU ZeroCopy).
Для примера использования cudaFromNumpy()
, см. образец cuda-from-numpy.py
из jetson-utils.Обратите внимание, что изображения OpenCV находятся в цветовом пространстве BGR, поэтому, если изображение поступает из OpenCV, вам следует вызвать cv2.cvtColor()
с cv2.COLOR_BGR2RGB
вначале.
Функция cudaConvertColor()
использует GPU для преобразования между форматами изображений и цветовыми пространствами. Например, вы можете преобразовать из RGB в BGR (или наоборот), из YUV в RGB, RGB в оттенки серого, и т.д. Вы также можете изменять тип данных и количество каналов (например, RGB8 на RGBA32F). Для получения дополнительной информации о различных форматах, доступных для преобразования, см. раздел Форматы изображений выше.
cudaConvertColor()
имеет следующие ограничения и не поддерживаемые преобразования:
uchar3
) и RGBA8 (uchar4
)Следующий псевдокод Python/C++ загружает изображение в формате RGB8 и преобразует его в RGBA32F (отметим, что это чисто иллюстративный пример, так как изображение может быть загружено напрямую в формате RGBA32F). Для более полного примера см. cuda-examples.py
.#### Python
import jetson.utils
```# Загрузите входное изображение (по умолчанию формат rgb8)
imgInput = jetson.utils.loadImage('my_image.jpg', format='rgb8') # по умолчанию формат 'rgb8', но может быть и 'rgba8', 'rgb32f', 'rgba32f'
# Выделите выходное изображение в формате rgba32f, с тем же шириной/высотой, что и у входного
imgOutput = jetson.utils.cudaAllocMapped(width=imgInput.width, height=imgInput.height, format='rgba32f')
# Преобразуйте из rgb8 в rgba32f (форматы для преобразования берутся из капсул изображений)
jetson.utils.cudaConvertColor(imgInput, imgOutput)
#include <jetson-utils/cudaColorspace.h>
#include <jetson-utils/cudaMappedMemory.h>
#include <jetson-utils/imageIO.h>
uchar3* imgInput = NULL; // входное изображение rgb8 (uchar3)
float4* imgOutput = NULL; // выходное изображение rgba32f (float4)
int width = 0;
int height = 0;
// Загрузите изображение в формате rgb8 (uchar3)
if( !loadImage("my_image.jpg", &imgInput, &width, &height) )
return false;
// Выделите выходное изображение в формате rgba32f (float4), с тем же шириной/высотой
if( !cudaAllocMapped(&imgOutput, width, height) )
return false;
// Преобразуйте из rgb8 в rgba32f
if( CUDA_FAILED(cudaConvertColor(imgInput, IMAGE_RGB8, imgOutput, IMAGE_RGBA32F, width, height)) )
return false; // произошла ошибка или не поддерживаемое преобразование
cudaResize()
использует GPU для изменения размера изображений (как уменьшение, так и увеличение). В следующем псевдокоде на Python/C++ загружается изображение, а затем изменяется его размер в определённом соотношении (в примере изображение уменьшается вдвое). Для более полного примера см. cuda-examples.py
.#### Pythonimport jetson.utils
# Загрузите входное изображение
imgInput = jetson.utils.loadImage('my_image.jpg')
# Выделите выходное изображение, с половиной размера входного
imgOutput = jetson.utils.cudaAllocMapped(width=imgInput.width * 0.5,
height=imgInput.height * 0.5,
format=imgInput.format)
# Измените размер изображения (размеры берутся из капсул изображений)
jetson.utils.cudaResize(imgInput, imgOutput)
#include <jetson-utils/cudaResize.h>
#include <jetson-utils/cudaMappedMemory.h>
#include <jetson-utils/imageIO.h>
// Загрузка входного изображения
uchar3* imgInput = NULL;
int inputWidth = 0;
int inputHeight = 0;
if (!loadImage("my_image.jpg", &imgInput, &inputWidth, &inputHeight))
return false;
// Выделяем память для выходного изображения, с половиной размера входного изображения
uchar3* imgOutput = NULL;
int outputWidth = inputWidth * 0.5f;
int outputHeight = inputHeight * 0.5f;
if (!cudaAllocMapped(&imgOutput, outputWidth, outputHeight))
return false;
// Масштабируем изображение
if (CUDA_FAILED(cudaResize(imgInput, inputWidth, inputHeight, imgOutput, outputWidth, outputHeight)))
return false;
Функция cudaCrop()
использует GPU для обрезки изображения до определенной области интереса (ROI). В следующем псевдокоде на Python/C++ загружается изображение, а затем обрезается по центру изображения. Для более полного примера см. cuda-examples.py
.
Обратите внимание, что координаты прямоугольников ROI предоставляются в формате (left, top, right, bottom)
.## Нормализация
Функция cudaNormalize()
использует GPU для изменения диапазона интенсивностей пикселей в изображении. Например, преобразует изображение с пиксельными значениями в диапазоне [0, 1]
в изображение с пиксельными значениями в диапазоне [0, 255]
. Другой распространенный диапазон для пиксельных значений — диапазон между [-1, 1]
.> Примечание: все остальные функции в jetson-inference и jetson-utils ожидают изображения с диапазоном пиксельных значений между [0, 255]
, поэтому обычно нет необходимости использовать cudaNormalize()
, но он доступен в случае, если вы работаете с данными из альтернативного источника или назначения.В следующем псевдокоде на Python/C++ загружаются два изображения, и они накладываются вместе рядом в выходном изображении.
import jetson.utils
# загрузка входных изображений
imgInputA = jetson.utils.loadImage('my_image_a.jpg')
imgInputB = jetson.utils.loadImage('my_image_b.jpg')
#include <jetson-utils/cudaOverlay.h>
#include <jetson-utils/cudaMappedMemory.h>
#include <jetson-utils/imageIO.h>
uchar3* imgInputA = NULL;
uchar3* imgInputB = NULL;
uchar3* imgOutput = NULL;
int widthA = 0;
int heightA = 0;
int widthB = 0;
int heightB = 0;
// загрузка входных изображений
if( !loadImage("my_image_a.jpg", &imgInputA, &widthA, &heightA) )
return false;
if( !loadImage("my_image_b.jpg", &imgInputB, &widthB, &heightB) )
return false;
```# Выделение выходного изображения с размерами, достаточными для размещения обоих входных изображений рядом
imgOutput = jetson.utils.cudaAllocMapped(width=imgInputA.width + imgInputB.width,
height=max(imgInputA.height, imgInputB.height),
format=imgInputA.format)# Накладка двух изображений (последние два аргумента — координаты x, y в выходном изображении)
jetson.utils.cudaOverlay(imgInputA, imgOutput, 0, 0)
jetson.utils.cudaOverlay(imgInputB, imgOutput, imgInputA.width, 0)
#include <jetson-utils/cudaOverlay.h>
#include <jetson-utils/cudaMappedMemory.h>
#include <jetson-utils/imageIO.h>
#include <algorithm> // для std::max()
uchar3* imgInputA = NULL;
uchar3* imgInputB = NULL;
uchar3* imgOutput = NULL;
int2 dimsA = make_int2(0, 0);
int2 dimsB = make_int2(0, 0);
// Загрузка входных изображений
if (!loadImage("my_image_a.jpg", &imgInputA, &dimsA.x, &dimsA.y))
return false;
if (!loadImage("my_image_b.jpg", &imgInputB, &dimsB.x, &dimsB.y))
return false;
// Выделение выходного изображения с размерами, достаточными для размещения обоих входных изображений рядом
const int2 dimsOutput = make_int2(dimsA.x + dimsB.x, std::max(dimsA.y, dimsB.y));
if (!cudaAllocMapped(&imgOutput, dimsOutput.x, dimsOutput.y))
return false;
// Накладка двух изображений (последние два аргумента — координаты x, y в выходном изображении)
CUDA(cudaOverlay(imgInputA, dimsA, imgOutput, dimsOutput, 0, 0));
CUDA(cudaOverlay(imgInputB, dimsB, imgOutput, dimsOutput, dimsA.x, 0));
cudaDraw.h
определяет несколько функций для рисования базовых фигур, включая круги, линии и прямоугольники.
Ниже приведены простые псевдокоды на Python и C++ для использования этих функций — см. cuda-examples.py
для функционирующего примера.
# Загрузка входного изображения
input = jetson.utils.loadImage("my_image.jpg")
```# jetson.utils.cudaDrawCircle(input, (cx, cy), radius, (r, g, b, a), output=None)
jetson.utils.cudaDrawCircle(input, (50, 50), 25, (0, 255, 127, 200))
# jetson.utils.cudaDrawRect(input, (left, top, right, bottom), (r, g, b, a), output=None)
jetson.utils.cudaDrawRect(input, (200, 25, 350, 250), (255, 127, 0, 200))
# jetson.utils.cudaDrawLine(input, (x1, y1), (x2, y2), (r, g, b, a), line_width, output=None)
jetson.utils.cudaDrawLine(input, (25, 150), (325, 15), (255, 0, 200, 200), 10)
Примечание: Если не указано обязательное изображение
output
, операция будет выполняться на месте на изображенииinput
.
#include <jetson-utils/cudaDraw.h>
#include <jetson-utils/imageIO.h>
uchar3* img = NULL;
int width = 0;
int height = 0;
// Загрузка примерного изображения
if (!loadImage("my_image.jpg", &img, &width, &height))
return false; // Ошибка загрузки
// См. cudaDraw.h для определений
CUDA(cudaDrawCircle(img, width, height, 50, 50, 25, make_float4(0, 255, 127, 200)));
CUDA(cudaDrawRect(img, width, height, 200, 25, 350, 250, make_float4(255, 127, 0, 200)));
CUDA(cudaDrawLine(img, width, height, 25, 150, 325, 15, make_float4(255, 0, 200, 200), 10));
Далее | Узлы глубокого обучения для ROS/ROS2
Назад | Трансляция видеокамеры и мультимедиа
© 2016-2020 NVIDIA | Содержание
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )