npp

  • visual studio project - template cuda 12.3

  • header:
    • C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.3\include
    • npp.h, nppdefs.h, nppcore.h, nppi.h, npps.h
    • visual studio > project > 우클릭 > properties > CUDA C/C++ > Common > Additional include directories
  • library:
    • CUDA Linker
      • C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.3\lib\x64
    • dependecies
      • npps: NPP signal processing library
      • nppi.lib: NPP image processing library
      • nppc.lib: NPP Core Library 기본적인 npp 기능
  • dll
    • C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.3\bin
    • npps64_12.dll
    • nppc64_12.dll: module nppc / 64 bit / cuda version 12
    • nppial64_12.dll: NPP image arithmetic and logical operations
    • nppicc64_12.dll: NPP image color conversion
    • nppidei64_12.dll: NPP Image Filtering
    • nppif64_12.dll: NPP Image Fourier Transform
    • nppig64_12.dll: NPP Image Geometry Transformation
    • nppim64_12.dll: NPP Image Morphological Operation
    • nppist64_12.dll: NPP Image Statistics
    • nppisu64_12.dll: NPP Image Support Functions 보조함수들
    • nppitc64_12.dll: NPP Image Thresholding and Compare Operations

관리형 스트림 컨텍스

  • 개념

Compute Capability

Memory

  • cudaMemcpy(…) host to device
  • cuda kernel
  • cudaMemcopy(…) device to host

Scratch Buffer and Host Pointer

  • NPP의 일부 기본 요소는 계산을 위한 추가 디바이스 메모리 버퍼(스크래치 버퍼)가 필요합니다. 예를 들어, 신호 및 이미지 축소(Sum, Max, Min, MinMax 등)에 해당합니다. 메모리 할당 및 성능과 관련된 사용자의 최대 제어를 제공하기 위해 임시 버퍼를 할당하고 삭제하는 것은 사용자의 책임입니다. 이렇게 하면 라이브러리가 사용자에게 알리지 않고도 메모리를 할당하지 않게 되는 이점이 있습니다. 또한 동일한 원시 함수를 여러 번 호출하는 개발자가 스크래치를 한 번만 할당하여 성능 및 장치 메모리 조각화 가능성을 개선할 수 있습니다.

스크래치 버퍼 메모리는 구조화되지 않으며 초기화되지 않은 상태로 원시 함수에 전달될 수 있습니다. 이로 인해 충분한 크기로 스크래치 버퍼를 재사용할 수 있습니다.

특정 원시 함수(예: nppsSum_32f())에 대한 최소 스크래치 버퍼 크기는 동반 함수(예: nppsSumGetBufferSize_32f())를 사용하여 얻을 수 있습니다. 버퍼 크기는 호스트 포인터를 통해 반환되며 스크래치 버퍼의 할당은 CUDA 런타임 호스트 코드를 통해 수행됩니다.

신호 합 원시 함수를 호출하고 필요한 스크래치 메모리를 할당하고 해제하는 예제:

// pSrc, pSum, pDeviceBuffer는 모두 디바이스 포인터입니다.
Npp32f * pSrc;
Npp32f * pSum;
Npp8u * pDeviceBuffer;
int nLength = 1024;

// 디바이스 메모리 할당
cudaMalloc((void **)(&pSrc), sizeof(Npp32f) * nLength);
nppsSet_32f(1.0f, pSrc, nLength);
cudaMalloc((void **)(&pSum), sizeof(Npp32f) * 1);

// 스크래치 메모리 버퍼의 적절한 크기를 계산합니다.
int nBufferSize;
nppsSumGetBufferSize_32f(nLength, &nBufferSize);
// 스크래치 버퍼 할당
cudaMalloc((void **)(&pDeviceBuffer), nBufferSize);

// 스크래치 버퍼와 함께 원시 함수 호출
nppsSum_32f(pSrc, nLength, pSum, pDeviceBuffer);
Npp32f nSumHost;
cudaMemcpy(&nSumHost, pSum, sizeof(Npp32f) * 1, cudaMemcpyDeviceToHost);
printf("sum = %f\n", nSumHost); // nSumHost = 1024.0f;

// 디바이스 메모리 해제
cudaFree(pSrc);
cudaFree(pDeviceBuffer);
cudaFree(pSum);
  • 이렇게 하면 스크래치 메모리를 할당하고 관리하여 NPP 함수를 사용할 수 있습니다.

General Convnetions

Function Naming

  • NPP은 C API이기 때문에 다른 데이터 유형에 대한 함수 오버로딩을 허용하지 않으며, 이에 대한 함수 명명 규칙은 동일한 알고리즘 또는 기본 함수의 다른 데이터 유형 버전을 구별해야 하는 필요에 대응합니다. 이 기본 함수의 다른 버전을 구별하기 위해 데이터 유형 및 기타 구별 정보를 포함하는 접미사를 사용하여 이 구분을 해결합니다.

또한, 모든 NPP 함수는 “npp”라는 접두사로 시작하며, NPP의 이미지 처리 모듈에 속하는 기본 함수는 “nppi”라는 접두사가 추가됩니다. 마찬가지로 신호 처리 기본 함수는 “npps”로 시작합니다.

일반적인 명명 규칙은 다음과 같습니다:

npp__ 데이터 유형 정보는 Basic NPP Data Types와 동일한 이름을 사용합니다. 예를 들어 데이터 유형 정보 "8u"는 Npp8u 데이터에서 작동하는 것을 의미합니다.

원시 함수가 소비하는 데이터 유형이 생산하는 데이터 유형과 다른 경우, 두 유형이 소비된 순서로 나열됩니다.

