原文: https://zetcode.com/gfx/java2d/java2dimages/

在 Java 2D 教程的这一部分中,我们将处理图像。

BufferedImage是使用 Java 2D 处理图像的基础类。 它是存储在内存中的像素矩形。

显示图像

在第一个示例中,我们在面板上显示图像。

DisplayImageEx.java

  1. package com.zetcode;
  2. import java.awt.Dimension;
  3. import java.awt.EventQueue;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.Image;
  7. import javax.swing.ImageIcon;
  8. import javax.swing.JFrame;
  9. import javax.swing.JPanel;
  10. class Surface extends JPanel {
  11. private Image mshi;
  12. public Surface() {
  13. loadImage();
  14. setSurfaceSize();
  15. }
  16. private void loadImage() {
  17. mshi = new ImageIcon("mushrooms.jpg").getImage();
  18. }
  19. private void setSurfaceSize() {
  20. Dimension d = new Dimension();
  21. d.width = mshi.getWidth(null);
  22. d.height = mshi.getHeight(null);
  23. setPreferredSize(d);
  24. }
  25. private void doDrawing(Graphics g) {
  26. Graphics2D g2d = (Graphics2D) g;
  27. g2d.drawImage(mshi, 0, 0, null);
  28. }
  29. @Override
  30. public void paintComponent(Graphics g) {
  31. super.paintComponent(g);
  32. doDrawing(g);
  33. }
  34. }
  35. public class DisplayImageEx extends JFrame {
  36. public DisplayImageEx() {
  37. initUI();
  38. }
  39. private void initUI() {
  40. add(new Surface());
  41. pack();
  42. setTitle("Mushrooms");
  43. setLocationRelativeTo(null);
  44. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  45. }
  46. public static void main(String[] args) {
  47. EventQueue.invokeLater(new Runnable() {
  48. @Override
  49. public void run() {
  50. DisplayImageEx ex = new DisplayImageEx();
  51. ex.setVisible(true);
  52. }
  53. });
  54. }
  55. }

在示例中,我们在面板上显示图像。 调整窗口大小以适合图像的大小。

  1. private void loadImage() {
  2. mshi = new ImageIcon("mushrooms.jpg").getImage();
  3. }

我们使用ImageIcon类加载图像。 该图像位于当前工作目录中。

  1. private void setSurfaceSize() {
  2. Dimension d = new Dimension();
  3. d.width = mshi.getWidth(null);
  4. d.height = mshi.getHeight(null);
  5. setPreferredSize(d);
  6. }

我们确定加载图像的大小。 使用setPreferredSize()方法,我们设置Surface面板的首选大小。 JFrame容器的pack()方法将使框架适合其子代的大小。 在我们的例子中是Surface面板。 结果,窗口将被调整大小以精确显示加载的图像。

  1. private void doDrawing(Graphics g) {
  2. Graphics2D g2d = (Graphics2D) g;
  3. g2d.drawImage(mshi, 0, 0, null);
  4. }

使用drawImage()方法在面板上绘制图像。 最后一个参数是ImageObserver类。 有时用于异步加载。 当我们不需要异步加载图像时,可以将null放在此处。

  1. private void initUI() {
  2. ...
  3. pack();
  4. ...
  5. }

pack()方法调整容器的大小以适合子面板的大小。

灰度图像

在计算中,灰度数字图像是其中每个像素的值是单个样本的图像,也就是说,它携带有关其强度的完整(且唯一)信息。 这种图像仅由中性灰色阴影组成,从最弱的黑色到最强的白色不等。 (维基百科)

在下一个示例中,我们使用 Java 2D 创建灰度图像。

