Developer Reference for Intel® Integrated Performance Primitives
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:
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.
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:
Figure Simple Image Resize shows a simple image resizing example, in which image resolution is increased by 1.5x.
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; }
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:
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.
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; }
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.
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; }