《Java数字图像处理:编程技巧与应用实践》——2.3 基于BufferedImageOp的图像滤镜演示

2.3 基于BufferedImageOp的图像滤镜演示

通过前面两节的学习,我们已经大致了解BufferedImageOp接口及其实现类的功能。实践出真知,本节将演示BufferedImageOp接口中每个实现类的实际使用场景,达到知行合一、学以致用的目的,帮助大家解决项目中遇到的实际问题。为了让大家对应用效果有更加深刻的印象,下面会使用BufferedImageOp的实现类来实现如下几个滤镜特效功能。

  • 黑白滤镜:将彩色图像自动转换为黑白两色图像。
  • 灰度滤镜:将彩色图像自动转换为灰度图像。
  • 模糊滤镜:使图像产生模糊效果。
  • 放缩滤镜:使图像放大或缩小。

1 . UI实现部分

在介绍基于Swing的UI实现时,关于Swing UI部分的编程知识将在下一章中详细剖析与解释,本节的重点放在滤镜实现部分,大致的UI布局如图2-3所示。

2.滤镜部分的实现

(1)ColorConvertOp实现灰度功能

ColorConvertOp主要用于实现各种色彩空间的转换,从而达到转换BufferedImage对象类型的目的,也可以在实例化ColorConvertOp对象时指定色彩空间。当前支持的色彩空间有五种,实现灰度功能时,只需在实例化ColorConvertOp时指定色彩空间为ColorSpace.CS_GRAY,然后调用它的filter方法得到返回图像即可。灰度化的源代码如下:

