views:

169

answers:

2

I'm trying to use the position of the mouse to calculate the scaling factor for scaling an image. Basically, the further you get away from the center of the image, the bigger it gets; and the closer to the center you get, the smaller it gets. I have some code so far but it's acting really strange and I have absolutely no more ideas. First I'll let you know, one thing I was trying to do is average out 5 distances to get a more smooth resize animation. Here's my code:

private void pictureBoxScale_MouseMove(object sender, MouseEventArgs e)
{
    if (rotateScaleMode && isDraggingToScale)
    {
        // For Scaling              
        int sourceWidth = pictureBox1.Image.Width;
        int sourceHeight = pictureBox1.Image.Height;
        float dCurrCent = 0; // distance between the current mouse pos and the center of the image
        float dPrevCent = 0; // distance between the previous mouse pos and the center of the image

        System.Drawing.Point imgCenter = new System.Drawing.Point();

        imgCenter.X = pictureBox1.Location.X + (sourceWidth / 2);
        imgCenter.Y = pictureBox1.Location.Y + (sourceHeight / 2);

        // Calculating the distance between the current mouse location and the center of the image
        dCurrCent = (float)Math.Sqrt(Math.Pow(e.X - imgCenter.X, 2) + Math.Pow(e.Y - imgCenter.Y, 2));

        // Calculating the distance between the previous mouse location and the center of the image
        dPrevCent = (float)Math.Sqrt(Math.Pow(prevMouseLoc.X - imgCenter.X, 2) + Math.Pow(prevMouseLoc.Y - imgCenter.Y, 2));

        if (smoothScaleCount < 5)
        {
            dCurrCentSmooth[smoothScaleCount] = dCurrCent;
            dPrevCentSmooth[smoothScaleCount] = dPrevCent;
        }


        if (smoothScaleCount == 4)
        {
            float currCentSum = 0;
            float prevCentSum = 0;
            for (int i = 0; i < 4; i++)
            {
                currCentSum += dCurrCentSmooth[i];
            }
            for (int i = 0; i < 4; i++)
            {
                prevCentSum += dPrevCentSmooth[i];
            }

            float scaleAvg = (currCentSum / 5) / (prevCentSum / 5);


            int destWidth = (int)(sourceWidth * scaleAvg);
            int destHeight = (int)(sourceHeight * scaleAvg);

            // If statement is for limiting the size of the image
            if (destWidth > (currentRotatedImage.Width / 2) && destWidth < (currentRotatedImage.Width * 3) && destHeight > (currentRotatedImage.Height / 2) && destWidth < (currentRotatedImage.Width * 3))
            {
                AForge.Imaging.Filters.ResizeBilinear resizeFilter = new AForge.Imaging.Filters.ResizeBilinear(destWidth, destHeight);
                pictureBox1.Image = resizeFilter.Apply((Bitmap)currentRotatedImage);
                pictureBox1.Size = pictureBox1.Image.Size;
                pictureBox1.Refresh();
            }

            smoothScaleCount = -1;
        }
        prevMouseLoc = e.Location;
        currentScaledImage = pictureBox1.Image;
        smoothScaleCount++;

    }
}

EDIT: Thanks to Ben Voigt and Ray everything works well now. The only thing wrong is that with the way I'm doing it the image doesn't keep it's ratio; but I'll fix that later. Here's what I have for those who want to know:

private void pictureBoxScale_MouseMove(object sender, MouseEventArgs e)
    {
        if (rotateScaleMode && isDraggingToScale)
        {
            // For Scaling              
            int sourceWidth = pictureBox1.Image.Width;
            int sourceHeight = pictureBox1.Image.Height;
            int scale = e.X + p0.X; //p0 is the location of the mouse when the button first came down
            int destWidth = (int)(sourceWidth + (scale/10)); //I divide it by 10 to make it slower
            int destHeight = (int)(sourceHeight + (scale/10));

            if (destWidth > 20 && destWidth < 1000 && destHeight > 20 && destWidth < 1000)
            {
                AForge.Imaging.Filters.ResizeBilinear resizeFilter = new AForge.Imaging.Filters.ResizeBilinear(destWidth, destHeight);
                pictureBox1.Image = resizeFilter.Apply((Bitmap)currentRotatedImage);
                pictureBox1.Size = pictureBox1.Image.Size;
                pictureBox1.Refresh();
            }
            currentScaledImage = pictureBox1.Image; // This is only so I can rotate the scaled image in another part of my program

        }
    }
+1  A: 

It looks to me (from the scaleAvg calculation) like you're rescaling the already-scaled image. This is a really bad idea because scaling is lossy and the errors will accumulate. Instead, keep a copy of the crisp original image and scale the original directly to the current size.

Also, I would suggest using a different norm, perhaps Manhattan distance, instead of the current Cartesian distance which is a two-norm.

If you do continue using the two-norm, consider getting rid of the Math.Pow calls. They are probably such a small part of the overall scaling complexity that it doesn't matter, but multiplying by itself should be much faster than Math.Pow for squaring a number.

Ben Voigt
+1  A: 

You're scaling won't be smooth if you use the center of the image. Instead, use the initial mouse down point (call it p0). Also, rather than using the distance from that point to the current drag point (e), just take the difference along one axis (e.g. exp(e.Y - p0.Y)).

Ray
I'm not sure why you're suggesting exp for calculation of the infinity-norm. Did you mean just max(abs(x1-x2), abs(y1-y2)) ?
Ben Voigt
Makes sense to me, I like it.
Gaax
@Ben. You don't want a norm. You need +/- values. The exp is just some way to get a scaling value from a +/- value.
Ray