Intel® Integrated Performance Primitives
Deliberate problems developing high-performance vision, signal, security, and storage applications.
6765 Discussions

Cleaned up code showing use of scaling and filtering IPP calls with caveats in comments.

noemata
Beginner
489 Views
// Authored by: Mario Pintaric
//
// Use Unicode char set for build.

// This code produces results on a par with most commercial scalers thanks to IPP.
// With a couple more tweaks, it will surpass even the best of them.
// Please contact the author if you are interested in more sophisticated approaches,
// otherwise you are free to use this code as you wish.

#include "stdafx.h"

#include
#include
#include

#pragma comment(lib, "gdiplus")

#include "ipp.h"

#pragma comment(lib, "ippcore.lib")
#pragma comment(lib, "ippi.lib")

using namespace Gdiplus;

class GdiplusInit
{
public:
GdiplusInit()
{
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
}
~GdiplusInit()
{
GdiplusShutdown(gdiplusToken);
}
protected:
private:
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
};

GdiplusInit InitGDIPlus;

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array
// in bytes

Gdiplus::ImageCodecInfo* pImageCodecInfo = NULL;

Gdiplus::GetImageEncodersSize(&num, &size);

if(size == 0)
return -1;

pImageCodecInfo = (Gdiplus::ImageCodecInfo*)(malloc(size));
if(pImageCodecInfo == NULL)
return -1;

GetImageEncoders(num, size, pImageCodecInfo);

for(UINT j = 0; j < num; ++j)
{
if( wcscmp(pImageCodecInfo.MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo.Clsid;
free(pImageCodecInfo);
return j; // Success
}
}

free(pImageCodecInfo);
return -1;
}

IppStatus GaussBmp(Ipp8u *pSrc, IppiSize srcSize, int nChannels, IppiMaskSize op)
{
Ipp8u *pad;
int nPad, padStep;
IppiSize padSize;
Ipp8u *pBorderOffset;
IppStatus status = ippStsNoMemErr;

// Set mask size and corresponding border padding.
if ( op == ippMskSize3x3 )
{
nPad = 3;
}
else if ( op == ippMskSize5x5 )
{
nPad = 5;
}
else
return ippStsNotSupportedModeErr;

// Calculate padded bitmap size.
padSize.width = srcSize.width + (2 * nPad);
padSize.height = srcSize.height + (2 * nPad);

// Allocate padded bitmap that will contain replicated border of source bitmap.
if (nChannels == 1)
{
pad = ippiMalloc_8u_C1(padSize.width, padSize.height, &padStep);
}
else if (nChannels == 3)
{
pad = ippiMalloc_8u_C3(padSize.width, padSize.height, &padStep);
}
else if (nChannels == 4)
{
pad = ippiMalloc_8u_AC4(padSize.width, padSize.height, &padStep);
}
else
return ippStsNotSupportedModeErr;

if ( !pad )
goto Fault;

// Note: Intel IPP v6.0 incorrectly caclulates the step value!!!
// Especially bad for small bitmap sizes (< 10 bytes wide) which leads to completely invalid results.
// So we calcultate the value ourselves.
padStep = padSize.width * nChannels;

// Create an artificial border around the bitmap so that actual border pixel values are calculated correctly.
// Intel IPP could handle this as a special case without the need for doing this.
// The code would be painfull to write, but the current cost is even pricier for IPP users processing HiDef images.
if (nChannels == 1)
{
status = ippiCopyReplicateBorder_8u_C1R(pSrc, srcSize.width * nChannels, srcSize, pad, padStep, padSize, nPad, nPad);
}
else if (nChannels == 3)
{
status = ippiCopyReplicateBorder_8u_C3R(pSrc, srcSize.width * nChannels, srcSize, pad, padStep, padSize, nPad, nPad);
}
else if (nChannels == 4)
{
status = ippiCopyReplicateBorder_8u_AC4R(pSrc, srcSize.width * nChannels, srcSize, pad, padStep, padSize, nPad, nPad);
}

if ( status )
goto Fault;

// Start blur operation from upper left corner of first non-border (image) pixel.
pBorderOffset = &pad[(padSize.width * nPad * nChannels) + (nPad * nChannels)];

if (nChannels == 1)
{
status = ippiFilterGauss_8u_C1R(pBorderOffset, padStep, pSrc, srcSize.width * nChannels, srcSize, op);
}
else if (nChannels == 3)
{
status = ippiFilterGauss_8u_C3R(pBorderOffset, padStep, pSrc, srcSize.width * nChannels, srcSize, op);
}
else if (nChannels == 4)
{
status = ippiFilterGauss_8u_AC4R(pBorderOffset, padStep, pSrc, srcSize.width * nChannels, srcSize, op);
}

Fault:
if ( pad )
ippiFree(pad);

return status;
}

