Developer Reference

  • 2020
  • 10/21/2020
  • Public Content
Contents

Using Intel® IPP Resize Functions with Prior Initialization

You can use one of the following approaches to image resizing:
Interpolation algorithms of the Lanczos, Linear, and Cubic types use edge pixels of the source image that are out of the image origin. When calling the
ippiResize<Filter>
function with one of these interpolation algorithms applied, you need to specify the appropriate border type. The following border types are supported:
  • Replicated borders: border pixels are replicated from the source image boundary pixels;
  • Borders in memory: the source image border pixels are obtained from the source image pixels in memory;
  • Mixed borders: a combined approach is applied.
If you want to resize an image with antialiasing, follow the same instructions as provided below, but use
ippiResizeAntialiasing<Filter>Init
instead of
ippiResize<Filter>Init
for initialization, and
ippiResizeAntialiasing<Filter>
instead of
ippiResize<Filter>
, as a processing function.

Resizing the Whole Image

You can apply the approach described below to resize when source and destination images are fully accessible in memory. However, this method only runs on a single thread.
To resize the whole image:
  1. Call the
    ippiResizeGetSize
    function with the appropriate interpolation type. This function uses source and destination image sizes to calculate how much memory must be allocated for the
    IppResizeSpec
    structure and initialization work buffer.
  2. Initialize the
    IppResizeSpec
    structure by calling the
    ippiResize<Filter>Init
    , where
    <Filter>
    can take one of the following values:
    Nearest
    ,
    Linear
    ,
    Cubic
    ,
    Lanczos
    , and
    Super
    . These prerequisite steps allow resize to be called multiple times without recalculations.
  3. Call the
    ippiResizeGetBufferSize
    function for the initialized
    IppResizeSpec
    structure. This function uses the destination image size to calculate how much memory must be allocated for the resize work buffer.
  4. Call
    ippiResize<Filter>
    with the appropriate image type.
  5. If you call the
    ippiResize<Filter>
    function with a
    ippBorderInMem
    border or any mixed border type, the applied interpolation algorithm uses weighted values from edge pixels of the source image when outside the image boundaries. To obtain the size of the border required for correct edge calculation, call the
    ippiResizeGetBorderSize
    function for the appropriate flavor. In case of mixed border type, out of image pixels are used only behind the non-replicated edge.
  6. You can use mixed borders by using the bitwise
    OR
    operation between the
    ippBorderRepl
    type and the following border types:
    ippBorderInMemTop
    ,
    ippBorderInMemBottom
    ,
    ippBorderInMemLeft
    ,
    ippBorderInMemRight
    .
Figure
Simple Image Resize
shows a simple image resizing example, in which image resolution is increased by 1.5x.
Simple Image Resize
Example
The code example below demonstrates whole image resizing with the Lanczos interpolation method:
IppStatus resizeExample_C3R(Ipp8u* pSrc, IppiSize srcSize, Ipp32s srcStep, Ipp8u* pDst, IppiSize dstSize, Ipp32s dstStep) { IppiResizeSpec_32f* pSpec = 0; int specSize = 0, initSize = 0, bufSize = 0; Ipp8u* pBuffer = 0; Ipp8u* pInitBuf = 0; Ipp32u numChannels = 3; IppiPoint dstOffset = {0, 0}; IppStatus status = ippStsNoErr; IppiBorderType border = ippBorderRepl; /* Spec and init buffer sizes */ status = ippiResizeGetSize_8u(srcSize, dstSize, ippLanczos, 0, &specSize, &initSize); if (status != ippStsNoErr) return status; /* Memory allocation */ pInitBuf = ippsMalloc_8u(initSize); pSpec = (IppiResizeSpec_32f*)ippsMalloc_8u(specSize); if (pInitBuf == NULL || pSpec == NULL) { ippsFree(pInitBuf); ippsFree(pSpec); return ippStsNoMemErr; } /* Filter initialization */ status = ippiResizeLanczosInit_8u(srcSize, dstSize, 3, pSpec, pInitBuf); ippsFree(pInitBuf); if (status != ippStsNoErr) { ippsFree(pSpec); return status; } /* work buffer size */ status = ippiResizeGetBufferSize_8u(pSpec, dstSize, numChannels, &bufSize); if (status != ippStsNoErr) { ippsFree(pSpec); return status; } pBuffer = ippsMalloc_8u(bufSize); if (pBuffer == NULL) { ippsFree(pSpec); return ippStsNoMemErr; } /* Resize processing */ status = ippiResizeLanczos_8u_C3R(pSrc, srcStep, pDst, dstStep, dstOffset, dstSize, border, 0, pSpec, pBuffer); ippsFree(pSpec); ippsFree(pBuffer); return status; }

