【树莓派】使用JavaCV1.5.2调用摄像头,自写的摄像头控件

已更新JavaCV1.5.3版本:https://blog.csdn.net/ap114/article/details/105531743


做毕设要用到摄像头,可是树莓派上的摄像头就那么几种,jmf、webcam、javacv,前两个都老掉牙了,windows上还行,树莓派上各种不得劲,armhf平台还有奇奇怪怪的问题,javacv又特别庞大,真是太难选择了,为啥java用个摄像头就这么麻烦。各种爬坑在此做下笔记,本帖阐述了如何使用java在树莓派上使用opencv开发摄像头。

因为是开发树莓派桌面应用,所以开发swing我选择的IDE只有Netbeans用着最顺手。我的版本是8.2。

树莓派上的java版本是1.8.0_231,安装见https://blog.csdn.net/ap114/article/details/102757478


1.下载jar

【树莓派】使用JavaCV1.5.2调用摄像头,自写的摄像头控件_第1张图片

本项目使用的是javacv1.5.2,首先下载jar,github地址:https://github.com/bytedeco/javacv/releases或者见文末的项目打包链接,里面附带图中的jar

【树莓派】使用JavaCV1.5.2调用摄像头,自写的摄像头控件_第2张图片

为了方便在windows上调试,所以同时导入了windows平台包和armhf平台包。

2.导入jar

【树莓派】使用JavaCV1.5.2调用摄像头,自写的摄像头控件_第3张图片

3.导入类

查阅多方资料特地写了一个摄像头面板类,本摄像头面板可以使用VideoCapture和OpenCVFrameGrabber两种方式读取摄像头

readType =0 就使用VideoCapture方式获取摄像头数据

readType =1 就使用OpenCVFrameGrabber方式获取摄像头数据

deviceNumber就是摄像头编号,默认为0,就是笔记本上的摄像头

deviceStr是摄像头字符串编号,例如/dev/video0

JPanel parent主要是获取父面板的宽高,用于设置画板宽高,绘制摄像头的数据

主要的功能如下

【树莓派】使用JavaCV1.5.2调用摄像头,自写的摄像头控件_第4张图片

【树莓派】使用JavaCV1.5.2调用摄像头,自写的摄像头控件_第5张图片

CameraJPanel类就是一个摄像头的面板控件,随意发挥用途。

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.SwingUtilities;
import javax.swing.JPanel;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_videoio.VideoCapture;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.javacv.FrameGrabber.Exception;
import org.bytedeco.javacv.OpenCVFrameGrabber;
/**
 *树莓派摄像头面板组件
 *原贴地址:https://blog.csdn.net/ap114/article/details/105250320
 * @author 钟离彧
 */
