I have been working hard on learning Global Illumination through a program called SmallPaint. You can find it here:
I spent some time trying to get it to work in Windows then finally gave up and got it working Linux pretty quick. After digging through the code I was able to get a decent image from it but it too a looooooonnnggg time.
I found a lot of cool things in the code, including OpenMP for parallelism. Anyway, to cut straight to it- I was able to optimize this program a lot and even fixed a couple of bugs. In the end I got it to be about 5x faster than it was before. For a render of 256 x 256 pixels @ 64 samples per pixel it originally took 16 seconds and now it takes 3 seconds. The biggest problem was in lack of parallel thread support for random numbers. Besides this there were several places where too many structures were being created or unnecessarily having constructors called.
Here’s the image I got:
You’ll notice it has some “fire flies” on the walls. This lead to my second major endeavor. To rid the scene of fire flies as safely as possible.
I decided to use iPython/Spyder to do my filtering on this image. The process is much longer than I’d like to explain but after much exploration I settled on this algorithm:
- Convert RGB image into HSV
- Run a custom filter function, Firefly filter, on V channel
- Get difference with each neighbor pixel
- Return the smallest difference
- Save results as “Firefly map”
- Step through V channel with the Firefly map:
- If value at location in firefly map is above threshold
- Then replace image pixel by blending neighbor pixel values
- Else, this image pixel is fine so keep it
- Save results as “New V”
- Convert HS(New V) into new RGB image
- Save new RGB image
Here are several of the in between images that were generated
The process to detect fire flies took some time and here you can see a few steps as progress was made:
Those may be hard to see on your monitor if you zoom in you should see them. Also hopefully the process of saving, as png, and rendering on your browser or paint program are not filtering the image too much.
Ok, lastly I’ll just show the V channel progress and the original image next to the final one.
Flies here, Flies gone
Original and Final Image
You’ll notice that not much has changed. That is on purpose. The main goal of this filtering is to not touch the image unless it needs to be touched up. That is why I call it “Firefly” filtering, we just want to get rid of the flies and leave everything else alone.
Explanation of the Filter
Here are the notes I committed with my checkin. If you’re curious about what works and doesn’t work, or the logic behind the filtering, I explain it here.
Explanation of the rational behind this filter
For my images I realized, just by looking, that these sparks (fire flies)
were usually one-off (isolated pixel) issues where a light path added too
much energy. I seen other solutions of bilinear filtering and other global
convolution style fixes. The problem is that I want to maintain the strong
highlights that appear correct in the image. I was afraid these global solutions would wash up my nice pretty highlights, so I did not want to use them. So I created this filter that assumes if two pixels have similar intensity (even if it is really bright) that it was meant to be. But if no neighbors (in a 9 pixel kernel) have a similar value to yours then you must be a firefly and you must be blotted out.
A real, correct, one pixel highlight would get washed out. I am ok with this. Because if I really wanted that detail then I should render at a higher resolution. Any highlight that can have at least a little real light in a single neighbor should survive.
1. If a correct highlight exists as one pixel it will get washed out. I consider this ok because if we really want that detail then we should render at a higher resolution. Any highlight that can have at least a little real light in a single neighbor should survive.
2. I purposely don’t apply this filter for dark pixels. At least with my tests I was only interested in killing the single bright spots. Dark spots didn’t seem common or to even bother me if they did exist in my tests.
3. If two pixels are near each other and they both happen to get a freaky streak of intense light, then this filter won’t clean them out. I would need to do more testing to detect this type of a problem.