Resizing a Tiled Image with One Prior Initialization

You can apply the approach described below to resize when source and destination images are not fully accessible in memory, or to improve the performance of resizing by external threading.
The main difference between this approach and whole image resizing is that the processing is split into sections of the image called tiles. Each call of the
Resize<Filter>
function works with the destination image origin region of interest (ROI) that is defined by
dstOffset
and
dstSize
parameters. The destination and source ROI must be fully accessible in memory.
To resize an image with the tiled approach:
  1. Call the
    ippiResizeGetSize
    function with the appropriate interpolation type. This function uses the source and destination image sizes to calculate how much memory must be allocated for the
    IppResizeSpec
    structure and initialization work buffer.
  2. Initialize the
    IppResizeSpec
    structure by calling
    ippiResize<Filter>Init
    , where
    <Filter>
    can take one of the following values:
    Nearest
    ,
    Cubic
    ,
    Linear
    , and
    Lanczos
    .
  3. Determine an appropriate partitioning scheme to divide the destination image into tiles. Tiles can be sets of rows or a regular grid of subimages. A simple vertical subdivision into sets of lines is often sufficient.
  4. Obtain the source ROI for the defined destination tile by calling the
    ippiResizeGetSrcRoi
    function for the corresponding flavor. The algorithm uses edge pixels that are out of the source ROI to calculate edge pixels of the destination ROI. These out of the source ROI edge pixels must be accessible in memory.
  5. If the source ROI is an interior field of the source image origin, obtain the border ROI size by calling the
    ippiResizeGetBorderSize
    function for the corresponding flavor.
  6. If the source ROI is an edge tile, the algorithm can interpolate pixels beyond the image boundary as in the previous method.
  7. If the source and destination images are fully accessible in memory, you can use the source ROI offset for the
    pSrc
    calculation. To obtain the offset, call the
    ippiResizeGetSrcOffset
    function for the corresponding flavor.
  8. Call the
    ippiResizeGetBufferSize
    function to obtain the size of the resize work buffer required for each tile processing. The
    dstSize
    parameter must be equal to the tile size.
  9. Call
    ippiResize<Filter>
    for each tile (ROI). The
    dstOffset
    parameter must specify the image ROI offset with respect to the destination image origin. The
    dstSize
    parameter must be equal to the ROI size. Parameters
    pSrc
    and
    pDst
    must point to the beginning of the source and destination ROI in memory respectively. The source and destination ROIs must be fully accessible in memory.
    You can process tiles in any order. When using multitple threads you can process all tiles simultaneously.