public class CameraJPanel extends JPanel{
    //摄像头打开关闭标志
    private boolean open = false;
    //摄像头暂停标志
    private boolean pause = false;
    //摄像头画板
    private final Canvas canvas = new Canvas();
    //当前帧是否绘制完成的锁,若不加锁会造成阻塞
    private boolean isDrawfinish = false;
    //通过OpenCVFrameGrabber方式过得每帧图像
    private OpenCVFrameGrabber grabber;
    //通过VideoCapture方式过得每帧图像
    private VideoCapture capture;
    //OpenCV_VideoCapture格式转换Java图像格式
    private final Java2DFrameConverter java2dFrameConverter = new Java2DFrameConverter(); 
    //OpenCV_OpenCVFrameGrabber格式转换Java图像格式
    private static OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
    //当前每帧的图像
    private BufferedImage bufferedImage = null;
    //通过Opencv方式的摄像头获取到的帧
    private Mat mat = new Mat();
    //不停读取摄像头并刷新画板数据的后台线程
    private Thread refreshCanvasThread;
    //读取方式0=VideoCapture方式读摄像头,1=OpenCVFrameGrabber方式
    private int readType = 0;
    private int deviceNumber = 0;
    private String deviceStr = "";
    public CameraJPanel(JPanel parent,int deviceNumber){
        this(parent);
        this.deviceNumber = deviceNumber;
    }
    public CameraJPanel(JPanel parent){
        this.setBounds(0, 0, parent.getWidth(), parent.getHeight());
        initOpenCV();
    }
    public CameraJPanel(JPanel parent,int deviceNumber,int readType){
        this(parent);
        this.deviceNumber = deviceNumber;
        if(readType!=0 && readType!=1)readType=0;
        this.readType = readType;
        initOpenCV();
    }
    //例如:/dev/video0
    public CameraJPanel(JPanel parent,String deviceStr){
        this(parent);
        this.deviceStr = deviceStr;
    }
    public CameraJPanel(){
        this.setBounds(0, 0, 640, 480);
        initOpenCV();
    }
    public CameraJPanel(int width,int height){
        this.setBounds(0, 0, width, height);
        initOpenCV();
    }
    private void initOpenCV(){
        canvas.setBounds(0, 0, this.getWidth(), this.getHeight());
        this.add(canvas);
        openCamera();
    }
    //打开摄像头
    public void openCamera(){
        this.pause = false;
        this.open = true;
        if("".equals(deviceStr)){
            if(readType == 0){
                capture = new VideoCapture(deviceNumber);
            }else if(readType == 1){
                grabber = new OpenCVFrameGrabber(deviceNumber);  
                try {
                    grabber.start();   //开始获取摄像头数据
                } catch (Exception ex) {
                    System.out.println(ex.getMessage());
                    return;
                }
            }
        }else{
            if(readType == 0){
                capture = new VideoCapture(deviceStr);
            }else if(readType == 1){
                grabber = new OpenCVFrameGrabber(deviceStr);  
                try {
                    grabber.start();   //开始获取摄像头数据
                } catch (Exception ex) {
                    System.out.println(ex.getMessage());
                    return;
                }
            }
        }
        //设置刷新画板线程
        refreshCanvasThread = new Thread(){
            public void run() {
                while(open){
                    while(pause)thread_sleep(1);//发现暂停锁,进入暂停
                    //画板绘图是否完成标志
                    isDrawfinish = false;
                    //跨线程更新UI
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            if(readType == 0){
                                getImageByVideoCapture();
                            }else{
                                getImageByOpenCVFrameGrabber();
                            }
                            isDrawfinish = true;
                        }
                    });
                    //上次的绘图没有完成就不允许继续读取摄像头
                    while(!isDrawfinish)thread_sleep(1);
                }
            }
        };
        refreshCanvasThread.start();
    }
    //暂停绘画,但不会停止摄像头
    public void pauseCamera(){
        this.pause = true;
    }
    //继续绘画
    public void continueCamera(){
        this.pause = false;
    }
    //关闭摄像头
    public void closeCamera(){
        this.pause = false;
        this.open = false;
        if(readType == 0){
            capture.close();
        }else if(readType == 1){
            try {
                grabber.stop();
                grabber.close();
                grabber.release();
            } catch (Exception ex) {
                System.out.println(ex.getMessage());
            }
        }
    }
    //清除画板
    public void clearCanvas(Color c){
        canvas.getGraphics().setColor(c);
        canvas.getGraphics().fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
    }
    //清除画板
    public void clearCanvas(){
        canvas.getGraphics().clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
    }
    //通过VideoCapture获取当前帧
    public BufferedImage getImageByVideoCapture(){
        boolean have = capture.read(mat);
        bufferedImage = null;
        if(have){
            Frame convertFrame2 = converter.convert(mat);
            bufferedImage= java2dFrameConverter.convert(convertFrame2);
            Image newimage = bufferedImage.getScaledInstance(getWidth(), getHeight(), Image.SCALE_FAST);
            canvas.getGraphics().drawImage(newimage, 0, 0,null);
        }
        return bufferedImage;
    }
    //通过OpenCVFrameGrabber获取当前帧
    public BufferedImage getImageByOpenCVFrameGrabber(){
        bufferedImage = null;
        try {
            if(this.open==false)return null;
            Frame convertFrame2 = grabber.grabFrame();
            bufferedImage= java2dFrameConverter.convert(convertFrame2);
            Image newimage = bufferedImage.getScaledInstance(getWidth(), getHeight(), Image.SCALE_FAST);
            canvas.getGraphics().drawImage(newimage, 0, 0,null);
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
        return bufferedImage;
    }
    //保存当前帧,默认class路径/时间戳.png
    public String saveImage(){
        String savepath= getClass().getResource("").getPath() + System.currentTimeMillis() + ".png";
        //一个奇怪的BUG,通过上行代码获取当前运行路径会以/开头,例如"/C:/img/1.png"
        if(savepath.charAt(0) == '/'){
            savepath = savepath.substring(1, savepath.length());
        }
        return saveImage(savepath);
    }
    //保存当前帧
    public String saveImage(String path) {
        try {
            boolean isSave = false;
            if(readType==1)mat = converter.convertToMat(grabber.grabFrame());
            isSave = opencv_imgcodecs.imwrite(path, mat);
            if(isSave == false)path=""; //保存失败返回空
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
         System.out.println(path);
        return path;
    }
    private void thread_sleep(int time){
        try {
            Thread.sleep(time);//time毫秒刷新一次图像
        } catch (InterruptedException ex) {
           System.out.println(ex.getMessage());
        }
    }
}

使用方法(伪代码

JPanel jp1;//你自己的组件,可以随意拖动设计
private CameraJPanel camera;//摄像头控件

//构造函数
public NewJFrame() {
	initComponents();
}
//初始化组件
private void initComponents() {
	jp1 = new javax.swing.JPanel();
	jp1.setBackground(new Color(0, 0, 0));//整个黑色背景高大上
	camera = new CameraJPanel(jp1);
	jp1.add(camera);
}
//保存图片
private void btn_saveOnclick(ActionEvent evt) {                                         
	System.out.println(camera.saveImage());
}
//暂停绘画,摄像头灯会亮着,不会关闭
private void btn_pauseOnclick(ActionEvent evt) {     
	camera.pauseCamera();
} 
//继续绘画
private void btn_continueOnclick(ActionEvent evt) {    
	camera.continueCamera();
}                                            
//关闭摄像头
private void btn_closeOnclick(ActionEvent evt) {       
	camera.closeCamera();
	camera.clearCanvas(Color.BLACK);
}                                         
//开启摄像头
private void btn_openOnclick(ActionEvent evt) {       
	camera.openCamera();
}

 

效果

【树莓派】使用JavaCV1.5.2调用摄像头,自写的摄像头控件_第6张图片


Github地址:做完毕设就传上来

项目已打包送给各位伸手党,如果对您有用,您点个赞就行了,转载请注明出处。

Netbeans打包项目下载:https://download.csdn.net/download/ap114/12293162

项目中的控件未和博文控件同步,请以博文贴出的控件代码为准

你可能感兴趣的:(树莓派,Java)