Simple "flat" blur image filter

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
Message
Author
Akjosch
Regular
Posts: 74
Joined: Wed Jul 25, 2012 5:48 am
Contact:

Simple "flat" blur image filter

#1 Post by Akjosch » Tue Sep 11, 2012 8:38 am

It's an awful hack, and not very fast (takes a few seconds on my laptop for a 800x600 image with radius=5), and doesn't do proper colour management, and is mathematically wrong on the edges of the image (... but I'm too tired with trying all kinds of stuff to fix it now), ...

... but it's still better than using pygame's scaling functions to do the same. Whoever wrote those has no idea whatsoever about signal processing, colour spaces or maths in general. Try it out - scale a simple drawing down by a factor of 20, scale it back to original size, and weep about the result.

Code: Select all

init python:

    import pygame

    class FlatBlur(im.ImageBase):

        def __init__(self, baseImage, radius, **properties):

            baseImage = im.image(baseImage)
            super(FlatBlur, self).__init__(baseImage, radius, **properties)

            self.image = baseImage
            self.radius = radius

        def get_mtime(self):
            return self.image.get_mtime()

        def load(self):
            surf = im.cache.get(self.image)
            rv = renpy.display.pgrender.surface(surf.get_size(), True)
            width = surf.get_width()
            height = surf.get_height()
            
            # output surface
            rvpa = pygame.PixelArray(rv)
            
            for x in range(0, width -1):
                for y in range(0, height -1):
                    # calculate the region we want to use for averaging
                    top = max(0, y - self.radius)
                    left = max(0, x - self.radius)
                    bottom = min(height - 1, y + self.radius)
                    right = min(width - 1, x + self.radius)
                    r = pygame.Rect(left, top, (right - left), (bottom - top))
                    rvpa[x][y] = rv.map_rgb(pygame.transform.average_color(surf, r))
            
            return rv
            
        def predict_files(self):
            return self.image.predict_files()
Usage example (in the init section, usually), for a blur radius of 5 (... or, in other words, a 11x11 pixels "box").

Code: Select all

    image bg filename_blurred = FlatBlur("filename.jpg", 5)
PS: Can we please have numpy support in Ren'py? :D

User avatar
AxemRed
Veteran
Posts: 482
Joined: Sun Jan 09, 2011 7:10 am
Contact:

Re: Simple "flat" blur image filter

#2 Post by AxemRed » Tue Sep 11, 2012 10:20 am

Your implementation is O(radius^2 * N), a box blur can be implemented in O(N).

Akjosch
Regular
Posts: 74
Joined: Wed Jul 25, 2012 5:48 am
Contact:

Re: Simple "flat" blur image filter

#3 Post by Akjosch » Tue Sep 11, 2012 10:23 am

AxemRed wrote:Your implementation is O(radius^2 * N), a box blur can be implemented in O(N).
Yes.

I did so as well originally. It was an order of magnitude slower for typical image sizes since I had to write nearly everything in Python (including the calculation of R, G, B and A components) instead of relying on native C code.

Sometimes, the O() notation isn't telling the whole story. :)

Anyway, this is just a by-product of my trying to make a proper generic convolution applicator, so I won't re-visit and re-code it any time soon. :D

Post Reply

Who is online

Users browsing this forum: No registered users