已更新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
本项目使用的是javacv1.5.2,首先下载jar,github地址:https://github.com/bytedeco/javacv/releases或者见文末的项目打包链接,里面附带图中的jar
为了方便在windows上调试,所以同时导入了windows平台包和armhf平台包。
查阅多方资料特地写了一个摄像头面板类,本摄像头面板可以使用VideoCapture和OpenCVFrameGrabber两种方式读取摄像头
readType =0 就使用VideoCapture方式获取摄像头数据
readType =1 就使用OpenCVFrameGrabber方式获取摄像头数据
deviceNumber就是摄像头编号,默认为0,就是笔记本上的摄像头
deviceStr是摄像头字符串编号,例如/dev/video0
JPanel parent主要是获取父面板的宽高,用于设置画板宽高,绘制摄像头的数据
主要的功能如下
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();
}
Github地址:做完毕设就传上来
项目已打包送给各位伸手党,如果对您有用,您点个赞就行了,转载请注明出处。
Netbeans打包项目下载:https://download.csdn.net/download/ap114/12293162
项目中的控件未和博文控件同步,请以博文贴出的控件代码为准