2011-12-19

Resampler Exact

Dla każdego promienia skanujemy piksele wokół (uwzględniamy promień filtra). Dla każdego piksela wagę liczymy dokładnie z filtra. Nie korzystamy z żadnych prekalkulowanych tabelek. Metoda ta powinna zapewniać największą dokładność.

W analizie kodu najważniejsze jest dobre zrozumienie wszystkich $\pm\frac{1}{2}$, $\pm 1$, Floor, Ceiling, Round.

Kod:
public struct ColorInterpolator
{
    private ColorFloat m_numerator;
    private double m_denumerator;

    public void Collect(ColorFloat a_ray_color, double a_filter_weight)
    {
        m_numerator += a_ray_color * a_filter_weight;
        m_denumerator += a_filter_weight;
    }

    public ColorFloat Color
    {
        get
        {
            if (m_denumerator == 0)
                return ColorFloat.Black;

            return m_numerator / m_denumerator;
        }
    }
}

public abstract class Resampler
{
    public Filter Filter = new BoxFilter();
    internal Film Film;

    public static Resampler Create(ResamplerType a_type)
    {
        if (a_type == ResamplerType.Exact)
            return new ExactResampler();
        else if (a_type == ResamplerType.LocalGrid)
            return new LocalGridResampler();
        else if (a_type == ResamplerType.Resizer)
            return new ResizerResampler();
        else
            throw new NotImplementedException();
    }

    public Rectangle GetAffectedRect(Rectangle a_src_rect)
    {
        int border = (int)Math.Ceiling(Filter.Ray) + 2;
        a_src_rect.Inflate(border, border);
        a_src_rect.Intersect(new Rectangle(0, 0, Film.Width, Film.Height));
        return a_src_rect;
    }

    public void Resample(Rectangle a_rect, ColorArrayBase a_dest)
    {
        for (int y = a_rect.Top; y < a_rect.Bottom; y++)
        {
            for (int x = a_rect.Left; x < a_rect.Right; x++)
            {
                a_dest.SetColor(x, y, ResamplePixel(x, y));
            }
        }
    }

    public abstract void RenderStart(RenderStartPhase a_phase);
    protected abstract ColorFloat ResamplePixel(int a_x, int a_y);
    public abstract void SetRayColor(Vector2 a_pixel, ColorFloat a_color);
}

public class ExactResampler : InterpolateResampler
{
    public override void SetRayColor(Vector2 a_pixel, ColorFloat a_color)
    {
        int start_x = (int)Math.Floor(a_pixel.X - Filter.Ray);
        int end_x = (int)Math.Ceiling(a_pixel.X + Filter.Ray) - 1;

        int start_y = (int)Math.Floor(a_pixel.Y - Filter.Ray);
        int end_y = (int)Math.Ceiling(a_pixel.Y + Filter.Ray) - 1;

        for (int y = start_y; y <= end_y; y++)
        {
            int cy = m_overlay_corrector.CorrectY(y);
            double wy = Filter.Evaluate((y + 0.5) - a_pixel.Y);

            for (int x = start_x; x <= end_x; x++)
            {
                int cx = m_overlay_corrector.CorrectX(x);
                double wx = Filter.Evaluate((x + 0.5) - a_pixel.X);

                m_rays[cx, cy].Collect(a_color, wy * wx);
            }
        }

        Debug.Assert(Filter.Evaluate(
            a_pixel.X - (start_x + 0.5 - 1)).IsAlmostEquals(0));
        Debug.Assert(Filter.Evaluate(
            a_pixel.X - (end_x + 0.5 + 1)).IsAlmostEquals(0));
        Debug.Assert(Filter.Evaluate(
            a_pixel.Y - (start_y + 0.5 - 1)).IsAlmostEquals(0));
        Debug.Assert(Filter.Evaluate(
            a_pixel.Y - (end_y + 0.5 + 1)).IsAlmostEquals(0));
    }
}

Brak komentarzy:

Prześlij komentarz