寒假期间,一起做了一款pc端的美颜相机,在交流会期间,看到有的同学做了一些非常牛的功能添加,心血来潮,想整个视频运动追踪识别,在这和大家分析一下算法思路
1、原理很简单,就是在视频中的物体,就可以通过得到前后两张图片提供的二维数组之差,当两者之前的差值超过一个数,前后相差过大的时候,我们通过画笔去把他画出来,就可以得到我们想要的结果。
2、之前第一步遇到的问题,就是如何得到第一张图的数据,并且去和第二张图进行对比,在这里的话,我的思路是用一个数组去存储通过摄像头获取到底第一张图片,并且通过BufferedImage来缓存图片,然后重新定义一个数组缓存第二张图片的数据,就可以进行处理后的数据啦
3、代码如下(菜单栏的一些功能代码尚未给出,这里仅仅给出运动追踪识别的代码)
a.窗体界面
public class CameraUI { public void draw(){ JFrame jFrame=new JFrame(); jFrame.setTitle("视频运动追踪识别"); jFrame.setSize(800,900); jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); jFrame.setLocationRelativeTo(null); BorderLayout borderLayout=new BorderLayout(); jFrame.setLayout(borderLayout); JPanel cennter=new JPanel(); cennter.setBackground(Color.cyan); jFrame.add(cennter,BorderLayout.CENTER); //菜单栏以及相关功能的实现 //开始定义菜单,菜单栏以及相关按钮 JMenuBar jMenuBar=new JMenuBar(); jFrame.add(jMenuBar,BorderLayout.NORTH); //监听器的设置 CameraMouse cameraMouse=new CameraMouse(); //定义在菜单栏中的菜单选项 String[]name1={"运动追踪","视频","图片","拍照","特殊滤镜","编辑","帮助"}; //开辟一个menu数组来存储调用与添加 JMenu[]menu=new JMenu[name1.length]; for (int i=0;i< name1.length;i++) { JMenu jMenu = new JMenu(name1[i]); menu[i]=jMenu; jMenuBar.add(jMenu); } //子菜单中的菜单选项 String[]name={"打开","关闭","退出"}; for (int i=0;i<name.length;i++) { JMenuItem jMenuItem = new JMenuItem(name[i]); jMenuItem.addActionListener(cameraMouse); menu[0].add(jMenuItem); } //可视一定要在画笔前 jFrame.setVisible(true); cameraMouse.graphicsmouse=cennter.getGraphics(); } public static void main(String[] args) { CameraUI cameraUI=new CameraUI(); cameraUI.draw(); } }
b.监听器以及线程控制
public class CameraMouse implements ActionListener { //获取点击按钮的名字 public String name; //获取画笔 public Graphics graphicsmouse; //创造线程的对象进行调用 public CameraThread cameraThread; public void actionPerformed(ActionEvent e){ String name=e.getActionCommand(); System.out.println("正在点击的操作是:"+name); if ("打开".equals(name)){ if (cameraThread==null){ cameraThread=new CameraThread(graphicsmouse); //开始启动线程 cameraThread.start(); } }else if ("关闭".equals(name)){ cameraThread.flag=false; } } }
c.运动追踪方法实现以及相关线程代码
public class CameraThread extends Thread{ //开关控制线程 public boolean flag=true; //画笔初始化 public Graphics graphicsthread; //方法构造传送画笔 public CameraThread(Graphics graphicsthread){ this.graphicsthread=graphicsthread; } //线程的书写和使用 public void run(){ //摄像头的调动 Webcam webcam=Webcam.getDefault(); webcam.open(); System.out.println("启动线程.."+this.getName()); //利用while循环来使用线程,线程只能运行一次,结束后的线程不能重新调用 while (flag) { //获取摄像头拍到的数据 BufferedImage bufferedImage1 = webcam.getImage(); //用数组存储下来,获取第一张图片 int[][]pixel1Arr=getImagePixel(bufferedImage1); //创建缓冲图片 BufferedImage buffer=new BufferedImage(bufferedImage1.getWidth(),bufferedImage1.getHeight(),BufferedImage.TYPE_INT_BGR); //获取缓存区的画笔 Graphics grabuffer=buffer.getGraphics(); //获取摄像头拍到的数据,重新获取 BufferedImage bufferedImage2 = webcam.getImage(); //用数组存储下来,存储的目的就是与第一张已经缓存下来的图片做对比, int[][]pixel2Arr=getImagePixel(bufferedImage2); //遍历第二张图片的数组 for (int i=0;i<pixel2Arr.length;i++){ for (int j=0;j<pixel2Arr[0].length;j++){ //第二张图片的像素 int pixel2=pixel2Arr[i][j]; if ((null!=pixel2Arr)){ // 转成Color对象,设置给画笔,画出像素点 Color rc=new Color(pixel2); Color pixel1=new Color(pixel1Arr[i][j]); //前后相互比较,这里是取的G值做的差 int diff=Math.abs(rc.getGreen()- pixel1.getGreen()); if ((diff)>30) { grabuffer.setColor(Color.white); grabuffer.fillOval(i, j, 2, 2); } else if (diff<30) { grabuffer.setColor(Color.black); grabuffer.fillOval(i, j, 2, 2); } } } } //把拍到的数据画出来 graphicsthread.drawImage(buffer, 100, 100,600,600, null); } System.out.println("结束线程"); } //数据存储 public int[][] getImagePixel(BufferedImage buffImage) { 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; } }
4.视频效果图不方便截图,感兴趣的小伙伴来call我