“additional flavor information”에 대한 자세한 내용은 각 NPP 모듈에 대해 제공되며, 각 문제 영역이 다른 flavor 정보 접미사를 사용합니다.

Integer Result Scaling

  • NPP 신호 처리 및 이미지 기본 함수는 주로 정수 데이터에서 작동합니다. 이 정수 데이터는 일반적으로 어떤 물리적인 크기 (예: 조도)의 고정 소수점 표현입니다. 이러한 고정 소수점 표현의 특성으로 인해 많은 숫자 연산 (예: 덧셈 또는 곱셈)은 일반적인 정수로 처리할 경우 원래의 고정 소수점 범위를 초과하는 결과를 생성할 가능성이 있습니다.

결과가 원래 범위를 초과하는 경우 이러한 함수는 결과 값을 다시 유효한 범위 내로 클램핑합니다. 예를 들어, 16비트 무부호 정수의 최대 양수 값은 32767입니다. 4 * 10000 = 40000과 같은 곱셈 연산은 이 범위를 초과합니다. 결과는 32767로 클램핑됩니다.

클램핑으로 인한 정보 손실의 수준을 피하기 위해 대부분의 정수 기본 함수는 결과 스케일링을 허용합니다. 결과 스케일링이 있는 기본 함수의 이름에는 “Sfs” 접미사가 포함되며, “nScaleFactor”라는 매개변수를 제공하여 스케일링 양을 제어합니다. 연산의 결과가 유효한 출력 데이터 범위로 클램핑되기 전에 결과를 2^-nScaleFactor로 곱셈하여 스케일링합니다.

  • 예제: nppsSqr_8u_Sfs() 기본 함수는 신호 (값의 1차원 배열) 내의 8비트 무부호 샘플 값의 제곱을 계산합니다. 8비트 값의 최대값은 255입니다. 255의 제곱은 결과 스케일링을 수행하지 않는 경우 255로 클램핑될 것입니다. 최대값 255를 결과값의 255로 매핑하려면 정수 결과 스케일링 계수로 8을 지정해야 합니다. 즉, 각 결과를 2^-8 = 1 / 2^8 = 1/256로 곱합니다. 따라서 제곱하고 스케일링된 255의 최종 결과는 다음과 같습니다: 255^2*2^-8=254.00390625 이 값은 최종 결과로 254로 반올림될 것입니다.

중간 회색값인 128은 다음과 같이 계산됩니다: 128^2*2^-8=64

Rounding Mode

NPP 라이브러리에서는 부동 소수점 값을 정수로 변환해야 하는 많은 함수가 있습니다. Rounding Modes 열거형은 NPP에서 지원하는 반올림 모드를 나열합니다. NPP의 기능 중에서는 사용자가 사용할 반올림 모드를 지정할 수 있는 함수도 있지만, 모든 NPP 함수가 이를 지원하지는 않습니다. 대신 NPP의 기본 반올림 모드, 즉 NPP_RND_FINANCIAL 모드가 사용됩니다.

Rounding Mode 매개변수 일부 NPP 함수는 기능의 일부로 반올림을 수행하며 사용자가 사용할 반올림 모드를 Rounding Modes 유형의 매개변수를 통해 지정할 수 있습니다.

Image Processing Conventions

Function Naming

  • 이미지 처리 관련 함수의 이름에는 여러 가지 다른 기능을 나타내는 서로 다른 종류의 접미사(suffix)가 사용됩니다. 이러한 기능 접미사는 다음 약어를 사용하여 표시됩니다.

“A”: 이미지가 4채널 이미지인 경우, 결과 알파 채널이 해당 기본 작업에 영향을 받지 않음을 나타냅니다. “Cn”: 이미지는 n채널로 패킹된 픽셀로 구성되며, n은 1, 2, 3 또는 4일 수 있습니다. “Pn”: 이미지는 n개의 별도의 이미지 평면으로 구성되며, n은 1, 2, 3 또는 4일 수 있습니다. “C” (채널 정보 뒤에 따라)는 기본 작업이 컬러 채널 중 하나, “channel-of-interest”만 처리함을 나타냅니다. 다른 출력 채널은 해당 기본 작업에 영향을받지 않습니다. “I”: 기본 작업이 “in-place”로 작동함을 나타냅니다. 이 경우 이미지 데이터 포인터는 일반적으로 이미지 데이터가 동시에 소스와 대상 역할을 함을 나타내는 pSrcDst로 명명됩니다. “M”: “masked operation” 작업을 나타냅니다. 이러한 유형의 기본 작업은 추가 “마스크 이미지”를 입력으로 사용합니다. 대상 이미지의 각 픽셀은 마스크 이미지의 해당 픽셀에 해당합니다. 해당 마스크 픽셀이 있는 픽셀만 처리됩니다. “R”: 기본 작업이 직사각형 ROI(region_of_interest)에서만 작동함을 나타냅니다. 모든 ROI 기본 작업은 처리할 직사각형 영역의 너비와 높이를 지정하는 NppiSize 유형의 추가 입력 매개변수를 가져옵니다. “Sfs”: 결과 값이 고정 스케일링 및 포화 처리되어 쓰여짐을 나타냅니다. 위의 접미사는 항상 알파벳 순서대로 나타납니다. 예를 들어, 알파 채널에 영향을 미치지 않는 4채널 기본 작업, 마스크 작업, 인플레이스 및 스케일링/포화 및 ROI가 있는 경우 접미사는 “AC4IMRSfs”가 됩니다.

