Intel® Integrated Performance Primitives
Deliberate problems developing high-performance vision, signal, security, and storage applications.
6814 讨论

Gaussian Blur on entire image, filters with borders

pelesl
初学者
1,525 次查看
Hello,

I am not an experienced programmer, but I know a bit. I'm currently trying to migrate from IPL 2.5 to IPP 5.0 in a program we have written here.

I believe there is a problem with the documentation relating to applying filters that require borders to whole images. As an example, I try to do a simple Gaussian blur with a 3x3 kernel. (Some background - images in our software are input as Ipp32f. I am using VS2005 writing in C.) As I understand it, since the filter has a 3x3 kernel, I need a 1-pixel border all around my image, which means my "scratch space" needs to be width+2, height+2. The code is below, and should be self-explanatory:

---------
typedef struct IppImage
{
Ipp32f *pixels; //allocate this with IppiMalloc
IppiSize dims; //width and height
int StepBytes; //for ippiMalloc
} IppImage;

IppImage MakeIppImage(int width, int height)
{
IppImage image;

image.dims.width=width;
image.dims.height=height;

image.pixels=ippiMalloc_32f_C1(width,height,&(image.StepBytes));
if(image.pixels==NULL)
{...}

return image;
}

void DeleteIppImage(IppImage img)
{
ippiFree(img.pixels);
img.dims.width=0;
img.dims.height=0;
}

IppStatus BlurIppImage(IppImage img)
{
IppImage scratch;
IppStatus retval;

//this image has to be bigger to accomodate the border
scratch=MakeIppImage(img.dims.width+2,img.dims.height+2);

retval=ippiCopyReplicateBorder_32s_C1R((const Ipp32s *)img.pixels, img.StepBytes, img.dims, (Ipp32s *)scratch.pixels, scratch.StepBytes, scratch.dims, 1, 1);
if(retval!=ippStsNoErr)
{...}

retval=ippiFilterGauss_32f_C1R(scratch.pixels, scratch.StepBytes, img.pixels, img.StepBytes, img.dims, ippMskSize3x3);
if(retval!=ippStsNoErr)
{...}

DeleteIppImage(scratch);
return retval;
}

--------
Some notes:

1. Since there is no "ippiCopyReplicateBorder_32f_C1R", I use 32s since I figured the data structures are the same size and this function should not interpret any values, just copy them. I put some typecasts in there for formality's sake, but maybe I shouldn't?

2. Elipses (...) are error-handling code that is irrelevant.

So here is what happens:

1. I load an image of width 400 into my program and convert it to an IppImage. Because of ippiMalloc's aligning, the step size is 1600 as predicted

2. Scratch is made to be 402x402 since I should only need a 1-pixel border to do a 3x3 kernel [with the anchor point in the center]. As predicted, the stepsize for scratch is 1632. I verify this scratch image is indeed 402x402 with the original 400x400 image in the center surrounded by a 1-pixel border that is a copy of the outer pixels of the original image.

3. I get a read access violation in the ippiFilterGauss_32f_C1R function, because it is trying to read scratch.pixels before the pointer. In other words, the position at which it causes the violation minus the position of scratch.pixels is -1636, which coincidently equals scratch's stepsize plus one pixel [of Ipp32f type]. At this point I figured that in ippiFilterGauss_32f_C1R, it seems the first parameter is not the pointer to the source image but rather the pointer to the "first possible anchor point" in the image, thus if the first pixel in scratch has coordinates "(0,0)" then this pointer should point to "(1,1)". If this is true, then the documentation is wrong. I need clarifica tion on this.

4. If I do indeed offset the input pointer to ippiFilterGauss_32f_C1R by passing it

&(scratch.pixels[scratch.dims.width])

Then it doesn't cause the violation, but it is apparent that the output image has the wrong offset - i.e., it is shifted relative to the unblurred original. In Photoshop this shows up as a black band and what looks to be part of the image looped again on the left side. I'm guessing the display order is standard which means this stuff on the left is actually at the end of each row, so I suspect it's the padding that ippiMalloc puts in to align memory.

5. If I then set the destination pointer to img.pixels-7 instead of just img.pixels then my blurred image lines up with the original with the exception that [in Photoshop] the black band and image snippets are now on the right. At this point I'm writing the message because this seems like senseless trial and error.

Conclusion:

1. My lack of experience is probably causing me to make a really dumb mistake somewhere.

2. The manual is unclear, since at it is written I believe my unmodified code above should have worked.

Please, someone write me a code snippet that does what my function above intends to do: apply a 3x3 Gaussian blur to an entire image.

Thank you.

P.S. Forgive me for the ugly code; I don't know how to paste it with nice formatting on this thing.
0 项奖励
5 回复数
Vladimir_Dudnik
1,525 次查看

Hi,

actually you are not so bad as programmer:) Really.

You probably may want to look at some IPP base concepts, like image representation, ROI concept and so on. You can find all those information just in the beginning of IPP manual.

As I understand, you need to point ippiFilterGauss to the second line of image. Because IPP function assume that caller provided borders around image ROI it intended to process.

You were absolutely right with using Copy for 32s data types instead of missed 32f data types. It was the reason why we did not duplicate similar functionality.

Regards,
Vladimir

Message Edited by vdudnik on 03-30-2006 01:06 AM

0 项奖励
pelesl
初学者
1,525 次查看
Hello Vladimir,

