精华内容
下载资源
问答
  • java 图像灰度化

    2017-11-13 22:56:08
    BufferedImage.TYPE_USHORT_GRAY:表示一个无符号short 灰度图像(无索引)。 BufferedImage.TYPE_BYTE_BINARY:表示一个不透明的以字节打包的 1、2 或 4 位图像。 BufferedImage.TYPE_BYTE_INDEXED:表示带...
       public void jGrey_ActionPerformed(ActionEvent e) throws IOException{  

            if(flag_load){

                    // 读入图片

            File inputFile = new File("G:\\vehicle license plate recognition system\\image\\1.png");

                   // BufferedImage的主要作用就是将一副图片加载到内存中

            BufferedImage input = ImageIO.read(inputFile);

                  int wi=input.getWidth();

                  int hi=input.getHeight();

                  pixels new int[iw*ih];

                 // 将相应的图片转换

                 BufferedImage grayImage = new BufferedImage(iw, ih,   
                        BufferedImage.TYPE_BYTE_GRAY);  
                for(int i=0; i<iw; i++){  
                    for(int j=0; j<ih; j++){  

                        int rgb = input.getRGB(i, j);  

                   //红色:(255,0,0)或0x00FF0000

              //绿色:(0,255,0)或0x0000FF00

                   //蓝色:(255,255,255)或0x00FFFFFF 

                   // 取与运算的结果就是除了表示所对应的颜色的数字值之外其余部分都是零

                  //加权平均值法

                        int grey = (int) (0.299*((rgb&0xff0000 )>>16)+0.587*((rgb&0xff00 )>>8)+0.114*((rgb&0xff)));  

                  //  就是把四个八位二进制数

                           //拼接成一个二进制数32位

                          // 255<<24:11111111变成111111110000...(24个0)

                        rgb = 255<<24|grey<<16|grey<<8|grey;  
                        grayImage.setRGB(i, j, rgb);  
                    }  
                }  
                tmp = grayImage;  
                try{  

                    PixelGrabber pg = new PixelGrabber(tmp,0,0,iw,ih,pixels,0,iw);  

                    // 得到像素数组

                    pg.grabPixels();  
                }catch(InterruptedException e3){  
                    e3.printStackTrace();  
            }  
                flag_grey = true;  
                repaint();  
                } else{  
                    JOptionPane.showMessageDialog(null, "先点击“装载图像”,3Q!","提示:",  
                            JOptionPane.WARNING_MESSAGE);  
                }  

        } 

    BufferedImage.TYPE_INT_RGB:8 位 RGB 颜色分量,不带alpha通道。
    BufferedImage.TYPE_INT_ARGB:8 位 RGBA 颜色分量,带alpha通道。
    BufferedImage.TYPE_INT_ARGB_PRE:8 位 RGBA 颜色分量,已预乘以 alpha。
    BufferedImage.TYPE_INT_BGR:8 位 RGB 颜色分量Windows 或 Solaris 风格的图像,不带alpha通道。
    BufferedImage.TYPE_3BYTE_BGR:8位GBA颜色分量,用3字节存储Blue、Green和Red三种颜色,不存在alpha。
    BufferedImage.TYPE_4BYTE_ABGR:8位RGBA颜色分量,用3字节存储Blue、Green和Red三种颜色以及1字节alpha。
    BufferedImage.TYPE_4BYTE_ABGR_PRE:具有用3字节存储的Blue、Green和Red三种颜色以及1字节alpha。
    BufferedImage.TYPE_USHORT_565_RGB:具有5-6-5RGB颜色分量(5位Red、6位Green、5位Blue)的图像,不带alpha。
    BufferedImage.TYPE_USHORT_555_RGB:具有5-5-5RGB颜色分量(5位Red、5位Green、5位Blue)的图像,不带alpha。
    BufferedImage.TYPE_BYTE_GRAY:表示无符号byte灰度级图像(无索引)。
    BufferedImage.TYPE_USHORT_GRAY:表示一个无符号short 灰度级图像(无索引)。
    BufferedImage.TYPE_BYTE_BINARY:表示一个不透明的以字节打包的 1、2 或 4 位图像。
    BufferedImage.TYPE_BYTE_INDEXED:表示带索引的字节图像。 


    展开全文
  • Java图像灰度化的实现过程解析2016年09月08日 14:54:20阅读数:3791概要本文主要介绍了灰度化的几种方法,以及如何使用Java实现灰度化。同时分析了网上一种常见却并不妥当的Java灰度化实现,以及证明了opencv的灰度...

    Java图像灰度化的实现过程解析

    概要

    本文主要介绍了灰度化的几种方法,以及如何使用Java实现灰度化。同时分析了网上一种常见却并不妥当的Java灰度化实现,以及证明了opencv的灰度化是使用“加权灰度化”法

    24位彩色图与8位灰度图

    首先要先介绍一下24位彩色图像,在一个24位彩色图像中,每个像素由三个字节表示,通常表示为RGB。通常,许多24位彩色图像存储为32位图像,每个像素多余的字节存储为一个alpha值,表现有特殊影响的信息[1]。

    在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值),灰度范围为0-255[2]。这样就得到一幅图片的灰度图。

    几种灰度化的方法

    • 分量法:使用RGB三个分量中的一个作为灰度图的灰度值。
    • 最值法:使用RGB三个分量中最大值或最小值作为灰度图的灰度值。
    • 均值法:使用RGB三个分量的平均值作为灰度图的灰度值。
    • 加权法:由于人眼颜色敏感度不同,按下一定的权值对RGB三分量进行加权平均能得到较合理的灰度图像。一般情况按照:Y = 0.30R + 0.59G + 0.11B

    [注]加权法实际上是取一幅图片的亮度值作为灰度值来计算,用到了YUV模型。在[3]中会发现作者使用了Y = 0.21 * r + 0.71 * g + 0.07 * b来计算灰度值(显然三个权值相加并不等于1,可能是作者的错误?)。实际上,这种差别应该与是否使用伽马校正有关[1]。

    一种Java实现灰度化的方法

    如果你搜索“Java实现灰度化”,十有八九都是一种方法(代码):

    1. public void grayImage() throws IOException{
    2. File file = new File(System.getProperty("user.dir")+"/test.jpg");
    3. BufferedImage image = ImageIO.read(file);
    4. int width = image.getWidth();
    5. int height = image.getHeight();
    6. BufferedImage grayImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
    7. for(int i= 0 ; i < width ; i++){
    8. for(int j = 0 ; j < height; j++){
    9. int rgb = image.getRGB(i, j);
    10. grayImage.setRGB(i, j, rgb);
    11. }
    12. }
    13. File newFile = new File(System.getProperty("user.dir")+"/method1.jpg");
    14. ImageIO.write(grayImage, "jpg", newFile);
    15. }

    test.jpg的原图为:

    使用上述方法得到的灰度图:

    看到这幅灰度图,似乎还真是可行,但是如果我们使用opencv来实现灰度化或使用PIL(Python),你会发现效果相差很大:

    1. img = cv2.imread('test.jpg',cv2.IMREAD_COLOR)
    2. gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    3. cv2.imwrite('PythonMethod.jpg', gray)

    可以清楚的看到,使用opencv(PIL也是一样的)得到的灰度图要比上面Java方法得到的方法好很多,很多细节都能够看得到。这说明,网上这种流行的方法一直都存在这某种问题,只是一直被忽略。

    opencv如何实现灰度化

    如果读过opencv相关的书籍或代码,大概都能知道opencv灰度化使用的是加权法,之所以说是大概,因为我们不知道为什么opencv灰度化的图像如此的好,是否有其他的处理细节被我们忽略了?

    验证我们的猜想很简单,只要查看像素值灰度化前后的变化就知道了,可以如下测试:

    1. img = cv2.imread('test.jpg',cv2.IMREAD_COLOR)
    2. h, w = img.shape[:2]
    3. gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    4. for j in range(w):
    5. for i in range(h):
    6. print str(i) + " : " + str(j) + " " + str(gray[i][j])
    7. print img[h-1][w-1][0:3]

    以下打印了这么多像素点,我们也很难判断,但是我们只要关注一下最后一个像素点,就能够发现端倪: 原图最后的像素点RGB值为44,67,89,而灰度化之后的值为71。正好符合加权法计算的灰度值。如果你检查之前用Java灰度化的图片的像素值,你会发现不仅仅像素值不符合这个公式,甚至相差甚远。

    到此,我们猜测opencv(也包括PIL)是使用加权法实现的灰度化。

    Java实现加权法灰度化

    如果网上那段流行的方法不行,我们该如何使用Java实现灰度化?实际上[3]已经成功的实现了(多种方法的)灰度化(外国友人搞技术还是很给力的),在此仅仅提取必要的代码:

    1. private static int colorToRGB(int alpha, int red, int green, int blue) {
    2. int newPixel = 0;
    3. newPixel += alpha;
    4. newPixel = newPixel << 8;
    5. newPixel += red;
    6. newPixel = newPixel << 8;
    7. newPixel += green;
    8. newPixel = newPixel << 8;
    9. newPixel += blue;
    10. return newPixel;
    11. }
    12. public static void main(String[] args) throws IOException {
    13. BufferedImage bufferedImage
    14. = ImageIO.read(new File(System.getProperty("user.dir" + "/test.jpg"));
    15. BufferedImage grayImage =
    16. new BufferedImage(bufferedImage.getWidth(),
    17. bufferedImage.getHeight(),
    18. bufferedImage.getType());
    19. for (int i = 0; i < bufferedImage.getWidth(); i++) {
    20. for (int j = 0; j < bufferedImage.getHeight(); j++) {
    21. final int color = bufferedImage.getRGB(i, j);
    22. final int r = (color >> 16) & 0xff;
    23. final int g = (color >> 8) & 0xff;
    24. final int b = color & 0xff;
    25. int gray = (int) (0.3 * r + 0.59 * g + 0.11 * b);;
    26. System.out.println(i + " : " + j + " " + gray);
    27. int newPixel = colorToRGB(255, gray, gray, gray);
    28. grayImage.setRGB(i, j, newPixel);
    29. }
    30. }
    31. File newFile = new File(System.getProperty("user.dir") + "/ok.jpg");
    32. ImageIO.write(grayImage, "jpg", newFile);
    33. }

    上面的代码会打印出灰度化后的像素值,如果再与上面的Python代码做对比,你会发现像素值完全的对应上了。colorToRGB方法中对彩色图的处理正好是4个字节,其中之一是alpha参数(前文所讲),下图是这段代码灰度化后的图像:

    对于其他方法,依次同理可得。

    总结

    本文的成因本是希望使用Java实现几种灰度化操作,并使用opencv来验证转化的对错,但在实际测试中发现了一些问题(转化后的图片有差异,以及如何在转化后根据灰度值生成灰度图等问题),并就此进行了一定的思考与验证。

    这里需要注意的是,网上的一些文章或多或少没有做更进一步的思考(甚至很多都是照搬,尤其是国内的文章),而对于这些实际问题,动手实现并验证是非常重要的方法。希望本文对大家有所帮助。

    参考

    展开全文
  • 示例代码:public class MyShowImage extends JFrame {// 保存当前操作的像素...// 图像的路径private String fileString = null;// 用于显示图像的标签private JLabel imageLabel = null;// 加载的图像private B...

    示例代码:

    public class MyShowImage extends JFrame {

    // 保存当前操作的像素矩阵

    private int currentPixArray[] = null;

    // 图像的路径

    private String fileString = null;

    // 用于显示图像的标签

    private JLabel imageLabel = null;

    // 加载的图像

    private BufferedImage newImage;

    // 图像的高和宽

    private int h, w;

    // 保存历史操作图像矩阵

    private LinkedList imageStack = new LinkedList();

    private LinkedList tempImageStack = new LinkedList();

    public MyShowImage(String title) {

    super(title);

    this.setSize(800, 600);

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // 创建菜单

    JMenuBar jb = new JMenuBar();

    JMenu fileMenu = new JMenu("文件");

    jb.add(fileMenu);

    JMenuItem openImageMenuItem = new JMenuItem("打开图像");

    fileMenu.add(openImageMenuItem);

    openImageMenuItem.addActionListener(new OpenListener());

    JMenuItem exitMenu = new JMenuItem("退出");

    fileMenu.add(exitMenu);

    exitMenu.addActionListener(new ActionListener() {

    public void actionPerformed(ActionEvent e) {

    System.exit(0);

    }

    });

    JMenu operateMenu = new JMenu("图像处理");

    jb.add(operateMenu);

    JMenuItem RGBtoGrayMenuItem = new JMenuItem("灰度图像转换");

    operateMenu.add(RGBtoGrayMenuItem);

    RGBtoGrayMenuItem.addActionListener(new RGBtoGrayActionListener());

    JMenuItem balanceMenuItem = new JMenuItem("均衡化");

    operateMenu.add(balanceMenuItem);

    balanceMenuItem.addActionListener(new BalanceActionListener());

    JMenu frontAndBackMenu = new JMenu("历史操作");

    jb.add(frontAndBackMenu);

    JMenuItem backMenuItem = new JMenuItem("后退");

    frontAndBackMenu.add(backMenuItem);

    backMenuItem.addActionListener(new BackActionListener());

    JMenuItem frontMenuItem = new JMenuItem("前进");

    frontAndBackMenu.add(frontMenuItem);

    frontMenuItem.addActionListener(new FrontActionListener());

    this.setJMenuBar(jb);

    imageLabel = new JLabel("");

    JScrollPane pane = new JScrollPane(imageLabel);

    this.add(pane, BorderLayout.CENTER);

    this.setVisible(true);

    }

    private class OpenListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {

    JFileChooser jc = new JFileChooser();

    jc.setFileFilter(new FileFilter() {

    public boolean accept(File f) { // 设定可用的文件的后缀名

    if (f.getName().endsWith(".jpg") || f.isDirectory()|| f.getName().endsWith(".gif") || f.getName().endsWith(".bmp")) {

    return true;

    }

    return false;

    }

    public String getDescription() {

    return "图片(*.jpg,*.gif,*bmp)";

    }

    });

    int returnValue = jc.showOpenDialog(null);

    if (returnValue == JFileChooser.APPROVE_OPTION) {

    File selectedFile = jc.getSelectedFile();

    if (selectedFile != null) {

    fileString = selectedFile.getAbsolutePath();

    try {

    newImage = ImageIO.read(new File(fileString));

    w = newImage.getWidth();

    h = newImage.getHeight();

    currentPixArray = getPixArray(newImage, w, h);

    imageStack.clear();

    tempImageStack.clear();

    imageStack.addLast(currentPixArray);

    imageLabel.setIcon(new ImageIcon(newImage));

    } catch (IOException ex) {

    System.out.println(ex);

    }

    }

    }

    MyShowImage.this.repaint();

    // MyShowImage.this.pack();

    }

    }

    // 菜单监听器///

    private class RGBtoGrayActionListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {

    int[] resultArray = RGBtoGray(currentPixArray);

    imageStack.addLast(resultArray);

    currentPixArray = resultArray;

    showImage(resultArray);

    tempImageStack.clear();

    }

    }

    private class BalanceActionListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {

    int[] resultArray = balance(currentPixArray);

    imageStack.addLast(resultArray);

    currentPixArray = resultArray;

    showImage(resultArray);

    tempImageStack.clear();

    }

    }

    private class BackActionListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {

    if (imageStack.size() <= 1) {

    JOptionPane.showMessageDialog(null, "此幅图片的处理已经没有后退历史操作了", "提示",

    JOptionPane.INFORMATION_MESSAGE);

    } else {

    tempImageStack.addLast(imageStack.removeLast());

    currentPixArray = imageStack.getLast();

    showImage(currentPixArray);

    }

    }

    }

    private class FrontActionListener implements ActionListener {

    public void actionPerformed(ActionEvent e) {

    if (tempImageStack.size() < 1) {

    JOptionPane.showMessageDialog(null, "此幅图片的处理已经没有前进历史操作了", "提示",

    JOptionPane.INFORMATION_MESSAGE);

    } else {

    currentPixArray = tempImageStack.removeFirst();

    imageStack.addLast(currentPixArray);

    showImage(currentPixArray);

    }

    }

    }

    // 获取图像像素矩阵/

    private int[] getPixArray(Image im, int w, int h) {

    int[] pix = new int[w * h];

    PixelGrabber pg = null;

    try {

    pg = new PixelGrabber(im, 0, 0, w, h, pix, 0, w);

    if (pg.grabPixels() != true)

    try {

    throw new java.awt.AWTException("pg error" + pg.status());

    } catch (Exception eq) {

    eq.printStackTrace();

    }

    } catch (Exception ex) {

    ex.printStackTrace();

    }

    return pix;

    }

    // 显示图片///

    private void showImage(int[] srcPixArray) {

    Image pic = createImage(new MemoryImageSource(w, h, srcPixArray, 0, w));

    ImageIcon ic = new ImageIcon(pic);

    imageLabel.setIcon(ic);

    imageLabel.repaint();

    }

    // 灰度转换///

    private int[] RGBtoGray(int[] ImageSource) {

    int[] grayArray = new int[h * w];

    ColorModel colorModel = ColorModel.getRGBdefault();

    int i, j, k, r, g, b;

    for (i = 0; i < h; i++) {

    for (j = 0; j < w; j++) {

    k = i * w + j;

    r = colorModel.getRed(ImageSource[k]);

    g = colorModel.getGreen(ImageSource[k]);

    b = colorModel.getBlue(ImageSource[k]);

    int gray = (int) (r * 0.3 + g * 0.59 + b * 0.11);

    r = g = b = gray;

    grayArray[i * w + j] = (255 << 24) | (r << 16) | (g << 8) | b;

    }

    }

    return grayArray;

    }

    // ///图像均衡化///

    private int[] balance(int[] srcPixArray) {

    int[] histogram = new int[256];

    int[] dinPixArray = new int[w * h];

    for (int i = 0; i < h; i++) {

    for (int j = 0; j < w; j++) {

    int grey = srcPixArray[i * w + j] & 0xff;

    histogram[grey]++;

    }

    }

    double a = (double) 255 / (w * h);

    double[] c = new double[256];

    c[0] = (a * histogram[0]);

    for (int i = 1; i < 256; i++) {

    c[i] = c[i - 1] + (int) (a * histogram[i]);

    }

    for (int i = 0; i < h; i++) {

    for (int j = 0; j < w; j++) {

    int grey = srcPixArray[i * w + j] & 0x0000ff;

    int hist = (int) c[grey];

    dinPixArray[i * w + j] = 255 << 24 | hist << 16 | hist << 8

    | hist;

    }

    }

    return dinPixArray;

    }

    public static void main(String[] args) {

    new MyShowImage("ShowImage");

    }

    }

    展开全文
  • 转载:http://www.chinasb.org/archives/2013/01/5053.shtml 1: package org.chinasb.client; 2: 3: import java.awt.Color; 4: import java.awt.image.BufferedImage; 5: import java.io.File;...

    转载:http://www.chinasb.org/archives/2013/01/5053.shtml

     

    效果

    src

    dest

    转载于:https://www.cnblogs.com/ZJUT-jiangnan/p/3619288.html

    展开全文
  • 上一篇文章中,已经说过了图像灰度化灰度化之后,我们希望了解像素在0-255的分布情况。这就是我们下面要说的图像的灰度直方图,这是最简单的一种。但是原理都是一样的。图像的直方图有大量的用处,它反应了图像...
  • 主要介绍了灰度化的几种方法,以及如何使用Java实现灰度化。同时分析了网上一种常见却并不妥当的Java灰度化实现,以及证明了opencv的灰度化是使用“加权灰度化”法,下面一起来看看。
  • java使图像灰度化

    2009-04-29 17:05:09
    使用java语言编写,使图像灰度化,简单的小程序,可运行
  • Fa=1 Fb≠0时,图像灰度上移或下移,效果为图像变亮或变暗。Fa=-1,Fb=255时,发生图像反转。注意:线性变换会出现亮度饱和而丢失细节。2)对数变换t=c * log(1+s)c为变换尺度,s为源灰度,t为变换后的灰度。对数变...
  • 几种灰度化的方法 分量法:使用RGB三个分量中的一个作为灰度图像的灰度值 最值法:使用RGB三个分量中最大值或最小值作为灰度图像的灰度值 均值法:使用RGB三个分量的平均值作为灰度图像的灰度值 ...
  • java-图像灰度化与二值化.docx》由会员分享,提供在线免费全文阅读可下载,此文档格式为docx,更多相关《java-图像灰度化与二值化.docx》文档请在天天文库搜索。1、java 图像灰度化与二值化 1: package org....
  • java实现图像灰度化

    2015-12-23 22:03:00
    /*在研究Java实现将一张图片转成字符画的时候,发现将图像转化字符串是根据照片的灰度采用不同的字符画出来,形成一个灰度表。于是就研究了下关于灰度值这个东西,于是跳了一个大坑。。。因为鄙人用的ubuntu,所以我...
  • import org.opencv.core.*;... //图像灰度化 Imgproc.cvtColor(srcImage, dstImage, Imgproc.COLOR_BGR2GRAY); //保存图像 Imgcodecs.imwrite("d:/Java/opencv/example_gray.jpg", dstImage); } }
  • java 图像灰度直方图

    千次阅读 2014-07-27 15:00:09
    上一篇文章中,已经说过了图像灰度化灰度化之后,我们希望了解图0-255
  • 灰度图像上每个像素的颜色值又称为灰度,指黑白图像中点的颜色深度,范围一般从0到255,白色为255,黑色为0。所谓灰度值是指色彩的浓淡程度,灰度直方图是指一幅数字图像中,对应每一个灰度值统计出具有该灰度值的象...
  • GrayImage = imread('E:/image/matlab/MouseGray.png');%原始的灰度图像[rows , cols] = ...%初始一个矩阵,用来存储原来灰度图像的直方图NewZhiFang = zeros(1 , 256);%初始一个矩阵,用来存储变化之后的灰度...
  • 效果在这里 https://www.cnblogs.com/jnhs/p/11325173.html 开发环境opencv4.0.1 java swing netbeans8 maven 解压后,使用nb打开,运行即可,需要jdk1.8 其他版本的jdk需要你自己去修改
  • Java之Opencv图像灰度化处理-yellowcong

    千次阅读 2018-01-07 10:43:53
    在进行图片识别的操作前,我们都会对图片进行灰度化处理,灰度化后的图片,便于我们通过opencv来进行图片的读取等操作 代码地址#码云地址 https://gitee.com/yellowcong/opencv #github ...灰化处理后的图片 ...
  • public class PictureTest {/*** 几种灰度化的方法分量法:使用RGB三个分量中的一个作为灰度图的灰度值。最值法:使用RGB三个分量中最大值或最小值作为灰度图的灰度值。均值法:使用RGB三个分量的平均值作为灰度图的...
  • java语言实现灰度 均衡 中值滤波等
  • java实现图片灰度化(二值化)此函数功能: 1. 读图 2. 创建缓冲区 3. 将图片像素复制到缓冲区的相应位置 4. 输出比较输入: RGB真彩图片 输出: 灰度化(二值化)图片源码如下:import java.awt....
  • opencv java图片灰度化处理

    千次阅读 2018-08-25 20:51:50
    图像灰度化处理有2中方式: 注:参数IMREAD_GRAYSCALE表示图片灰度 1.加载时指定灰度参数 Mat mat = Imgcodecs.imread(&amp;quot;/data/data/WindowsLogo.jpg&amp;quot;, Imgcodecs.IMREAD_GRAYSCALE)...
  • java实现图像灰度反白

    2015-05-17 12:32:24
    java编写的实现对图片的灰度化以及反白操作并保存
  • java图像处理方法:灰度化,二值化,降噪,切割,裁剪,找相似等
  • java图像处理方法:灰度化,二值化,降噪,切割,裁剪,找相似等.zip
  • 下面我们就讲讲如何对图像去色,即灰度化。 上一篇我们说到过,对图像处理的事实,我们更关心图像的亮度信息,也就是灰度,如何将彩色图像转换成灰度图像呢?很简单,只要令R,G,B三个值相等即可。那么这个值和...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 208
精华内容 83
关键字:

java图像灰度化

java 订阅