GrayScaleImage.java

  1. package com.zetcode;
  2. import java.awt.Dimension;
  3. import java.awt.EventQueue;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.Image;
  7. import java.awt.image.BufferedImage;
  8. import javax.swing.ImageIcon;
  9. import javax.swing.JFrame;
  10. import javax.swing.JPanel;
  11. class Surface extends JPanel {
  12. private Image mshi;
  13. private BufferedImage bufimg;
  14. private Dimension d;
  15. public Surface() {
  16. loadImage();
  17. determineAndSetSize();
  18. createGrayImage();
  19. }
  20. private void determineAndSetSize() {
  21. d = new Dimension();
  22. d.width = mshi.getWidth(null);
  23. d.height = mshi.getHeight(null);
  24. setPreferredSize(d);
  25. }
  26. private void createGrayImage() {
  27. bufimg = new BufferedImage(d.width, d.height,
  28. BufferedImage.TYPE_BYTE_GRAY);
  29. Graphics2D g2d = bufimg.createGraphics();
  30. g2d.drawImage(mshi, 0, 0, null);
  31. g2d.dispose();
  32. }
  33. private void loadImage() {
  34. mshi = new ImageIcon("mushrooms.jpg").getImage();
  35. }
  36. private void doDrawing(Graphics g) {
  37. Graphics2D g2d = (Graphics2D) g;
  38. g2d.drawImage(bufimg, null, 0, 0);
  39. }
  40. @Override
  41. public void paintComponent(Graphics g) {
  42. super.paintComponent(g);
  43. doDrawing(g);
  44. }
  45. }
  46. public class GrayScaleImageEx extends JFrame {
  47. public GrayScaleImageEx() {
  48. initUI();
  49. }
  50. private void initUI() {
  51. Surface dpnl = new Surface();
  52. add(dpnl);
  53. pack();
  54. setTitle("GrayScale image");
  55. setLocationRelativeTo(null);
  56. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  57. }
  58. public static void main(String[] args) {
  59. EventQueue.invokeLater(new Runnable() {
  60. @Override
  61. public void run() {
  62. GrayScaleImageEx ex = new GrayScaleImageEx();
  63. ex.setVisible(true);
  64. }
  65. });
  66. }
  67. }

有几种创建灰度图像的方法。 我们通过将图像数据写入BufferedImage.TYPE_BYTE_GRAY类型的缓冲图像中来实现。

  1. bufimg = new BufferedImage(d.width, d.height,
  2. BufferedImage.TYPE_BYTE_GRAY);

我们创建一个BufferedImage.TYPE_BYTE_GRAY类型的BufferedImage类。

  1. Graphics2D g2d = bufimg.createGraphics();
  2. g2d.drawImage(mshi, 0, 0, null);

在这里,我们将蘑菇图像绘制到缓冲图像中。

  1. g2d.dispose();

使用createGraphics()方法创建的图形对象应手动释放。 当这些对象返回时,作为对象的paint()update()方法的参数提供的图形对象将由系统自动释放。

  1. private void doDrawing(Graphics g) {
  2. Graphics2D g2d = (Graphics2D) g;
  3. g2d.drawImage(bufimg, null, 0, 0);
  4. }

缓冲的图像通过drawImage()方法绘制在面板上。

图像翻转

以下示例翻转图像。 我们将要过滤图像。 有一种filter()方法正在转换图像。

