1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/duping812-jetson-inference

Клонировать/Скачать
aux-image.md 32 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 02.06.2025 15:38 5e07e18

Назад | Далее | Содержание
Приложение

Манипуляции с изображениями с использованием CUDA

Эта страница охватывает множество функций управления форматами изображений, конвертации и предварительной/последующей обработки, реализованных в jetson-utils с использованием CUDA:

Управление изображениями

CUDA процедуры

Формат строки 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
  • Глубина цвета представляет собой количество бит на пиксель.
  • Для подробных спецификаций форматов YUV обратитесь к [fourcc. org](http://fourcc. org/yuv. php).

Примечание: в 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++ для выделения/синхронизации/освобождения памяти без копирования:

Python

import jetson.utils

# выделение изображения размером 1920x1080 в формате rgb8
img = jetson.utils.cudaAllocMapped(width=1920, height=1080, format='rgb8')

# выполнение обработки на GPU здесь
...

# ожидание завершения обработки GPU
jetson.utils.cudaDeviceSynchronize()

# Python автоматически освободит память, но вы можете явно это сделать с помощью 'del'
del img

C++

#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:

Python

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)

C++

#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

Когда вы выделяете изображение в 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.

Преобразование в массивы 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

Предположим, у вас есть изображение в массиве 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() имеет следующие ограничения и не поддерживаемые преобразования:

  • Форматы YUV не поддерживают BGR/BGRA или оттенки серого (только RGB/RGBA)
  • YUV NV12, YUYV, YVYU и UYVY могут быть преобразованы только в RGB/RGBA (не из)
  • Форматы Bayer могут быть преобразованы только в RGB8 (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)

C++

#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.#### Python

import 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)

C++

#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++ загружаются два изображения, и они накладываются вместе рядом в выходном изображении.

Python

import jetson.utils

# загрузка входных изображений
imgInputA = jetson.utils.loadImage('my_image_a.jpg')
imgInputB = jetson.utils.loadImage('my_image_b.jpg')

C++

#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)

C++

#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 для функционирующего примера.

Python

# Загрузка входного изображения
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.

C++

#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 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/duping812-jetson-inference.git
git@api.gitlife.ru:oschina-mirror/duping812-jetson-inference.git
oschina-mirror
duping812-jetson-inference
duping812-jetson-inference
master