#define BUFSIZE 4096

int _tmain(int argc, _TCHAR* argv[])
{
DWORD retval=0;
TCHAR buf[BUFSIZE]=TEXT("");
TCHAR buffer[BUFSIZE]=TEXT("");
TCHAR** lppPart={NULL};

const IppLibraryVersion* ippVersion = ippiGetLibVersion();

printf("IPP: [ %s %s ]\n", ippVersion->Name, ippVersion->Version);

if (argc != 2)
{
_tprintf(TEXT("\nUsage: %s [file]\n"), argv[0]);
return 1;
}

retval = GetFullPathName(argv[1], BUFSIZE, buffer, lppPart);

if (retval == 0)
{
printf ("File not found: %s\n", argv[1]);
return 2;
}

Bitmap inBmp(buffer);

if ( inBmp.GetLastStatus() == Ok )
{
PixelFormat fmt = inBmp.GetPixelFormat();

if ( PixelFormat24bppRGB == fmt )
{
BitmapData InData, growData, shrinkData;

// We can use the same smoothing trick when scaling up.
// Photoshop produces a slightly sharper result, but with edges that appear more jagged.
// If we scaled up by 16, and down by 8, the result is virtually identical to Photoshop.
// Clearly not practical for large images unless we use a tiling technique.

// Double scale
//int growwidth = inBmp.GetWidth() * 8;
//int growheight = inBmp.GetHeight() * 8;
//int shrinkwidth = growwidth / 4;
//int shrinkheight = growheight / 4;

// When scaling down, we first scale up, apply blur so that supersampling produces
// smoother edges when scaling down. It's a trick. There are several other tricks we could use.
// Photoshop preserves luminance better, but to my eyes the result achieved by this
// code is just a tad better because of the way edges are aliased.

// Half scale
int growwidth = inBmp.GetWidth() * 4;
int growheight = inBmp.GetHeight() * 4;
int shrinkwidth = growwidth / 8;
int shrinkheight = growheight / 8;

// The only thing missing from making this production grade code are checks for failure here ...
Bitmap growBmp(growwidth, growheight, PixelFormat24bppRGB);
Bitmap shrinkBmp(shrinkwidth, shrinkheight, PixelFormat24bppRGB);

// ... on save ... and here ... plus a couple sanity checks for things like GDI+ and IPP before trying to use either.
// I'll leave it to the next poster to pretty this up some more and add the needed warnings.
inBmp.LockBits(0, ImageLockModeRead, PixelFormat24bppRGB, &InData);
growBmp.LockBits(0, ImageLockModeWrite, PixelFormat24bppRGB, &growData);
shrinkBmp.LockBits(0, ImageLockModeWrite, PixelFormat24bppRGB, &shrinkData);

IppStatus status;
IppiSize inSize = { inBmp.GetWidth(), inBmp.GetHeight() };
IppiRect inRect = { 0, 0, inBmp.GetWidth(), inBmp.GetHeight() };
IppiSize growSize = { growwidth, growheight };
IppiRect growRect = { 0, 0, growwidth, growheight };
IppiSize shrinkSize = { shrinkwidth, shrinkheight };
IppiRect shrinkRect = { 0, 0, shrinkwidth, shrinkheight };

double xgrowFactor = growwidth / (double)inBmp.GetWidth();
double ygrowFactor = growheight / (double)inBmp.GetHeight();
double xshrinkFactor = shrinkwidth / (double)growwidth;
double yshrinkFactor = shrinkheight / (double)growheight;
double xShift = 0.0;
double yShift = 0.0;
int nChannel = 3;

int bufferSize = 0;
Ipp8u *pshrinkBuffer = NULL, *pgrowBuffer = NULL;

// Options are:
// IPPI_INTER_NN
// IPPI_INTER_LINEAR
// IPPI_INTER_CUBIC
// IPPI_INTER_CUBIC2P_BSPLINE
// IPPI_INTER_CUBIC2P_CATMULLROM
// IPPI_INTER_CUBIC2P_B05C03
// IPPI_INTER_SUPER
// IPPI_INTER_LANCZOS

int growInterpolation = IPPI_INTER_LANCZOS; // I think this is the best choice for scaling up? Docs are poor on a couple of the others.
int shrinkInterpolation = IPPI_INTER_SUPER;

status = ippiResizeGetBufSize(inRect, growRect, nChannel, growInterpolation, &bufferSize);
// Note: The call below returns the same result as above, but will fail when interpolation is supported only during downsampling.
//status = ippiResizeSqrPixelGetBufSize(growSize, nChannel, growInterpolation, &bufferSize);

if ( status )
goto Fault;

pgrowBuffer = (Ipp8u *)ippMalloc(bufferSize);

if ( !pgrowBuffer )
goto Fault;

status = ippiResizeGetBufSize(growRect, shrinkRect, nChannel, shrinkInterpolation, &bufferSize);
// Note: The call belowfails when set to IPPI_INTER_SUPER, presumably because it assumes upsampling. This issue needs to be rectified by Intel.
// status = ippiResizeSqrPixelGetBufSize(shrinkSize, nChannel, shrinkInterpolation, &bufferSize);

if ( status )
goto Fault;

pshrinkBuffer = (Ipp8u *)ippMalloc(bufferSize);

if ( !pshrinkBuffer )
goto Fault;

status = ippiResizeSqrPixel_8u_C3R((Ipp8u*)InData.Scan0, inSize, inBmp.GetWidth() * nChannel, inRect, (Ipp8u*)growData.Scan0, growwidth * nChannel, growRect, xgrowFactor, ygrowFactor, 0, 0, growInterpolation, pgrowBuffer);

if ( status )
goto Fault;

// Blur upscaled bitmap so that we downscale more smoothly.
status = GaussBmp((Ipp8u*)growData.Scan0, growSize, nChannel, ippMskSize5x5);

if ( status )
goto Fault;

// Blur some more ...
status = GaussBmp((Ipp8u*)growData.Scan0, growSize, nChannel, ippMskSize5x5);

if ( status )
goto Fault;

// Yet one more time when scaling up. Given how crazy fast this is, why not.
//status = GaussBmp((Ipp8u*)growData.Scan0, growSize, nChannel, ippMskSize5x5);

//if ( status )
// goto Fault;

status = ippiResizeSqrPixel_8u_C3R((Ipp8u*)growData.Scan0, growSize, growBmp.GetWidth() * nChannel, growRect, (Ipp8u*)shrinkData.Scan0, shrinkwidth * nChannel, shrinkRect, xshrinkFactor, yshrinkFactor, 0, 0, shrinkInterpolation, pshrinkBuffer);

Fault:
if ( status == ippStsInterpolationErr )
printf("Invalid interpolation mode!\n");

if ( pshrinkBuffer )
ippFree(pshrinkBuffer);

if ( pgrowBuffer )
ippFree(pgrowBuffer);

inBmp.UnlockBits(&InData);
growBmp.UnlockBits(&growData);
shrinkBmp.UnlockBits(&shrinkData);

if ( status == ippStsNoErr )
{
CLSID clsidBMP;

// Add failure checks here ...
GetEncoderClsid(L"image/bmp", &clsidBMP);

// ... and here.
growBmp.Save(L"grow.bmp", &clsidBMP);
shrinkBmp.Save(L"shrink.bmp", &clsidBMP);
}
}
}

return 0;
}
0 Kudos
1 Reply
Vladimir_Dudnik
Employee
489 Views

Please find attached corrected code (msvc2008 project and 15x15.bmp included)

Vladimir
0 Kudos
Reply