FlippedImageEx.java

  1. package com.zetcode;
  2. import java.awt.Dimension;
  3. import java.awt.EventQueue;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.Image;
  7. import java.awt.geom.AffineTransform;
  8. import java.awt.image.AffineTransformOp;
  9. import java.awt.image.BufferedImage;
  10. import javax.swing.ImageIcon;
  11. import javax.swing.JFrame;
  12. import javax.swing.JPanel;
  13. class Surface extends JPanel {
  14. private Image mshi;
  15. private BufferedImage bufimg;
  16. private final int SPACE = 10;
  17. public Surface() {
  18. loadImage();
  19. createFlippedImage();
  20. setSurfaceSize();
  21. }
  22. private void loadImage() {
  23. mshi = new ImageIcon("mushrooms.jpg").getImage();
  24. }
  25. private void createFlippedImage() {
  26. bufimg = new BufferedImage(mshi.getWidth(null),
  27. mshi.getHeight(null), BufferedImage.TYPE_INT_RGB);
  28. Graphics gb = bufimg.getGraphics();
  29. gb.drawImage(mshi, 0, 0, null);
  30. gb.dispose();
  31. AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
  32. tx.translate(-mshi.getWidth(null), 0);
  33. AffineTransformOp op = new AffineTransformOp(tx,
  34. AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
  35. bufimg = op.filter(bufimg, null);
  36. }
  37. private void setSurfaceSize() {
  38. int w = bufimg.getWidth();
  39. int h = bufimg.getHeight();
  40. Dimension d = new Dimension(3*SPACE+2*w, h+2*SPACE);
  41. setPreferredSize(d);
  42. }
  43. private void doDrawing(Graphics g) {
  44. Graphics2D g2d = (Graphics2D) g;
  45. g2d.drawImage(mshi, SPACE, SPACE, null);
  46. g2d.drawImage(bufimg, null, 2*SPACE + bufimg.getWidth(), SPACE);
  47. }
  48. @Override
  49. public void paintComponent(Graphics g) {
  50. super.paintComponent(g);
  51. doDrawing(g);
  52. }
  53. }
  54. public class FlippedImageEx extends JFrame {
  55. public FlippedImageEx() {
  56. initUI();
  57. }
  58. private void initUI() {
  59. add(new Surface());
  60. pack();
  61. setTitle("Flipped image");
  62. setLocationRelativeTo(null);
  63. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  64. }
  65. public static void main(String[] args) {
  66. EventQueue.invokeLater(new Runnable() {
  67. @Override
  68. public void run() {
  69. FlippedImageEx ex = new FlippedImageEx();
  70. ex.setVisible(true);
  71. }
  72. });
  73. }
  74. }

在我们的代码示例中,我们水平翻转图像。

  1. AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
  2. tx.translate(-castle.getWidth(null), 0);

翻转图像意味着对其进行缩放和平移。 因此,我们进行了AffineTransform操作。

  1. AffineTransformOp op = new AffineTransformOp(tx,
  2. AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
  3. bufferedImage = op.filter(bufferedImage, null)

这是可用的过滤操作之一。 这也可以通过像素操纵来完成。 但是 Java 2D 提供了高级类,使操作图像更加容易。 在我们的情况下,AffineTransformOp类对图像像素执行缩放和平移。

  1. private void doDrawing(Graphics g) {
  2. Graphics2D g2d = (Graphics2D) g;
  3. g2d.drawImage(mshi, SPACE, SPACE, null);
  4. g2d.drawImage(bufimg, null, 2*SPACE + bufimg.getWidth(), SPACE);
  5. }

这两个图像都画在面板上。

  1. private void setSurfaceSize() {
  2. int w = bufimg.getWidth();
  3. int h = bufimg.getHeight();
  4. Dimension d = new Dimension(3*SPACE+2*w, h+2*SPACE);
  5. setPreferredSize(d);
  6. }

我们设置面板的首选大小。 我们计算大小,以便可以在面板上放置两个图像,并在它们之间以及图像和窗口边框之间留一些空间。

图像模糊

下一个代码示例使图像模糊。 模糊意味着没有聚焦的图像。 为了模糊图像,我们使用了卷积运算。 这是一种数学运算,也用于边缘检测或噪声消除。 模糊操作可用于各种图形效果。 例如,创建速度错觉或显示人的注意力不集中。

模糊滤镜操作用像素及其邻居的平均值替换图像中的每个像素。 卷积是每个像素的运算。 对图像中的每个像素重复相同的算法。 内核可以看作是一个二维的数字网格,它按顺序遍历图像的每个像素,并一路执行计算。 由于图像也可以被视为数字的二维网格,因此将内核应用于图像可以可视化为在较大的网格(图像)上移动的小网格(内核)。 (developer.apple.com)

BlurredImageEx.java

  1. package com.zetcode;
  2. import java.awt.Dimension;
  3. import java.awt.EventQueue;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.image.BufferedImage;
  7. import java.awt.image.BufferedImageOp;
  8. import java.awt.image.ConvolveOp;
  9. import java.awt.image.Kernel;
  10. import java.io.File;
  11. import java.io.IOException;
  12. import java.util.logging.Level;
  13. import java.util.logging.Logger;
  14. import javax.imageio.ImageIO;
  15. import javax.swing.JFrame;
  16. import javax.swing.JPanel;
  17. class Surface extends JPanel {
  18. private BufferedImage mshi;
  19. private BufferedImage bluri;
  20. public Surface() {
  21. loadImage();
  22. createBlurredImage();
  23. setSurfaceSize();
  24. }
  25. private void loadImage() {
  26. try {
  27. mshi = ImageIO.read(new File("mushrooms.jpg"));
  28. } catch (IOException ex) {
  29. Logger.getLogger(Surface.class.getName()).log(
  30. Level.WARNING, null, ex);
  31. }
  32. }
  33. private void createBlurredImage() {
  34. float[] blurKernel = {
  35. 1 / 9f, 1 / 9f, 1 / 9f,
  36. 1 / 9f, 1 / 9f, 1 / 9f,
  37. 1 / 9f, 1 / 9f, 1 / 9f
  38. };
  39. BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel));
  40. bluri = blur.filter(mshi, new BufferedImage(mshi.getWidth(),
  41. mshi.getHeight(), mshi.getType()));
  42. }
  43. private void setSurfaceSize() {
  44. Dimension d = new Dimension();
  45. d.width = mshi.getWidth(null);
  46. d.height = mshi.getHeight(null);
  47. setPreferredSize(d);
  48. }
  49. private void doDrawing(Graphics g) {
  50. Graphics2D g2d = (Graphics2D) g;
  51. g2d.drawImage(bluri, null, 0, 0);
  52. }
  53. @Override
  54. public void paintComponent(Graphics g) {
  55. super.paintComponent(g);
  56. doDrawing(g);
  57. }
  58. }
  59. public class BlurredImageEx extends JFrame {
  60. public BlurredImageEx() {
  61. setTitle("Blurred image");
  62. add(new Surface());
  63. pack();
  64. setLocationRelativeTo(null);
  65. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  66. }
  67. public static void main(String[] args) {
  68. EventQueue.invokeLater(new Runnable() {
  69. @Override
  70. public void run() {
  71. BlurredImageEx ex = new BlurredImageEx();
  72. ex.setVisible(true);
  73. }
  74. });
  75. }
  76. }

