2

I've been experimenting with the image bicubic resampling algorithm present in the AForge framework with the idea of introducing something similar into my image processing solution. See the original algorithm here and interpolation kernel here

Unfortunately I've hit a wall. It looks to me like somehow I am calculating the sample destination position incorrectly, probably due to the algorithm being designed for Format24bppRgb images where as I am using a Format32bppPArgb format.

Here's my code:

public Bitmap Resize(Bitmap source, int width, int height)
{
    int sourceWidth = source.Width;
    int sourceHeight = source.Height;

    Bitmap destination = new Bitmap(width, height, PixelFormat.Format32bppPArgb);
    destination.SetResolution(source.HorizontalResolution, source.VerticalResolution);

    using (FastBitmap sourceBitmap = new FastBitmap(source))
    {
        using (FastBitmap destinationBitmap = new FastBitmap(destination))
        {
            double heightFactor = sourceWidth / (double)width;
            double widthFactor = sourceHeight / (double)height;

            // Coordinates of source points
            double ox, oy, dx, dy, k1, k2;
            int ox1, oy1, ox2, oy2;

            // Width and height decreased by 1
            int maxHeight = height - 1;
            int maxWidth = width - 1;

            for (int y = 0; y < height; y++)
            {
                // Y coordinates
                oy = (y * widthFactor) - 0.5;

                oy1 = (int)oy;
                dy = oy - oy1;

                for (int x = 0; x < width; x++)
                {
                    // X coordinates
                    ox = (x * heightFactor) - 0.5f;
                    ox1 = (int)ox;
                    dx = ox - ox1;

                    // Destination color components
                    double r = 0;
                    double g = 0;
                    double b = 0;
                    double a = 0;

                    for (int n = -1; n < 3; n++)
                    {
                        // Get Y cooefficient
                        k1 = Interpolation.BiCubicKernel(dy - n);

                        oy2 = oy1 + n;
                        if (oy2 < 0)
                        {
                            oy2 = 0;
                        }

                        if (oy2 > maxHeight)
                        {
                            oy2 = maxHeight;
                        }

                        for (int m = -1; m < 3; m++)
                        {
                            // Get X cooefficient
                            k2 = k1 * Interpolation.BiCubicKernel(m - dx);

                            ox2 = ox1 + m;
                            if (ox2 < 0)
                            {
                                ox2 = 0;
                            }

                            if (ox2 > maxWidth)
                            {
                                ox2 = maxWidth;
                            }

                            Color color = sourceBitmap.GetPixel(ox2, oy2);

                            r += k2 * color.R;
                            g += k2 * color.G;
                            b += k2 * color.B;
                            a += k2 * color.A;
                        }
                    }

                    destinationBitmap.SetPixel(
                        x, 
                        y, 
                        Color.FromArgb(a.ToByte(), r.ToByte(), g.ToByte(), b.ToByte()));
                }
            }

        }
    }

    source.Dispose();
    return destination;
}

And the kernel which should represent the given equation on Wikipedia

public static double BiCubicKernel(double x)
{
    if (x < 0)
    {
        x = -x;
    }

    double bicubicCoef = 0;

    if (x <= 1)
    {
        bicubicCoef = (1.5 * x - 2.5) * x * x + 1;
    }
    else if (x < 2)
    {
        bicubicCoef = ((-0.5 * x + 2.5) * x - 4) * x + 2;
    }

    return bicubicCoef;
}

Here's the original image at 500px x 667px.

picture of a tree

And the image resized to 400px x 543px.

tree with bicubic algorithm applied

Visually it appears that the image is over reduced and then the same pixels are repeatedly applied once we hit a particular point.

Can anyone give me some pointers here to solve this?

Note FastBitmap is a wrapper for Bitmap that uses LockBits to manipulate pixels in memory. It works well with everything else I apply it to.

Edit

As per request here's the methods involved in ToByte

public static byte ToByte(this double value)
{
    return Convert.ToByte(ImageMaths.Clamp(value, 0, 255));
}

public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
{
    if (value.CompareTo(min) < 0)
    {
        return min;
    }

    if (value.CompareTo(max) > 0)
    {
        return max;
    }

    return value;
}
James South
  • 10,147
  • 4
  • 59
  • 115
  • Could you share the original picture? I've tried the one you posted before, but the result is different. I guess stackoverflow applies some modifications to posted images. – netaholic Aug 19 '15 at 08:26
  • Yeah of course. It's on github here. https://raw.githubusercontent.com/JimBobSquarePants/ImageProcessor/27cbae6c27c1b0e5094d378fa9af63678178c9b0/src/ImageProcessor.Playground/images/input/tree.jpg – James South Aug 19 '15 at 08:29

2 Answers2

2

You are limiting your ox2 and oy2 to destination image dimensions, instead of source dimensions.

Change this:

// Width and height decreased by 1
int maxHeight = height - 1;
int maxWidth = width - 1;

to this:

// Width and height decreased by 1
int maxHeight = sourceHeight - 1;
int maxWidth = sourceWidth - 1;
vgru
  • 49,838
  • 16
  • 120
  • 201
1

Well, I've met a very strange thing, which might be or might be not a souce of the problem. I've started to try implementing convolution matrix by myself and encountered strange behaviour. I was testing code on a small image 4x4 pixels. The code is following:

 var source = Bitmap.FromFile(@"C:\Users\Public\Pictures\Sample Pictures\Безымянный.png");
 using (FastBitmap sourceBitmap = new FastBitmap(source))
        {
            for (int TY = 0; TY < 4; TY++)
            {
                for (int TX = 0; TX < 4; TX++)
                {
                    Color color = sourceBitmap.GetPixel(TX, TY);
                    Console.Write(color.B.ToString().PadLeft(5));
                }
                Console.WriteLine();
            }
        }

The output

Althought I'm printing out only blue channel value, it's still clearly incorrect.

On the other hand, your solution partitially works, what makes the thing I've found kind of irrelevant. One more guess I have: what is your system's DPI?

From what I have found helpfull, here are some links:

That's my answer so far, but I will try further.

Community
  • 1
  • 1
netaholic
  • 1,345
  • 10
  • 22