If you resize a tiled image with the Super Sampling algorithm, and the source image width to destination image width ratio is
m/n
, you can reach better performance of resize operation if all destination tiles have width that is a multiple of
n
.
Figure
Tiling Image Resize
shows the resize of the image divided into tiles.
Tiling Image Resize
Tiling Picture Resize
Example
The code example below demonstrates a multithreading resize operation using OpenMP* with parallelization only in the y direction:
#define MAX_NUM_THREADS 16 IppStatus tileResizeExample_C3R(Ipp8u* pSrc, IppiSize srcSize, Ipp32s srcStep, Ipp8u* pDst, IppiSize dstSize, Ipp32s dstStep) { IppiResizeSpec_32f* pSpec = 0; int specSize = 0, initSize = 0, bufSize = 0; Ipp8u* pBuffer = 0; Ipp8u* pInitBuf = 0; Ipp32u numChannels = 3; IppiPoint dstOffset = {0, 0}; IppiPoint srcOffset = {0, 0}; IppStatus status = ippStsNoErr; IppiBorderSize borderSize = {0, 0, 0, 0}; IppiBorderType border = ippBorderRepl; int numThreads, slice, tail; int bufSize1, bufSize2; IppiSize dstTileSize, dstLastTileSize; IppStatus pStatus[MAX_NUM_THREADS]; /* Spec and init buffer sizes */ status = ippiResizeGetSize_8u(srcSize, dstSize, ippLinear, 0, &specSize, &initSize); if (status != ippStsNoErr) return status; /* Memory allocation */ pInitBuf = ippsMalloc_8u(initSize); pSpec = (IppiResizeSpec_32f*)ippsMalloc_8u(specSize); if (pInitBuf == NULL || pSpec == NULL) { ippsFree(pInitBuf); ippsFree(pSpec); return ippStsNoMemErr; } /* Filter initialization */ status = ippiResizeLinearInit_8u(srcSize, dstSize, pSpec); ippsFree(pInitBuf); if (status != ippStsNoErr) { ippsFree(pSpec); return status; } status = ippiResizeGetBorderSize_8u(pSpec, &borderSize); if (status != ippStsNoErr) { ippsFree(pSpec); return status; } /* General transform function */ /* Parallelized only by Y-direction here */ #pragma omp parallel num_threads(MAX_NUM_THREADS) { #pragma omp master { numThreads = omp_get_num_threads(); slice = dstSize.height / numThreads; tail = dstSize.height % numThreads; dstTileSize.width = dstLastTileSize.width = dstSize.width; dstTileSize.height = slice; dstLastTileSize.height = slice + tail; ippiResizeGetBufferSize_8u(pSpec, dstTileSize, ippC3, &bufSize1); ippiResizeGetBufferSize_8u(pSpec, dstLastTileSize, ippC3, &bufSize2); pBuffer = ippsMalloc_8u(bufSize1 * (numThreads - 1) + bufSize2); } #pragma omp barrier { if (pBuffer) { Ipp32u i; Ipp8u *pSrcT, *pDstT; Ipp8u *pOneBuf; IppiPoint srcOffset = {0, 0}; IppiPoint dstOffset = {0, 0}; IppiSize srcSizeT = srcSize; IppiSize dstSizeT = dstTileSize; i = omp_get_thread_num(); dstSizeT.height = slice; dstOffset.y += i * slice; if (i == numThreads - 1) dstSizeT = dstLastTileSize; pStatus[i] = ippiResizeGetSrcRoi_8u(pSpec, dstOffset, dstSizeT, &srcOffset, &srcSizeT); if (pStatus[i] == ippStsNoErr) { pSrcT = (Ipp8u*)((char*)pSrc + srcOffset.y * srcStep); pDstT = (Ipp8u*)((char*)pDst + dstOffset.y * dstStep); pOneBuf = pBuffer + i * bufSize1; pStatus[i] = ippiResizeLinear_8u_C3R (pSrcT, srcStep, pDstT, dstStep, dstOffset, dstSizeT, border, 0, pSpec, pOneBuf); } } } } ippsFree(pSpec); if (pBuffer == NULL) return ippStsNoMemErr; ippsFree(pBuffer); for (Ipp32u i = 0; i < numThreads; ++i) { /* Return bad status */ if(pStatus[i] != ippStsNoErr) return pStatus[i]; } return status; }

Resizing a Tiled Image with Prior Initialization for Each Tile

