Java图像处理(后面会补充PS啥的)

Posted 你说得对!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java图像处理(后面会补充PS啥的)相关的知识,希望对你有一定的参考价值。

图像处理方法java(一)

1.引言

  初学java,想做一个简易的美颜相机,对图像处理的一些方法进行了相应的学习。主要包括图像的原理、图像的数据处理、位运算、滤镜效果、图片读写等。

2.基本原理

2.1图像的基本原理

  每一张图像,其实都是由一个个的带有颜色的点组成的矩阵,也就是我们常说的像素点矩阵,同时横纵坐标像素点的个数乘积就是图像的分辨率。
  每一个像素点的颜色,都是有相应的编号的,一般的存储格式是RGB和ARGB格式。RGB格式:RGB其实就是三原色,Red、Green、Blue,每一种原色的编号范围是0-255,也就是存储在一个字节(8位2进制)中。Red从0到255颜色会越来越“红”,红的程度会加深。
  举个例子来说,对于RGB: 123, 200,13 来说,这个颜色是由Red123,Green200,Blue13这三种颜色合成的。
  所以我们很容易知道,RGB是占三个字节,可以存储在整型变量(四个字节)中,并且存储在低24位,那么就会出现高8位没有使用的情况。ARGB格式就是把高8位也利用起来,用来存其他的信息例如照片拍摄日期等等。

2.2图像数据的处理

  初始化生成一个窗体,然后通过随机数随机生成一张RGB格式的图片。通过位移运算计算RGB格式里red、green、blue的值(位移运算在2.3会讲解)。对于滤镜等操作,就可以通过调节red、green、blue的值来实现,比如说red=red/2,就能实现图片红色降度,也就是照片的红色不会太鲜艳。
  我们常常也会听说灰度图片,就是照片会变成灰色,这种照片的每一个像素点的RGB值均满足red=green=blue,我们可以调整三原色的占比来确定灰阶(详见代码)。
  二维码:有了灰阶,也就是对每一个像素值都有唯一标准了。我们可以确定一个分界值a,当算出的灰度值大于a,就用白色来表示;若小于a,就用黑色来表示,并且使用fiilRect方法的时候,把像素点的宽和高调大一点,我们就可以得到这张图片的二维码了,也就是说,这个二维码储存了我这张图片的数据。

import javax.swing.*;
import java.awt.*;
import java.util.Random;

import javax.swing.JFrame;
public class ImageUI extends JFrame {
    //初始化一个窗体
    
    public void initUI(){
        setTitle("图像编程");
        setSize(1000,1000);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
    /*
    * 窗体出现在屏幕上时调用
    * 绘制窗体本身
    * 在窗体上绘制像素点
    * @param g :图形对象 可以来绘制图形
    * Graphics 图形
    * g.fillRect(x,y,w,h);
    */
    public void paint(Graphics g){
        super.paint(g);
        //g.fillRect(100,100,10,10);
        int[][] imgarr=new int[300][300];
        // 随机创建颜色矩阵
        Random random = new Random();
        for (int i = 0; i < 300; i++) {
            for (int j = 0; j < 300; j++) {
                int colorValue = random.nextInt(256*256*256);
                imgarr[i][j]=colorValue;
                Color color = new Color(colorValue);
                g.setColor(color);
                g.fillRect(50+i,50+j,1,1);
            }
        }
        for (int i = 0; i <imgarr.length; i+=10) {
            for (int j = 0; j < imgarr[i].length; j+=10) {
                int value = imgarr[i][j];
                int red =(value>>16)&0xFF;
                int green =(value>>8)&0xFF;
                int blue =(value>>0)&0xFF;
    // 灰度 r=g=b
    // 有了灰阶 一张照片所有的像素值都具有同样的标准了
                int gray =(int)(red*0.36+green*0.29+blue*0.35);
                if(gray>128){
                g.setColor(Color.white);
                }
                else{
                    g.setColor(Color.black);
                }
                g.fillRect(400+i,50+j,10,10);
            }
        }
    }
    // 启动程序
    public static void main(String[] args) {
        ImageUI img = new ImageUI();
        img.initUI();
    }
}

运行图片

2.3位运算

