Wow ... ippiResizeSqrPixel_8u_C3R as good as photoshop.

Wow ... ippiResizeSqrPixel_8u_C3R as good as photoshop.

// Authored by: Mario Pintaric

// ippTest.cpp : Defines the entry point for the console application.
//

#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[j].MimeType, format) == 0 )
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}

free(pImageCodecInfo);
return -1;
}

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

// Set mask operation. Intel IPP docs do not provide an adequate explanation of border value condition handling.
// Overall, the docs are riddled with mistakes, poor grammar and incomplete explanations.
if ( op == ippMskSize3x3 )
{
nPad = 3;
}
else if ( op == ippMskSize5x5 )
{
nPad = 5; // Crashes when set to 5. Why???
}
else
return ippStsNotSupportedModeErr;

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

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

if ( !pad )
goto Fault;

// Note: Intel IPP seems to incorrectly caclulate the step value!!! Why??? Especially bad for small bitmap sizes (< 10 bytes wide)
step = padSize.width * nChannels;

if (nChannels == 1)
{
status = ippiCopyReplicateBorder_8u_C1R(pSrc, srcSize.width * nChannels, srcSize, pad, step, padSize, nPad, nPad);
}
else if (nChannels == 3)
{
status = ippiCopyReplicateBorder_8u_C3R(pSrc, srcSize.width * nChannels, srcSize, pad, step, padSize, nPad, nPad);
}
else if (nChannels == 4)
{
status = ippiCopyReplicateBorder_8u_AC4R(pSrc, srcSize.width * nChannels, srcSize, pad, step, padSize, nPad, nPad);
}

if ( status )
goto Fault;

// The docs do not provide any clarity on the need to do this. None of the samples I've seen suggest this is required.
// Half a day blown because of poor documentation!
pBorderOffset = &pad[(padSize.width * nPad * nChannels) + (nPad * nChannels)];

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

if ( status )
goto Fault;

Fault:
if ( pad )
ippiFree(pad);

return status;
}

int _tmain(int argc, _TCHAR* argv[])
{
const IppLibraryVersion* ippVersion = ippiGetLibVersion();

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

Bitmap inBmp(L"bg.bmp");

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

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

int growwidth = inBmp.GetWidth() * 4;
int growheight = inBmp.GetHeight() * 4;

int shrinkwidth = growwidth / 8;
int shrinkheight = growheight / 8;

Bitmap growBmp(growwidth, growheight, PixelFormat24bppRGB);
Bitmap shrinkBmp(shrinkwidth, shrinkheight, PixelFormat24bppRGB);

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;

// 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;
int shrinkInterpolation = IPPI_INTER_SUPER;

status = ippiResizeSqrPixelGetBufSize(growSize, nChannel, growInterpolation, &bufferSize);

if ( status )
goto Fault;

pgrowBuffer = (Ipp8u *)ippMalloc(bufferSize);

if ( !pgrowBuffer )
goto Fault;

// Note: Docs suggest IPPI_INTER_SUPER is only supported for downsampling. This call fails when set to IPPI_INTER_SUPER???
status = ippiResizeSqrPixelGetBufSize(shrinkSize, nChannel, shrinkInterpolation, &bufferSize);

// Note: the above call will fail, so we use a bufferSize that is more than big enough.
// Question: How is the bufferSize value to be calculated for IPPI_INTER_SUPER?

//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;

// Wow ... as good as Photoshop downsampling.
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;

GetEncoderClsid(L"image/bmp", &clsidBMP);

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

return 0;
}

10 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi Mario,

Thanks for sharing thesample here, I think itis quite good and helpful for those who looking for some start point with IPP image processing functions. Although there are several mistakes which were fixed in attached modified sample (msvc2008 project file included)

Regards,
Vladimir

Attachments: 

AttachmentSize
Downloadapplication/zip resize.zip49.22 KB

Quoting - Vladimir Dudnik (Intel)

Hi Mario,

Thanks for sharing thesample here, I think itis quite good and helpful for those who looking for some start point with IPP image processing functions. Although there are several mistakes which were fixed in attached modified sample (msvc2008 project file included)

Regards,
Vladimir

Best so far Vladimir. I am curious about the ippiResizeSqrPixelGetBufSize ... it returns an invalid interpolation method error when set to IPPI_INTER_SUPER. Is that intended behaviour for this API?

Also, the method used to select a border size by this code, is it correct? I'm not convinced I understand what criteria to use for the border size for the different APIs that need a replicated border to arrive at a given target bitmap size.

Thanks

Hello,