You can apply this approach only in cases when the destination image can be divided into tiles so that each destination tile corresponds to a source image tile that starts with an integer pixel value origin. For example, if the ratio of the source and destination images sizes is 2/3, the destination image can be divided into 3x3 tiles, each of which corresponds to the source image tile 2x2.
This approach is useful if there are restrictions on memory size when processing an image, or if the image size is large and
ippiResizeGetBufferSize
function returns
ippStsSizeErr
error. The initialization data for a tile is less than the same data for the whole image.
Each tile of the source image can be considered as an independent image that can be resized. For interior tile processing, the border must be always of the
ippBorderInMem
type. If you need to replicate any borders of the source image origin, you should combine the border type of the outer tiles so that interior tiles edges have border in memory and external tile borders are of the specified border type. This approach enables the right linking order of tiles.
Figure
Resize of the Image Divided into Subimages
shows the approach, when the source image is divided into several subimages that are resized independently.
Resize of the Image Divided into Subimages
Resize of the image divided into subimages
Example
The code example below divides the source image into tiles and resizes each image independently:
IppStatus separateTileResizeExample_C3R(Ipp8u* pSrc, IppiSize srcTileSize, Ipp32s srcStep, Ipp8u* pDst, IppiSize dstTileSize, Ipp32s dstStep, Ipp32s xNumTiles, Ipp32s yNumTiles) { IppiResizeSpec_32f* pSpec = 0; int specSize = 0, initSize = 0, bufSize = 0; Ipp8u* pBuffer = 0; Ipp8u* pInitBuf = 0; Ipp32u numChannels = 3; IppStatus status = ippStsNoErr; /* tiles cicle */ for (Ipp32s j = 0; j < xNumTiles; j ++) { for (Ipp32s i = 0; i < yNumTiles; i ++) { /* calculation of the destination image ROI offset */ IppiPoint dstOffset = {j * dstTileSize.width, i * dstTileSize.height}; Ipp8u* pDstT = pDst + dstStep * dstOffset.y + dstOffset.x * numChannels * sizeof(Ipp8u); /* calculation of the source image ROI offset */ IppiPoint srcOffset = {j * srcTileSize.width, i * srcTileSize.height}; Ipp8u* pSrcT = pSrc + srcStep * srcOffset.y + srcOffset.x * numChannels * sizeof(Ipp8u); IppiBorderType borderT = ippBorderRepl; IppiPoint dstOffsetZero = {0, 0}; /* correction of the border type for the tile processing */ if (j > 0) /* the processed tile is not on the left image origin edge*/ { borderT = (IppiBorderType)((int)borderT | (int)ippBorderInMemLeft); } if (j < xNumTiles - 1) /* the processed tile is not on the right image origin edge*/ { borderT = (IppiBorderType)((int)borderT | (int)ippBorderInMemRight); } if (i > 0) /* the processed tile is not on the top image origin edge*/ { borderT = (IppiBorderType)((int)borderT | (int)ippBorderInMemTop); } if (i < yNumTiles - 1) /* the processed tile is not on the bottom image origin edge*/ { borderT = (IppiBorderType)((int)borderT | (int)ippBorderInMemBottom); } /* Spec and init buffer sizes */ status = ippiResizeGetSize_8u(srcTileSize, dstTileSize, ippLanczos, 0, &specSize, &initSize); if (status != ippStsNoErr) return status; /* Memory allocation */ pInitBuf = ippsMalloc_8u(initSize); pSpec = (IppiResizeSpec_32f*)ippsMalloc_8u(specSize); if (pInitBuf == NULL || pSpec == NULL) { ippsFree(pInitBuf); ippsFree(pSpec); return ippStsNoMemErr; } /* Filter initialization */ status = ippiResizeLanczosInit_8u(srcTileSize, dstTileSize, 3, pSpec, pInitBuf); ippsFree(pInitBuf); if (status != ippStsNoErr) { ippsFree(pSpec); return status; } /* work buffer size */ status = ippiResizeGetBufferSize_8u(pSpec, dstTileSize, numChannels, &bufSize); if (status != ippStsNoErr) { ippsFree(pSpec); return status; } pBuffer = ippsMalloc_8u(bufSize); if (pBuffer == NULL) { ippsFree(pSpec); return ippStsNoMemErr; } /* Resize processing */ status = ippiResizeLanczos_8u_C3R(pSrcT, srcStep, pDstT, dstStep, dstOffsetZero, dstTileSize, borderT, 0, pSpec, pBuffer); ippsFree(pSpec); ippsFree(pBuffer); if (status != ippStsNoErr) return status; } } return ippStsNoErr; }

Product and Performance Information

1

Performance varies by use, configuration and other factors. Learn more at www.Intel.com/PerformanceIndex.