  这里主要讲如何通过存储RGB的整型int得到red、green、blue的值;以及red、green、blue如何合成为一个int。
  我们了解了RGB的存储原理,很容易知道:17-24位存red,9-16位存green,1-8位存blue,用除法和减法可以提取出red、green、blue,但是这样运算速度会慢一些,可以用位移运算,运算速度会快一些。
  假设RGB的值存储在int value中,通过位移运算有:
      int red =(value>>16)&0xFF;
      int green =(value>>8)&0xFF;
      int blue =(value>>0)&0xFF;
为什么要位移之后与0xFF求与运算呢? 仔细分析后,我们知道,与0xFF做与运算后,可以将除低8位以外的所有位的数字变为0。
  所以我们以此类推,我们发现把red、green、blue合成一个RGB时,可以通过位移加上或运算来实现:
      int rgb= (red<<16)|(green<<8)|blue;

3.图片读写与滤镜效果

3.1图片读写

  图片的输入,我写了一个输入图片的方法,加上想要读取的图片路径,路径是相对路径和绝对路径都可以,我的图片和代码是放在一个文件夹里的,所以用的相对路径。
使用ImageIO.read()方法的时候,需要创建BufferedImage类的对象

    int[][] imgarr = getImagePixArr("picture4.jpeg");
    //这里imgarr是创建的二维数组,用来存储图片的像素点矩阵。
    public int[][] getImagePixArr(String path){
        // 声明一个文件对象
        File file = new File(path);
        BufferedImage buffimg=null;
        try {// 处理文件IO异常
            // 读取为 buffimg 对象
            buffimg = ImageIO.read(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 宽 高 图片类型 ARGB
        int w = buffimg.getWidth();
        int h = buffimg.getHeight();
        // 根据 图片对象的宽 高 生成一个二维数组 -- 线性结构
        int[][] imgarr = new int[w][h];
        // 遍历存储像素值
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                imgarr[i][j]= buffimg.getRGB(i,j);
            }
        }
        return imgarr;
    }

  图片输出的话也是一样,也需要创建BufferedImage类的对象,代码如下:

public void outImagePixArr(int[][] imgarr ,String path){
        // 声明一个文件对象
        File file = new File(path);
        BufferedImage buffimg = new BufferedImage(imgarr.length ,imgarr[0].length ,BufferedImage.TYPE_INT_ARGB);
        for (int i = 0; i < imgarr.length; i++){
            for (int j = 0; j < imgarr[i].length; j++){
                buffimg.setRGB(i, j, imgarr[i][j]);
            }
        }
        try {// 处理文件IO异常
            // 读取为 buffimg 对象
            ImageIO.write(buffimg, "PNG", file);
        } catch (IOException e) {
            e.printStackTrace();
        
        //return;
        }
    }

3.2滤镜效果

3.2.1原图输出

    // 原图
        for (int i = 0; i < imgarr.length; i++) {
            for (int j = 0; j < imgarr[i].length; j++) {
                Color color = new Color(imgarr[i][j]);
                g.setColor(color);
                g.fillRect(i,j,1,1);
            }
        }

3.2.2马赛克

  马赛克就是模糊处理,把一个像素点的长宽变大即可。

// 马赛克
        for (int i = 0; i < imgarr.length; i+=10) {
            for (int j = 0; j < imgarr[i].length; j+=10) {
                Color color = new Color(imgarr[i][j]);
                g.setColor(color);
                g.fillRect(750+i,j,10,10);
            }
        }

3.2.3灰度

  在前面已经讲过,灰度图片的red=green=blue。

// 灰度
        for (int i = 0; i < imgarr.length; i++) {
            for (int j = 0; j < imgarr[i].length; j++) {
                int value = imgarr[i][j];
                int red=(value>>16)&0xFF;
                int green=(value>>8)&0xFF;
                int blue=(value>>0)&0xFF;
                int gray = (red+green+blue)/3;
                Color color = new Color(gray,gray,gray);
                g.setColor(color);
                g.fillRect(i,400+j,1,1);
            }
        }

3.2.4滤镜颜色

