Tutorial: Image Blurring and Rotation with Intel® Integrated Performance Primitives

ID 751830
Date 7/15/2020
Public

Creating an Application for Image Blurring and Rotation

The code example below represents a simple application that loads an image from the BMP file, rotates and blurs it after user presses left/right or up/down arrow keys.

The source code for this application can be downloaded from the Intel® Integrated Performance Primitives - Sample and Tutorial website.

/* C++ source code is found in ipp_blur_rotate.cpp */

#include "ipps.h"
#include "ipp_blur_rotate.h"

#include "bmpreader.h"

#include <math.h>
#include <stdio.h>

char titleBuffer[256];

video *v;

#if (defined unix || defined UNIX)
#include <X11/keysym.h>
#define VK_LEFT     XK_Left
#define VK_UP       XK_Up
#define VK_RIGHT    XK_Right
#define VK_DOWN     XK_Down
#define VK_ESCAPE   XK_Escape
#elif defined __APPLE__
#define VK_LEFT     0xf702
#define VK_UP       0xf700
#define VK_RIGHT    0xf703
#define VK_DOWN     0xf701
#define VK_ESCAPE   0x1B
#endif

IppStatus warpAffine(Ipp8u* pSrc, IppiSize srcSize, int srcStep, Ipp8u* pDst, IppiSize dstSize, int dstStep, const double coeffs[2][3])
{
    /* IPP functions status */
    IppStatus status = ippStsNoErr;

    /* number of image channels */
    const Ipp32u numChannels = 3;

    /* border value to extend the source image */
    Ipp64f pBorderValue[numChannels];

    /* sizes for WarpAffine data structure, initialization buffer, work buffer */
    int specSize = 0, initSize = 0, bufSize = 0;

    /* pointer to work buffer */
    Ipp8u* pBuffer  = NULL;

    /* pointer to WarpAffine data structure */
    IppiWarpSpec* pSpec = NULL;

    /* set offset of the processing destination ROI */
    IppiPoint dstOffset = {0, 0};

    /* border type for affine transform */
    IppiBorderType borderType = ippBorderConst;

    /* direction of warp affine transform */
    IppiWarpDirection direction = ippWarpForward;

    /* set border value to extend the source image */
    for (int i = 0; i < numChannels; ++i) pBorderValue[i] = 255.0;

    /* computed buffer sizes for warp affine data structure and initialization buffer */
    status = ippiWarpAffineGetSize(srcSize, dstSize, ipp8u, coeffs, ippLinear, direction, borderType,
        &specSize, &initSize);

    /* allocate memory */
    pSpec = (IppiWarpSpec*)ippsMalloc_8u(specSize);

    /* initialize data for affine transform */
    if (status >= ippStsNoErr) status = ippiWarpAffineLinearInit(srcSize, dstSize, ipp8u, coeffs, direction, numChannels, borderType, pBorderValue, 0, pSpec);

    /* get work buffer size */
    if (status >= ippStsNoErr) status = ippiWarpGetBufferSize(pSpec, dstSize, &bufSize);

    /* allocate memory for work buffer */
    pBuffer = ippsMalloc_8u(bufSize);

    /* affine transform processing */
    if (status >= ippStsNoErr) status = ippiWarpAffineLinear_8u_C3R(pSrc, srcStep, pDst, dstStep, dstOffset, dstSize, pSpec, pBuffer);

    /* free allocated memory */
    ippsFree(pSpec);
    ippsFree(pBuffer);

    return status;
}

void ipp_blur_rotate::process(const drawing_memory &dm)
{
    IppStatus status = ippStsNoErr;
    /* number of image channels */
    int numChannels = 3;

    /* perform filtering */
    if (bFilterUpdate)
    {
        /* temporary work buffer */
        Ipp8u* pBuffer = NULL;
        /* buffer size for filtering */
        int bufSize = 0;

        /* Get work buffer size */
        status = ippiFilterBoxBorderGetBufferSize(srcSize,maskSize,ipp8u,3,&bufSize);

        /* allocate buffer memory */
        pBuffer = ippsMalloc_8u(bufSize);

        /* Image filtering */
        if (status >= ippStsNoErr) status = ippiFilterBoxBorder_8u_C3R(pSrc, srcStep, pBlur, blurStep, srcSize, maskSize, ippBorderRepl, NULL, pBuffer);

        /* filtration flag is dropped */
        bFilterUpdate = false;
        /* rotation operation should be applied after filtration */
        bRotateUpdate = true;

        /* free buffer memory */
        ippsFree(pBuffer);
    }

    /* perform rotation */
    if (bRotateUpdate)
    {
        IppiSize roiSize = {dstSize.width / 2, dstSize.height };
        Ipp8u* pDstRoi = pBlurRot;
        IppiRect srcRoi = {0};
        srcRoi.width  = srcSize.width;
        srcRoi.height = srcSize.height;

        /* affine transform coefficients */
        double coeffs[2][3]  = {0};

        /* affine transform bounds */
        double bound[2][2]   = {0};

        /* compute affine transform coefficients by angle and x- and y-shifts */
        if (status >= ippStsNoErr) status = ippiGetRotateTransform(angle, 0, 0, coeffs);

        /* get bounds of transformed image */
        if (status >= ippStsNoErr) status = ippiGetAffineBound(srcRoi, bound, coeffs);

        /* fit source image to dst */
        coeffs[0][2] = -bound[0][0] + (dstSize.width / 2.f  - (bound[1][0] - bound[0][0])) / 2.f;
        coeffs[1][2] = -bound[0][1] + (dstSize.height - (bound[1][1] - bound[0][1])) / 2.f;

        /* perform affine processing for the blurred image */
        if (status >= ippStsNoErr) status = warpAffine(pBlur, srcSize, blurStep, pDstRoi, roiSize, blurRotStep, coeffs);

        /* set destination ROI for the not blurred image */
        pDstRoi = pBlurRot + roiSize.width * numChannels;

        /* perform affine processing for the original image */
        if (status >= ippStsNoErr) status = warpAffine(pSrc, srcSize, srcStep, pDstRoi, roiSize, blurRotStep, coeffs);

        /* rotation flag is dropped */
        bRotateUpdate = false;

        /* needs to redraw the image */
        bRedraw       = true;
    }

    if (bRedraw)
    {
        drawing_area area( 0, 0, dstSize.width, dstSize.height, dm) ;

        /* pass information message to window's title */
    #if defined _WIN32
        sprintf_s(titleBuffer, sizeof(titleBuffer)/sizeof(titleBuffer[0]), "Intel(R) IPP: blur + rotate tutorial : rotation angle %.0f : box filter mask size {%d, %d}", angle - 360.0 * floor(angle / 360.0), maskSize.width, maskSize.height);
    #elif (defined unix || defined UNIX)
        sprintf(titleBuffer, "Intel(R) IPP: blur + rotate tutorial : rotation angle %.0f : box filter mask size {%d, %d}", angle - 360.0 * floor(angle / 360.0), maskSize.width, maskSize.height);
    #elif defined __APPLE__
        sprintf(titleBuffer, "Intel(R) IPP: blur + rotate tutorial : rotation angle %.0f : box filter mask size {%d, %d}", angle - 360.0 * floor(angle / 360.0), maskSize.width, maskSize.height);
    #endif
        v->title = titleBuffer;
        v->show_title();

        // fill the rendering area
        for (int y = 0; y < dstSize.height; ++y)
        {
            Ipp8u* dstt = pBlurRot + y * blurRotStep;
            area.set_pos( 0, y );
            for (int x = 0, j = 0; x < dstSize.width; ++x, j += 3)
            {
                area.put_pixel( v->get_color(dstt[j+2], dstt[j+1], dstt[j]) );
            }
        }

        bRedraw = false;
    }
}

