【问题标题】:Java Convert a greyscale and sepia version of an image with BufferedImageJava 使用 BufferedImage 转换图像的灰度和棕褐色版本
【发布时间】:2016-10-15 07:46:24
【问题描述】:

我想读取图像并转换并输出原始图像、灰度版本和棕褐色版本。我在转换时遇到问题,对 BufferedImage 不是很熟悉,尤其是 getRGB 和 setRGB 方法有问题。到目前为止我有这个

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import javax.imageio.stream.ImageOutputStream;

public class ChangeColor{
  static BufferedImage readImage( String Pic ) throws Exception {

    BufferedImage image = ImageIO.read( new File("Pic.jpg") );
    return( image );
  }

  public static void saveImage( BufferedImage img, File file ) throws IOException {

        ImageWriter      writer = null;
        java.util.Iterator iter = ImageIO.getImageWritersByFormatName("jpg");

        if( iter.hasNext() ){
            writer = (ImageWriter)iter.next();
        }

        ImageOutputStream ios = ImageIO.createImageOutputStream( file );
        writer.setOutput(ios);

        ImageWriteParam param = new JPEGImageWriteParam( java.util.Locale.getDefault() );
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
        param.setCompressionQuality(0.98f);

        writer.write(null, new IIOImage( img, null, null ), param);

    }

  public static BufferedImage color2gray( BufferedImage inImage ) {

    int            width    = inImage.getWidth();
    int            height   = inImage.getHeight();
    BufferedImage  outImage = new BufferedImage( width, height, BufferedImage.TYPE_3BYTE_BGR );

    for(int i=0; i<height; i++){
        for(int j=0; j<width; j++){
           Color c = new Color(image.getRGB(j, i));
           int red = (int)(c.getRed() * 0.2126);
           int green = (int)(c.getGreen() * 0.7152);
           int blue = (int)(c.getBlue() *0.0722);
           Color newColor = new Color(red+green+blue,
           red+green+blue,red+green+blue);
           image.setRGB(j,i,newColor.getRGB());
           }
    }

    return( outImage );
  }

  public static BufferedImage color2sepia( BufferedImage inImage ) {

    int            width    = inImage.getWidth();
    int            height   = inImage.getHeight();
    BufferedImage  outImage = new BufferedImage( width, height, BufferedImage.TYPE_3BYTE_BGR );
    for(int i=0; i<height; i++){
        for(int j=0; j<width; j++){
           Color c = new Color(image.getRGB(j, i));
           int red = (int)(c.getRed());
           int green = (int)(c.getGreen());
           int blue = (int)(c.getBlue());
           Color newColor = new Color(red* .393)+(green*.769)+(blue* .189),
           (red* .349)+(green*.686)+(blue* .168),(red* .272)+(green*.534)+(blue* .131);
           image.setRGB(j,i,newColor.getRGB());
           }
    }
    return( outImage );
  }



  public static void main(String[] args) throws Exception {

    BufferedImage colorImage, grayImage, sepiaImage;

    if (args.length != 1)
        System.out.println( "" );
    else
    {
        colorImage = readImage  ( args[0] );
    grayImage  = color2gray ( colorImage );
    sepiaImage = color2sepia( colorImage );

    saveImage( grayImage,  new File( "greyPic.jpg" + args[0] ) );
    saveImage( sepiaImage, new File( "sepiaPic.jpg"+ args[0] ) );
    }
  }

}

这是输出应该是什么样子的图像:

谢谢。