Iwas not able to reproduce your issuewith codesample attached in previous post (resize.zip)

      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 below fails 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;

Regards,
Vladimir

Quoting - Vladimir Dudnik (Intel)

Hello,

Iwas not able to reproduce your issuewith codesample attached in previous post (resize.zip)

      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 below fails 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;

Regards,
Vladimir

I commented out the ippiResizeSqrPixelGetBufSize call because it fails when using the IPPI_INTER_SUPER interpolation method. I don't see how you could tell whether you are growing or shrinking the image with this API given thatit only accepts the"shrinkSize" value. I should think the API should thusassume that you are shrinking the bitmap if you provide an interpolation method that can only support downsampling.

It's clear why the other API will work correctly, since it can decern between up/down sampling. I was just wondering why the other SqrPixel specific buffer sizingAPI fails since it isintended forthe SqrPixel resize call and is the only such API in support of that particular function. The docs mention elsewhere that some of the other APIs will eventually be depricated.

Not to kill your joy or debate about anything, but "as good as Photoshop" isn't too hard these days, Photoshop still has this poor 4x oversampling for its font rasterizer (3D cards do better and in realtime), it only recently got 16bit RGB processing (and still hasn't full 32bit processing), and yes, as for image resizing, Photoshop isn't one of the best either.

Quoting - gol

Not to kill your joy or debate about anything, but "as good as Photoshop" isn't too hard these days, Photoshop still has this poor 4x oversampling for its font rasterizer (3D cards do better and in realtime), it only recently got 16bit RGB processing (and still hasn't full 32bit processing), and yes, as for image resizing, Photoshop isn't one of the best either.

No not hard at all,however if you take a look at the code aboveit's not doable in just one step. And to get to the level of the very best solutions, you have to jump through four otherhoops.

What is cool is that using IPP allows you to up/down sample faster than any of the commercialsolutions out there while achieving a higher quality level (provided you know what sequence of image processing to employ).

We now have an in house scaler that is at least double the quality level of anything commercially available; all done with IPP and some creative use of it's features. Companies like IMAX have internal work that is even better but they've got mathematicians dedicated to the task with a render farm to absorb the computational expense.

Quoting - noemata
We now have an in house scaler that is at least double the quality level of anything commercially available; all done with IPP and some creative use of it's features.

You sure? For scaling down, traditional resampling is indeed what I'd use. But for scaling up, I would rather trust fractal-based or other 'intelligent' algo's. An algo that doesn't analyze/can't recognize the content isn't gonna stretch it up well.
Or is this what you're doing too?

Quoting - gol

You sure? For scaling down, traditional resampling is indeed what I'd use. But for scaling up, I would rather trust fractal-based or other 'intelligent' algo's. An algo that doesn't analyze/can't recognize the content isn't gonna stretch it up well.
Or is this what you're doing too?

We use a staged process that has a focus on luminocity and edge perservation/enhancement. Color is folded back in at the very end of the process (stems from my work with colorization in the mid 80's).Operating onthe color vectoris actually a waist of valuable computational resources for most image processing tasks. How's that for a convoluted hint?

Fractal-based methods tend to introduce "interesting" artifacts that manifest as image features that aren't really there. When scaling up, the challenge is in deciding when edge areas need to be curved or jointed.

I wouldn't try to read too much into the sample provided. It bearsno resemblance to the way our in housescaler works.

Hi Mario,

Coming back to conversation on the interface...

The function ippiResizeSqrPixelGetBufSize is obsolete. Please use the function ippiResizeGetBufSize for calculation of the size of the buffer.

From the documentation:

Chapter ResizeSqrPixel

"The function requires the external buffer pBuffer, its size can be previously computed by calling the function ippiResizeSqrPixelGetBufSize or ippiResizeGetBufSize"

My note: You can use any function but the first doesn't support Super Sampling :(

Chapter ResizeSqrPixelGetBufSize

"Caution: THIS FUNCTION IS DEPRECATED. Please use the function ippiResizeGetBufSize instead"

I apologise for repetition...

+++
I would like to add that there is a new functionality for image reducing with Super Sampling quality and the best performance in the IPP 6.1. It has another interface:

IppStatus ippiSuperSampling_ (const Ipp* pSrc, int srcStep, IppiSize srcRoiSize, Ipp* pDst, int dstStep, IppiSize dstRoiSize, Ipp8u* pBuffer);

Down-sampling factors are determined by RoiSize fields. For example input image has srcRoiSize = {1920, 1080} and output image has dstRoiSize = {1280, 720}.

Thanks,

Yuri

Leave a Comment

Please sign in to add a comment. Not a member? Join today