  这个滤镜是我自己调的,可能不是很好看,red变为原来的0.8,green变成原来的0.5,blue变成原来的0.6。

//滤镜
        for (int i = 0; i < imgarr.length; i++){
            for (int j = 0; j < imgarr[i].length; j++){
                int value = imgarr[i][j];
                int red=(value>>16)&0xFF;
                int green=(value>>8)&0xFF;
                int blue=(value>>0)&0xFF;
                red = red*4/5;
                green = green/2;
                blue = blue*3/5;
                //imgarr1 = (red<<16)|(green<<8)|blue;
                Color color = new Color(red,green,blue);
                g.setColor(color);
                g.fillRect(i,400+j,1,1);
            }
        }

3.2.5轮廓

//轮廓
        for (int i = 0; i < imgarr.length-2; i++) {
            for (int j = 0; j < imgarr[i].length-2; j++) {
                int value = imgarr[i][j];
                int red=(value>>16)&0xFF;
                int green=(value>>8)&0xFF;
                int blue=(value>>0)&0xFF;
                int gray = (red+green+blue)/3;
                int nvalue = imgarr[i+2][j+2];
                int nred=(nvalue>>16)&0xFF;
                int ngreen=(nvalue>>8)&0xFF;
                int nblue=(nvalue>>0)&0xFF;
                int ngray = (nred+ngreen+nblue)/3;
                if(Math.abs(gray-ngray)>10){
                    g.setColor(Color.white);
                }else{
                    g.setColor(Color.black);
                }
                // Color color = new Color(ngray,ngray,ngray);
                g.fillRect(750+i,400+j,1,1);
            }
        }

运行图片

4.交互式处理

4.1带按钮的窗体

  创建一个UI类,可以生成带按钮的窗体。那个布局方法是自动把按钮布局。

import javax.swing.*;
import java.awt.*;
import javax.swing.JFrame;
public class UI extends JFrame{
    // 构造方法
    // 创建对象时调用
    String[] strs={"原图","灰度","怀旧","马赛克","二值化","融合","轮廓"};
    public void initUI() {
        setTitle("图像窗体");
        setSize(2200, 1000);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //布局
        FlowLayout fl =new FlowLayout();
        setLayout(fl);
        for (int i = 0; i < strs.length; i++) {
            JButton btn = new JButton(strs[i]);
            add(btn);
        }
        setVisible(true);
    }
    public static void main(String[] args) {
        UI img = new UI();
        img.initUI();
    }
}

运行结果

但是我们在窗体上点击按钮,是没有反应的,于是我们需要一个Listener类,也就是监听器,来监听按钮是否被点击,如果被点击就会进行相应操作。

4.2动作监听器

  ActionListener——动作监听器,作用是监听按钮是否被点击了,如果被点击了就调用一个预设好的方法(方法的内容需要自己来写 )。实现接口interface时,用implements关键字,可以变相实现多继承。

import java.awt.event.ActionListener;
// 1:首先创建一个类 使用 implements ActionListener
public class ImageListener implements ActionListener{
	// 2:重写ActionListener 中的方法
	public void actionPerformed(ActionEvent e){
		// 3: 点什么按钮就绘制什么图像
		System.out.println("点击了按钮 ");
	}
}

运行结果:点一下按钮就会输出“点击了按钮 ”

4.3综合起来

  前文讲了很多细节的处理以及方法,现在我们就可以创建一个窗体,一个有按钮的窗体,点击按钮能得到相应“滤镜”的图片,不再是4.1那样没有反应的按钮;也不再是4.2只输出一句话,我们重写方法后,可以把图片输出在窗体上。

4.3.1创建ImageUtils类

  创建这个工具类目的有两个:第一,读取某个路径下的图片,把他存在我们的像素点矩阵imgarr(二维数组)中,这个我在3.1中有介绍;第二,就是能把处理后的像素点矩阵“画出来”,也就是图片。

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class ImageUtils {
    public int[][] getImagePixArr(String path){
        以上是关于Java图像处理(后面会补充PS啥的)的主要内容,如果未能解决你的问题,请参考以下文章

VSCode 配置 用户自定义代码片段 自定义自动代码补充

VSCODE snippets的使用

java异常处理字符串类型信息

java编程:eclipse 中感叹号是啥意思?需要做啥处理?缺省包和jre系统库是干啥的?本分人新手!

找出当前显示的片段是啥的最佳方法是啥?

求教大神,java中的jdbc程序为啥要加finally,不是加了try catch以后,后面的语句就会执行了啊