I see you're the guru when it comes to these forums! (Thank you for the programming compliment.)

However I was very impatient and since I had actually paid for the library I opened a support ticket and yes, I was not pointing the function to the correct pixel.

I will post a short discussion here because I believe the documentation is very unclear (wrong?) in how it presents these functions.

In filters that require borders, such as ippiFilterGauss, the documentation states that pSrc is "Pointer to the source image ROI". In reality:

pSrc is "Pointer to the source image ROI relative to the bordered image".

In other words, to apply ippiFilterGauss [with the 3x3 kernel], one must add a border of 1 pixel around the source image. In my case, I copied my source image this way:

int border=1;

retval=ippiCopyReplicateBorder_32s_C1R((const Ipp32s *)img.pixels, img.StepBytes, img.dims, (Ipp32s *)scratch.pixels, scratch.StepBytes, scratch.dims, border, border);

(If you're looking at this post, read my code sample in the first post of the thread to see why I have img.pixels for my pointer.)

The idea is that now I have an image called "scratch" that is equal to my original image "img" plus a 1-pixel border. Now if I want to filter the entire image "img" I need to pass "scratch" as the source since it has the appropriate border. But now pixel "(0,0)" on "img" is pixel "(1,1)" relative to the coordinate space of "scratch". So the correct way to apply the filter is:

Ipp32f * pStart = scratch.pixels + scratch.StepBytes*border + border*sizeof(Ipp32f);

retval=ippiFilterGauss_32f_C1R(pStart, scratch.StepBytes, img.pixels, img.StepBytes, img.dims, ippMskSize3x3);

(The preceding has been paraphrased from the solution presented in my support ticket).




In short: when applying filters where you must create your own borders, you must pass the as pSrc the pointer to the first pixel in your ROI in the coordinate system of the bordered image.

Thanks again to you and Chao for the help. I will shortly post an example of how to do a generalized Gaussian blur with any size kernel.
0 项奖励
Vladimir_Dudnik
1,525 次查看

Hello,

of course I'm not a guru. I just have a possibility to ask help from manyIPP expertsin different application areas

you are right,it always will be someroom for improvement in documentation, but just want to refer you that we have documented some basic concepts in ippi manual, for example chapter 2 IPP Concepts, article Major Operation Models (page 2-20), where concept of ROIis briefly described

Anyway, thanks for your interest to IPP, we will be glad to hear your expession about this product. What can be improved, added and so on. Are you interested also on 64-bit or Xscale platforms?

Regards,
Vladimir

0 项奖励
pelesl
初学者
1,525 次查看
Hi Vladimir,

As I said I'm a beginner. The code I'm working on is a bit achane, but I'm trying to modernize it as I go. I don't know if it compiles on 64-bit systems, and if it does, if it would make any difference, as most of the slow parts of the software are complicated "for" loops that just need to be rewritten to something a bit more intelligent.

Right now we run the software on two-year-old Xeons with HT and also AMD Opterons. But the image processing part that IPP does is quite small in the grand scheme of things.

Thanks to you and Intel support for the help.

Emilio
0 项奖励
kemicalbrother
初学者
1,525 次查看
Hello,
First of all i'm a beginner. I have some problems with ippiFilterGauss. i have to blurr an image with gaussian kernel 5X5 on a R8G8B8 - 24bit (no alpha channel) image. pls help me out
my repressentation image class uses unsigned char for each channel value in pixel.
I wrote a short console application to figure out:
my output image appears to be blurred but with a lot of green on it (some colors changed in dif level of green). i've got no error on syntax or running the program. i'm not sure about image.StepBytes value -return from ippiMalloc - i get 1920 here
i read it is pointer to the step in bytes through the image - so which would be the correct value here?
it's 640pix*3ch per pix=1920
my test image is a png-24bit file 640X480

so, my code works - but plus blur effect i have a lot of green on the image!
here is my code similar to what i saw earlier:


IppImage MakeImage(int x_dim, int y_dim) ///allocates space for an IppImage
{

////create IppImage
IppImage image;
image.dims.width=x_dim;
image.dims.height=y_dim;

image.pixels=ippiMalloc_8u_C3(x_dim, y_dim, &(image.StepBytes));
if(image.pixels==NULL)
coutcoutreturn image;
}

void BlurImage(IppImage img) ///Blurs the IppImage with a gaussian kernel
{
int kernel_width=5; //I have a 5X5 gaussian kernel
int border=(kernel_width-1)/2;
///final border will be 2*border
IppStatus retval;
IppImage scratch;

//make a bigger Image which has a border size=border
scratch=MakeImage(img.dims.width+2*border,img.dims.height+2*border);

retval=ippiCopyReplicateBorder_8u_C3R((const Ipp8u*)img.pixels,img.StepBytes,img.dims,(Ipp8u *)scratch.pixels,scratch.StepBytes,scratch.dims,border,border);
if(retval==ippStsNoErr)
cout
Ipp8u* pStart = scratch.pixels+scratch.StepBytes*border+border*sizeof(Ipp8u); ///points to source Image ROI

retval=ippiFilterGauss_8u_C3R(pStart,scratch.StepBytes,img.pixels,img.StepBytes,img.dims,ippMskSize5x5);
if(retval==ippStsNoErr)
cout
DeleteImage(scratch);

}
0 项奖励
回复