在 Java 中镜像图像

Mirroring an Image in Java

背景信息: 我制作了一个程序,使用 JFileChooser 上传图像并填充 JFrame 的 space。然后我通过按钮应用过滤器。目前我的 4 个滤镜中有 3 个在工作,或者除了我的镜像滤镜之外的所有滤镜。它随机地(或者我想不是那么随机,因为总有一个原因,我只是不知道我做了什么)工作了几次。

我的问题: 我尝试按照教程进行操作,但必须稍作更改,以便我可以通过具有 JFile 选择器的按钮将其保存到新文件。目前,当我保存它时,它不会镜像,但是我的其他过滤器会做同样的细微变化。我的镜像逻辑有什么问题?感谢您的帮助!

我的代码:

全局变量:

// Save Button
private final JButton saveButton = new JButton("Save Files");
private File selFile1;
private File output = new File("Output.png");
private File selFile2;
private String path1;

上传文件:

// Browse Files Button ActionListener
BrowseButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        JFileChooser getFile = new JFileChooser();
        getFile.setCurrentDirectory(new File(System.getProperty("user.home")));
        // Filter files
        FileNameExtensionFilter filter1 = new FileNameExtensionFilter("*.Images", "jpg", "png");
        getFile.addChoosableFileFilter(filter1);
        int res = getFile.showSaveDialog(null);
        if(res == JFileChooser.APPROVE_OPTION) {
            selFile1 = getFile.getSelectedFile();
            path1 = selFile1.getAbsolutePath();
            label.setIcon(resize(path1));
            System.out.println("1st selFile1 = " + selFile1);
            try {
                getImg = ImageIO.read(selFile1);
            } catch (IOException ex) {
                System.out.println(ex);
            } // End try-catch
            
        } // End if
    } // End actionPerformer
}); // End ActionListener
}); // End ActionListener

保存文件:

    // Save Files Button ActionListener
    saveButton.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            JFileChooser getFile = new JFileChooser();
            getFile.setCurrentDirectory(new File(System.getProperty("user.home")));
            // Filter files
            FileNameExtensionFilter filter1 = new FileNameExtensionFilter("*.Images", "jpg",
                    "png");
            getFile.addChoosableFileFilter(filter1);
            int res = getFile.showSaveDialog(null);
            if(res == JFileChooser.APPROVE_OPTION) {
                selFile2 = getFile.getSelectedFile();
                path1 = selFile2.getAbsolutePath();
                label.setIcon(resize(path1));
                System.out.println("1st selFile1 = " + selFile2);                    
                try {
                   
                    ImageIO.write(getImg, "png", selFile2);
                } catch (IOException ex) {
                    System.out.println(ex);
                } // End try-catch
                
            } // End if
        } // End actionPerformer
    }); // End ActionListener

有效的灰度方法:

grayScale.setMnemonic('G'); //Hot key
        // ActionListener for grayscale button
        grayScale.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                   
            File f = selFile1;

            // Read image
            try{
                getImg = ImageIO.read(f);
            } catch(IOException ef){
                System.out.println(ef);
            } // End try-catch

            // Get image width and height
            int width = getImg.getWidth();
            int height = getImg.getHeight();

            // Convert to grayscale
            for(int y = 0; y < height; y++){
                for(int x = 0; x < width; x++){
                    int p = getImg.getRGB(x,y);

                    int a = (p>>24)&0xff;
                    int r = (p>>16)&0xff;
                    int g = (p>>8)&0xff;
                    int b = p&0xff;

                    // Calculate average
                    int avg = (r+g+b)/3;

                    // Replace RGB value with avg
                    p = (a<<24) | (avg<<16) | (avg<<8) | avg;

                    getImg.setRGB(x, y, p);
                }
            } // End for
            JOptionPane.showMessageDialog(comboBoxPanel, "Gray Scale Filter has been applied. Save to view!");
            // Write image
            try {
                f = output;
                ImageIO.write(getImg, "png", f);
            } catch(IOException ex){
                System.out.println(ex);
            } // End try-catch
            
            } // End actionPerformed
        }); //End ActionListener

镜像方法不行:

