Blame exrenvmap/blurImage.cpp

Packit 0d464f
///////////////////////////////////////////////////////////////////////////
Packit 0d464f
//
Packit 0d464f
// Copyright (c) 2007, Industrial Light & Magic, a division of Lucas
Packit 0d464f
// Digital Ltd. LLC
Packit 0d464f
// 
Packit 0d464f
// All rights reserved.
Packit 0d464f
// 
Packit 0d464f
// Redistribution and use in source and binary forms, with or without
Packit 0d464f
// modification, are permitted provided that the following conditions are
Packit 0d464f
// met:
Packit 0d464f
// *       Redistributions of source code must retain the above copyright
Packit 0d464f
// notice, this list of conditions and the following disclaimer.
Packit 0d464f
// *       Redistributions in binary form must reproduce the above
Packit 0d464f
// copyright notice, this list of conditions and the following disclaimer
Packit 0d464f
// in the documentation and/or other materials provided with the
Packit 0d464f
// distribution.
Packit 0d464f
// *       Neither the name of Industrial Light & Magic nor the names of
Packit 0d464f
// its contributors may be used to endorse or promote products derived
Packit 0d464f
// from this software without specific prior written permission. 
Packit 0d464f
// 
Packit 0d464f
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit 0d464f
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit 0d464f
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
Packit 0d464f
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
Packit 0d464f
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
Packit 0d464f
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
Packit 0d464f
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit 0d464f
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit 0d464f
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit 0d464f
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
Packit 0d464f
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit 0d464f
//
Packit 0d464f
///////////////////////////////////////////////////////////////////////////
Packit 0d464f
Packit 0d464f
Packit 0d464f
//-----------------------------------------------------------------------------
Packit 0d464f
//
Packit 0d464f
//	function blurImage() -- performs a hemispherical blur
Packit 0d464f
//
Packit 0d464f
//-----------------------------------------------------------------------------
Packit 0d464f
Packit 0d464f
#include "blurImage.h"
Packit 0d464f
Packit 0d464f
#include "namespaceAlias.h"
Packit 0d464f
Packit 0d464f
#include <resizeImage.h>
Packit 0d464f
#include <cstring>
Packit 0d464f
#include "Iex.h"
Packit 0d464f
#include <iostream>
Packit 0d464f
#include <algorithm>
Packit 0d464f
#include <string.h>
Packit 0d464f
Packit 0d464f
Packit 0d464f
using namespace IMF;
Packit 0d464f
using namespace std;
Packit 0d464f
using namespace IMATH;
Packit 0d464f
Packit 0d464f
Packit 0d464f
inline int
Packit 0d464f
toInt (float x)
Packit 0d464f
{
Packit 0d464f
    return int (x + 0.5f);
Packit 0d464f
}
Packit 0d464f
Packit 0d464f
Packit 0d464f
inline double
Packit 0d464f
sqr (double x)
Packit 0d464f
{
Packit 0d464f
    return x * x;
Packit 0d464f
}
Packit 0d464f
Packit 0d464f
Packit 0d464f
void
Packit 0d464f
blurImage (EnvmapImage &image1, bool verbose)
Packit 0d464f
{
Packit 0d464f
    //
Packit 0d464f
    // Ideally we would blur the input image directly by convolving
Packit 0d464f
    // it with a 180-degree wide blur kernel.  Unfortunately this
Packit 0d464f
    // is prohibitively expensive when the input image is large.
Packit 0d464f
    // In order to keep running times reasonable, we perform the
Packit 0d464f
    // blur on a small proxy image that will later be re-sampled
Packit 0d464f
    // to the desired output resolution.
Packit 0d464f
    //
Packit 0d464f
    // Here's how it works:
Packit 0d464f
    //
Packit 0d464f
    // * If the input image is in latitude-longitude format,
Packit 0d464f
    //   convert it into a cube-face environment map.
Packit 0d464f
    //
Packit 0d464f
    // * Repeatedly resample the image, each time shrinking
Packit 0d464f
    //   it to no less than half its current size, until the
Packit 0d464f
    //   width of each cube face is MAX_IN_WIDTH pixels.
Packit 0d464f
    // 
Packit 0d464f
    // * Multiply each pixel by a weight that is proportinal
Packit 0d464f
    //   to the solid angle subtended by the pixel as seen
Packit 0d464f
    //   from the center of the environment cube.
Packit 0d464f
    //
Packit 0d464f
    // * Create an output image in cube-face format.
Packit 0d464f
    //   The cube faces of the output image are OUT_WIDTH
Packit 0d464f
    //   pixels wide.
Packit 0d464f
    //
Packit 0d464f
    // * For each pixel of the output image:
Packit 0d464f
    //
Packit 0d464f
    //       Set the output pixel's color to black
Packit 0d464f
    //
Packit 0d464f
    //       Determine the direction, d2, from the center of the
Packit 0d464f
    //       output environment cube to the center of the output
Packit 0d464f
    //	     pixel.
Packit 0d464f
    //   
Packit 0d464f
    //       For each pixel of the input image:
Packit 0d464f
    //
Packit 0d464f
    //           Determine the direction, d1, from the center of
Packit 0d464f
    //           the input environment cube to the center of the
Packit 0d464f
    //           input pixel.
Packit 0d464f
    //    
Packit 0d464f
    //           Multiply the input pixel's color by max (0, d1.dot(d2))
Packit 0d464f
    //           and add the result to the output pixel.
Packit 0d464f
    //
Packit 0d464f
Packit 0d464f
    const int MAX_IN_WIDTH = 40;
Packit 0d464f
    const int OUT_WIDTH = 100;
Packit 0d464f
Packit 0d464f
    if (verbose)
Packit 0d464f
	cout << "blurring map image" << endl;
Packit 0d464f
Packit 0d464f
    EnvmapImage image2;
Packit 0d464f
    EnvmapImage *iptr1 = &image1;
Packit 0d464f
    EnvmapImage *iptr2 = &image2;
Packit 0d464f
Packit 0d464f
    int w = image1.dataWindow().max.x - image1.dataWindow().min.x + 1;
Packit 0d464f
    int h = w * 6;
Packit 0d464f
Packit 0d464f
    if (iptr1->type() == ENVMAP_LATLONG)
Packit 0d464f
    {
Packit 0d464f
	//
Packit 0d464f
	// Convert the input image from latitude-longitude
Packit 0d464f
	// to cube-face format.
Packit 0d464f
	//
Packit 0d464f
Packit 0d464f
	if (verbose)
Packit 0d464f
	    cout << "    converting to cube-face format" << endl;
Packit 0d464f
Packit 0d464f
	w /= 4;
Packit 0d464f
	h = w * 6;
Packit 0d464f
Packit 0d464f
	Box2i dw (V2i (0, 0), V2i (w - 1, h - 1));
Packit 0d464f
	resizeCube (*iptr1, *iptr2, dw, 1, 7);
Packit 0d464f
Packit 0d464f
	swap (iptr1, iptr2);
Packit 0d464f
    }
Packit 0d464f
Packit 0d464f
    while (w > MAX_IN_WIDTH)
Packit 0d464f
    {
Packit 0d464f
	//
Packit 0d464f
	// Shrink the image.
Packit 0d464f
	//
Packit 0d464f
Packit 0d464f
	if (w >= MAX_IN_WIDTH * 2)
Packit 0d464f
	    w /= 2;
Packit 0d464f
	else
Packit 0d464f
	    w = MAX_IN_WIDTH;
Packit 0d464f
Packit 0d464f
	h = w * 6;
Packit 0d464f
Packit 0d464f
	if (verbose)
Packit 0d464f
	{
Packit 0d464f
	    cout << "    resizing cube faces "
Packit 0d464f
		    "to " << w << " by " << w << " pixels" << endl;
Packit 0d464f
	}
Packit 0d464f
Packit 0d464f
	Box2i dw (V2i (0, 0), V2i (w - 1, h - 1));
Packit 0d464f
	resizeCube (*iptr1, *iptr2, dw, 1, 7);
Packit 0d464f
Packit 0d464f
	swap (iptr1, iptr2);
Packit 0d464f
    }
Packit 0d464f
Packit 0d464f
    if (verbose)
Packit 0d464f
	cout << "    computing pixel weights" << endl;
Packit 0d464f
Packit 0d464f
    { 
Packit 0d464f
        //
Packit 0d464f
        // Multiply each pixel by a weight that is proportinal
Packit 0d464f
        // to the solid angle subtended by the pixel.
Packit 0d464f
        //
Packit 0d464f
Packit 0d464f
	Box2i dw = iptr1->dataWindow();
Packit 0d464f
	int sof = CubeMap::sizeOfFace (dw);
Packit 0d464f
Packit 0d464f
	Array2D<Rgba> &pixels = iptr1->pixels();
Packit 0d464f
Packit 0d464f
	double weightTotal = 0;
Packit 0d464f
Packit 0d464f
	for (int f = CUBEFACE_POS_X; f <= CUBEFACE_NEG_Z; ++f)
Packit 0d464f
	{
Packit 0d464f
	    if (verbose)
Packit 0d464f
		cout << "        face " << f << endl;
Packit 0d464f
Packit 0d464f
	    CubeMapFace face = CubeMapFace (f);
Packit 0d464f
	    V3f faceDir (0, 0, 0);
Packit 0d464f
            int ix = 0, iy = 0, iz = 0;
Packit 0d464f
Packit 0d464f
	    switch (face)
Packit 0d464f
	    {
Packit 0d464f
	      case CUBEFACE_POS_X:
Packit 0d464f
		faceDir = V3f (1, 0, 0);
Packit 0d464f
                ix = 0;
Packit 0d464f
                iy = 1;
Packit 0d464f
                iz = 2;
Packit 0d464f
		break;
Packit 0d464f
Packit 0d464f
	      case CUBEFACE_NEG_X:
Packit 0d464f
		faceDir = V3f (-1, 0, 0);
Packit 0d464f
                ix = 0;
Packit 0d464f
                iy = 1;
Packit 0d464f
                iz = 2;
Packit 0d464f
		break;
Packit 0d464f
Packit 0d464f
	      case CUBEFACE_POS_Y:
Packit 0d464f
		faceDir = V3f (0, 1, 0);
Packit 0d464f
                ix = 1;
Packit 0d464f
                iy = 0;
Packit 0d464f
                iz = 2;
Packit 0d464f
		break;
Packit 0d464f
Packit 0d464f
	      case CUBEFACE_NEG_Y:
Packit 0d464f
		faceDir = V3f (0, -1, 0);
Packit 0d464f
                ix = 1;
Packit 0d464f
                iy = 0;
Packit 0d464f
                iz = 2;
Packit 0d464f
		break;
Packit 0d464f
Packit 0d464f
	      case CUBEFACE_POS_Z:
Packit 0d464f
		faceDir = V3f (0, 0, 1);
Packit 0d464f
                ix = 2;
Packit 0d464f
                iy = 0;
Packit 0d464f
                iz = 1;
Packit 0d464f
		break;
Packit 0d464f
Packit 0d464f
	      case CUBEFACE_NEG_Z:
Packit 0d464f
		faceDir = V3f (0, 0, -1);
Packit 0d464f
                ix = 2;
Packit 0d464f
                iy = 0;
Packit 0d464f
                iz = 1;
Packit 0d464f
		break;
Packit 0d464f
	    }
Packit 0d464f
Packit 0d464f
	    for (int y = 0; y < sof; ++y)
Packit 0d464f
	    {
Packit 0d464f
		bool yEdge = (y == 0 || y == sof - 1);
Packit 0d464f
Packit 0d464f
		for (int x = 0; x < sof; ++x)
Packit 0d464f
		{
Packit 0d464f
		    bool xEdge = (x == 0 || x == sof - 1);
Packit 0d464f
Packit 0d464f
		    V2f posInFace (x, y);
Packit 0d464f
Packit 0d464f
		    V3f dir =
Packit 0d464f
			CubeMap::direction (face, dw, posInFace).normalized();
Packit 0d464f
Packit 0d464f
		    V2f pos =
Packit 0d464f
			CubeMap::pixelPosition (face, dw, posInFace);
Packit 0d464f
Packit 0d464f
                    //
Packit 0d464f
                    // The solid angle subtended by pixel (x,y), as seen
Packit 0d464f
                    // from the center of the cube, is proportional to the
Packit 0d464f
                    // square of the distance of the pixel from the center
Packit 0d464f
                    // of the cube and proportional to the dot product of
Packit 0d464f
                    // the viewing direction and the normal of the cube
Packit 0d464f
                    // face that contains the pixel.
Packit 0d464f
                    //
Packit 0d464f
Packit 0d464f
                    double weight =
Packit 0d464f
                        (dir ^ faceDir) *
Packit 0d464f
                        (sqr (dir[iy] / dir[ix]) + sqr (dir[iz] / dir[ix]) + 1);
Packit 0d464f
Packit 0d464f
                    //
Packit 0d464f
                    // Pixels at the edges and corners of the
Packit 0d464f
                    // cube are duplicated; we must adjust the
Packit 0d464f
                    // pixel weights accordingly.
Packit 0d464f
                    //
Packit 0d464f
Packit 0d464f
		    if (xEdge && yEdge)
Packit 0d464f
			weight /= 3;
Packit 0d464f
		    else if (xEdge || yEdge)
Packit 0d464f
			weight /= 2;
Packit 0d464f
Packit 0d464f
		    Rgba &pixel = pixels[toInt (pos.y)][toInt (pos.x)];
Packit 0d464f
Packit 0d464f
		    pixel.r *= weight;
Packit 0d464f
		    pixel.g *= weight;
Packit 0d464f
		    pixel.b *= weight;
Packit 0d464f
		    pixel.a *= weight;
Packit 0d464f
Packit 0d464f
		    weightTotal += weight;
Packit 0d464f
		}
Packit 0d464f
	    }
Packit 0d464f
	}
Packit 0d464f
Packit 0d464f
	//
Packit 0d464f
	// The weighting operation above has made the overall image darker.
Packit 0d464f
	// Apply a correction to recover the image's original brightness.
Packit 0d464f
	//
Packit 0d464f
Packit 0d464f
	int w = dw.max.x - dw.min.x + 1;
Packit 0d464f
	int h = dw.max.y - dw.min.y + 1;
Packit 0d464f
	size_t numPixels = w * h;
Packit 0d464f
	double weight = numPixels / weightTotal;
Packit 0d464f
Packit 0d464f
	Rgba *p = &pixels[0][0];
Packit 0d464f
	Rgba *end = p + numPixels;
Packit 0d464f
Packit 0d464f
	while (p < end)
Packit 0d464f
	{
Packit 0d464f
	    p->r *= weight;
Packit 0d464f
	    p->g *= weight;
Packit 0d464f
	    p->b *= weight;
Packit 0d464f
	    p->a *= weight;
Packit 0d464f
Packit 0d464f
	    ++p;
Packit 0d464f
	}
Packit 0d464f
    } 
Packit 0d464f
Packit 0d464f
    { 
Packit 0d464f
	if (verbose)
Packit 0d464f
	    cout << "    generating blurred image" << endl;
Packit 0d464f
Packit 0d464f
	Box2i dw1 = iptr1->dataWindow();
Packit 0d464f
	int sof1 = CubeMap::sizeOfFace (dw1);
Packit 0d464f
Packit 0d464f
	Box2i dw2 (V2i (0, 0), V2i (OUT_WIDTH - 1, OUT_WIDTH * 6 - 1));
Packit 0d464f
	int sof2 = CubeMap::sizeOfFace (dw2);
Packit 0d464f
Packit 0d464f
	iptr2->resize (ENVMAP_CUBE, dw2);
Packit 0d464f
	iptr2->clear ();
Packit 0d464f
Packit 0d464f
	Array2D<Rgba> &pixels1 = iptr1->pixels();
Packit 0d464f
	Array2D<Rgba> &pixels2 = iptr2->pixels();
Packit 0d464f
Packit 0d464f
	for (int f2 = CUBEFACE_POS_X; f2 <= CUBEFACE_NEG_Z; ++f2)
Packit 0d464f
	{
Packit 0d464f
	    if (verbose)
Packit 0d464f
		cout << "        face " << f2 << endl;
Packit 0d464f
Packit 0d464f
	    CubeMapFace face2 = CubeMapFace (f2);
Packit 0d464f
Packit 0d464f
	    for (int y2 = 0; y2 < sof2; ++y2)
Packit 0d464f
	    {
Packit 0d464f
		for (int x2 = 0; x2 < sof2; ++x2)
Packit 0d464f
		{
Packit 0d464f
		    V2f posInFace2 (x2, y2);
Packit 0d464f
Packit 0d464f
		    V3f dir2 = CubeMap::direction
Packit 0d464f
			(face2, dw2, posInFace2);
Packit 0d464f
			
Packit 0d464f
		    V2f pos2 = CubeMap::pixelPosition
Packit 0d464f
			(face2, dw2, posInFace2);
Packit 0d464f
		    
Packit 0d464f
		    double weightTotal = 0;
Packit 0d464f
		    double rTotal = 0;
Packit 0d464f
		    double gTotal = 0;
Packit 0d464f
		    double bTotal = 0;
Packit 0d464f
		    double aTotal = 0;
Packit 0d464f
Packit 0d464f
		    Rgba &pixel2 =
Packit 0d464f
			pixels2[toInt (pos2.y)][toInt (pos2.x)];
Packit 0d464f
Packit 0d464f
		    for (int f1 = CUBEFACE_POS_X; f1 <= CUBEFACE_NEG_Z; ++f1)
Packit 0d464f
		    {
Packit 0d464f
			CubeMapFace face1 = CubeMapFace (f1);
Packit 0d464f
Packit 0d464f
			for (int y1 = 0; y1 < sof1; ++y1)
Packit 0d464f
			{
Packit 0d464f
			    for (int x1 = 0; x1 < sof1; ++x1)
Packit 0d464f
			    {
Packit 0d464f
				V2f posInFace1 (x1, y1);
Packit 0d464f
Packit 0d464f
				V3f dir1 = CubeMap::direction
Packit 0d464f
				    (face1, dw1, posInFace1);
Packit 0d464f
				    
Packit 0d464f
				V2f pos1 = CubeMap::pixelPosition
Packit 0d464f
				    (face1, dw1, posInFace1);
Packit 0d464f
				
Packit 0d464f
				double weight = dir1 ^ dir2;
Packit 0d464f
Packit 0d464f
				if (weight <= 0)
Packit 0d464f
				    continue;
Packit 0d464f
Packit 0d464f
				Rgba &pixel1 =
Packit 0d464f
				    pixels1[toInt (pos1.y)][toInt (pos1.x)];
Packit 0d464f
Packit 0d464f
				weightTotal += weight;
Packit 0d464f
				rTotal += pixel1.r * weight;
Packit 0d464f
				gTotal += pixel1.g * weight;
Packit 0d464f
				bTotal += pixel1.b * weight;
Packit 0d464f
				aTotal += pixel1.a * weight;
Packit 0d464f
			    }
Packit 0d464f
			}
Packit 0d464f
		    }
Packit 0d464f
Packit 0d464f
		    pixel2.r = rTotal / weightTotal;
Packit 0d464f
		    pixel2.g = gTotal / weightTotal;
Packit 0d464f
		    pixel2.b = bTotal / weightTotal;
Packit 0d464f
		    pixel2.a = aTotal / weightTotal;
Packit 0d464f
		}
Packit 0d464f
	    }
Packit 0d464f
	}
Packit 0d464f
Packit 0d464f
	swap (iptr1, iptr2);
Packit 0d464f
    } 
Packit 0d464f
Packit 0d464f
    //
Packit 0d464f
    // Depending on how many times we've re-sampled the image,
Packit 0d464f
    // the result is now either in image1 or in image2.
Packit 0d464f
    // If necessary, copy the result into image1.
Packit 0d464f
    //
Packit 0d464f
Packit 0d464f
    if (iptr1 != &image1)
Packit 0d464f
    {
Packit 0d464f
	if (verbose)
Packit 0d464f
	    cout << "    copying" << endl;
Packit 0d464f
Packit 0d464f
	Box2i dw = iptr1->dataWindow();
Packit 0d464f
	image1.resize (ENVMAP_CUBE, dw);
Packit 0d464f
Packit 0d464f
	int w = dw.max.x - dw.min.x + 1;
Packit 0d464f
	int h = dw.max.y - dw.min.y + 1;
Packit 0d464f
	size_t size = w * h * sizeof (Rgba);
Packit 0d464f
Packit 0d464f
	memcpy (&image1.pixels()[0][0], &iptr1->pixels()[0][0], size);
Packit 0d464f
    }
Packit 0d464f
}