近些天,一直想做一个pc版的美颜相机,但是万事开头总要有第一步,首先就是美颜相机的添加滤镜问题,直接上手视频未免过于着急,于是就想先对单张图片来实现滤镜功能的添加。
1.第一步,就是窗体和面板的创立,以及相关监听器的添加和画笔的传递
public class EDrawUI { //定义方法 public void init() { //绘制窗体基本 JFrame jf = new JFrame(); jf.setSize(810, 900); jf.setTitle("小浩的美颜相机3.9"); jf.setLocationRelativeTo(null); jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //布局的确定 BorderLayout border = new BorderLayout(); jf.setLayout(border); //确定north与center JPanel jp_north=new JPanel(); jp_north.setBackground(Color.white); Dimension ds_north=new Dimension(0,40); jp_north.setPreferredSize(ds_north); jf.add(jp_north,BorderLayout.NORTH); //加入一个使用的CmyPanel面板 EMyPanel center=new EMyPanel(); center.setBackground(Color.white); jf.add(center,BorderLayout.CENTER); //监听器对象的创立 EPixelMouse ePixelMouse=new EPixelMouse(); //开始设置菜单栏 JMenuBar jmu=new JMenuBar(); jp_north.add(jmu); //设置菜单选项 String[]menuText={"文件","编辑","常规滤镜","帮助","特殊滤镜","导航","工具"}; //设置一个关于菜单选项的对象能直接调用的 JMenu[]menu=new JMenu[menuText.length]; for (int i=0;i< menuText.length;i++){ JMenu jMenu=new JMenu(menuText[i]); menu[i]=jMenu; Dimension ds=new Dimension(110,30); jMenu.setPreferredSize(ds); jmu.add(jMenu); } //文件子菜单选项 String[]name={"打开","保存","关闭"}; for (int j=0;j< name.length;j++){ JMenuItem jme=new JMenuItem(name[j]); Dimension ds=new Dimension(110,30); jme.setPreferredSize(ds); jme.addActionListener(ePixelMouse); menu[0].add(jme); } //编辑子菜单的选项 String[]name1={"左旋转90度","右旋转90度","左旋转180度","右旋转180度"}; for (int j=0;j< name1.length;j++){ JMenuItem jme=new JMenuItem(name1[j]); Dimension ds=new Dimension(110,30); jme.setPreferredSize(ds); jme.addActionListener(ePixelMouse); menu[1].add(jme); } //滤镜子菜单的选项 String[]name2={"原图","马赛克","灰度","油画","素描","珠纹"}; for (int j=0;j< name2.length;j++){ JMenuItem jme=new JMenuItem(name2[j]); Dimension ds=new Dimension(110,30); jme.setPreferredSize(ds); jme.addActionListener(ePixelMouse); menu[2].add(jme); } //特殊滤镜子菜单的选项 String[]name3={"怀旧","梦幻","红外扫描","浮雕","特殊1","特殊2","卷积","合并"}; for (int j=0;j< name3.length;j++){ JMenuItem jme=new JMenuItem(name3[j]); Dimension ds=new Dimension(110,30); jme.setPreferredSize(ds); jme.addActionListener(ePixelMouse); menu[4].add(jme); } //帮助子菜单的选项 JMenuItem jm1=new JMenuItem("撤回"); Dimension dss=new Dimension(110,30); jm1.setPreferredSize(dss); jm1.addActionListener(ePixelMouse); menu[3].add(jm1); jf.setVisible(true); //从窗体获取画笔 Graphics graphics=center.getGraphics(); //画笔赋予 ePixelMouse.g=graphics; // ePixelMouse.g= center.getGraphics(); center.filterArr=ePixelMouse.filterArr; } public static void main(String[] args) { EDrawUI eDrawUI = new EDrawUI(); eDrawUI.init(); } }
2.需要的就是需要设置监听器的相关点击的作用,并获取到点击对象
public class EPixelMouse implements ActionListener { //定义画笔 public Graphics g; //定义方点击按钮所获取得到的操作名称 public String name; //设置存储滤镜效果的数组,设置为1000种效果 public EPixelFilter[] filterArr = new EPixelFilter[1000]; //操作数下标 public int index = 0; //判断是否需要旋转的操作数 public int flag=0; //当前滤镜效果 public EPixelFilter ep; //鼠标监听器 public void actionPerformed(ActionEvent e) { String name = e.getActionCommand(); System.out.println("name:" + name); //加载图片(I/O) //图片路径 String path = "C:\\qyl4.jpg"; String path1 = "C:\\qyl3.jpg"; //调用getImagePixel方法,把他存储的数据里面 int[][] pixelArr = getImagePixel(path); //调用getImagePixel方法,把他存储的数据里面 int[][] pixel1Arr = getImagePixel(path1); //将其绘制出来,通过点击的方法所获取的不同信息进行 if (name.equals("原图")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("马赛克")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if(name.equals("灰度")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("油画")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("素描")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("浮雕")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("怀旧")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("梦幻")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("红外扫描")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("特殊1")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("特殊2")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("珠纹")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("合并")){ ep = new EPixelFilter(pixelArr,pixel1Arr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("卷积")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); flag=1; //把滤镜效果保存在数组中 filterArr[index++]=ep; } if (name.equals("左旋转90度")){ ep = new EPixelFilter(pixelArr,name); ep.filter(g); //把滤镜效果保存在数组中 filterArr[index++]=ep; } } //创造getImagePixel方法 public int[][] getImagePixel(String path) { File file = new File(path); // ImageIO:对图片文件进行I/O // 获取缓冲图片 BufferedImage buffImage = null; try { buffImage = ImageIO.read(file); } catch (IOException e1) { e1.printStackTrace(); } int w = buffImage.getWidth(); int h = buffImage.getHeight(); int[][] pixelArr = new int[w][h]; // 获取图片中的每一个像素值保存到二维数组中 for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { int pixel = buffImage.getRGB(i, j); pixelArr[i][j] = pixel; } } return pixelArr; } }
3.主要就是相关滤镜算法的编写,并对其封装调用
public class EPixelFilter { //保存当前图片数据的存储 public int[][] pixelArr; public int[][] pixel1Arr; //通过name来识别滤镜效果 public String name; //设置方法来传递滤镜效果的存储滤镜名称的参数 public EPixelFilter(int[][] pixelArr, String name) { this.pixelArr = pixelArr; this.name = name; } public EPixelFilter(int[][] pixelArr,int[][] pixel1Arr, String name) { this.pixelArr = pixelArr; this.pixel1Arr=pixel1Arr; this.name = name; } //创造方法来点击调用,注意什么时候的值可以不同类别中互相传送 public void filter(Graphics g) { System.out.println("name:" + name); if (name.equals("原图")) { drawY(g); } if (name.equals("马赛克")) { drawMSK(g); } if (name.equals("灰度")) { drawHH(g); } if (name.equals("油画")) { drawYH(g); } if (name.equals("素描")) { drawSM(g); } if (name.equals("浮雕")) { drawFD(g); } if (name.equals("怀旧")) { drawHJ(g); } if (name.equals("梦幻")) { drawMH(g); } if (name.equals("红外扫描")) { drawHW(g); } if (name.equals("特殊1")) { drawT1(g); } if (name.equals("特殊2")) { drawT2(g); } if (name.equals("珠纹")) { drawZW(g); } if (name.equals("卷积")) { drawJJ(g,pixelArr,kernel); } if (name.equals("合并")) { drawHB(g); } if (name.equals("左旋转90度")) { rotate(g); } } //创造不同点击所对应的作用,写出对应的算法 //原图,首先创缓冲图片,Buffermage //开始写算法 public void drawY(Graphics g) { // 创建缓冲图片:BufferedImage BufferedImage buff = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_RGB); // 获取缓冲区画笔 Graphics buffG = buff.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { // 像素值 int pixel = pixelArr[i][j]; // 马赛克,灰度,底片 // 转成Color对象,设置给画笔,画出像素点 Color color = new Color(pixel); // 把像素点画在缓冲图片上 buffG.setColor(color); buffG.drawLine(i, j, i, j); } } // 把缓冲图片画在界面上 g.drawImage(buff, 0, 0, null); } public void drawMSK(Graphics g) { // 创建缓冲图片:BufferedImage BufferedImage buff = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_RGB); // 获取缓冲区画笔 Graphics buffG = buff.getGraphics(); for (int i = 0; i < pixelArr.length; i += 10) { for (int j = 0; j < pixelArr[0].length; j += 10) { // 像素值 int pixel = pixelArr[i][j]; // 把像素点画在缓冲图片上 buffG.setColor(new Color(pixel)); buffG.fillRect(i, j, 10, 10); } } // 把缓冲图片画在界面上 g.drawImage(buff, 0, 0, null); } public void drawHH(Graphics g) { // 创建缓冲图片:BufferedImage BufferedImage buff = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_RGB); // 获取缓冲区画笔 Graphics buffG = buff.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { // 像素值 int pixel = pixelArr[i][j]; // 马赛克,灰度,底片 // 转成Color对象,设置给画笔,画出像素点 Color color = new Color(pixel); // 取出三原色 int red = color.getRed(); int blue = color.getBlue(); int green = color.getGreen(); // 算法平均值 int num = (red + blue + green) / 3; Color pixelColor = new Color(num, num, num); // 把像素点画在缓冲图片上 buffG.setColor(pixelColor); buffG.drawLine(i, j, i, j); } } // 把缓冲图片画在界面上 g.drawImage(buff, 0, 0, null); } public void drawYH(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buffG = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { int pixel = pixelArr[i][j]; Color color = new Color(pixel); buffG.setColor(color); Random random = new Random(); int r = random.nextInt(20) + 5; buffG.fillOval(i, j, r, r); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public void drawSM(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i += 2) { for (int j = 0; j < pixelArr[0].length; j += 2) { int pixel = pixelArr[i][j]; Color color = new Color(pixel); int b = color.getBlue(); if (b < 180) { buff.setColor(Color.black); } else { buff.setColor(Color.WHITE); } buff.fillOval(i, j, 4, 4); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public void drawFD(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { int pixel = pixelArr[i][j]; pixel = (int) (pixel * 0.9848); Color color = new Color(pixel); buff.setColor(color); buff.drawLine(i, j, i, j); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public void drawHJ(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { int pixel = pixelArr[i][j]; pixel = (int) (pixel * 0.99899); Color color = new Color(pixel); buff.setColor(color); buff.drawLine(i, j, i, j); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public void drawMH(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { int pixel = pixelArr[i][j]; Color color = new Color(pixel); int b = color.getBlue(); int a = color.getRed(); int c = color.getGreen(); Color color1 = new Color(b, a, c); buff.setColor(color1); buff.drawLine(i, j, i, j); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public void drawHW(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { int pixel = pixelArr[i][j]; pixel = (int) (pixel * 0.95899); Color color = new Color(pixel); buff.setColor(color); buff.drawLine(i, j, i, j); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public void drawT1(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { int pixel = pixelArr[i][j]; Color color = new Color(pixel); int b = color.getBlue(); if (b > 200) { buff.setColor(Color.black); } else { pixel = (int) (pixel * 0.988); Color color1 = new Color(pixel); buff.setColor(color1); } buff.drawLine(i, j, i, j); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public void drawT2(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i++) { for (int j = 0; j < pixelArr[0].length; j++) { int pixel = pixelArr[i][j]; Color color = new Color(pixel); int b = color.getGreen(); if (b > 200) { buff.setColor(Color.cyan); } else { Color color1 = new Color(50, 100, 150); buff.setColor(color1); } buff.drawLine(i, j, i, j); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public void drawZW(Graphics g) { //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixelArr.length, pixelArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixelArr.length; i += 8) { for (int j = 0; j < pixelArr[0].length; j += 8) { int pixel = pixelArr[i][j]; Color color = new Color(pixel); buff.setColor(color); buff.fillOval(i, j, 8, 8); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } float[][]kernel={{0,-1,0},{-1,1.99f,1},{0,-1,0}}; float[][]kernel1={{0.8f,0,0.8f},{-1,1.8f,-1},{-1,0.8f,-1}}; float[][]kernel2={{0,-1,0},{-1,4,-1},{0,-1,0}}; float[][]kernel3={{-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1},{-1,-1,25,-1,-1},{-1,-1,-1,-1,-1},{-1,-1,-1,-1,-1}}; public void drawJJ(Graphics g,int[][]pixelArr,float[][]kernel) { int[][] pixel=juanji(g, pixelArr, kernel); //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(pixel.length, pixel[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < pixel.length; i ++) { for (int j = 0; j < pixel[0].length; j ++) { int pix=pixel[i][j]; Color color=new Color(pix,pix,pix); buff.setColor(color); buff.fillRect(i,j,1,1); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } public int[][]juanji(Graphics g,int[][]pixelArr,float[][]kernel){ //保存卷积计算的数据大小与kernel大小一致 int[][]tem=new int[kernel.length][kernel[0].length]; int w= pixelArr[0].length-kernel[0].length+1; int h= pixelArr.length-kernel.length+1; //保存运算时候的有效数据,两个矩阵相乘,遍历卷积核 int[][]juanji=new int[h][w]; for (int i=0;i<h;i++){ for (int j=0;j<w;j++){ for (int m=0;m< kernel.length;m++){ for (int n=0;n< kernel[0].length;n++){ //把矩阵相乘后的值保存到tem数组当中 tem[m][n]=(int) ((pixelArr[i+m][j+n])*(kernel[m][n])); } } //把tem数组中保存的数据进行相加 int num=0; for (int y=0;y<kernel.length;y++){ for (int z=0;z<kernel[0].length;z++){ num+=tem[y][z]; } } //把num限制在一个字节当中 if (num<0)num=0; if (num>255)num=255; juanji[i][j]=num; } } return juanji; } public void drawHB(Graphics g){ int w=Math.min(pixelArr.length, pixel1Arr.length); int h=Math.min(pixelArr[0].length,pixel1Arr[0].length); //创建缓冲图片BufferImage BufferedImage bufferedImage=new BufferedImage(w,h,BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff=bufferedImage.getGraphics(); for (int i=0;i<w;i++){ for (int j=0;j<h;j++){ int pixel=pixelArr[i][j]; int pixel1=pixel1Arr[i][j]; Color color=new Color(pixel); Color color1=new Color(pixel1); int r=(int) (color.getRed()*0.5+color1.getRed()*0.5); int a=(int) (color.getGreen()*0.5+color1.getGreen()*0.5); int b=(int) (color.getBlue()*0.5+color1.getBlue()*0.5); Color color2=new Color(r,a,b); buff.setColor(color2); buff.drawLine(i,j,i,j); } } // 把缓冲图片画在界面上 g.drawImage(bufferedImage, 0, 0, null); } //设置旋转方法 // public void rotate (Graphics g){ // int[][] newArr = new int[pixelArr[0].length][pixelArr.length]; // for (int i = 0, n = pixelArr.length - 1; i < pixelArr.length; i++, n--) { // for (int j = 0; j < pixelArr[0].length; j++) { // newArr[j][n] = pixelArr[i][j]; // } // } // } // public void rotate (Graphics g){ // //创建缓冲图片BufferImage // BufferedImage bufferedImage=new BufferedImage(pixelArr.length,pixelArr[0].length,BufferedImage.TYPE_INT_BGR); // //获取缓冲区画笔 // Graphics buff=bufferedImage.getGraphics(); // int h= pixelArr.length; // int w=pixelArr[0].length; // int nh=w; // int nw=h; // int[][]newArr=new int[nh][nw]; // for (int i=0;i<nw;i++){ // for (int j=0;j<nh;j++){ // newArr[i][j]=pixelArr[h-j-1][i]; // } // } public void rotate (Graphics g){ int[][] newArr = new int[pixelArr[0].length][pixelArr.length]; for (int i = 0, n = pixelArr.length - 1; i < pixelArr.length; i++, n--) { for (int j = 0; j < pixelArr[0].length; j++) { newArr[j][n] = pixelArr[i][j]; } } //创建缓冲图片BufferImage BufferedImage bufferedImage = new BufferedImage(newArr.length, newArr[0].length, BufferedImage.TYPE_INT_BGR); //获取缓冲区画笔 Graphics buff = bufferedImage.getGraphics(); for (int i = 0; i < newArr.length; i += 8) { for (int j = 0; j < newArr[0].length; j += 8) { int pixel = newArr[i][j]; Color color = new Color(pixel); g.setColor(color); g.fillOval(i, j, 8, 8); } } } }
4.就是对面板类,以及图像滤镜的重绘效果展示
public class EMyPanel extends JPanel { public EPixelFilter[] filterArr=null; public EPixelFilter ep=null; //重写绘制面板的方法 public void paint(Graphics g){ // 1.绘制组件本身(调用父类中的paint方法) super.paint(g); System.out.println("绘制JPanel = "+ep); //2.绘制滤镜图像效果 for (int i = 0; i < filterArr.length; i++){ EPixelFilter filter=filterArr[i]; if (filter != null) { // 还原当前的滤镜效果 filter.filter(g); } else break; } }
5.效果如图还是照常展示给大家(滤镜随机放了几个,然后有些,类似导航,工具,文件这些还未来得及添加)
6.有情况随时call我,欢迎随时交流