/* Key processing */
void ipp_blur_rotate::onKey( int key )
{
    if (pSrc == NULL || pBlur ==NULL || pBlurRot == NULL) return;

    /* up or down arrow key is pressed */
    if (key == VK_UP || key == VK_DOWN)
    {
        /* max size of mask for image blurring */
        const IppiSize maxMaskSize = {31,31};

        /* increase or decrease mask size on 2 depending on the key */
        maskSize.width  = (key == VK_DOWN) ? maskSize.width  - 2 : maskSize.width  + 2;
        maskSize.height = (key == VK_DOWN) ? maskSize.height - 2 : maskSize.height + 2;

        /* check that both mask width and mask height are positive */
        if (maskSize.width  < 1) maskSize.width  = 1;
        if (maskSize.height < 1) maskSize.height = 1;

        /* check that both mask width and mask height are at more the maximum mask size */
        if (maskSize.width  > maxMaskSize.width)  maskSize.width  = maxMaskSize.width;
        if (maskSize.height > maxMaskSize.height) maskSize.height = maxMaskSize.height;

        /* filtration operation should be applied */
        bFilterUpdate = true;
    }

     /* left or right arrow key is pressed */
    if(key == VK_RIGHT || key == VK_LEFT)
    {
        /* increase or decrease angle on 2 depending on the key */
        angle = (key == VK_LEFT) ? angle + 2 : angle - 2;
        /* rotation operation should be applied after filtration */
        bRotateUpdate = true;
    }

    if (key == VK_ESCAPE) v->running = false;
}

bool ipp_blur_rotate::loadFileBMP( const char* bmpImageFile )
{
    /* check the pointer */
    if (bmpImageFile == NULL) return false;

    /* number of image channels */
    int numChannels = 0;

    /* Read data from a file (only bmp format is supported) */
    Status status = ReadFile(bmpImageFile, &pSrc, srcSize, srcStep, numChannels);

    if (numChannels != 3)
    {
        status = STS_ERR_UNSUPPORTED;
    }

    if (status == STS_OK)
    {
        /* set blurred image step */
        blurStep = srcStep;

        /* set rotated image size to keep whole rotated image */
        dstSize.width  = static_cast<int>(sqrt((float)srcSize.width  * srcSize.width + srcSize.height  * srcSize.height) + 0.5f) * 2;
        dstSize.height = static_cast<int>(sqrt((float)srcSize.width  * srcSize.width + srcSize.height  * srcSize.height) + 0.5f);

        /* set image step for blurred and rotated image */
        blurRotStep = dstSize.width * numChannels;

        /* Memory allocation for the intermediate images */
        pBlur = ippsMalloc_8u(srcStep * srcSize.height);

        /* Memory allocation for the intermediate images */
        pBlurRot = ippsMalloc_8u(dstSize.width * numChannels * dstSize.height);

        maskSize.width  = 3;
        maskSize.height = 3;

        bFilterUpdate = bRotateUpdate = bRedraw = true;

        return true;
    }

    return false;
}

ipp_blur_rotate::ipp_blur_rotate()
    : angle(0.0), pSrc(NULL), pBlur(NULL), pBlurRot(NULL),
      bFilterUpdate(false), bRotateUpdate(false), bRedraw(false)
{
    dstSize.width  = srcSize.width  = 0;
    dstSize.height = srcSize.height = 0;
}

ipp_blur_rotate::~ipp_blur_rotate()
{
    ippsFree(pSrc);
    ippsFree(pBlur);
    ippsFree(pBlurRot);
}