Java类似相机中图像处理(上)

Java类似相机中图像处理(上)

一、前情提要

二、项目流程

\qquad 1. 创建窗体

\qquad 2. 动作监听器

\qquad 3. 获取图片文件像素值

\qquad 4. 画原画

\qquad 5. 画灰度图

\qquad 6. 画黑白二值图

\qquad 7. 画马赛克图

三、效果图

一、前情提要

\qquad 上篇文章介绍了图像的原理,RGB三色值用int值存储以及再拆分为三色值,Java中的位移运算符以及十六进制数,这篇文章我将把这些付诸实际,制作几个简单的图像处理功能。

二、项目流程

1. 创建窗体

  首先,我们要有窗体来显示图像,就用到了之前学习的JFrame,并为每个按钮添加动作监听器ImageListener来分别实现功能。

public class ImagePad {
    String[] strs = {"打开","保存","原图","灰度","二值化","马赛克","圆点马赛克",
            "怀旧","轮廓","素描","锐化","缩小","放大",
    };// 本篇没有全部实现,不过后面会做出完整的项目
    // 创建监听器对象
    ImageListener img1 = new ImageListener();

    public void showUI(){
        JFrame jf = new JFrame("图像处理V1.0");
        jf.setSize(800,600);//大小
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//直接关闭
        jf.setLocationRelativeTo(null);//中心弹窗
        jf.setLayout(new FlowLayout());//流式布局

        // 创建按钮对象 | 设置属性(文本 尺寸 背景颜色 字体大小 监听器)
        Font font = new Font("黑体",Font.BOLD,16);
        for (int i = 0; i < strs.length; i++) {
            JButton btn = new JButton(strs[i]);
            btn.setBackground(Color.WHITE);
            btn.setFont(font);
            btn.addActionListener(img1);//添加动作监听器
            jf.add(btn);
        }

        jf.setVisible(true);
        // 可视化之后 获取一个Graphics
        img1.g = jf.getGraphics();//监听器获取窗体中的画笔
    }

    public static void main(String[] args) {
        new ImagePad().showUI();
    }
}

2. 动作监听器

  在讲动作监听器之前,我先列举一下这次的动作监听器用到的新知识。

  1. 代码块:{}

    • 在程序中用一对中括号包括一些代码语句,表示创建对象的时候执行,代表初始化

    • 可以放一些程序中经常需要同样调用的函数,可以节省时间,也避免了不必要的开销。

  2. switch(a) case语句

    • switch-case语句可以很方便的进行情况列举,比通常的if else if…简便很多,根据a和case的值匹配,运行相应的代码。

    • 不要忘记break;跳出循环,也可以写上default语句,便于都不满足时执行。

  下面是动作监听器ImageListener的代码

public class ImageListener implements ActionListener {
    ImageTools imgtools = new ImageTools();
    Graphics g;
    int[][] imageArr;

    {// 代码块 创建对象的时候就执行 初始化的方法调用
        String path = "D:\\OneDrive\\桌面\\xinhai.jpg";
        // 传入 图片路径 得到图片的像素矩二维数组
        imageArr = imgtools.getImagePiexArray (path);//下面会讲到,不用着急
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        // 获取按钮上的字符串
        String btnStr = e.getActionCommand();
        System.out.println("点击了:"+ btnStr);

        // 根据点击的字符串 操作绘制图片
        switch (btnStr){
            case "原图":
                // 绘制图片
                System.out.println("原图");
                imgtools.drawImage(imageArr,g);
                break;
            case "灰度":
                imgtools.drawGrayImage(imageArr,g);
                break;
            case "二值化":
                imgtools.drawBinaryImage(imageArr,g);
                break;
            case "马赛克":
                imgtools.drawMosaicImage(imageArr,g);
                break;
        }
    }
}

3. 获取图片文件像素值

  这样基本的框架就打好了,我们现在来学习绘图功能的实现。

先普及个小知识,在idea中已经写好的方法前面输入/**在回车,会出现一种新的注释,叫做文档注释例如

/** 文档注释
     * 根据一个路径来获取图片的二维像素数组
     * @param path 参数: 图片路径
     * @return 图片的二位数组
     */

  我们先考虑如何画出原图

  首先要创建一个获取图像中像素的方法,由于图像就是一个二维矩阵,所以我们的返回值也要是一个二维数组。

  我们要先获得图像文件中的像素,自然就需要用到文件操作,

/** 文档注释
 * 根据一个路径来获取图片的二维像素数组
 * @param path 参数: 图片路径
 * @return 图片的二位数组
 */  
public int[][] getImagePiexArray(String path){
    File file = new File(path);//path是文件的路径
}

  然后我们使用BufferedImage类创建一个图像缓冲区,用来将图片加载到内存中,通过图像缓冲区能够很方便的操作图像。

BufferedImage buffimg = null;

  通过ImageIO中的read方法读取上面创建的图片文件对象file,装载在buffimg中

buffimg = ImageIO.read(file);

  但是直接这么写你会发现报错
在这里插入图片描述

  提示我们要try catch环绕,那就点击照它说的做
在这里插入图片描述

  这下就不报错了

  现在这张图片的数据已经都存在buffimg中了,我们可以进行进一步操作。

  图片的本质也就是像素值的二维矩阵,对于buffimg也同理,我们分别获取这个二维矩阵的长和宽

//buffimg 宽 高 像素值
int width = buffimg.getWidth();
int height = buffimg.getHeight();

  现在我们创建一个同样长同样宽的二维数组imgArr,为了下面存储图片每个位置的像素值。