【问题讨论】:

    标签: java image-processing


    【解决方案1】:

    灰度相当容易,棕褐色则不那么容易。我从网上偷了算法...

    import java.awt.EventQueue;
    import java.awt.GridBagLayout;
    import java.awt.color.ColorSpace;
    import java.awt.image.BufferedImage;
    import java.awt.image.ColorConvertOp;
    import java.awt.image.WritableRaster;
    import java.io.File;
    import java.io.IOException;
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JLabel;
    import javax.swing.JOptionPane;
    import javax.swing.JPanel;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    
    public class ColorAlteration {
    
        public static void main(String[] args) {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    try {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    }
                    try {
                        BufferedImage master = ImageIO.read(new File("C:\\hold\\thumbnails\\_cg_836___Tilting_Windmills___by_Serena_Clearwater.png"));
                        BufferedImage gray = toGrayScale(master);
                        BufferedImage sepia = toSepia(master, 80);
    
                        JPanel panel = new JPanel(new GridBagLayout());
                        panel.add(new JLabel(new ImageIcon(master)));
                        panel.add(new JLabel(new ImageIcon(gray)));
                        panel.add(new JLabel(new ImageIcon(sepia)));
    
                        JOptionPane.showMessageDialog(null, panel);
    
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                }
            });
        }
    
        public static BufferedImage toGrayScale(BufferedImage master) {
            BufferedImage gray = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
    
            // Automatic converstion....
            ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
            op.filter(master, gray);
    
            return gray;
        }
    
        public static BufferedImage toSepia(BufferedImage img, int sepiaIntensity) {
    
            BufferedImage sepia = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
            // Play around with this.  20 works well and was recommended
            //   by another developer. 0 produces black/white image
            int sepiaDepth = 20;
    
            int w = img.getWidth();
            int h = img.getHeight();
    
            WritableRaster raster = sepia.getRaster();
    
            // We need 3 integers (for R,G,B color values) per pixel.
            int[] pixels = new int[w * h * 3];
            img.getRaster().getPixels(0, 0, w, h, pixels);
    
            //  Process 3 ints at a time for each pixel.  Each pixel has 3 RGB
            //    colors in array
            for (int i = 0; i < pixels.length; i += 3) {
                int r = pixels[i];
                int g = pixels[i + 1];
                int b = pixels[i + 2];
    
                int gry = (r + g + b) / 3;
                r = g = b = gry;
                r = r + (sepiaDepth * 2);
                g = g + sepiaDepth;
    
                if (r > 255) {
                    r = 255;
                }
                if (g > 255) {
                    g = 255;
                }
                if (b > 255) {
                    b = 255;
                }
    
                // Darken blue color to increase sepia effect
                b -= sepiaIntensity;
    
                // normalize if out of bounds
                if (b < 0) {
                    b = 0;
                }
                if (b > 255) {
                    b = 255;
                }
    
                pixels[i] = r;
                pixels[i + 1] = g;
                pixels[i + 2] = b;
            }
            raster.setPixels(0, 0, w, h, pixels);
    
            return sepia;
        }
    }
    

    你可以找到 sepia 算法的原贴here

    因为我很固执...我更改了棕褐色算法以处理基于 alpha 的图像...

    public static BufferedImage toSepia(BufferedImage img, int sepiaIntensity) {
    
        BufferedImage sepia = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_ARGB);
        // Play around with this.  20 works well and was recommended
        //   by another developer. 0 produces black/white image
        int sepiaDepth = 20;
    
        int w = img.getWidth();
        int h = img.getHeight();
    
        WritableRaster raster = sepia.getRaster();
    
        // We need 3 integers (for R,G,B color values) per pixel.
        int[] pixels = new int[w * h * 3];
        img.getRaster().getPixels(0, 0, w, h, pixels);
    
        for (int x = 0; x < img.getWidth(); x++) {
            for (int y = 0; y < img.getHeight(); y++) {
    
                int rgb = img.getRGB(x, y);
                Color color = new Color(rgb, true);
                int r = color.getRed();
                int g = color.getGreen();
                int b = color.getBlue();
                int gry = (r + g + b) / 3;
    
                r = g = b = gry;
                r = r + (sepiaDepth * 2);
                g = g + sepiaDepth;
    
                if (r > 255) {
                    r = 255;
                }
                if (g > 255) {
                    g = 255;
                }
                if (b > 255) {
                    b = 255;
                }
    
                // Darken blue color to increase sepia effect
                b -= sepiaIntensity;
    
                // normalize if out of bounds
                if (b < 0) {
                    b = 0;
                }
                if (b > 255) {
                    b = 255;
                }
    
                color = new Color(r, g, b, color.getAlpha());
                sepia.setRGB(x, y, color.getRGB());
    
            }
        }
    
        return sepia;
    }
    

    【讨论】:

    • 喜欢你选择的图片:)
    • @peeskillet 是的 MegaTokyo...觉得棕褐色有点发黄,需要更多地使用强度值...
    • 这很有趣,我会多研究一下。你知道我可以做些什么来修复我的原始代码吗?我想尝试让它工作而不是完全切换算法。
    • @MadProgrammer:创建棕褐色调效果最快(至少最简单)的方法是首先转换为灰色,然后使用RescaleOp 缩放/偏移红色、蓝色和绿色(在效果与您在上面实现的非常相似)。为努力和好榜样+1! :-)
    • @Salivan 并不是我真的在使用它,必须从原始代码中遗留下来:P(至少从第二个示例中)这就是为什么我可能在 getPixels 上使用 getRGB :P
    【解决方案2】:

    我使用@@MadProgrammer 代码来编写此代码。我认为它更有效。

    1. 使用图像的光栅数据而不是访问图像的每个字节。虽然看起来它是将数据复制到像素数组中,但程序中并没有使用它。

    2. 你每次都调用 getRGB + getWidth() + getHeight() + getRed(), getGreen() + getBlue()。

    3. 直接将颜色写入图像,我认为这是一个瓶颈,一旦使用 setRGB 写入颜色,您将失去图形处理器的优势。 (我在某处读过,但现在找不到链接。)

    4. 将颜色转换回 Color 对象并使用 getRGB() 将其取回。

    我所做的只是使用非常快的按位运算符,然后在我完成处理后复制像素数组。函数调用很昂贵,我避免使用它们。

    不过,感谢@MadProgrammer 的想法。

    public static BufferedImage toSepia(BufferedImage image, int sepiaIntensity) {
    
        int width = image.getWidth();
        int height = image.getHeight();
        int sepiaDepth = 20;
    
        int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
    
        for (int i = 0; i < imagePixels.length; i++) {
            int color = imagePixels[i];
    
            int r = (color >> 16) & 0xff;
            int g = (color >> 8) & 0xff;
            int b = (color) & 0xff;
            int gry = (r + g + b) / 3;
    
            r = g = b = gry;
            r = r + (sepiaDepth * 2);
            g = g + sepiaDepth;
    
            if (r > 255) {
                r = 255;
            }
            if (g > 255) {
                g = 255;
            }
            if (b > 255) {
                b = 255;
            }
    
            // Darken blue color to increase sepia effect
            b -= sepiaIntensity;
    
            // normalize if out of bounds
            if (b < 0) {
                b = 0;
            }
            if (b > 255) {
                b = 255;
            }
    
            imagePixels[i] = (color & 0xff000000) + (r << 16) + (g << 8) + b;
        }
    
        BufferedImage res = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        res.setRGB(0, 0, width, height, imagePixels, 0, width);
        return res;
    }
    

    【讨论】:

      【解决方案3】:

      您可以为代码重用创建过滤器接口。

      过滤器应用

      import java.awt.image.BufferedImage;
      import java.io.File;
      import java.io.IOException;
      
      import javax.imageio.ImageIO;
      
      public class FilterApp {
          public static ClassLoader loader = FilterApp.class.getClassLoader();
          public static String outputDir = "build";
      
          public static void main(String[] args) {
              try {
                  BufferedImage srcImage = loadImage("lobster.jpg");
                  File dir = new File(outputDir);
      
                  if (!dir.exists()) {
                      dir.mkdirs();
                  }
      
                  for (FilterType filter : FilterType.values()) {
                      BufferedImage filteredImage = filter.applyFilter(srcImage);
                      String filename = String.format("%s/lobster_%s", outputDir, filter.name().toLowerCase());
                      writeImage(filteredImage, filename, "jpg");
                  }
              } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      
          private static BufferedImage loadImage(String filename) throws IOException {
              return ImageIO.read(loader.getResourceAsStream("resources/" + filename));
          }
      
          private static void writeImage(BufferedImage image, String filename, String ext) throws IOException {
              ImageIO.write(image, ext, new File(filename + '.' + ext));
          }
      }
      

      过滤器类型

      import java.awt.image.BufferedImage;
      
      import filter.GreyscaleFilter;
      import filter.ImageFilter;
      import filter.InvertFilter;
      import filter.SepiaFilter;
      
      public enum FilterType {
          GREYSCALE(new GreyscaleFilter()),
          INVERT(new InvertFilter()),
          SEPIA_10(new SepiaFilter(10));
      
          private ImageFilter filter;
      
          public ImageFilter getFilter() { return filter; }
      
          public BufferedImage applyFilter(BufferedImage img) {
              return this.filter.apply(img);
          }
      
          private FilterType(ImageFilter filter) {
              this.filter = filter;
          }
      }
      

      图像过滤器

      package filter;
      
      import java.awt.image.BufferedImage;
      
      /** Common Interface for different filters. */ 
      public interface ImageFilter {
          public BufferedImage apply(BufferedImage img);
      }
      

      灰度滤镜

      package filter;
      
      import java.awt.color.ColorSpace;
      import java.awt.image.BufferedImage;
      import java.awt.image.ColorConvertOp;
      
      public class GreyscaleFilter implements ImageFilter {
          @Override
          public BufferedImage apply(BufferedImage img) {
              BufferedImage result = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
              ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
      
              op.filter(img, result);
      
              return result;
          }
      }
      

      反转过滤器

      package filter;
      
      import java.awt.Color;
      import java.awt.image.BufferedImage;
      
      public class InvertFilter implements ImageFilter {
          @Override
          public BufferedImage apply(BufferedImage img) {
              BufferedImage result = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
      
              for (int x = 0; x < img.getWidth(); x++) {
                  for (int y = 0; y < img.getHeight(); y++) {
                      int rgb = img.getRGB(x, y);
                      Color color = new Color(rgb, true);
                      int r = 255 - color.getRed();
                      int g = 255 - color.getGreen();
                      int b = 255 - color.getBlue();
      
                      color = new Color(r, g, b, color.getAlpha());
                      result.setRGB(x, y, color.getRGB());
                  }
              }
      
              return result;
          }
      }
      

      SepiaFilter

      package filter;
      
      import java.awt.Color;
      import java.awt.image.BufferedImage;
      
      // Algorithm obtained from http://stackoverflow.com/questions/21899824
      public class SepiaFilter implements ImageFilter {
          private int intensity;
      
          public void setIntensity(int intensity) { this.intensity = intensity; }
          public int getIntensity() { return intensity; }
      
          public SepiaFilter(int intensity) {
              this.intensity = intensity;
          }
      
          @Override
          public BufferedImage apply(BufferedImage img) {
              BufferedImage result = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
              // Play around with this.
              // 20 works well and was recommended by another developer.
              // 0 produces black/white image
              int sepiaDepth = 20;
      
              int w = img.getWidth();
              int h = img.getHeight();
      
              // We need 3 integers (for R,G,B color values) per pixel.
              int[] pixels = new int[w * h * 3];
              img.getRaster().getPixels(0, 0, w, h, pixels);
      
              for (int x = 0; x < img.getWidth(); x++) {
                  for (int y = 0; y < img.getHeight(); y++) {
                      int rgb = img.getRGB(x, y);
                      Color color = new Color(rgb, true);
                      int r = color.getRed();
                      int g = color.getGreen();
                      int b = color.getBlue();
                      int gry = (r + g + b) / 3;
      
                      r = g = b = gry;
                      r = r + (sepiaDepth * 2);
                      g = g + sepiaDepth;
      
                      if (r > 255) { r = 255; }
                      if (g > 255) { g = 255; }
                      if (b > 255) { b = 255; }
      
                      // Darken blue color to increase sepia effect
                      b -= this.intensity;
      
                      // normalize if out of bounds
                      if (b < 0)   { b = 0; }
                      if (b > 255) { b = 255; }
      
                      color = new Color(r, g, b, color.getAlpha());
                      result.setRGB(x, y, color.getRGB());
                  }
              }
      
              return result;
          }
      }
      

      输出

      源图片

      生成的图像

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-05
        • 1970-01-01
        • 2011-06-05
        • 2011-05-07
        相关资源
        最近更新 更多