在代码示例中,我们从磁盘加载图像,对图像执行模糊操作,然后在窗口上显示结果。

  1. private void loadImage() {
  2. try {
  3. mshi = ImageIO.read(new File("mushrooms.jpg"));
  4. } catch (IOException ex) {
  5. Logger.getLogger(Surface.class.getName()).log(
  6. Level.WARNING, null, ex);
  7. }
  8. }

ImageIO类的read()方法从磁盘读取图像并返回BufferedImage

  1. float[] blurKernel = {
  2. 1 / 9f, 1 / 9f, 1 / 9f,
  3. 1 / 9f, 1 / 9f, 1 / 9f,
  4. 1 / 9f, 1 / 9f, 1 / 9f
  5. };

该矩阵称为内核。 所述值是应用于改变的像素的相邻值的权重。

  1. BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel));
  2. bluri = blur.filter(mshi, new BufferedImage(mshi.getWidth(),
  3. mshi.getHeight(), mshi.getType()));

在这里,我们对图像应用模糊滤镜。

  1. private void doDrawing(Graphics g) {
  2. Graphics2D g2d = (Graphics2D) g;
  3. g2d.drawImage(bluri, null, 0, 0);
  4. }

模糊的图像绘制在窗口上。

反射

在下一个示例中,我们显示反射图像。 这种效果使人产生幻觉,好像图像在水中被反射一样。 以下代码示例受到 jhlabs.com 中代码的启发。

