【CUDA】CUDA: (十一) NVIDIA 2D Image And Signal Performance Primitives (NPP) 初探
NPP初探1. NPP是什么为什么要用NPPnpp是图像和信号处理的GPU加速库在我们安装cuda的时候其实也安装了对应的一些library只是以前LZ就是纯粹的调用cuda和cudnn来做深度学习做训练和测试也没有关注cuda的一些具体组成部分自从参加完GTC CHINA以后LZ居然真的要用到这些了世界真是奇妙。原因很简单因为要处理的图像数据都在GPU上那么可以使用opencvcuda也可以直接使用npp进行处理两者LZ都试了在服务器上gcc和g版本太低了升级个版本估计相当于重新配置一遍环境LZ于是决定还是重新用npp写了一遍。2. NPP的函数命名方式“A”如果图像是4通道图像则为“ A”这表示结果alpha通道不受图元影响。“Cn”图像由n个通道压缩像素组成其中n可以是1、2、3或4。“C”在通道信息之后指示图元仅在颜色通道之一“感兴趣的通道”上运行。所有其他输出通道不受原语影响。”I“表示primitive“in-place”的方式工作。在这种情况下图像数据指针通常命名为pSrcDst以指示图像数据同时用作源和目标。“ M”表示“mask操作”。这些类型的图元具有附加的“mask图像”作为输入。目标图像中的每个像素对应于mask图像中的像素。仅处理具有相应非零掩码像素的像素。“ R”表示图元仅在矩形的“感兴趣区域”或“ ROI”上运行。所有ROI基元都采用NppiSize类型的附加输入参数该参数指定基元应处理的矩形区域的宽度和高度。有关基元如何在ROI上运行的详细信息请参见感兴趣区域ROI。ROI太重要了你做cropresize都需要非常非常有用“ Sfs”表示结果值在写入之前通过固定的缩放比例和饱和度进行处理。上面的后缀始终按字母顺序显示。The suffixes above always appear in alphabetical order. E.g. a 4 channel primitive not affecting the alpha channel with masked operation, in place and with scaling/saturation and ROI would have the postfix: “AC4IMRSfs”.3. 图像数据1. 图像数据通过一对参数传入和传出NPPI primitive指向图像基础数据类型的指针。以字节为单位的行步长有时也称为行跨步。ps:这个很重要要会计算LZ好几次程序出问题都是这个算错了/(ㄒoㄒ)/~~这种相当低级的图像数据传递方式背后的总体思路是易于将其引入现有软件项目中通过将原始指针传递给基础像素数据类型而不是结构化按颜色通道像素数据可以在各种情况下使用该功能从而避免了有风险的类型转换或昂贵的图像数据副本。分别传递数据指针和行步而不是更高级别的图像结构可以轻松实现因为不需要特定的图像表示从而避免了将图像数据从主机应用程序打包到NPP特定图像表示的麻烦。PS所以需要对图像存储结构、顺序、大小、深度、类型等一些参数都要很清楚才可以2. Line Step行步也称为“行跨步”或“行步”允许通过在行的末尾添加许多未使用的字节将大小不等的图像行从对齐好的地址上开始。这种类型的行填充很长时间以来一直是数字图像处理中的常规做法并且并非仅限于GPU图像处理。行步是一行中包含填充的字节数。解释该数字的另一种方法是说它是图像中连续行的第一个像素之间的字节数或者通常是任何像素列中两个相邻像素之间的字节数。出现行台阶的一般原因是像素的均匀对齐行可以优化内存访问模式。即使NPP中的所有功能都适用于任意对齐的图像但只有对齐良好的图像数据才能实现最佳性能。 CUDA运行时中使用NPP图像分配器或2D内存分配器分配的任何图像数据均已对齐。特别是在支持CUDA的较旧GPU上未对齐数据的性能下降可能会很大数量级。传递给NPPI primitive的所有图像数据都需要提供一个行步骤。重要的是要记住此行步骤始终以字节而不是像素为单位指定。3. Parameter Names for Image Dataa. Passing Source-Image Data通常原图像数据通过指针传递可以命名为pSrc批量的图像指针通过NppiImageDescriptor type 命名为pSrcBatchListSource-Planar-Image Pointer Array以平面方式排列的图像数据类型pSrc1, pSrc2, …Source-Image Line Step, 源图像行的步长是图像中连续行之间的字节数。 源图像行步长参数为: nSrcStepSource-Planar-Image Line Step Array,源平面图像线步进数组是一个数组其中数组的每个元素都包含输入图像中特定平面的连续行之间的字节数。 源平面图像线步进数组参数为:rSrcStep[]Source-Planar-Image Line Step, 源平面图像行步长是多平面输入图像的特定平面中连续行之间的字节数。 源平面图像线步进参数为:nSrcStep1, nSrcStep2, …b. Passing Destination-Image Data把上述Src全部转换成Dst即可 如pDstc. Passing In-Place Image Data在源图像或者目标图像前加上字母p即可如pSrcDstd. Passing Mask-Image DataMask-Image PointerpMaskMask-Image Line StepnMaskStepPassing Channel-of-Interest DatanCOI4. Image Data Alignment Requirements数据如果不做alignment那么有时候会影响代码效率的也可以看下LZ写的bmp的一篇博客NPP要求像素数据遵守某些对齐约束。对于2和4通道图像以下对齐要求成立data_pointer\ #channels * sizeof通道类型 0例如。 具有基础类型Npp8u8位无符号的4通道图像将要求所有像素落在4的倍数的地址上4通道* 1字节大小。由于所有像素都按照其自然大小对齐的逻辑结果因此2通道和4通道图像的图像线步长也必须是像素大小的倍数。对于1通道和3通道图像仅要求像素指针与基础数据类型对齐即pDatasizofdata type0。因此相应的行步也要满足此要求。5. Image Data Related Error Codes这个是LZ养成的习惯一定要使用NppStatus做做检查如果出了问题会有对应的错误标识typedefenum{/* negative return-codes indicate errors */NPP_NOT_SUPPORTED_MODE_ERROR-9999,NPP_INVALID_HOST_POINTER_ERROR-1032,NPP_INVALID_DEVICE_POINTER_ERROR-1031,NPP_LUT_PALETTE_BITSIZE_ERROR-1030,NPP_ZC_MODE_NOT_SUPPORTED_ERROR-1028,/** ZeroCrossing mode not supported */NPP_NOT_SUFFICIENT_COMPUTE_CAPABILITY-1027,NPP_TEXTURE_BIND_ERROR-1024,NPP_WRONG_INTERSECTION_ROI_ERROR-1020,NPP_HAAR_CLASSIFIER_PIXEL_MATCH_ERROR-1006,NPP_MEMFREE_ERROR-1005,NPP_MEMSET_ERROR-1004,NPP_MEMCPY_ERROR-1003,NPP_ALIGNMENT_ERROR-1002,NPP_CUDA_KERNEL_EXECUTION_ERROR-1000,NPP_ROUND_MODE_NOT_SUPPORTED_ERROR-213,/** Unsupported round mode*/NPP_QUALITY_INDEX_ERROR-210,/** Image pixels are constant for quality index */NPP_RESIZE_NO_OPERATION_ERROR-201,/** One of the output image dimensions is less than 1 pixel */NPP_OVERFLOW_ERROR-109,/** Number overflows the upper or lower limit of the data type */NPP_NOT_EVEN_STEP_ERROR-108,/** Step value is not pixel multiple */NPP_HISTOGRAM_NUMBER_OF_LEVELS_ERROR-107,/** Number of levels for histogram is less than 2 */NPP_LUT_NUMBER_OF_LEVELS_ERROR-106,/** Number of levels for LUT is less than 2 */NPP_CORRUPTED_DATA_ERROR-61,/** Processed data is corrupted */NPP_CHANNEL_ORDER_ERROR-60,/** Wrong order of the destination channels */NPP_ZERO_MASK_VALUE_ERROR-59,/** All values of the mask are zero */NPP_QUADRANGLE_ERROR-58,/** The quadrangle is nonconvex or degenerates into triangle, line or point */NPP_RECTANGLE_ERROR-57,/** Size of the rectangle region is less than or equal to 1 */NPP_COEFFICIENT_ERROR-56,/** Unallowable values of the transformation coefficients */NPP_NUMBER_OF_CHANNELS_ERROR-53,/** Bad or unsupported number of channels */NPP_COI_ERROR-52,/** Channel of interest is not 1, 2, or 3 */NPP_DIVISOR_ERROR-51,/** Divisor is equal to zero */NPP_CHANNEL_ERROR-47,/** Illegal channel index */NPP_STRIDE_ERROR-37,/** Stride is less than the row length */NPP_ANCHOR_ERROR-34,/** Anchor point is outside mask */NPP_MASK_SIZE_ERROR-33,/** Lower bound is larger than upper bound */NPP_RESIZE_FACTOR_ERROR-23,NPP_INTERPOLATION_ERROR-22,NPP_MIRROR_FLIP_ERROR-21,NPP_MOMENT_00_ZERO_ERROR-20,NPP_THRESHOLD_NEGATIVE_LEVEL_ERROR-19,NPP_THRESHOLD_ERROR-18,NPP_CONTEXT_MATCH_ERROR-17,NPP_FFT_FLAG_ERROR-16,NPP_FFT_ORDER_ERROR-15,NPP_STEP_ERROR-14,/** Step is less or equal zero */NPP_SCALE_RANGE_ERROR-13,NPP_DATA_TYPE_ERROR-12,NPP_OUT_OFF_RANGE_ERROR-11,NPP_DIVIDE_BY_ZERO_ERROR-10,NPP_MEMORY_ALLOCATION_ERR-9,NPP_NULL_POINTER_ERROR-8,NPP_RANGE_ERROR-7,NPP_SIZE_ERROR-6,NPP_BAD_ARGUMENT_ERROR-5,NPP_NO_MEMORY_ERROR-4,NPP_NOT_IMPLEMENTED_ERROR-3,NPP_ERROR-2,NPP_ERROR_RESERVED-1,/* success */NPP_NO_ERROR0,/** Error free operation */NPP_SUCCESSNPP_NO_ERROR,/** Successful operation (same as NPP_NO_ERROR) *//* positive return-codes indicate warnings */NPP_NO_OPERATION_WARNING1,/** Indicates that no operation was performed */NPP_DIVIDE_BY_ZERO_WARNING6,/** Divisor is zero however does not terminate the execution */NPP_AFFINE_QUAD_INCORRECT_WARNING28,/** Indicates that the quadrangle passed to one of affine warping functions doesnt have necessary properties. First 3 vertices are used, the fourth vertex discarded. */NPP_WRONG_INTERSECTION_ROI_WARNING29,/** The given ROI has no interestion with either the source or destination ROI. Thus no operation was performed. */NPP_WRONG_INTERSECTION_QUAD_WARNING30,/** The given quadrangle has no intersection with either the source or destination ROI. Thus no operation was performed. */NPP_DOUBLE_SIZE_WARNING35,/** Image size isnt multiple of two. Indicates that in case of 422/411/420 sampling the ROI width/height was modified for proper processing. */NPP_MISALIGNED_DST_ROI_WARNING10000,/** Speed reduction due to uncoalesced memory accesses warning. */}NppStatus;4. Region-of-Interest (ROI)在实践中处理图像的矩形子区域通常比处理完整图像更为普遍。 NPP的大多数图像处理原语都允许处理此类子区域也称为关注区域或ROI。所有支持ROI处理的原语在其名称后缀中均带有“ R”标记。 在大多数情况下ROI是作为单个NppiSize结构传递的该结构提供ROI的宽度和高度。 这就提出了一个问题即图元如何知道该矩形宽度高度在图像中的位置。 ROI的“起始像素”由图像数据指针隐式给出。 即 用户无需显式传递左上角最低的内存地址的像素坐标而是仅使图像数据指针偏移以指向ROI的第一个像素。实际上这意味着对于图像pSrcnSrcStep并且ROI的起始像素位于位置xy将通过pSrcOffsetpSrcy*nSrcStepx*PixelSize;作为图元的图像数据源。 PixelSize通常计算为PixelSizeNumberOfColorChannels*sizeof(PixelDataType).E.g. for a pimitive like nppiSet_16s_C4R() we would haveNumberOfColorChannels 4; sizeof(Npp16s) 2; and thus PixelSize 4 * 2 8;Masked Operation一些原始支持mask操作。 这些变量的后缀中的“ M”表示mask的操作。 支持mask操作的基元会消耗通过“mask图像指针”和“mask图像行阶”提供的其他输入图像。 这些图元将mask图像解释为布尔图像。 Npp8u类型的值被解释为布尔值其中值0表示false任何非零值表示true。除非另外指出否则仅在其空间上对应的遮罩像素为真非零的像素上执行该操作。 例如。 mask复制操作只会复制ROI中具有相应非零mask像素的那些像素。5. Channel-of-Interest API一些原语允许将操作限制在多通道图像内的单个感兴趣通道上。 这些原语以字母“ C”为后缀在通道信息之后例如nppiCopy_8u_C3CR这个LZ还踩过坑后来发现这个目标图像是多通道的一定要目标图像是单通道还是多通道。。。。 通常通过使图像数据指针偏移以直接指向感兴趣的通道而不是ROI中第一像素的基点来选择感兴趣的通道。 一些原语还显式地指定选定的频道号并通过整数将其传递例如 nppiMean_StdDev_8u_C3CR。1. Select-Channel Source-Image Pointer这是指向源图像的第一个像素内的关注通道的指针。 例如。 如果pSrc是指向三通道图像ROI内第一个像素的指针。 使用适当的选择通道复制原语可以通过将指针偏移一个从而将该源图像的第二通道复制到pDst给定的目标图像的第一通道中nppiCopy_8u_C3CR(pSrc 1, nSrcStep, pDst, nDstStep, oSizeROI);2. Select-Channel Source-Image一些原语允许用户通过指定频道号nCOI选择感兴趣的频道。 该方法通常用于图像统计功能。 例如nppiMean_StdDev_8u_C3CR(pSrc, nSrcStep, oSizeROI, nCOI, pDeviceBuffer, pMean, pStdDev );感兴趣的通道可以是1 2 或者33. Select-Channel Destination-Image Pointer对于目标图像同理可得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是什么区分两个主要情况是有意义的点操作这些是类似nppiAddC_8u_C1RSfs的原语。每个输出像素仅需要读取一个输入像素。邻居操作这些是类似nppiFilterBox_8u_C1R的基元需要从源图像读取一组像素才能产生单个输出。1. Point-Wise Operations如上所述逐点运算消耗来自输入图像的单个像素如果所讨论的运算具有多个输入图像则消耗每个输入图像的单个像素以产生单个输出像素。2. Neighborhood Operations在邻域操作的情况下在一个或多个输入图像中读取多个输入像素像素的“邻域”以便计算单个输出像素。 过滤功能和形态运算的所有功能都是邻域运算。这些函数中的大多数具有影响邻域的大小和相对位置的参数mask-size结构和achor-point结构。 这两个参数将在下一节中详细介绍a. Mask-Size Parameter许多NPP邻域操作允许用户通过通常名为NppiSize类型的oMaskSize的参数来指定邻域的大小。 在那些情况下从源读取的像素邻域恰好是mask的大小。 假设mask anchor 在位置00请参见下面的锚点参数并且大小为wh即* * assert(oMaskSize.w w); * assert(oMaskSize.h h); * assert(oAnchor.x 0); * assert(oAnchor.y 0); *b. Anchor-Point Parameter许多执行邻域操作的NPP原语允许用户通过通常称为NppiPoint类型的oAnchor的参数指定邻域的相对位置。 使用锚点开发人员可以选择相对于当前像素索引的mask位置请参见“mask大小参数”。使用与Mask-Size Parameter中相同的示例但是这次的锚点位置为abassert(oMaskSize.w w); * assert(oMaskSize.h h); * assert(oAnchor.x a); * assert(oAnchor.y b);c. Sampling Beyond Image Boundaries一般而言NPP基元特别是NPP邻域操作要求读取和写入的所有像素位置均有效且在相应图像的边界内。在定义的图像数据区域之外进行采样会导致不确定的行为并可能导致系统不稳定。这在实践中造成了一个问题在处理全尺寸图像时不能选择目标ROI与源图像相同的大小。由于邻域操作从扩大的源ROI读取像素因此必须缩小目标ROI以使扩大的源ROI不会超过源图像的大小。对于无法接受的目标图像尺寸“缩小”的情况NPP提供了一组边界扩展的Copy原语。例如。 nppiCopyConstBorder_8u_C1RnppiCopyReplicateBorder_8u_C1R和nppiCopyWrapBorder_8u_C1R。用户可以使用这些原语通过三种扩展模式之一来“扩展”源图像的大小。然后可以将展开的图像安全地传递到邻域操作从而生成完整尺寸的结果.参考地址1.https://docs.nvidia.com/cuda/npp/nppi_conventions_lb.html在使用当中一定要注意开辟内存关闭内存要检查cudaMemcpy时候成功指针是否为空等一系列问题。Deadline是第一生产力当快到项目截止点那种效率真的奇高。。。PS:累计确诊78631总算增速在放缓了累计治愈也有三万多人了这一切都离不开一线医护人员的努力警察和军队的支持各个小区的配合。谈胜利还显得有些太早了只希望春天的脚步近一些再近一些期待春暖花开的日子武汉加油湖北加油中国加油↖(ω)↗