19

I want to be able to take an image and blur it relatively quickly (say in 0.1 sec). Image size would almost never be larger than 256 x 256 px.

Do I have to loop thru every pixel and average them with neighbors or is there a higher-level way that I could do this?

PS: I am aware that multiple box blurs can approximate a gaussian blur.

Cœur
  • 37,241
  • 25
  • 195
  • 267
willc2
  • 38,991
  • 25
  • 88
  • 99

7 Answers7

36

I found a really fast pretty crappy way for iOS3.2+ apps

  UIView *myView = [self view];
  CALayer *layer = [myView layer];
  [layer setRasterizationScale:0.25];
  [layer setShouldRasterize:YES];

This rasterizes the view down to 4x4 pixel chunks then scales it back up using bilinear filtering... it's EXTREMELY fast and looks ok if you are just wanting to blur a background view under a modal view.

To undo it, just set the rasterization scale back to 1.0 or turn off rasterization.

Gabe
  • 2,279
  • 1
  • 23
  • 22
  • Indeed a neat solution! I noticed something strange just recently - If I do powers of 1/2 the blur is great on the simulator, but on the device I get a margin appearing on the side - anyone notice it? 10x! – Oded Ben Dov Jul 27 '11 at 09:05
  • @Gabe : Can you help me with this question: "Blur Effect (Wet in Wet effect) in Paint Application Using OpenGL-ES" http://stackoverflow.com/questions/6980402/blur-effect-wet-in-wet-effect-in-paint-application-using-opengl-es/6980512#6980512 – rohan-patel Aug 10 '11 at 11:54
  • 1
    I used this on labels, too. Great resource! – Agos Oct 10 '11 at 22:55
  • 3
    Please note that the blur will look different on retina/nonretina devices. You should multiply 0.25 with [[UIScreen mainScreen] scale] to make it look the same across all pixel densities. – Maciej Swic Dec 12 '12 at 10:37
  • This is faster than any custom blur implementation I've seen! – jjxtra Jan 19 '13 at 03:36
5

From how-do-i-create-blurred-text-in-an-iphone-view:

Take a look at Apple's GLImageProcessing iPhone sample. It does some blurring, among other things.

The relevant code includes:

static void blur(V2fT2f *quad, float t) // t = 1
{
    GLint tex;
    V2fT2f tmpquad[4];
    float offw = t / Input.wide;
    float offh = t / Input.high;
    int i;

    glGetIntegerv(GL_TEXTURE_BINDING_2D, &tex);

    // Three pass small blur, using rotated pattern to sample 17 texels:
    //
    // .\/.. 
    // ./\\/ 
    // \/X/\   rotated samples filter across texel corners
    // /\\/. 
    // ../\. 

    // Pass one: center nearest sample
    glVertexPointer  (2, GL_FLOAT, sizeof(V2fT2f), &quad[0].x);
    glTexCoordPointer(2, GL_FLOAT, sizeof(V2fT2f), &quad[0].s);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glColor4f(1.0/5, 1.0/5, 1.0/5, 1.0);
    validateTexEnv();
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // Pass two: accumulate two rotated linear samples
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    for (i = 0; i < 4; i++)
    {
        tmpquad[i].x = quad[i].s + 1.5 * offw;
        tmpquad[i].y = quad[i].t + 0.5 * offh;
        tmpquad[i].s = quad[i].s - 1.5 * offw;
        tmpquad[i].t = quad[i].t - 0.5 * offh;
    }
    glTexCoordPointer(2, GL_FLOAT, sizeof(V2fT2f), &tmpquad[0].x);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glActiveTexture(GL_TEXTURE1);
    glEnable(GL_TEXTURE_2D);
    glClientActiveTexture(GL_TEXTURE1);
    glTexCoordPointer(2, GL_FLOAT, sizeof(V2fT2f), &tmpquad[0].s);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB,      GL_INTERPOLATE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB,         GL_TEXTURE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB,         GL_PREVIOUS);
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB,         GL_PRIMARY_COLOR);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB,     GL_SRC_COLOR);
    glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA,    GL_REPLACE);
    glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA,       GL_PRIMARY_COLOR);

    glColor4f(0.5, 0.5, 0.5, 2.0/5);
    validateTexEnv();
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // Pass three: accumulate two rotated linear samples
    for (i = 0; i < 4; i++)
    {
        tmpquad[i].x = quad[i].s - 0.5 * offw;
        tmpquad[i].y = quad[i].t + 1.5 * offh;
        tmpquad[i].s = quad[i].s + 0.5 * offw;
        tmpquad[i].t = quad[i].t - 1.5 * offh;
    }
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // Restore state
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glClientActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, Half.texID);
    glDisable(GL_TEXTURE_2D);
    glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB,     GL_SRC_ALPHA);
    glActiveTexture(GL_TEXTURE0);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glDisable(GL_BLEND);
}
Community
  • 1
  • 1
mahboudz
  • 39,196
  • 16
  • 97
  • 124
4

If you always or at least often use the same blur settings you might gain speed by doing the filtering in frequency domain instead of the spatial domain.

  1. Precaclulate your filter image G(u,v), which is a 2D gaussian
  2. Apply fourier transform to your input image f(x,y)->F(u,v)
  3. Filter by multiplication: H(u,v) = F(u,v) .* G(u,v) (pixelwise multiplication, not matrix multiplication)
  4. Transform your filtered image back into the spatial domain by inverse fourier transform: H(u,v) -> h(x,y)

The pros of this approach is that pixel-wise multiplication should be pretty fast compared to averaging a neighborhood. So if you process a lot of images this might help.

The downside is that I have no idea how fast you can do fourier transforms on the iPhone so this might very well be much slower than other implementations.

Other than that I guess since the iPhone has OpenGL support you could maybe use its texturing functions/drawing to do it. Sorry to say though that I am no OpenGL expert and can't really give any practical advice as how that is done.

Hannes Ovrén
  • 21,229
  • 9
  • 65
  • 75
3

Here's two tricks for poor man's blur:

  1. Take the image, a draw it at partial opacity 5 or 6 (or however many you want) times each time offseting by a couple pixels in a different direction. drawing more times in more directions gets you a better blur, but you obviously trade off processing time. This works well if you want a blur with a relatively small radius.

  2. For monochromatic images, you can actually use the build in shadow as a simple blur.

amattn
  • 10,045
  • 1
  • 36
  • 33
  • 1
    If the "partial opacity" is a Gaussin bell curve function of the "couple pixels," you have the defintion of a gaussian blur (minus aliasing issues). – balpha Jul 16 '09 at 21:19
  • Do you know how fast the multiple drawings would be for an 4-way blur? That is, 4 left and 4 right. – willc2 Jul 17 '09 at 01:15
2

You might want to take a look at Mario Klingemann's StakBlur algorithm. It's not quite Gaussian, but pretty close.

balpha
  • 50,022
  • 18
  • 110
  • 131
1

Any algorithm that modifys images on a pixel level via openGL is going to be a tad slow; pixel-by-pixel manipulation on a openGL texture and then update it every frame is sadly performance in-adequate.

Spend some time writing a test rig and experimenting with pixel manipulation before committing to implementing a complex blur routine.

Phill
  • 1,325
  • 10
  • 14
0

The very basic of blur (or maybe more of a soften), is to average 2 neighboring pixels and apply the average to both pixels. iterate this throughout the image, and you get a slight blur (soften).

GeneCode
  • 7,545
  • 8
  • 50
  • 85