ReflectionEx.java

  1. package com.zetcode;
  2. import java.awt.AlphaComposite;
  3. import java.awt.Color;
  4. import java.awt.Dimension;
  5. import java.awt.EventQueue;
  6. import java.awt.GradientPaint;
  7. import java.awt.Graphics;
  8. import java.awt.Graphics2D;
  9. import java.awt.image.BufferedImage;
  10. import java.io.File;
  11. import java.util.logging.Level;
  12. import java.util.logging.Logger;
  13. import javax.imageio.ImageIO;
  14. import javax.swing.JFrame;
  15. import javax.swing.JPanel;
  16. class Surface extends JPanel {
  17. private BufferedImage image;
  18. private BufferedImage refImage;
  19. private int img_w;
  20. private int img_h;
  21. private final int SPACE = 30;
  22. public Surface() {
  23. loadImage();
  24. getImageSize();
  25. createReflectedImage();
  26. }
  27. private void loadImage() {
  28. try {
  29. image = ImageIO.read(new File("rotunda.jpg"));
  30. } catch (Exception ex) {
  31. Logger.getLogger(Surface.class.getName()).log(
  32. Level.WARNING, null, ex);
  33. }
  34. }
  35. private void getImageSize() {
  36. img_w = image.getWidth();
  37. img_h = image.getHeight();
  38. }
  39. private void createReflectedImage() {
  40. float opacity = 0.4f;
  41. float fadeHeight = 0.3f;
  42. refImage = new BufferedImage(img_w, img_h,
  43. BufferedImage.TYPE_INT_ARGB);
  44. Graphics2D rg = refImage.createGraphics();
  45. rg.drawImage(image, 0, 0, null);
  46. rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN));
  47. rg.setPaint(new GradientPaint(0, img_h * fadeHeight,
  48. new Color(0.0f, 0.0f, 0.0f, 0.0f), 0, img_h,
  49. new Color(0.0f, 0.0f, 0.0f, opacity)));
  50. rg.fillRect(0, 0, img_w, img_h);
  51. rg.dispose();
  52. }
  53. private void doDrawing(Graphics g) {
  54. Graphics2D g2d = (Graphics2D) g.create();
  55. int win_w = getWidth();
  56. int win_h = getHeight();
  57. int gap = 20;
  58. g2d.setPaint(new GradientPaint(0, 0, Color.black, 0,
  59. win_h, Color.darkGray));
  60. g2d.fillRect(0, 0, win_w, win_h);
  61. g2d.translate((win_w - img_w) / 2, win_h / 2 - img_h);
  62. g2d.drawImage(image, 0, 0, null);
  63. g2d.translate(0, 2 * img_h + gap);
  64. g2d.scale(1, -1);
  65. g2d.drawImage(refImage, 0, 0, null);
  66. g2d.dispose();
  67. }
  68. @Override
  69. public void paintComponent(Graphics g) {
  70. super.paintComponent(g);
  71. doDrawing(g);
  72. }
  73. @Override
  74. public Dimension getPreferredSize() {
  75. return new Dimension(img_w + 2 * SPACE, 2 * img_h + 3 * SPACE);
  76. }
  77. }
  78. public class ReflectionEx extends JFrame {
  79. public ReflectionEx() {
  80. initUI();
  81. }
  82. private void initUI() {
  83. add(new Surface());
  84. pack();
  85. setTitle("Reflection");
  86. setLocationRelativeTo(null);
  87. setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  88. }
  89. public static void main(String[] args) {
  90. EventQueue.invokeLater(new Runnable() {
  91. @Override
  92. public void run() {
  93. ReflectionEx ex = new ReflectionEx();
  94. ex.setVisible(true);
  95. }
  96. });
  97. }
  98. }

在示例中,我们创建了反射图像的错觉。

  1. refImage = new BufferedImage(img_w, img_h,
  2. BufferedImage.TYPE_INT_ARGB);
  3. Graphics2D rg = refImage.createGraphics();
  4. rg.drawImage(image, 0, 0, null);

创建已加载图像的副本。

  1. rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN));
  2. rg.setPaint(new GradientPaint(0, img_h * fadeHeight,
  3. new Color(0.0f, 0.0f, 0.0f, 0.0f), 0, img_h,
  4. new Color(0.0f, 0.0f, 0.0f, opacity)));
  5. rg.fillRect(0, 0, img_w, img_h);

这是代码中最重要的部分。 我们使第二个图像透明。 但是透明度不是恒定不变的。 图像逐渐淡出。 这是通过GradientPaint类实现的。

  1. g2d.setPaint(new GradientPaint(0, 0, Color.black, 0,
  2. win_h, Color.darkGray));
  3. g2d.fillRect(0, 0, win_w, win_h);

窗口的背景填充有渐变颜料。 涂料是从黑色到深灰色的平滑混合。

  1. g2d.translate((win_w - img_w) / 2, win_h / 2 - img_h);
  2. g2d.drawImage(image, 0, 0, null);

普通图像将移动到窗口的中心并绘制。

  1. g2d.translate(0, 2 * imageHeight + gap);
  2. g2d.scale(1, -1);

此代码翻转图像并将其转换为原始图像下方。 平移操作是必需的,因为缩放操作会使图像上下颠倒并向上平移图像。 要了解发生了什么,只需拍摄照片并将其放在桌子上并翻转即可。

  1. g2d.drawImage(refImage, 0, 0, null);

绘制反射的图像。

  1. @Override
  2. public Dimension getPreferredSize() {
  3. return new Dimension(img_w + 2 * SPACE, 2 * img_h + 3 * SPACE);
  4. }

设置组件的首选大小的另一种方法是重写getPreferredSize()方法。

图像 - 图1

图:反射

在 Java2D 教程的这一部分中,我们处理了图像。