int[][] imgArr = new int[width][height];

  接下来我们遍历这个二维数组,将bufferimg中的像素RGB值存入imgArr。使用的是BufferedImage中的getRGB(int x,int y)方法获取某一位置的像素

for (int i = 0; i < imgArr.length; i++) {
    for (int j = 0; j < imgArr[i].length; j++) {
        imgArr[i][j] = buffimg.getRGB(i,j);
    }
}
return imgArr;

  这样获取图像像素值的方法就写完了,我们可以在别的方法中调用以快速获取图片像素。

  完整代码如下

public int[][] getImagePiexArray(String path){
        File file = new File(path);
        BufferedImage buffimg = null;

        try {
            buffimg = ImageIO.read(file);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //buffimg 宽 高 像素值
        int width = buffimg.getWidth();
        int height = buffimg.getHeight();
        // 根据宽 高 创建一个二维数组
        int[][] imgArr = new int[width][height];

        // 遍历循环 将所有的像素值 存入数组中了
        // 遍历 从头到尾 全部取出来
        for (int i = 0; i < imgArr.length; i++) {
            for (int j = 0; j < imgArr[i].length; j++) {
                imgArr[i][j] = buffimg.getRGB(i,j);
            }
        }
        return imgArr;
}

4. 画原图

  现在我们来画原图。思路很简单,就是把获得的imgArr数组原封不动的画出来,因为我们的imgArr本来就是存储的图片文件的像素,那么直接遍历数组画出来就是原图。

  画图我们肯定需要画笔对象Graphics,每个像素值我们可以用自己喜欢的方式去绘制,比如按照矩形绘制,按照圆形绘制等等。

    /**
     * 原图
     * @param imgArr
     * @param g
     */
    public void drawImage(int[][] imgArr, Graphics g){
        for (int i = 0; i < imgArr.length; i++) {
            for (int j = 0; j < imgArr[i].length; j++) {
                int pixnum = imgArr[i][j];//获取当前像素颜色,用int值表示
                Color color = new Color(pixnum);
                g.setColor(color);
                X Y 为预先定义好的偏置值,设定图像的位置
                g.fillRect(X + i, Y + j, 1, 1);//采用矩形绘制,原图像素点也是1*1的矩形,因此不会有任何缝隙
            }
        }
    }

5. 画灰度图

  灰度图的画法与原图类似,只是颜色值不能够直接使用imgArr[i][j],而是要拆分出三原色并按一定比例混合

画灰度图的6种方法:

1.浮点法:Gray=R0.3+G0.59+B*0.11

2.整数法:Gray=(R30+G59+B*11)/100

3.移位法:Gray =(R77+G151+B*28)>>8;

4.平均值法:Gray=(R+G+B)/3;

5.仅取绿色:Gray=G;

6.Gamma校正算法:

在这里插入图片描述

  这里我用的是第四种平均值法:

  用到了上节课的int值拆分为RGB

    /**
     * 灰度图
     * @param imgArr
     * @param g
     */
    public void drawGrayImage(int[][] imgArr, Graphics g){
        for (int i = 0; i < imgArr.length; i++) {
            for (int j = 0; j < imgArr[i].length; j++) {
                int pixnum = imgArr[i][j];
                // 拆分像素值为 R G B
                int red = (pixnum >> 16) & 255;
                int green = (pixnum >> 8) & 255;
                int blue = (pixnum >> 0) & 255;
                // 灰度的原理 R=G=B
                // 均值法
                int gray = (red + green + blue)/3;
                Color color = new Color(gray,gray,gray);
                g.setColor(color);
                g.fillRect(X + i, Y + j,1 ,1);
            }
        }
    }

6. 画黑白二值图

  原理也差不多,就是对灰度值再进行划分,大于等于127的设为白色,小于127的设为黑色,就完成了简单的二值化。

    /**
     * 黑白
     * @param imgArr
     * @param g
     */
    public void drawBinaryImage(int[][] imgArr, Graphics g){
        for (int i = 0; i < imgArr.length; i++) {
            for (int j = 0; j < imgArr[i].length; j++) {
                int pixnum = imgArr[i][j];
                // 拆分像素值为 R G B
                int red = (pixnum >> 16) & 255;
                int green = (pixnum >> 8) & 255;
                int blue = (pixnum >> 0) & 255;
                // 灰度的原理 R=G=B
                // 均值法
                int gray = (red + green + blue)/3;
                // 利用灰度值 做二分
                if(gray < 127){
                    g.setColor(Color.BLACK);
                }else {
                    g.setColor(Color.WHITE);
                }
                g.fillRect(X + i, Y + j,1 ,1);
            }
        }
    }    

7. 画马赛克图

  我们之前都是把矩形框按照1 * 1画的,假如我们都改成10 * 10,并且把数组的便利步长也改为加10,那么就达成了简单的马赛克效果,因为像素块变大了。

    /**
     * 马赛克
     * @param imgArr
     * @param g
     */
    public void drawMosaicImage(int[][] imgArr, Graphics g){
        for (int i = 0; i < imgArr.length; i+=10) {
            for (int j = 0; j < imgArr[i].length; j+=10) {
                int pixnum = imgArr[i][j];
                // 间距采样
                Color color = new Color(pixnum);
                g.setColor(color);
                g.fillRect(X+i,Y+j,10,10);
            }
        }
    }

三、效果图(使用流行的心海表情包)

原图
在这里插入图片描述

灰度图
在这里插入图片描述

黑白二值图
在这里插入图片描述

马赛克图
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Neko1145

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值