图像
最后修改于 2023 年 7 月 17 日
在本部分的Java 2D教程中,我们将处理图像。
BufferedImage
是Java 2D中处理图像的基础类。它是在内存中存储的像素矩形。
显示图像
在第一个示例中,我们在面板上显示一个图像。
package com.zetcode; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; class Surface extends JPanel { private Image mshi; public Surface() { loadImage(); setSurfaceSize(); } private void loadImage() { mshi = new ImageIcon("mushrooms.jpg").getImage(); } private void setSurfaceSize() { Dimension d = new Dimension(); d.width = mshi.getWidth(null); d.height = mshi.getHeight(null); setPreferredSize(d); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(mshi, 0, 0, null); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class DisplayImageEx extends JFrame { public DisplayImageEx() { initUI(); } private void initUI() { add(new Surface()); pack(); setTitle("Mushrooms"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { DisplayImageEx ex = new DisplayImageEx(); ex.setVisible(true); } }); } }
在示例中,我们在面板上显示一个图像。窗口会调整大小以适应图像的大小。
private void loadImage() { mshi = new ImageIcon("mushrooms.jpg").getImage(); }
我们使用ImageIcon
类加载图像。图像位于当前工作目录中。
private void setSurfaceSize() { Dimension d = new Dimension(); d.width = mshi.getWidth(null); d.height = mshi.getHeight(null); setPreferredSize(d); }
我们确定加载图像的大小。使用setPreferredSize
方法,我们设置Surface
面板的首选大小。JFrame
容器的pack
方法将使框架适应其子对象的大小。在我们的例子中是Surface
面板。因此,窗口的大小将正好显示加载的图像。
private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(mshi, 0, 0, null); }
图像使用drawImage
方法在面板上绘制。最后一个参数是ImageObserver
类。它有时用于异步加载。当我们不需要异步加载图像时,可以将null
放在那里。
private void initUI() { ... pack(); ... }
pack
方法将容器的大小调整为适合子面板的大小。
灰度图像
在计算学中,数字灰度图像是指其中每个像素的值都是一个单一的样本,即它承载了关于其强度的全部(且唯一)信息。此类图像完全由中性灰度的阴影组成,从最弱强度的黑色到最强白色的变化。(维基百科)
在下一个示例中,我们将使用Java 2D创建一个灰度图像。
package com.zetcode; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; class Surface extends JPanel { private Image mshi; private BufferedImage bufimg; private Dimension d; public Surface() { loadImage(); determineAndSetSize(); createGrayImage(); } private void determineAndSetSize() { d = new Dimension(); d.width = mshi.getWidth(null); d.height = mshi.getHeight(null); setPreferredSize(d); } private void createGrayImage() { bufimg = new BufferedImage(d.width, d.height, BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2d = bufimg.createGraphics(); g2d.drawImage(mshi, 0, 0, null); g2d.dispose(); } private void loadImage() { mshi = new ImageIcon("mushrooms.jpg").getImage(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bufimg, null, 0, 0); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class GrayScaleImageEx extends JFrame { public GrayScaleImageEx() { initUI(); } private void initUI() { Surface dpnl = new Surface(); add(dpnl); pack(); setTitle("GrayScale image"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { GrayScaleImageEx ex = new GrayScaleImageEx(); ex.setVisible(true); } }); } }
有几种方法可以创建灰度图像。我们通过将图像数据写入BufferedImage.TYPE_BYTE_GRAY
类型的缓冲图像来实现。
bufimg = new BufferedImage(d.width, d.height, BufferedImage.TYPE_BYTE_GRAY);
我们创建一个类型为BufferedImage.TYPE_BYTE_GRAY
的BufferedImage
类。
Graphics2D g2d = bufimg.createGraphics(); g2d.drawImage(mshi, 0, 0, null);
这里我们将蘑菇图像绘制到缓冲图像中。
g2d.dispose();
使用createGraphics
方法创建的Graphics对象应手动释放。作为组件的paint
和update
方法的参数提供的Graphics对象在这些方法返回时由系统自动释放。
private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bufimg, null, 0, 0); }
缓冲图像使用drawImage
方法在面板上绘制。
翻转图像
下面的示例翻转图像。我们将过滤图像。有一个filter
方法可以转换图像。
package com.zetcode; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.AffineTransform; import java.awt.image.AffineTransformOp; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JPanel; class Surface extends JPanel { private Image mshi; private BufferedImage bufimg; private final int SPACE = 10; public Surface() { loadImage(); createFlippedImage(); setSurfaceSize(); } private void loadImage() { mshi = new ImageIcon("mushrooms.jpg").getImage(); } private void createFlippedImage() { bufimg = new BufferedImage(mshi.getWidth(null), mshi.getHeight(null), BufferedImage.TYPE_INT_RGB); Graphics gb = bufimg.getGraphics(); gb.drawImage(mshi, 0, 0, null); gb.dispose(); AffineTransform tx = AffineTransform.getScaleInstance(-1, 1); tx.translate(-mshi.getWidth(null), 0); AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); bufimg = op.filter(bufimg, null); } private void setSurfaceSize() { int w = bufimg.getWidth(); int h = bufimg.getHeight(); Dimension d = new Dimension(3*SPACE+2*w, h+2*SPACE); setPreferredSize(d); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(mshi, SPACE, SPACE, null); g2d.drawImage(bufimg, null, 2*SPACE + bufimg.getWidth(), SPACE); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class FlippedImageEx extends JFrame { public FlippedImageEx() { initUI(); } private void initUI() { add(new Surface()); pack(); setTitle("Flipped image"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { FlippedImageEx ex = new FlippedImageEx(); ex.setVisible(true); } }); } }
在我们的代码示例中,我们将图像水平翻转。
AffineTransform tx = AffineTransform.getScaleInstance(-1, 1); tx.translate(-castle.getWidth(null), 0);
翻转图像意味着对其进行缩放和平移。因此,我们进行AffineTransform
操作。
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); bufferedImage = op.filter(bufferedImage, null)
这是可用的过滤操作之一。也可以通过像素操作来完成。但是Java 2D提供了高级类,可以更轻松地操作图像。在我们的例子中,AffineTransformOp
类对图像像素执行缩放和平移。
private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(mshi, SPACE, SPACE, null); g2d.drawImage(bufimg, null, 2*SPACE + bufimg.getWidth(), SPACE); }
两个图像都绘制在面板上。
private void setSurfaceSize() { int w = bufimg.getWidth(); int h = bufimg.getHeight(); Dimension d = new Dimension(3*SPACE+2*w, h+2*SPACE); setPreferredSize(d); }
我们为面板设置了首选大小。我们计算大小是为了能够在面板上放置两个图像,并在它们之间以及图像与窗口边框之间留出一些空间。
模糊图像
下一个代码示例模糊图像。模糊意味着图像不清晰。要模糊图像,我们使用卷积操作。它是一种数学运算,也用于边缘检测或噪声消除。模糊操作可用于各种图形效果。例如,创建速度错觉或显示人的模糊视野。
模糊滤镜操作会用像素及其邻居的平均值替换图像中的每个像素。卷积是逐像素操作。相同的算术运算会为图像中的每个像素重复执行。核可以被认为是数字的二维网格,它依次遍历图像的每个像素,并在过程中执行计算。由于图像也可以被认为是数字的二维网格,将核应用于图像可以被可视化为一个小的网格(核)在很大程度上更大的网格(图像)上移动。(developer.apple.com)
package com.zetcode; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.BufferedImageOp; import java.awt.image.ConvolveOp; import java.awt.image.Kernel; import java.io.File; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; class Surface extends JPanel { private BufferedImage mshi; private BufferedImage bluri; public Surface() { loadImage(); createBlurredImage(); setSurfaceSize(); } private void loadImage() { try { mshi = ImageIO.read(new File("mushrooms.jpg")); } catch (IOException ex) { Logger.getLogger(Surface.class.getName()).log( Level.WARNING, null, ex); } } private void createBlurredImage() { float[] blurKernel = { 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f }; BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel)); bluri = blur.filter(mshi, new BufferedImage(mshi.getWidth(), mshi.getHeight(), mshi.getType())); } private void setSurfaceSize() { Dimension d = new Dimension(); d.width = mshi.getWidth(null); d.height = mshi.getHeight(null); setPreferredSize(d); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bluri, null, 0, 0); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } } public class BlurredImageEx extends JFrame { public BlurredImageEx() { setTitle("Blurred image"); add(new Surface()); pack(); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { BlurredImageEx ex = new BlurredImageEx(); ex.setVisible(true); } }); } }
在代码示例中,我们从磁盘加载图像,对图像执行模糊操作,并在窗口上显示结果。
private void loadImage() { try { mshi = ImageIO.read(new File("mushrooms.jpg")); } catch (IOException ex) { Logger.getLogger(Surface.class.getName()).log( Level.WARNING, null, ex); } }
ImageIO
类的read
方法从磁盘读取图像并返回一个BufferedImage
。
float[] blurKernel = { 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f, 1 / 9f };
这个矩阵称为核。这些值是应用于要更改的像素的邻居值的权重。
BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel)); bluri = blur.filter(mshi, new BufferedImage(mshi.getWidth(), mshi.getHeight(), mshi.getType()));
在这里,我们将模糊滤镜应用于图像。
private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bluri, null, 0, 0); }
模糊图像在窗口上绘制。
反射
在下一个示例中,我们展示了一个反射图像。这种效果给人一种图像在水中反射的错觉。下面的代码示例受到jhlabs.com代码的启发。
package com.zetcode; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.util.logging.Level; import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; class Surface extends JPanel { private BufferedImage image; private BufferedImage refImage; private int img_w; private int img_h; private final int SPACE = 30; public Surface() { loadImage(); getImageSize(); createReflectedImage(); } private void loadImage() { try { image = ImageIO.read(new File("rotunda.jpg")); } catch (Exception ex) { Logger.getLogger(Surface.class.getName()).log( Level.WARNING, null, ex); } } private void getImageSize() { img_w = image.getWidth(); img_h = image.getHeight(); } private void createReflectedImage() { float opacity = 0.4f; float fadeHeight = 0.3f; refImage = new BufferedImage(img_w, img_h, BufferedImage.TYPE_INT_ARGB); Graphics2D rg = refImage.createGraphics(); rg.drawImage(image, 0, 0, null); rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN)); rg.setPaint(new GradientPaint(0, img_h * fadeHeight, new Color(0.0f, 0.0f, 0.0f, 0.0f), 0, img_h, new Color(0.0f, 0.0f, 0.0f, opacity))); rg.fillRect(0, 0, img_w, img_h); rg.dispose(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.create(); int win_w = getWidth(); int win_h = getHeight(); int gap = 20; g2d.setPaint(new GradientPaint(0, 0, Color.black, 0, win_h, Color.darkGray)); g2d.fillRect(0, 0, win_w, win_h); g2d.translate((win_w - img_w) / 2, win_h / 2 - img_h); g2d.drawImage(image, 0, 0, null); g2d.translate(0, 2 * img_h + gap); g2d.scale(1, -1); g2d.drawImage(refImage, 0, 0, null); g2d.dispose(); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } @Override public Dimension getPreferredSize() { return new Dimension(img_w + 2 * SPACE, 2 * img_h + 3 * SPACE); } } public class ReflectionEx extends JFrame { public ReflectionEx() { initUI(); } private void initUI() { add(new Surface()); pack(); setTitle("Reflection"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { ReflectionEx ex = new ReflectionEx(); ex.setVisible(true); } }); } }
在示例中,我们创建了一个反射图像的错觉。
refImage = new BufferedImage(img_w, img_h, BufferedImage.TYPE_INT_ARGB); Graphics2D rg = refImage.createGraphics(); rg.drawImage(image, 0, 0, null);
创建了加载图像的一个副本。
rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN)); rg.setPaint(new GradientPaint(0, img_h * fadeHeight, new Color(0.0f, 0.0f, 0.0f, 0.0f), 0, img_h, new Color(0.0f, 0.0f, 0.0f, opacity))); rg.fillRect(0, 0, img_w, img_h);
这是代码中最重要的部分。我们将第二个图像设置为透明。但透明度不是恒定的;图像会逐渐淡出。这是通过GradientPaint
类实现的。
g2d.setPaint(new GradientPaint(0, 0, Color.black, 0, win_h, Color.darkGray)); g2d.fillRect(0, 0, win_w, win_h);
窗口的背景填充了渐变画笔。画笔是从黑色到深灰色的平滑混合。
g2d.translate((win_w - img_w) / 2, win_h / 2 - img_h); g2d.drawImage(image, 0, 0, null);
普通图像被移动到窗口的中心并绘制。
g2d.translate(0, 2 * imageHeight + gap); g2d.scale(1, -1);
这段代码翻转图像并将其平移到原始图像下方。平移操作是必需的,因为缩放操作会使图像上下颠倒并将图像向上平移。要理解发生了什么,只需拿一张照片放在桌子上然后翻过来。
g2d.drawImage(refImage, 0, 0, null);
绘制了反射图像。
@Override public Dimension getPreferredSize() { return new Dimension(img_w + 2 * SPACE, 2 * img_h + 3 * SPACE); }
设置组件首选大小的另一种方法是覆盖getPreferredSize
方法。

在本部分Java 2D教程中,我们处理了图像。