Image Data

  • NPPI 기본 작업에 이미지 데이터는 다음과 같은 두 가지 매개변수 쌍을 통해 전달됩니다.

이미지의 기본 데이터 유형에 대한 포인터. 바이트로 표시된 라인 스텝(때로는 라인 스트라이드라고도 함). 이러한 이미지 데이터를 전달하는 상대적으로 낮은 수준의 방식의 일반적인 아이디어는 기존 소프트웨어 프로젝트에 쉽게 적용될 수 있도록 하는 것입니다.

이미지 데이터의 구조화된(색상별) 채널 픽셀 데이터가 아닌 기본 픽셀 데이터 유형에 대한 원시 포인터를 전달하면 위험한 형 변환을 피하거나 비용이 많이 드는 이미지 데이터 복사를 피할 수 있습니다. 데이터 포인터와 라인 스텝을 개별적으로 전달하면 더 높은 수준의 이미지 구조체를 필요로하지 않으므로 특정 이미지 표현을 요구하지 않고 호스트 응용 프로그램에서 NPP 특정 이미지 표현으로의 불편한 패킹 및 언패킹을 피할 수 있습니다.

Line step

  • 라인 스텝(또는 “라인 스트라이드” 또는 “행 스텝”이라고도 함)은 이미지의 행 크기가 홀수로 지정된 이미지의 경우, 라인 끝에 일부 사용되지 않는 바이트를 추가하여 잘 정렬된 주소에서 시작할 수 있도록 하는 것을 허용합니다. 이러한 유형의 라인 패딩은 오랫동안 디지털 이미지 처리에서 일반적인 실천 방식이며 GPU 이미지 처리에 특정한 것은 아닙니다.

라인 스텝은 패딩을 포함한 한 라인의 바이트 수입니다. 이 숫자를 해석하는 다른 방법은 이것이 이미지의 연속된 행의 첫 번째 픽셀 사이의 바이트 수, 또는 일반적으로 픽셀의 열에서 이웃하는 두 픽셀 사이의 바이트 수임을 말하는 것입니다.

라인 스텝의 존재의 일반적인 이유는 메모리 액세스 패턴의 최적화를 가능하게 하는 픽셀의 균일하게 정렬된 행이기 때문입니다.

NPP의 모든 함수가 임의로 정렬된 이미지에서 작동하더라도 최상의 성능은 잘 정렬된 이미지 데이터로만 얻을 수 있습니다. NPP 이미지 할당자 또는 CUDA 런타임의 2D 메모리 할당자를 사용하여 할당된 모든 이미지 데이터는 잘 정렬되어 있습니다.

특히 이전 CUDA 호환 GPU에서는 정렬되지 않은 데이터의 성능 저하가 상당할 것으로 예상되며(수십 배 이상), NPPI 원시에서 제공하는 성능 최적화가 이러한 이미지 데이터의 정렬 여부에 영향을 미칩니다.

NPPI 원시에 전달되는 모든 이미지 데이터에는 라인 스텝이 제공되어야 합니다. 이 라인 스텝은 항상 픽셀 단위가 아닌 바이트 단위로 지정되어야 함을 기억하는 것이 중요합니다.

Parameter Names for Image Data

  • 여기에는 NPP를 통해 이미지 데이터를 전달하는 세 가지 일반적인 경우가 있습니다.

소스 이미지 데이터 전달

이것은 알고리즘에서 사용하는 이미지입니다. 소스 이미지 데이터는 일반적으로 다음과 같은 이름의 포인터를 통해 전달됩니다. pSrc 소스 이미지 포인터는 일반적으로 상수로 정의되며, 해당 포인터로 가리키는 이미지 데이터를 변경하지 않도록 합니다. 예: nppiPrimitive_32s_C1R(const Npp32s * pSrc, …) 원시가 여러 이미지를 입력으로 사용하는 경우 소스 포인터는 다음과 같이 번호가 매겨집니다. pSrc1, pSrc2, … 소스-배치-이미지 데이터 전달

소스 이미지 데이터의 배치는 일반적으로 NppiImageDescriptor 유형의 포인터를 통해 전달됩니다. 소스 배치 포인터는 일반적으로 상수로 정의되며 해당 포인터로 가리키는 소스 데이터를 변경하지 않도록 합니다. 예: nppiYUVToRGBBatch_8u_C3R(NppiSize oSizeROI, const NppiImageDescriptor* pSrcBatchList, …) 배치 데이터를 처리하는 모든 원시는 배치의 크기를 별도의 매개변수로 제공해야 합니다. 소스-플레이너-이미지 포인터 배열

플레이너 소스 이미지 데이터는 다음과 같이 이름이 있는 포인터 배열을 통해 전달됩니다. pSrc[] 플레이너 소스 이미지 포인터 배열은 일반적으로 상수 포인터 배열로 정의되며, 해당 포인터로 가리키는 이미지 데이터를 변경하지 않도록 합니다. 예: nppiPrimitive_8u_P3R(const Npp8u * const pSrc[3], …) 배열의 각 포인터는 다른 이미지 평면을 가리킵니다. 소스-플레이너-이미지 포인터

다중 플레인 소스 이미지 데이터는 다음과 같이 다른 입력 이미지 평면을 가리키는 일련의 포인터를 통해 전달됩니다. pSrc1, pSrc2, … 소스 이미지 라인 스텝

소스 이미지 라인 스텝은 이미지의 연속된 행 사이의 바이트 수입니다. 소스 이미지 라인 스텝 매개변수는 다음과 같이 정의됩니다. nSrcStep 또는 여러 소스 이미지의 경우 nSrcStep1, nSrcStep2, … 소스 플레이너 이미지 라인 스텝 배열