mirrored.setMnemonic('M'); // Hot Key
        // ActionListener for mirror button
        mirrored.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

            File f = selFile1;
            BufferedImage res = null;
            
            try{
                getImg = ImageIO.read(f);
                res = ImageIO.read(f);
            } catch(IOException ex){
                System.out.println(ex);
            } // End try-catch
            // Get source image dimension
            int width = getImg.getWidth();
            int height = getImg.getHeight();
            //BufferedImage for mirror image
            res = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            //create mirror image pixel by pixel
            for(int y = 0; y < height; y++){
                for(int lx = 0, rx = width - 1; lx < width; lx++, rx--){
                // lx starts from the left side of the image
                // rx starts from the right side of the image
                // Get source pixel value
                int p = getImg.getRGB(lx, y);
                // Set mirror image pixel value - both left and right
                res.setRGB(rx, y, p);
                }
            } // End for
            JOptionPane.showMessageDialog(comboBoxPanel, "Mirrored Filter has been applied. Save to view!");
            // Write image
            try{
              f = output;
              ImageIO.write(res, "png", f);
            } catch(IOException ex){
                System.out.println(ex);
            } // End try-catch
        } // End actionPerformed 
        }); // End actionListener

GUI(供参考):

所以,推测,你开始做...

File f = selFile1;
BufferedImage res = null;

try {
    getImg = ImageIO.read(f);
    res = ImageIO.read(f);
} catch (IOException ex) {
    System.out.println(ex);
} // End try-catch

最后你这样做...

// Write image
try {
    f = output;
    ImageIO.write(res, "png", f);
} catch (IOException ex) {
    System.out.println(ex);
} // End try-catch

但是,getImg 仍然指向源(未修改)图像。如果您使用它向用户显示结果,那么您显示的是错误的图像。根据您的其他“过滤”操作,您正在直接修改 getImg

现在,话虽如此,您的代码充其量是有问题的。

让我解释一下...

File f = selFile1;
BufferedImage res = null;

try {
    getImg = ImageIO.read(f);
    res = ImageIO.read(f);
} catch (IOException ex) {
    System.out.println(ex);
} // End try-catch

你怎么知道 selFile1 不是 null 或者它指向一个有效的 File?你在这里没有做任何检查。另外,两次读取文件有什么意义,尤其是当你在它之后立即执行 res = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 时?

这又引出了一个问题,如果图片加载失败怎么办?您继续尝试使用 getImg 引用,它可能包含“脏”数据或更糟的是 null.

相反,我可能会做一些更像...

if (selFile1 == null) {
    // Show error message
    // Use JOptionPane to show message to user
    return;
}

try {
    BufferedImage sourceImage = ImageIO.read(selFile1);
    // Get source image dimension
    int width = sourceImage.getWidth();
    int height = sourceImage.getHeight();
    //BufferedImage for mirror image
    BufferedImage mirroredImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    //create mirror image pixel by pixel
    for (int y = 0; y < height; y++) {
        for (int lx = 0, rx = width - 1; lx < width; lx++, rx--) {
            // lx starts from the left side of the image
            // rx starts from the right side of the image
            // Get source pixel value
            int p = sourceImage.getRGB(lx, y);
            // Set mirror image pixel value - both left and right
            mirroredImage.setRGB(rx, y, p);
        }
    } // End for
    JOptionPane.showMessageDialog(comboBoxPanel, "Mirrored Filter has been applied. Save to view!");
    // Write image
    ImageIO.write(mirroredImage, "png", selFile1);
} catch (IOException ex) {
    ex.printStackTrace();
    // Use JOptionPane to show message to user
} // End try-catch

现在您也可以在内部 try-catch 中隔离写入,但重点是尝试进行一些先发制人的检查,以防止已知问题导致代码崩溃。

哦,你可能会发现...

BufferedImage mirrored = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mirrored.createGraphics();
g2d.scale(-1, 1);
g2d.translate(-source.getWidth(), 0);
g2d.drawImage(source, 0, 0, null);
g2d.dispose();

还有更简单的镜像方式

您可以使用 Graphics2D 对象的 scale() 方法为您完成此操作:

import java.awt.*;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.swing.*;
import javax.imageio.*;

public class MirrorImage extends JPanel
{
    public MirrorImage()
    {
        try
        {
            BufferedImage image = ImageIO.read( new File("dukewavered.gif") );

            JLabel label = new JLabel( new ImageIcon( image ) );
            add(label);

            BufferedImage mirrorImage = createMirror( image );
            JLabel mirror = new JLabel( new ImageIcon( mirrorImage ) );
            add(mirror);
        }
        catch (Exception e) {e.printStackTrace();}
    }

    public BufferedImage createMirror(Image image)
    {
        BufferedImage bi = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);

        Graphics2D g2d = bi.createGraphics();
        g2d.translate(bi.getWidth(), 0);
        g2d.scale(-1, 1);
        g2d.drawImage(image, 0, 0, null);

        g2d.dispose();

        return bi;
     }

    private static void createAndShowGUI()
    {
        JFrame frame = new JFrame("Mirror Image");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new MirrorImage());
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args) throws Exception
    {
        java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
    }
}