Question

I am a beginner at Java, only been coding for a year. My task is to create distortions to any image given. I have been having a lot of trouble with the bulge effect lately. I have been researching all around google and I found these links very helpful:

https://math.stackexchange.com/questions/266250/explanation-of-this-image-warping-bulge-filter-algorithm Image Warping - Bulge Effect Algorithm

I tried the algorithm that these two links gave me, but I ended up with nothing.

Let's say I have imported an image that is 100 pixels by 100 pixels, from the code below, am I using the algorithm correctly:

//modifiedImage is a global variable and contains the image that is 100x100    
    public BufferedImage buldge(){
                double X = 0;
                double Y = 0;
                BufferedImage anImage = new BufferedImage (1000, 1000, BufferedImage.TYPE_INT_ARGB);
                for(int x = 0; x < modifiedImage.getWidth(); x++){
                    for(int y = 0; y < modifiedImage.getHeight(); y++){
                            int rgb = modifiedImage.getRGB(x, y);
                            double newRadius = 0;
                            X = x - x/2;
                            Y = y - y/2;
                            double radius =  Math.sqrt(X*X + Y*Y);
                            double angle = Math.atan2(X, Y);
                            newRadius = Math.pow(radius,1.5);
                            X = (int)(newRadius*Math.sin(angle));
                            Y = (int)(newRadius*Math.cos(angle));
                            anImage.setRGB((int)X, (int)Y,rgb);

                    }
                }
                return anImage;  
            }

The problem is that this code doesn't really bulge the image in the middle. I have made a new BufferedImage of dimensions 1000x1000 because the pixels from the original one gets extended really far and some are extended beyond 1000x1000. If anyone would help me show the problems in this code concerning the bulge effect, I would greatly appreciate it.

Was it helpful?

Solution

I think one (main) part of the problem is that you are computing the radius of the bulge effect in pixels. Although I have not read all anwers in the threads that you linked, it seems like they are referring to texture coordinates - that is, to values between 0 and 1.

Apart from that: With the current approach, you will have a sampling problem. Imagine that one pixel at the center of the input image will be "stretched" so that it covers an area of, say, 10x10 pixels in the output image. But still, you are only computing one new position for this pixel.

Imagine it like you are taking pixels from the input image, and move them to a new position in the output image - but you have to do it the other way around: You have to check each pixel in the output image, and compute which pixel of the input image was moved there.

I created a small example: It allows moving a "magnifying glass" over the image with the mouse. With the mouse wheel, you can change the strength of the distortion. With SHIFT+MouseWheel, you can change the size of the magnifying glass.

enter image description here

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.image.BufferedImage;
import java.beans.Transient;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

public class ImageBulgeTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI();
            }
        });
    }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new GridLayout(1, 1));
        frame.getContentPane().add(new ImageBulgePanel());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}


class ImageBulgePanel extends JPanel
{
    private BufferedImage input;
    private BufferedImage output;
    private double bulgeStrength = 0.3;
    private double bulgeRadius = 100;

    ImageBulgePanel()
    {
        try
        {
            input = ImageIO.read(new File("lena512color.png"));
        }
        catch (IOException e1)
        {
            e1.printStackTrace();
        }

        addMouseMotionListener(new MouseAdapter()
        {
            @Override
            public void mouseMoved(MouseEvent e)
            {
                updateImage(e.getX(), e.getY());
            }
        });
        addMouseWheelListener(new MouseWheelListener()
        {

            @Override
            public void mouseWheelMoved(MouseWheelEvent e)
            {
                if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) ==
                    InputEvent.SHIFT_DOWN_MASK)
                {
                    bulgeRadius += 10 * e.getWheelRotation();
                    System.out.println("bulgeRadius "+bulgeRadius);
                }
                else
                {
                    bulgeStrength += 0.1 * e.getWheelRotation();
                    bulgeStrength = Math.max(0, bulgeStrength);
                    System.out.println("bulgeStrength "+bulgeStrength);
                }
                updateImage(e.getX(), e.getY());
            }
        });
    }

    @Override
    @Transient
    public Dimension getPreferredSize()
    {
        if (isPreferredSizeSet())
        {
            return super.getPreferredSize();
        }
        return new Dimension(input.getWidth(), input.getHeight());
    }

    @Override
    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        if (output != null)
        {
            g.drawImage(output, 0, 0, null);
        }
    }

    private void updateImage(int x, int y)
    {
        if (output == null)
        {
            output = new BufferedImage(
                input.getWidth(), input.getHeight(), 
                BufferedImage.TYPE_INT_ARGB);
        }
        computeBulgeImage(input, x, y, 
            bulgeStrength, bulgeRadius, 
            output);
        repaint();
    }

    private static void computeBulgeImage(
        BufferedImage input, int cx, int cy, 
        double bulgeStrength, double bulgeRadius, 
        BufferedImage output)
    {
        int w = input.getWidth();
        int h = input.getHeight();
        for(int x = 0; x < w; x++)
        {
            for(int y = 0; y < h; y++)
            {
                int dx = x-cx;
                int dy = y-cy;
                double distanceSquared = dx * dx + dy * dy;;
                int sx = x;
                int sy = y;
                if (distanceSquared < bulgeRadius * bulgeRadius)
                {
                    double distance = Math.sqrt(distanceSquared);
                    boolean otherMethod = false;
                    otherMethod = true;
                    if (otherMethod)
                    {
                        double r = distance / bulgeRadius;
                        double a = Math.atan2(dy, dx);
                        double rn = Math.pow(r, bulgeStrength)*distance; 
                        double newX = rn*Math.cos(a) + cx; 
                        double newY = rn*Math.sin(a) + cy;  
                        sx += (newX - x);
                        sy += (newY - y);
                    }
                    else
                    {
                        double dirX = dx / distance;
                        double dirY = dy / distance;
                        double alpha = distance / bulgeRadius;
                        double distortionFactor = 
                            distance * Math.pow(1-alpha, 1.0 / bulgeStrength);
                        sx -= distortionFactor * dirX;
                        sy -= distortionFactor * dirY;
                    }
                }
                if (sx >= 0 && sx < w && sy >= 0 && sy < h)
                {
                    int rgb = input.getRGB(sx, sy);
                    output.setRGB(x, y, rgb);
                }
            }
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top