소스 플레이너 이미지 라인 스텝 배열은 배열의 각 요소가 입력 이미지의 특정 평면에 대한 연속된 행 사이의 바이트 수를 포함하는 배열입니다. 소스 플레이너 이미지 라인 스텝 배열 매개변수는 다음과 같이 정의됩니다. rSrcStep[] 소스 플레이너 이미지 라인 스텝

소스 플레이너 이미지 라인 스텝은 다중 플레인 출력 이미지의 특정 평면에 대한 연속된 행 사이의 바이트 수입니다. 소스 플레이너 이미지 라인 스텝 매개변수는 다음과 같이 정의됩니다. nSrcStep1, nSrcStep2, … 대상 이미지 데이터 전달

이것은 알고리즘에서 생성된 이미지입니다. 대상 이미지 데이터는 일반적으로 다음과 같은 이름의 포인터를 통해 전달됩니다. pDst 원시가 여러 이미지를 출력으로 생성하는 경우 대상 포인터는 다음과 같이 번호가 매겨집니다. pDst1, pDst2, … 대상-배치-이미지 데이터 포인터

대상 이미지 데이터의 배치는 일반적으로 NppiImageDescriptor 유형의 포인터를 통해 전달됩니다. 배치 데이터를 처리하는 모든 원시는 배치의 크기를 별도의 매개변수로 제공해야 합니다. 대상-플레이너-이미지 포인터 배열

플레이너 대상 이미지 데이터 포인터는 일반적으로 다음과 같이 이름이 있는 포인터 배열을 통해 전달됩니다. pDst[] 배열의 각 포인터는 다른 이미지 평면을 가리킵니다. 대상-플레이너-이미지 포인터

대상 플레이너 이미지 데이터는 다중 플레인 출력 이미지의 각 평면에 대한 포인터로 전달됩니다. pDst1, pDst2, … 대상 이미지 라인 스텝

대상 이미지 라인 스텝 매개변수는 다음과 같이 정의됩니다. nDstStep 또는 여러 대상 이미지의 경우 nDstStep1, nDstStep2, … 대상 플레이너 이미지 라인 스텝

대상 플레이너 이미지 라인 스텝은 다중 플레인 출력 이미지의 특정 평면에 대한 연속된 행 사이의 바이트 수입니다. 대상 플레이너 이미지 라인 스텝 매개변수는 다음과 같이 정의됩니다. nDstStep1, nDstStep2, … 인플레이스 이미지 데이터 전달

인플레이스 처리의 경우, 소스와 대상은 동일한 포인터에 의해 제공되므로 인플레이스 이미지 데이터에 대한 포인터를 “pSrcDst”라고 합니다. 인플레이스 이미지 라인 스텝

인플레이스 라인 스텝 매개변수는 다음과 같이 정의됩니다. nSrcDstStep 마스크 이미지 데이터 전달

일부 이미지 처리 원시에는 마스크 작업을 지원하는 변형이 있습니다. 마스크 이미지 포인터

마스크 이미지 데이터는 일반적으로 다음과 같은 이름의 포인터를 통해 전달됩니다. pMask 마스크 이미지 라인 스텝

마스크 이미지 라인 스텝 매개변수는 다음과 같이 정의됩니다. nMaskStep 관심 채널 데이터 전달

일부 이미지 처리 원시는 관심 채널을 지원합니다. 관심 채널 번호

관심 채널 데이터는 일반적으로 정수 (1, 2, 또는 3)입니다. nCOI

변수 이름 설명
소스 이미지 데이터 전달 이미지 처리에 사용되는 입력 이미지
소스 이미지 포인터 pSrc
소스 배치 이미지 포인터 pSrcBatchList
소스 플레이너 이미지 포인터 배열 pSrc[]
소스 플레이너 이미지 포인터 pSrc1, pSrc2, …
소스 이미지 라인 스텝 nSrcStep, nSrcStep1, nSrcStep2, …
소스 플레이너 이미지 라인 스텝 배열 rSrcStep[]
대상 이미지 데이터 전달 이미지 처리에 의해 생성된 출력 이미지
대상 이미지 포인터 pDst, pDst1, pDst2, …
대상 배치 이미지 포인터 pDstBatchList
대상 플레이너 이미지 포인터 배열 pDst[]
대상 플레이너 이미지 포인터 pDst1, pDst2, …
대상 이미지 라인 스텝 nDstStep, nDstStep1, nDstStep2, …
대상 플레이너 이미지 라인 스텝 배열 nDstStep1, nDstStep2, …
인플레이스 이미지 데이터 전달 동일한 포인터를 통해 소스와 대상이 제공되는 경우의 이미지 데이터
인플레이스 이미지 포인터 pSrcDst
인플레이스 이미지 라인 스텝 nSrcDstStep
마스크 이미지 데이터 전달 일부 원시에서 지원하는 마스크 작업에 사용되는 이미지 데이터
마스크 이미지 포인터 pMask
마스크 이미지 라인 스텝 nMaskStep
관심 채널 데이터 전달 일부 원시에서 지원하는 관심 채널에 대한 데이터
관심 채널 번호 nCOI

Image Data Alignment Requirements

  • Image Data Alignment Requirements에 따라 NPP에서 이미지 데이터는 특정 정렬 제약을 따라야 합니다.

2 채널 및 4 채널 이미지의 경우 다음 정렬 요구 사항이 적용됩니다.

