美颜相机的基本功能实现
基本知识
在学习了在标准绘制图形库中进行简单的递归图像实现后,我们把目光聚焦到了对于复杂图片文件的处理上,在这里需要简单介绍一下关于图像色彩的知识,以及缓存图片类的介绍。
图形色彩参数“ARGB”
在一个复杂图片文件中,不管图片的大小和整体的色彩有何等的差异,它都可以被拆分成一个个小的像素块,每个像素块具有A:透明度,R:红色参数,G:绿色参数,B:蓝色参数,由于红绿蓝三种颜色的组合可以匹配所有的颜色,所以运用这四个参数,可以标识一个像素块的颜色情况,在实际编码过程中,正向设置各个参数比较容易,问题是在一张已有的图片中获取某个像素块的四个参数比较困难,虽然有库方法getRed以及其他方法可以直接调用获取参数,但在这里我想简要介绍一下通过位运算实现对像素点颜色参数获取:
位运算
在计算机中对于一个像素点的色彩信息存储是通过一个32位8比特的二进制码存储,每两个比特存储一个颜色参数(ARGB依次按照顺序从前到后存储),当我们想分别获取A,R,G,B的参数值时,等价于获取这串二进制码中的某一部分,这里运用二进制码中原码,反码与补码的关系,可以实现对这串二进制码的部分提取(运用与运算)。如下
int num=8421504;//某一像素点二进制码对应的十进制数,像素点自带的参数
int red=(num>>16)&0xFF;//“>>"表示把操作位进行向右移动,同时低位被舍去
//0xFF指代的是二进制码全为1
//与运算逻辑是对比每一位,相同时保留数字,不同时为0,
//因此可以选出特定位置的二进制码
int green=(num>>8)&0xFF;
int blue=(num>>0)&0xFF;
缓存图片的使用
在对图片进行操作时,一般不能直接对图片进行操作,需要我们在内存中创建一个缓存图片,用来提高对像素点数据的获取,以及获取像素点参数的效率,我们通过IO流的使用实现从图片文件到缓存文件的获取:
public class BeautifyUtils {static ArrayList<Order> range=new ArrayList<>();Graphics g;public static BufferedImage getImage(String path){File file =new File(path);System.out.println(file.getPath());BufferedImage image =null;try {image= ImageIO.read(file);}catch(Exception e){e.printStackTrace();}return image;}
美颜相机的实现
首先确定,我们需要创建一个窗体和几个操作面板(主要是为了图片效果的展示和功能按钮的分区),同时,为了建立按钮与图片效果的联系,需要创建监听器和方法,这里的方法操作要传递的内容除了画笔和缓存图片以外,还需要我们对缓存图片中的颜色参数进行线性存储在一个二维数组中,以方便我们对每个像素点的遍历和颜色更改的工作,最后是关于各种滤镜的算法,由于这些算法是对颜色参数的一些简单操作,且不涉及编码上的逻辑,我们会在使用到时给予注释,其本质是对色彩知识的运用,在此不多赘述。
在这里需要说明的是,我们可以不使用二维数组进行存储参数,而是通过直接对缓存图片进行操作,在这篇文章中主要是为了理解修改像素点色参的原理,所以采用了这种形式,编码不是死的,掌握了基础可以灵活运用。
窗体与面板实现
public void IninUI(){JFrame jf=new JFrame();jf.setTitle("美颜相机");jf.setSize(1000,750);jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//添加面板JPanel imgshowPanel =new JPanel();JPanel btnPanel =new JPanel();//设置面板的相关属性imgshowPanel.setBackground(Color.GRAY);btnPanel.setBackground(Color.LIGHT_GRAY);Dimension dim=new Dimension(200,0);btnPanel.setPreferredSize(dim);//只有窗体是setsizethis.SetButton(btnPanel);jf.add(btnPanel,BorderLayout.EAST);//此处采用边框布局,即设置东南西北方向和中心位置//,顺序原则是先东西,再南北,最后剩余的在中部//注意此处是给btnPanel赋值长度,add时应该按照先btn再img的顺序,不可颠倒,否则无法布置位置jf.add(imgshowPanel,BorderLayout.CENTER);jf.setVisible(true);//gr是局部变量,需要同步进行收集变量和传出变量Graphics gr=imgshowPanel.getGraphics();BeautifyListener.bft.g=gr;//把画笔传给监听器}
监听器的实现
public class BeautifyListener extends MouseAdapter implements ActionListener {static ArrayList<Order> range=new ArrayList<>();//用于之后图片效果撤回的链表,可以暂时不管//后面会给予说明Graphics g;static BeautifyListener bft =new BeautifyListener();//注意使用到静态方法,表明创建的对象唯一BeautifyUtils beautifyUtils =new BeautifyUtils();BufferedImage buffimg=null;int [][] imgarr={};{//代码块初始化(创建对象时运行,且只调用一次)buffimg=BeautifyUtils.getImage("C:\\Users\\Lenovo\\Desktop\\Mouse.jpg");//这里的图片可以自行修改,格式是路径\\文件名.文件类型imgarr =BeautifyUtils.getImageArray(buffimg);//获取缓存图片中的参数二维数组}@Overridepublic void actionPerformed(ActionEvent e) {//功能以及判断的插入}
按钮的创建和与监听器的链接
public void SetButton(JPanel jp){String[] btnstr = {"原图","美白","马赛克","灰度","反转","油画","撤回","保存"};//注意这种方便的字符串数组运用于按钮名的创立Dimension dim =new Dimension(90,25);for (int i = 0; i < btnstr.length; i++) {JButton jb=new JButton();jb.setText(btnstr[i]);//不能直接赋值就创立对象后命名jb.setBackground(Color.WHITE);jb.setPreferredSize(dim);jb.addActionListener(BeautifyListener.bft);jp.add(jb);}}//Order 对象的建立,存储功能代号
//在监听器方法actionPerformed中:
String btnstr =e.getActionCommand();//获取鼠标点击的按钮对应的文本if(btnstr.equals("原图")){//进行判断,不用害怕这么长的代码//大部分是重复的判断语句以及对相应功能方法的调用Order order=new Order();//order.num=1;range.add(order);BeautifyUtils.drawImage(g,buffimg);}else if(btnstr.equals("美白")){Order order=new Order();//每次创立一个顺序对象order.num=2;//存储功能代号range.add(order);BeautifyUtils.drawImage02(g,imgarr);//调用功能}else if(btnstr.equals("马赛克")){Order order=new Order();order.num=3;range.add(order);BeautifyUtils.drawImage03(g,imgarr);}else if(btnstr.equals("灰度")){Order order=new Order();order.num=4;range.add(order);BeautifyUtils.drawImage04(g,imgarr);}else if(btnstr.equals("反转")){Order order=new Order();order.num=5;range.add(order);BeautifyUtils.drawImage05(g,imgarr);}else if(btnstr.equals("油画")){Order order=new Order();order.num=6;range.add(order);BeautifyUtils.drawImage06(g,imgarr);}else if(btnstr.equals("撤回")){if(range.size()<=1){//是否为空的判断JOptionPane.showMessageDialog(null,"此时不可再撤回");//设置提示窗return;}range.remove(range.size()-1);Order last=range.get(range.size()-1);//移除链表中的一个操作代号,返回到上一个操作//判断上一操作的功能if(last.num==1){BeautifyUtils.drawImage(g,buffimg);}else if(last.num==2){BeautifyUtils.drawImage02(g,imgarr);}else if(last.num==3){BeautifyUtils.drawImage03(g,imgarr);}else if(last.num==4){BeautifyUtils.drawImage04(g,imgarr);}else if(last.num==5){BeautifyUtils.drawImage05(g,imgarr);}else if(last.num==6){BeautifyUtils.drawImage06(g,imgarr);}}else if(btnstr.equals("保存")){}
功能方法
获取缓存图片二维数组
public static int[][] getImageArray(BufferedImage image){int [][]rgbArray=new int[image.getWidth()][image.getHeight()];for(int i=0;i<image.getWidth();i++){for(int j=0;j<image.getHeight();j++){int rgb=image.getRGB(i,j);rgbArray[i][j]=rgb;//这里的rgb是我们之前提及的//32位存储颜色参数的二进制码}}return rgbArray;}
原图展示
public static void drawImage(Graphics g,BufferedImage buffimg){g.drawImage(buffimg,0,0,null);//使用从监听器中传来的//画笔绘制缓存图片}
美白
public static void drawImage02(Graphics g,int [][]imgarr){Order order=new Order();order.num=2;range.add(order);//存储操作代号,用于撤回,后面不给予说明for(int i=0;i<imgarr.length;i++){for(int j=0;j<imgarr[i].length;j++){//注意二维数组的长度获取Color old=new Color(imgarr[i][j]);int red=old.getRed();int green=old.getGreen();int blue= old.getBlue();//此处同样可以采用位运算,为了方便调用方法if(red<245&&green<245&&blue<245) {//注意不要越界(最大值为255,三者越靠近255,颜色越亮)Color now = new Color(red + 10, green + 10, blue + 10);//美白效果的rgb处理,整体调亮g.setColor(now);//注意此处必须先创立颜色对象}else{//越界处理g.setColor(old);}g.fillRect(i,j,1,1);}}}
马赛克效果
public static void drawImage03(Graphics g,int [][]imgarr){Order order=new Order();order.num=3;range.add(order);for(int i=0;i<imgarr.length;i+=5){for(int j=0;j<imgarr[i].length;j+=5){Color c =new Color(imgarr[i][j]);int red=c.getRed();int green=c.getGreen();int blue=c.getBlue();Color co =new Color(red,green,blue);g.setColor(co);g.fillRect(i,j,5,5);//放大像素块,达到马赛克效果}}}
灰白化
public static void drawImage04(Graphics gr,int [][]imgarr) {Order order=new Order();order.num=4;range.add(order);for (int i = 0; i < imgarr.length; i++) {for (int j = 0; j < imgarr[i].length; j++) {Color old = new Color(imgarr[i][j]);int r = old.getRed();int g = old.getGreen();int b = old.getBlue();int sum = (r + g + b) / 3;//平均色彩参数Color now = new Color(sum, sum, sum);//灰白化gr.setColor(now);gr.fillRect(i, j, 1, 1);}}}
反转
public static void drawImage05(Graphics gr,int [][]imgarr){Order order=new Order();order.num=5;range.add(order);for(int i=0;i<imgarr.length;i++){for(int j=0;j<imgarr[i].length;j++){Color old =new Color(imgarr[i][j]);int r= old.getRed();int g=old.getGreen();int b=old.getBlue();Color now =new Color(255-r,255-g,255-b);gr.setColor(now);gr.fillRect(i,j,1,1);}}}
油画
public static void drawImage06(Graphics gr,int [][]imgarr){Order order=new Order();order.num=6;range.add(order);for(int i=0;i<imgarr.length;i+=3){for(int j=0;j<imgarr[i].length;j+=3){Color now =new Color(imgarr[i][j]);gr.setColor(now);Random ran =new Random(66);//获取像素块长宽的随机值,//达到油画效果,虽然个人觉得与马赛克区别不大gr.fillRect(i,j,ran.nextInt(5)+3,ran.nextInt(5)+3);}}}
}
实现的效果
原图
灰白化
反转(慎看)
油画
至此对于美颜相机的简单功能的实现就完成了,对于功能过程中的卡顿和是图片的闪卡问题,我们将在之后进一步优化。