public BufferedImage doColorGray(BufferedImage bi)
{
    ColorConvertOp filterObj = new ColorConvertOp(
    ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
    return filterObj.filter(bi, null);
}

(2)LookupOp 实现黑白功能

LookupOp在实例化时需要传入LookupTable实例,当前LookupTable接口的两个实现类分别为ByteLookupTable与ShortLookupTable。类关系图2-4可以很好地说明它们之间的关系。

运用LookupOp实现彩色图像变成黑白单色图像的功能时,首先要将图像灰度化,然后针对灰度图像在LookupTable中根据像素值进行索引查找,以便设置新的像素值,从而得到黑白单色图像,代码如下:

public BufferedImage doBinaryImage(BufferedImage bi)
{
    bi = doColorGray(bi);
    byte[] threshold = new byte[256];
    for (int i = 0; i < 256; i++)
    {
        threshold[i] = (i < 128) ? (byte)0 : (byte)255;
    }
    BufferedImageOp thresholdOp =
    new LookupOp(new ByteLookupTable(0, threshold), null);
    return thresholdOp.filter(bi, null);
}

(3)ConvolveOp 实现模糊功能

ConvolveOp是实现模板卷积功能操作的类,通过简单设置卷积核/卷积模板就可以实现图像模糊功能,实现代码如下:

float ninth = 1.0f / 9.0f;
float[] blurKernel = {
        ninth, ninth, ninth,
        ninth, ninth, ninth,
        ninth, ninth, ninth
    };
BufferedImageOp blurFilter =
        new ConvolveOp(new Kernel(3, 3, blurKernel));
return blurFilter.filter(bi, null);

但是当你想对大多数JPG格式图片的BufferedImage对象实现模糊功能时,很多情况下Java会抛出如下错误消息:

unable to convolve src image

原因在于JDK读入JPG格式图像时,多数情况下使用了TYPE_3BYTE_BGR存储方式,而BufferedImageOp实现的滤镜不支持操作该存储方式的BufferedImage对象,这样就导致了上面的错误。解决之道很简单,就是通过ColorConvertOp把图像从类型TYPE_3BYTE_BGR转换为TYPE_INT_RGB的BufferedImage对象。所以模糊功能的完整源代码如下:

public BufferedImage doBlur(BufferedImage bi)
{
    // fix issue - unable to convolve src image
    if (bi.getType()==BufferedImage.TYPE_3BYTE_BGR)
    {
        bi=convertType(bi,
            BufferedImage.TYPE_INT_RGB);
        }

    float ninth = 1.0f / 9.0f;
    float[] blurKernel = {
            ninth, ninth, ninth,
            ninth, ninth, ninth,
            ninth, ninth, ninth
        };
    BufferedImageOp blurFilter =
        new ConvolveOp(new Kernel(3, 3, blurKernel));
    return blurFilter.filter(bi, null);
}

convertType方法的代码如下:

ColorConvertOp cco=new ColorConvertOp(null);
BufferedImage dest=new BufferedImage(
    src.getWidth(), src.getHeight(), type);
cco.filter(src, dest);
return dest;

(4)AffineTransformOp实现图像zoom in/out的功能

AffineTransformOp支持的操作包括图像的错切、旋转、放缩、平移。要实现图像的放缩功能,首先要通过AffineTransform.getScaleInstance来获取Scale实例,然后作为参数初始化AffineTransformOp对象实例,最后调用filter方法即可。实现图像放缩功能的代码如下:

public BufferedImage doScale(BufferedImage bi,
                        double sx, double sy)
{
    AffineTransformOp atfFilter = new AffineTransformOp(
        AffineTransform.getScaleInstance(sx, sy),
        AffineTransformOp.TYPE_BILINEAR);
    //计算放缩后图像的宽与高
    int nw = (int)(bi.getWidth() * sx);
    int nh = (int)(bi.getHeight() * sy);
    BufferedImage result = new BufferedImage(
        nw, nh, BufferedImage.TYPE_3BYTE_BGR);
    //实现图像放缩
    atfFilter.filter(bi, result);
    return result;
}

需要传入的三个参数包括:bi是BufferedImage对象实例,代表要放缩的图像;sx表示在X方向的放缩比率;sy表示在Y方向的放缩比率。

完整的UI部分代码如下:

package com.book.chapter.two;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class MyFilterUI extends JFrame
        implements ActionListener {

    /**
    *
    */
    private static final long serialVersionUID = 1L;
    public static final String GRAY_CMD = "灰度";
    public static final String BINARY_CMD = "黑白";
    public static final String BLUR_CMD = "模糊";
    public static final String ZOOM_CMD = "放缩";
    public static final String BROWSER_CMD = "选择...";

    private JButton grayBtn;
    private JButton binaryBtn;
    private JButton blurBtn;
    private JButton zoomBtn;
    private JButton browserBtn;
    private MyFilters filters;

    // image
    private BufferedImage srcImage;

    public MyFilterUI()
    {
        this.setTitle("JAVA 2D BufferedImageOp - 滤镜演示");
        grayBtn = new JButton(GRAY_CMD);
        binaryBtn = new JButton(BINARY_CMD);
        blurBtn = new JButton(BLUR_CMD);
        zoomBtn = new JButton(ZOOM_CMD);
        browserBtn = new JButton(BROWSER_CMD);

        // buttons
        JPanel btnPanel = new JPanel();
        btnPanel.setLayout(new
             FlowLayout(FlowLayout.RIGHT));
        btnPanel.add(grayBtn);
        btnPanel.add(binaryBtn);
        btnPanel.add(blurBtn);
        btnPanel.add(zoomBtn);
        btnPanel.add(browserBtn);

        // filters
        filters = new MyFilters();

        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(filters, BorderLayout.CENTER);
        getContentPane().add(btnPanel, BorderLayout.SOUTH);

        // setup listener
        setupActionListener();

    }
    private void setupActionListener() {
        grayBtn.addActionListener(this);
        binaryBtn.addActionListener(this);
        blurBtn.addActionListener(this);
        zoomBtn.addActionListener(this);
        browserBtn.addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if(srcImage == null)
        {
            JOptionPane.showMessageDialog(this,
                        "请先选择图像源文件");
            try {
                JFileChooser chooser = new JFileChooser();
                chooser.showOpenDialog(null);
                File f = chooser.getSelectedFile();
                srcImage = ImageIO.read(f);
                filters.setImage(srcImage);
                filters.repaint();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
            return;
        }
        if(GRAY_CMD.equals(e.getActionCommand()))
        {
            filters.doColorGray(srcImage);
            filters.repaint();
        }
        else if(BINARY_CMD.equals(e.getActionCommand()))
        {
            filters.doBinaryImage(srcImage);
            filters.repaint();
        }
        else if(BLUR_CMD.equals(e.getActionCommand()))
        {
            filters.doBlur(srcImage);
            filters.repaint();
        }
        else if(ZOOM_CMD.equals(e.getActionCommand()))
        {
            filters.doScale(srcImage, 1.5, 1.5);
            filters.repaint();
        }
        else if(BROWSER_CMD.equals(e.getActionCommand()))
        {
            try {
                JFileChooser chooser = new JFileChooser();
                chooser.showOpenDialog(null);
                File f = chooser.getSelectedFile();
                srcImage = ImageIO.read(f);
                filters.setImage(srcImage);
                filters.repaint();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        MyFilterUI ui = new MyFilterUI();
        ui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ui.setPreferredSize(new Dimension(800, 600));
        ui.pack();
        ui.setVisible(true);
    }

}

这里主要是基于JFrame对象实现UI部分,通过重载JPanel的paintComponent()方法来显示原图与处理后的效果图。按钮动作响应通过监听ActionListener来实现,处理完以后通过调用repaint()方法来实现UI刷新。

时间: 2024-12-03 22:06:13

《Java数字图像处理:编程技巧与应用实践》——2.3 基于BufferedImageOp的图像滤镜演示的相关文章

《Java数字图像处理:编程技巧与应用实践》——导读

前 言 为什么要写这本书 我对图像处理的认识最初来自于读软件工程专业时做毕业设计论文的需要,毕业论文做完以后,我便把所有关于图像处理的知识扔到了一边.2011年的一天有位朋友问了我几个简单的图像处理方面的问题,在解答问题的过程中我发现自己对图像处理的热情一直都在燃烧,从那一刻起我决定重新学习图像处理.这之后,我把以前买的几本图像处理的书都读了一遍,同时还坚持通过写博客来督促自己加深理解,随着学习的不断深入,对图像处理的认知也在不断加深,我越来越渴望自己能实现那些书中提到的图像处理手段与方法,于是

《Java数字图像处理:编程技巧与应用实践》——第2章 Java BufferedImage对象及其支持的API操作 2.1 BufferedImage对象的构成

第2章 Java BufferedImage对象及其支持的API操作 第1章我们一起学习了Java中的Graphics图形包基本概念与知识,本章将介绍Java中关于图像文件操作的基本知识.首先是Java 2D图像对象BufferedImage的组件构成.与图像文件之间的关系.格式支持,以及如何利用BufferedImage对象在Java语言中实现像素读写操作.然后通过BufferedImageOp接口介绍Java中几种非常有用的对像素操作的Buffered-ImageOp的实现类.最后将集合上述

《数字图像处理与机器视觉——Visual C++与Matlab实现》——1.5 图像的显示

1.5 图像的显示 数字图像处理与机器视觉--Visual C++与Matlab实现一般使用imshow函数来显示图像,该函数可以创建一个图像对象,并可以自动设置图像的诸多属性,从而简化编程操作.这里介绍imshow函数的几种常见调用方式. 1.imshow函数imshow函数用于显示工作区或图像文件中的图像,在显示的同时可控制部分效果(参见例12.6),常用的调用形式为: imshow(I, [low high], param1, value1, param2, value2, -) imsh

《Java数字图像处理:编程技巧与应用实践》——第3章 基本Swing UI组件与图像显示 3.1 JPanel组件与BufferedImage对象的显示

第3章 基本Swing UI组件与图像显示 上一章介绍了BufferedImageOp的一些重要知识,实现了几个常见的图像特效,本章介绍如何通过Swing UI组件显示与刷新图像.首先会介绍JAVA Swing的顶层组件JFrame,然后介绍Swing中最重要和使用频率最高的组件JPanel,教会读者重写JComponent中的paintComponent()方法来实现图像的显示,最后会介绍Swing组件JButton捕获与监听用户行为时最重要的ActionListener接口的使用,以及在Sw

《Java数字图像处理:编程技巧与应用实践》——2.4 小结

2.4 小结 本章重点介绍了Java 2D中关于图像方面的操作接口类BufferedImageOp,通过其实现类可以很方便地实现图像的色彩空间转换,自定义颜色查找表,卷积功能(包括边缘提取.线性模糊.高斯模糊),图像的放大与缩小.错切变化.平移变换.旋转变换等.最后本章通过编码实现了几种简单而且常见的图像处理功能,帮助读者加深对BufferedImageOp接口的认识.如果你还想更加深入地了解BufferedImageOp接口实现类的使用,请参照JDK官方文档说明,同时建议你多多编程实践,只有加

《Java数字图像处理:编程技巧与应用实践》——第1章 Java Graphics及其API简介 1.1 什么是Java图形设备Graphics

第1章 Java Graphics及其API简介 在开始本书内容之前,笔者假设你已经有了面向对象语言编程的基本概念,了解Java语言的基本语法与特征,原因在于本书的所有源代码都是基于Java语言实现的,而且是基于Java开发环境运行与演示所有图像处理算法的.本书第1章到第3章是为了帮助读者了解与掌握Java 图形与GUI编程的基本知识与概念而写的.本章主要介绍Java GUI编程中基本的图形知识,针对GUI编程,Java语言提供了两套几乎并行的API,分别是Swing与AWT.早期的Java G

《Java数字图像处理:编程技巧与应用实践》——3.6 小结

3.6 小结 本章一步一步地剖析如何了构建一个Swing UI程序,介绍了JPanel.JButton.JFile-Chooser等组件的用法,最后通过JFrame组件组合成为用户交互界面,实现了对图像文件的显示与操作,以及UI响应用户的操作与刷新.这也是本书后面多数章节中要用到的测试UI,所以学习与掌握本章知识,将为后面图像处理的代码提供一个UI现实与效果演示界面,帮助读者加深对知识的理解.前面三章已经介绍了Java图像处理API基础知识与Swing的基础知识,这为后面学习图像处理做了很好的铺

《Java数字图像处理:编程技巧与应用实践》——1.3 用Java Swing绘制自定义的JPanel

1.3 用Java Swing绘制自定义的JPanel Swing的JPanel组件是GUI编程中最重要的面板组件,可以通过重写JPanel中paint-Component方法实现对JPanel面板组件的背景颜色的调整或添加背景图片,进而实现自定义版本的面板(JPanel)组件.只要完成如下几步就可以实现一个简单自定义JPanel面板的绘制. 1)实现对JPanel面板的继承,代码如下: public class CustomJPanel extends JPanel { // 更多代码 } 2

《Java数字图像处理:编程技巧与应用实践》——3.4 基本JButton事件响应

3.4 基本JButton事件响应 在学习JButton事件响应的知识之前,首先来看一下Swing中如何实现对用户事件的监听与处理,认识一下Swing中事件响应最重要的线程-事件分派线程. 在Swing中有一个特殊的线程被称为Swing事件分配线程,如果对UI组件的操作不在Swing事件分派线程中,Swing将抛出异常.检测当前线程是否为事件分派线程可以通过Swing本身提供的一个简单方法SwingUtilities.isEventDispatchThread()来完成.对Swing UI组件的