data_pointer % (#channels * sizeof(channel type)) == 0

예를 들어, 기본 유형이 Npp8u (8비트 무부호)인 4 채널 이미지의 경우 모든 픽셀은 4의 배수인 주소에 있어야 합니다 (4 채널 * 1 바이트 크기).

모든 픽셀이 자연 크기에 정렬되어 있기 때문에 2 및 4 채널 이미지의 이미지 라인 스텝도 픽셀 크기의 배수이어야 합니다.

1 채널 및 3 채널 이미지의 경우 픽셀 포인터가 기본 데이터 유형에 정렬되어 있어야 하며, 따라서 라인 스텝도 이 요구 사항을 준수해야 합니다.

  • 이미지 데이터와 관련된 오류 코드는 다음과 같습니다.

NPP_STEP_ERROR: 데이터 스텝이 0 또는 음수인 경우 반환됩니다. NPP_NOT_EVEN_STEP_ERROR: 라인 스텝이 2 및 4 채널 이미지의 픽셀 크기의 배수가 아닌 경우 반환됩니다. NPP_NULL_POINTER_ERROR: 이미지 데이터 포인터가 0 (NULL)인 경우 반환됩니다. NPP_ALIGNMENT_ERROR: 이미지 데이터 포인터 주소가 2 및 4 채널 이미지의 픽셀 크기의 배수가 아닌 경우 반환됩니다. 이러한 오류 중 하나가 발생하면 해당 프리미티브가 실행되지 않고 오류 코드가 반환됩니다.

Region-Of-Interest (ROI)

  • 영역-오브-인터레스트(Region-Of-Interest, ROI)는 이미지의 직사각형 하위 영역을 처리하는 데 자주 사용됩니다. NPP의 대부분의 이미지 처리 프리미티브는 ROI(Region-Of-Interest) 처리를 지원하며 이러한 하위 영역은 일반적으로 ROI 또는 ROIs(Regions-Of-Interest)로도 참조됩니다.

ROI 처리를 지원하는 모든 프리미티브에는 이름 접미사로 “R”이 표시됩니다. 대부분의 경우 ROI는 폭과 높이를 제공하는 단일 NppiSize 구조체로 전달됩니다. 이로 인해 프리미티브가 (폭, 높이)의 이 사각형이 이미지에서 어디에 있는지를 어떻게 알 수 있는지라는 질문이 제기됩니다. ROI의 “시작 픽셀”은 이미지 데이터 포인터에 의해 암시적으로 제공됩니다. 즉, 좌측 상단 (가장 낮은 메모리 주소)의 픽셀 좌표를 명시적으로 전달하는 대신 사용자는 이미지 데이터 포인터를 ROI의 첫 번째 픽셀을 가리키도록 오프셋을 지정합니다.

실제로 이것은 이미지 (pSrc, nSrcStep)와 ROI의 시작 픽셀이 위치 (x, y)에 있는 경우 다음과 같이 전달됩니다.

pSrcOffset = pSrc + y * nSrcStep + x * PixelSize; 이것은 프리미티브에 대한 이미지 데이터 원본입니다. 여기서 PixelSize는 일반적으로 다음과 같이 계산됩니다.

PixelSize = NumberOfColorChannels * sizeof(PixelDataType); 예를 들어, nppiSet_16s_C4R()과 같은 프리미티브의 경우 다음과 같습니다.

NumberOfColorChannels == 4;

sizeof(Npp16s) == 2;

그래서 PixelSize = 4 * 2 = 8;

ROI(Region-Of-Interest) 이미지 데이터의 모든 NPPI 프리미티브는 ROI 크기 및 이미지의 단계 크기를 유효성 검사합니다. 유효성 검사에 실패하면 다음과 같은 오류 코드 중 하나가 반환되고 해당 프리미티브가 실행되지 않습니다:

NPP_SIZE_ERROR: ROI 폭 또는 ROI 높이 중 하나라도 음수인 경우 반환됩니다. NPP_STEP_ERROR: ROI 폭이 이미지의 라인 스텝을 초과하는 경우 반환됩니다. 수학적으로 (widthROI * PixelSize) > nLinStep인 경우 오류가 발생합니다. 이러한 오류 코드를 처리하여 프리미티브의 안전한 실행을 보장할 수 있습니다.

Masked Operation

몇몇 프리미티브는 마스크된 작업을 지원합니다. 이러한 변형의 접미사에 “M”이 포함되어 있으면 마스크된 작업을 나타냅니다. 마스크된 작업을 지원하는 프리미티브는 마스크 이미지 포인터와 마스크 이미지 라인 스텝을 통해 제공되는 추가 입력 이미지를 사용합니다. 마스크 이미지는 이러한 프리미티브에 의해 부울 이미지로 해석됩니다. Npp8u 형식의 값은 0이면 false를 나타내고 0이 아닌 값은 true를 나타냅니다.

그렇지 않은 한, 작업은 공간적으로 해당하는 마스크 픽셀이 true(0이 아닌)인 경우에만 수행됩니다. 예를 들어 마스크된 복사 작업은 ROI 내에서 해당하는 0이 아닌 마스크 픽셀을 가진 픽셀만 복사합니다.

Channel-of-Interest API

일부 프리미티브는 다중 채널 이미지 내에서 작업을 특정 채널로 제한할 수 있게 합니다. 이러한 프리미티브들은 “C”로 끝나며 (채널 정보 뒤에 오며 예: nppiCopy_8u_C3CR()), 채널-오브-인터레스트를 선택하려면 일반적으로 이미지 데이터 포인터를 채널-오브-인터레스트를 직접 가리키도록 오프셋을 주는 방식으로 선택됩니다. 일부 프리미티브는 선택한 채널 번호를 명시적으로 지정하고 정수를 통해 전달합니다. (예: nppiMean_StdDev_8u_C3CR())

Select-Channel Source-Image Pointer

이것은 소스 이미지의 첫 번째 픽셀 내에서 채널-오브-인터레스트를 가리키는 포인터입니다. 예를 들어 pSrc가 세 개의 채널 이미지의 ROI 내 첫 번째 픽셀을 가리키는 포인터인 경우, 적절한 채널 선택 복사 프리미티브를 사용하여 포인터를 오프셋하여 이 소스 이미지의 두 번째 채널을 pDst로 주어진 대상 이미지의 첫 번째 채널로 복사할 수 있습니다.

nppiCopy_8u_C3CR(pSrc + 1, nSrcStep, pDst, nDstStep, oSizeROI);

Select-Channel Source-Image

일부 프리미티브는 사용자가 채널 번호 (nCOI)를 지정하여 채널-오브-인터레스트를 선택할 수 있게 해줍니다. 이 접근 방식은 일반적으로 이미지 통계 함수에서 사용됩니다. 예를 들어,

nppiMean_StdDev_8u_C3CR(pSrc, nSrcStep, oSizeROI, nCOI, pDeviceBuffer, pMean, pStdDev);

채널-오브-인터레스트 번호는 1, 2 또는 3일 수 있습니다.

Select-Channel Destination-Image Pointer

이것은 대상 이미지의 첫 번째 픽셀 내에서 관심 채널을 가리키는 포인터입니다. 예를 들어, pDst가 세 채널 이미지의 ROI 내 첫 번째 픽셀을 가리킨다면, 적절한 선택 채널 복사 프리미티브를 사용하여 소스 이미지 (pSrc로 주어진)의 첫 번째 채널에서 대상 이미지의 두 번째 채널로 데이터를 복사할 수 있습니다. 이를 위해 대상 포인터를 하나 오프셋하면 됩니다.

nppiCopy_8u_C3CR(pSrc, nSrcStep, pDst + 1, nDstStep, oSizeROI);

Source-Image Sampling

NPP 이미지 처리 함수 중 많은 함수가 하나 이상의 소스 이미지를 사용하고 출력 이미지를 생성합니다 (예: nppiAddC_8u_C1RSfs() 또는 nppiFilterBox_8u_C1R()). 이러한 카테고리에 속하는 모든 NPP 함수는 또한 ROI(영역 지정)에서 작동합니다. 이러한 함수에서는 ROI가 대상 ROI를 나타낸다고 간주해야 합니다. 다시 말해 ROI는 대상 이미지 내의 직사각형 영역을 설명하며 해당 영역 내의 모든 픽셀은 해당 기능에 의해 쓰여집니다.

이러한 함수를 성공적으로 사용하려면 사용자가 사용자 정의 대상 ROI가 입력 이미지에서 어떤 픽셀을 읽는지에 미치는 영향을 이해하는 것이 중요합니다. ROI 전파(즉, 대상 ROI가 주어진 경우 소스에서 ROI가 무엇인지)를 논의하기 쉽게하기 위해 ROI 전파(대상 ROI가 주어진 경우 소스에서 ROI가 무엇인지)를 논의하기 쉽게하기 위해 두 가지 주요 경우를 구별하는 것이 합리적입니다.

점 단위 작업(Point-Wise Operations): 이러한 작업은 nppiAddC_8u_C1RSfs()와 같은 원시입니다. 각 출력 픽셀에는 정확히 하나의 입력 픽셀이 읽혀야 합니다.

이웃 작업(Neighborhood Operations): 이러한 작업은 nppiFilterBox_8u_C1R()와 같은 기본입니다. 하나의 출력을 생성하기 위해 하나 이상의 소스 이미지에서 픽셀 그룹이 읽어져야 합니다.

Point-Wise Operations

위에서 언급한 대로 점 단위 작업은 하나의 출력 픽셀을 생성하기 위해 입력 이미지로부터 하나의 픽셀(또는 작업이 하나 이상의 입력 이미지를 가지는 경우 각 입력 이미지로부터 하나의 픽셀)을 사용합니다.

Neighborhood Operations

네이버후드(neighborhood) 작업의 경우 하나의 출력 픽셀을 계산하기 위해 입력 이미지(또는 이미지)에서 입력 픽셀의 수(“네이버후드” 픽셀 집합)를 읽습니다. 이미지 필터링 함수와 이미지 형태학 작업에 대부분의 이러한 함수가 있습니다.

대부분의 이러한 함수는 네이버후드의 크기와 상대적 위치에 영향을주는 매개 변수를 갖습니다. 마스크 크기 구조체와 앵커 포인트 구조체 두 매개 변수에 대한 자세한 내용은 다음 부분에서 설명됩니다.

Mask-Size Parameter

많은 NPP 네이버후드(neighborhood) 작업은 일반적으로 NppiSize 유형의 oMaskSize라는 매개 변수를 통해 사용자가 네이버후드의 크기를 지정할 수 있도록 허용합니다. 이러한 경우 소스로부터 읽힌 픽셀의 네이버후드 크기는 마스크의 크기와 정확히 같습니다. 마스크가 (0, 0) 위치에 고정되고 크기가 (w, h)인 경우:

assert(oMaskSize.w == w)
assert(oMaskSize.h == h)
assert(oAnchor.x == 0)
assert(oAnchor.y == 0)

네이버후드 작업은 대상 픽셀을 계산하기 위해 다음과 같은 소스 픽셀을 읽게 됩니다.

Dij

\[\[ S_{i,j} \quad S_{i,j+1} \quad \ldots \quad S_{i,j+w-1} \] \[ S_{i+1,j} \quad S_{i+1,j+1} \quad \ldots \quad S_{i+1,j+w-1} \] \[ \vdots \quad \vdots \quad \ddots \quad \vdots \] \[ S_{i+h-1,j} \quad S_{i+h-1,j+1} \quad \ldots \quad S_{i+h-1,j+w-1} \]\]

Anchor-Point Parameter

NPP에서 이웃 연산을 수행하는 많은 기본 함수는 주로 NppiPoint 형식의 oAnchor라는 매개변수를 통해 이웃의 상대적인 위치를 지정할 수 있도록 허용합니다. 이 앵커를 사용하여 개발자는 마스크(마스크 크기 매개변수 참조)의 위치를 현재 픽셀 인덱스를 기준으로 선택할 수 있습니다.

마스크 크기 매개변수를 사용한 이전 예제와 동일한 경우, 이번에는 앵커 위치가 (a, b)인 경우를 고려해 보겠습니다:

assert(oMaskSize.w == w);
assert(oMaskSize.h == h);
assert(oAnchor.x == a);
assert(oAnchor.y == b);

위와 같은 상황에서 소스 이미지에서 다음과 같은 픽셀이 읽히게 됩니다:

\[\begin{array}{lllll} S_{i-a,j-b} & S_{i-a,j-b+1} & \ldots & S_{i-a,j-b+w-1} \\ S_{i-a+1,j-b} & S_{i-a+1,j-b+1} & \ldots & S_{i-a+1,j-b+w-1} \\ \vdots & \vdots & \ddots & \vdots \\ S_{i-a+h-1,j-b} & S_{i-a+h-1,j-b+1} & \ldots & S_{i-a+h-1,j-b+w-1} \\ \end{array}\]

Sampling Beyond Image Boundaries

NPP 프리미티브는 일반적으로 모든 픽셀 위치가 유효하고 해당 이미지의 경계 내에 있어야 합니다. 정의된 이미지 데이터 영역 외부에서 샘플링하면 정의되지 않은 동작이 발생하며 시스템의 불안정성으로 이어질 수 있습니다.

이것은 실제로 문제가 발생하는 상황입니다. 전체 크기의 이미지를 처리할 때 목적지 ROI를 소스 이미지와 동일한 크기로 선택할 수 없습니다. 이유는 이웃 연산이 확장된 소스 ROI에서 픽셀을 읽기 때문에 목적지 ROI는 확장된 소스 ROI가 소스 이미지의 크기를 초과하지 않도록 축소되어야 합니다. 또는 이웃 연산 함수가 Border 버전을 지원하는 경우, 이 버전을 사용하고 적절한 테두리 보호 모드를 선택하여 ROI 조정 없이 사용할 수 있습니다.

목적지 이미지 크기를 축소하는 것이 허용되지 않고 함수의 Border 버전이 사용 불가능한 경우, NPP는 테두리 확장 복사 프리미티브 세트를 제공합니다. 예를 들어, nppiCopyConstBorder_8u_C1R(), nppiCopyReplicateBorder_8u_C1R() 및 nppiCopyWrapBorder_8u_C1R()입니다. 사용자는 이러한 프리미티브 중 하나를 사용하여 소스 이미지의 크기를 “확장”하고 세 가지 확장 모드 중 하나를 선택하여 사용할 수 있습니다. 그런 다음 확장된 이미지를 안전하게 이웃 연산에 전달하여 전체 크기의 결과를 생성할 수 있습니다.

Signal Processing Conventions

Signal Data

Signal data is passed to and from NPPS primitives via a pointer to the signal’s data type.

The general idea behind this fairly low-level way of passing signal data is ease-of-adoption into existing software projects:

Passing the data pointer rather than a higher-level signal struct allows for easy adoption by not requiring a specific signal representation (that could include total signal size offset, or other additional information). This avoids awkward packing and unpacking of signal data from the host application to an NPP specific signal representation.

NPPS 프리미티브로 신호 데이터는 신호 데이터 형식의 포인터를 통해 전달됩니다.

신호 데이터를 이렇게 상당히 저수준의 방식으로 전달하는 일반 아이디어는 기존 소프트웨어 프로젝트에 쉽게 적용할 수 있도록 하는 것입니다:

데이터 포인터를 전달하는 대신 더 높은 수준의 신호 구조체가 필요하지 않아 특정 신호 표현(전체 신호 크기 오프셋 또는 기타 추가 정보를 포함할 수 있는)를 요구하지 않습니다. 이것은 호스트 응용 프로그램에서 NPP 특정 신호 표현으로의 신호 데이터의 어색한 패킹과 언패킹을 피하게 합니다.

Parameter Names for Signal Data

NPP에서 이미지 데이터 전달의 일반적인 경우는 다음 섹션에서 자세히 설명되어 있습니다.

이러한 것들은 알고리즘에서 사용하는 신호입니다.

소스 신호 포인터 일반적으로 소스 신호 데이터는 다음과 같이 명명된 포인터를 통해 전달됩니다.

pSrc 일반적으로 소스 신호 포인터는 상수로 정의되며, 해당 포인터가 가리키는 이미지 데이터를 변경하지 않도록 합니다. 예를 들어, nppsPrimitive_32s(const Npp32s * pSrc, …) 원시(primitive)가 여러 신호를 입력으로 사용하는 경우 소스 포인터는 다음과 같이 번호가 매겨집니다. pSrc1, pScr2, …

대상 신호 포인터 대상 신호 데이터는 일반적으로 다음과 같이 명명된 포인터를 통해 전달됩니다.

pDst 원시(primitive)가 여러 신호를 입력으로 사용하는 경우 소스 포인터는 다음과 같이 번호가 매겨집니다. pDst1, pDst2, …

인플레이스 신호 포인터 인플레이스 처리의 경우, 소스와 대상은 동일한 포인터로 제공되므로 인플레이스 신호 데이터를 가리키는 포인터는 다음과 같이 호출됩니다.

pSrcDst

데이터 유형 포인터 이름 예시 설명
소스 신호 pSrc 일반적으로 상수로 정의되며 이미지 데이터 변경을 허용하지 않음. 여러 신호를 사용하는 경우, pSrc1, pSrc2 등과 같이 번호를 부여할 수 있음.
대상 신호 pDst 이미지 데이터의 대상 위치를 가리킴. 여러 신호를 사용하는 경우, pDst1, pDst2 등과 같이 번호를 부여할 수 있음.
인플레이스 신호 pSrcDst 인플레이스 처리의 경우, 소스와 대상이 동일한 포인터로 제공됨.

Signal Data Alignment Requirements

NPP에서는 신호 샘플 데이터가 자연스럽게 정렬되어야 합니다. 즉, 다음과 같은 포인터:

NppType * p;

신호 내의 샘플을 가리켜야 하며 다음 조건을 충족해야 합니다:

assert(p % sizeof(NppType) == 0);

이를 만족하지 않으면 NPP 함수가 올바르게 작동하지 않을 수 있습니다.

NPP에서 신호 데이터를 처리하는 모든 NPPI 기본 연산은 신호 데이터 포인터의 적절한 정렬을 확인하고 포인터가 null이 아닌지 테스트합니다.

검증에 실패하면 다음 중 하나의 오류 코드가 반환되고 해당 기본 연산이 실행되지 않습니다:

신호 데이터 포인터가 0 (NULL)인 경우 NPP_NULL_POINTER_ERROR가 반환됩니다. 신호 데이터 포인터의 주소가 신호 데이터 유형 크기의 배수가 아닌 경우 NPP_ALIGNMENT_ERROR가 반환됩니다.

Signal Length

NPPS 함수 중 대다수는 처리를 시작하는 데이터 포인터로부터 몇 개의 신호 샘플을 처리해야 하는지를 알려주는 nLength 매개변수를 사용합니다.

길이 매개변수를 사용하는 모든 NPPS 함수는 이 입력을 유효성 검사합니다. 유효성 검사에 실패하면 다음과 같은 오류 코드가 반환되며 해당 함수는 실행되지 않습니다: 길이가 음수인 경우 NPP_SIZE_ERROR가 반환됩니다.

Data Types, Structs, Enums, and Constants

예시코드

#include <npp.h>  // NPP 라이브러리 헤더 파일

int main() {
    // 이미지 데이터를 저장할 버퍼 생성
    Npp8u* srcImage = ...  // 소스 이미지 데이터 포인터 (8비트/채널)
    Npp8u* dstImage = ...  // 결과 이미지 데이터 포인터 (8비트/채널)
    int imageWidth = ...  // 이미지 너비
    int imageHeight = ...  // 이미지 높이
    int imageStep = imageWidth * sizeof(Npp8u);  // 이미지 한 행의 바이트 수

    // 이미지 데이터 복사 (예: nppiCopy_8u_C1R 함수 사용)
    NppiSize imageSizeROI = { imageWidth, imageHeight };
    nppiCopy_8u_C1R(srcImage, imageStep, dstImage, imageStep, imageSizeROI);

    // 필터링 작업을 위한 필요한 데이터 및 매개변수 설정
    NppiSize maskSize = NPP_MASK_SIZE_3_X_3;  // 필터 마스크 크기 설정
    NppiPoint anchor = { 1, 1 };  // 필터 마스크의 앵커 포인트 설정
    NppiInterpolationMode interpolation = NPPI_INTER_LINEAR;  // 보간 모드 설정

    // 이미지 필터링 (예: nppiFilterBox_8u_C1R 함수 사용)
    nppiFilterBox_8u_C1R(dstImage, imageStep, dstImage, imageStep, imageSizeROI, maskSize, anchor, interpolation);

    // 여기에 추가 작업 수행 또는 결과를 사용하는 코드 작성

    return 0;
}

예시 2

#include <npp.h>
#include <iostream>

int main() {
    // 이미지 크기 및 데이터를 정의합니다.
    int width = 640;  // 이미지 너비
    int height = 480;  // 이미지 높이
    int imageSize = width * height * sizeof(Npp8u);  // 이미지 데이터 크기

    // 소스 이미지와 대상 이미지를 위한 메모리를 할당합니다.
    Npp8u* srcImage = new Npp8u[imageSize];
    Npp8u* dstImage = new Npp8u[imageSize];

    // 이미지 데이터 초기화 (예: 여기에서는 단순히 흰 바탕 이미지로 초기화)
    memset(srcImage, 255, imageSize);  // 흰 바탕 이미지로 초기화

    // 이미지 데이터를 복사합니다.
    NppiSize imageSizeROI = { width, height };
    int imageStep = width * sizeof(Npp8u);

    nppiCopy_8u_C1R(srcImage, imageStep, dstImage, imageStep, imageSizeROI);

    // 결과를 확인하기 위해 이미지 데이터를 파일로 저장하거나 출력할 수 있습니다.
    // 여기에 추가 작업을 수행할 수도 있습니다.

    // 메모리를 해제합니다.
    delete[] srcImage;
    delete[] dstImage;

    return 0;
}