当前位置: 首页 > 知识库问答 >
问题:

java 用openCV定时拍照画面有变化就报错,什么问题,怎么解决?

小牛23015
2024-12-04

java 用openCV定时每10s拍一次照片,当摄像头前没有变化时,都能拍照成功
当摄像头前有变化,就会报错,先报错grab() Error: Could not grab frame. (Has start() been called?),
然后我重新grabber.start()又会报错read() Error: Could not read frame in start().
image.png

package org.jeecg.bussiness.pressMachine.model.frontCamera.service;

//import com.github.sarxos.webcam.Webcam;
//import com.github.sarxos.webcam.WebcamResolution;
//import com.github.sarxos.webcam.WebcamUtils;
//import com.github.sarxos.webcam.util.ImageUtils;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacv.*;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.IplImage;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Scalar;
import org.jeecg.bussiness.pressMachine.model.frontCamera.FrontCameraService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

import static org.bytedeco.opencv.global.opencv_core.cvFlip;

@Slf4j
@Service(value = "frontCameraService")
public class FrontCameraServiceImpl implements FrontCameraService {

    @Value("${testBlock.cameraDownloadPathPrefix}")
    private String cameraDownloadPathPrefix;
//    static Webcam webcam = null;

    private LocalDateTime lastPictureTime = LocalDateTime.now();
//    static {
//        try {
//            webcam = Webcam.getDefault();
//            // 设置摄像头捕获的图像尺寸为VGA
//            webcam.setViewSize(WebcamResolution.VGA.getSize());
//            webcam.getDevice().setResolution(WebcamResolution.FHD.getSize());
//        }catch (Exception e){
//            log.error(e.getMessage(),e);
//        }
//    }

    private static int count = 0;


    @Override
    public synchronized String takePhoto() {
//        this.openWebcam();
        String fileFolderName = this.getAndCreatePhotoPathUlr(null);
        fileFolderName +="/"+System.currentTimeMillis()+"."+IMG_TYPE;
//        WebcamUtils.capture(webcam, fileFolderName, ImageUtils.FORMAT_JPG);
//        new GrabImageFromCamera().action(10);

        this.openPicture();
        try {
            grabAndOutput(fileFolderName);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            safeRelease();
            count = 999;
            fileFolderName="";
        }
        return fileFolderName ;
    }

    private void openPicture(){
        try {
            if(count >= 300 || grabber == null || lastPictureTime.compareTo(LocalDateTime.now().plusHours(-1)) <=0 ){
                safeRelease();
                init();
                count = 0;
                log.error("重启摄像头");
            }
        } catch (Exception e) {
            log.error(e.getMessage(),e);
        }
        count++;
        lastPictureTime = LocalDateTime.now();
    }

//    private synchronized void openWebcam(){
//        if(count >= 300){
//            webcam.close();
//            try {
//                Thread.sleep(1000);
//            } catch (InterruptedException e) {
//                log.error(e.getMessage(),e);
//            }
//            webcam = Webcam.getDefault();
//            // 设置摄像头捕获的图像尺寸为VGA
//            webcam.setViewSize(WebcamResolution.VGA.getSize());
//            webcam.getDevice().setResolution(WebcamResolution.FHD.getSize());
//            count = 0;
//            log.error("重新开启摄像头");
//        }
//        count++;
//    }

    private String getAndCreatePhotoPathUlr(String testBlockId){
        String fileFolderName = cameraDownloadPathPrefix+"/frontCamera/";
        File file=new File(fileFolderName);
        if  (!file .exists()  && !file.isDirectory()) {
            file .mkdir();
        }
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        fileFolderName = fileFolderName+"/"+dateStr+"/";
        file=new File(fileFolderName);
        if  (!file .exists()  && !file.isDirectory()) {
            file .mkdir();
        }

        if(testBlockId != null){
            fileFolderName = fileFolderName+"/"+ testBlockId;
            file=new File(fileFolderName);
            if  (!file .exists()  && !file.isDirectory()) {
                file .mkdir();
            }
        }

        return fileFolderName;
    }



    //===============================新版本拍照开始======================

    /**
     * 摄像头序号,如果只有一个摄像头,那就是0
     */
    protected static final int CAMERA_INDEX = 0;

    /**
     * 帧抓取器
     */
    protected FrameGrabber grabber;

    /**
     * 输出帧率
     */
    @Getter
    private final double frameRate = 30;

    /**
     * 摄像头视频的宽
     */
    @Getter
    private final int cameraImageWidth = 1280;

    /**
     * 摄像头视频的高
     */
    @Getter
    private final int cameraImageHeight = 720;

    /**
     * 转换器
     */
    private final OpenCVFrameConverter.ToIplImage openCVConverter = new OpenCVFrameConverter.ToIplImage();

    /**
     * 实例化、初始化输出操作相关的资源
     */
    protected void initOutput() throws Exception {
        // 啥也不用做
    }

    /**
     * 输出
     */
    protected void output(Frame frame,String filePath) throws Exception {
        // 图片的保存位置
        String imagePath = filePath;

        // 把帧对象转为Image对象
        BufferedImage bufferedImage = converter.getBufferedImage(frame);

        // 保存图片
        ImageIO.write(bufferedImage, IMG_TYPE, new FileOutputStream(imagePath));

        log.info("保存完成:{}", imagePath);
    }

    /**
     * 释放输出操作相关的资源
     */
    protected void releaseOutputResource() {
        // 啥也不用做
    }

    /**
     * 两帧之间的间隔时间
     * @return
     */
    protected int getInterval() {
        // 假设一秒钟15帧,那么两帧间隔就是(1000/15)毫秒
        return (int)(1000/ frameRate);
    }

    /**
     * 实例化帧抓取器,默认OpenCVFrameGrabber对象,
     * 子类可按需要自行覆盖
     * @throws FFmpegFrameGrabber.Exception
     */
    protected void instanceGrabber() throws FrameGrabber.Exception {
        grabber = new OpenCVFrameGrabber(CAMERA_INDEX);
    }

    /**
     * 用帧抓取器抓取一帧,默认调用grab()方法,
     * 子类可以按需求自行覆盖
     * @return
     */
    protected Frame grabFrame() throws FrameGrabber.Exception {
        return grabber.grab();
    }

    /**
     * 初始化帧抓取器
     * @throws Exception
     */
    protected void initGrabber() throws Exception {
        // 实例化帧抓取器
        if(grabber !=null){
            grabber.close();
            grabber.release();
        }
        instanceGrabber();

        // 摄像头有可能有多个分辨率,这里指定
        // 可以指定宽高,也可以不指定反而调用grabber.getImageWidth去获取,
        grabber.setImageWidth(cameraImageWidth);
        grabber.setImageHeight(cameraImageHeight);
        grabber.setMaxDelay(5000);
        // 开启抓取器
        grabber.start();
    }

    /**
     * 预览和输出
     * @param
     * @throws Exception
     */
    private void grabAndOutput(String picturePath) throws Exception {
        // 添加水印时用到的时间工具
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

//        long endTime = System.currentTimeMillis() + 1000L *grabSeconds;

        // 两帧输出之间的间隔时间,默认是1000除以帧率,子类可酌情修改
        int interVal = getInterval();

        // 水印在图片上的位置
//        org.bytedeco.opencv.opencv_core.Point point = new org.bytedeco.opencv.opencv_core.Point(15, 35);

        Frame captureFrame;
        IplImage img;
        Mat mat;

        // 超过指定时间就结束循环
//        while (System.currentTimeMillis()<endTime) {
            // 取一帧
            captureFrame = grabFrame();
//            grabber.stop();


        if (null==captureFrame) {
                log.error("帧对象为空");
                return;
//                break;
            }

            // 将帧对象转为IplImage对象
            img = openCVConverter.convert(captureFrame);

            // 镜像翻转
            cvFlip(img, img, 1);

            // IplImage转mat
            mat = new Mat(img);

            // 在图片上添加水印,水印内容是当前时间,位置是左上角
//            opencv_imgproc.putText(mat,
//                    simpleDateFormat.format(new Date()),
//                    point,
//                    opencv_imgproc.CV_FONT_VECTOR0,
//                    0.8,
//                    new Scalar(0, 200, 255, 0),
//                    1,
//                    0,
//                    false);

            // 子类输出
            output(openCVConverter.convert(mat),picturePath);

            // 适当间隔,让肉感感受不到闪屏即可
            if(interVal>0) {
                Thread.sleep(interVal);
            }
//        }
        mat.release();
        log.info("输出结束");
    }

    /**
     * 释放所有资源
     */
    private void safeRelease() {
        try {
            // 子类需要释放的资源
            releaseOutputResource();
        } catch (Exception exception) {
            log.error("do releaseOutputResource error", exception);
        }

        if (null!=grabber) {
            try {
                grabber.close();
            } catch (Exception exception) {
                log.error("close grabber error", exception);
            }
        }
    }

    /**
     * 整合了所有初始化操作
     * @throws Exception
     */
    private void init() throws Exception {
        long startTime = System.currentTimeMillis();

        // 设置ffmepg日志级别
        avutil.av_log_set_level(avutil.AV_LOG_INFO);
        FFmpegLogCallback.set();

        // 实例化、初始化帧抓取器
        initGrabber();

        // 实例化、初始化输出操作相关的资源,
        // 具体怎么输出由子类决定,例如窗口预览、存视频文件等
        initOutput();

        log.info("初始化完成,耗时[{}]毫秒,帧率[{}],图像宽度[{}],图像高度[{}]",
                System.currentTimeMillis()-startTime,
                frameRate,
                cameraImageWidth,
                cameraImageHeight);
    }

    /**
     * 执行抓取和输出的操作
     */
    public void action(int grabSeconds) {
        try {
            // 初始化操作
            init();
            // 持续拉取和推送
            log.error("-",new Date());
//            grabAndOutput(grabSeconds);
            log.error("-",new Date());
        } catch (Exception exception) {
            log.error("execute action error", exception);
        } finally {
            // 无论如何都要释放资源
            safeRelease();
        }
    }
    //==============实现类 开始====
    /**
     * 当前进程已经存储的图片数量
     */
    private int saveNums = 0;

    // 转换工具
    private Java2DFrameConverter converter = new Java2DFrameConverter();

    // 图片格式
    private final static String IMG_TYPE = "jpg";
    //==============实现类 结束====

    //===============================新版本拍照结束======================
}

共有1个答案

海嘉赐
2024-12-04

1.保持摄像头持续运行: 可以在程序启动时初始化摄像头,并保持摄像头持续运行,而不是每次拍照前重新初始化。这样可以避免频繁的初始化操作。
2.使用线程处理拍照任务: 使用单独的线程来处理拍照任务,这样可以在后台持续抓取帧,而主线程可以处理其他任务。
3.优化摄像头参数: 检查并优化摄像头的参数设置,确保摄像头在启动时不会加载过多的资源。
以下是一个示例代码:

import org.bytedeco.javacv.*;
import org.bytedeco.opencv.opencv_core.IplImage;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import static org.bytedeco.opencv.global.opencv_core.cvFlip;

public class FrontCameraServiceImpl {

    private static final int CAMERA_INDEX = 0;
    private OpenCVFrameGrabber grabber;
    private LocalDateTime lastPictureTime = LocalDateTime.now();
    private static int count = 0;
    private final static String IMG_TYPE = "jpg";
    private String cameraDownloadPathPrefix = "/path/to/save/images";

    public FrontCameraServiceImpl() throws FrameGrabber.Exception {
        grabber = new OpenCVFrameGrabber(CAMERA_INDEX);
        grabber.start();
    }

    public synchronized String takePhoto() {
        String fileFolderName = getAndCreatePhotoPathUlr(null);
        fileFolderName += "/" + System.currentTimeMillis() + "." + IMG_TYPE;

        try {
            grabAndOutput(fileFolderName);
        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            safeRelease();
            count = 999;
            fileFolderName = "";
        }
        return fileFolderName;
    }

    private void grabAndOutput(String fileFolderName) throws Exception {
        Frame frame = grabber.grab();
        if (frame == null) {
            throw new Exception("grab() Error: Could not grab frame. (Has start() been called?)");
        }
        OpenCVFrameConverter.ToIplImage converter = new OpenCVFrameConverter.ToIplImage();
        IplImage img = converter.convert(frame);
        if (img != null) {
            cvFlip(img, img, 1);
            BufferedImage bi = img.getBufferedImage();
            File output = new File(fileFolderName);
            ImageIO.write(bi, "jpg", output);
        }
    }

    private void safeRelease() {
        if (grabber != null) {
            try {
                grabber.stop();
                grabber.release();
            } catch (Exception e) {
                System.err.println("Error: " + e.getMessage());
            }
        }
    }

    private String getAndCreatePhotoPathUlr(String testBlockId) {
        String fileFolderName = cameraDownloadPathPrefix + "/frontCamera/";
        File file = new File(fileFolderName);
        if (!file.exists() && !file.isDirectory()) {
            file.mkdir();
        }
        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        fileFolderName = fileFolderName + "/" + dateStr + "/";
        file = new File(fileFolderName);
        if (!file.exists() && !file.isDirectory()) {
            file.mkdir();
        }

        if (testBlockId != null) {
            fileFolderName = fileFolderName + "/" + testBlockId;
            file = new File(fileFolderName);
            if (!file.exists() && !file.isDirectory()) {
                file.mkdir();
            }
        }

        return fileFolderName;
    }

    public static void main(String[] args) {
        try {
            FrontCameraServiceImpl cameraService = new FrontCameraServiceImpl();
            while (true) {
                cameraService.takePhoto();
                Thread.sleep(30000); // 每30秒拍一次照片
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 类似资料:
  • 有没有大佬来帮我看一看,是关于github actions的,我在实现一个推送代码到指定分支上时,会自动触发在远程目标服务器上面部署运行springboot项目,但是每一次运行到以下代码的时候: 它就会一直卡在这里。但是事实上,这一个项目是被正确运行了,监听端口也有信息,但是工作流中会报错: 我有一点不明白,部署成功之后,不就是应该断开ssh连接吗?有没有大佬解决一下? 因为这个地方报错,所以它工

  • 本文向大家介绍为什么会有跨域问题?怎么解决跨域?相关面试题,主要包含被问及为什么会有跨域问题?怎么解决跨域?时的应答技巧和注意事项,需要的朋友参考一下 为啥会有跨域问题 怎么解决跨域呢? 方案一 JSONP:通过script可以跨域的原理,执行服务端的回调函数 方案二 代理:nigix 或者webpack 代理 配置 方案三 CORS :"跨域资源共享",设置'Access-Control-All

  • markdown图片可以传到本地文件夹但只能传jpg,png传不了,数据库也有路径,但是不渲染出来 头像上传七牛云,密钥和域名都写得对的但点击就报400

  • vue3+vite打包的时候,报错Access is denied,请问怎么解决啊?重新装了node_modules包,用nvm试了npm的几个版本都不行

  • 问题内容: 拜托,有人可以帮我吗? 我有一个正在使用的项目,我想拍照并保存文件,但是没有实现函数。 问题答案: 结果:我实现了一个扩展JavaCameraView的新类,并为自己实现了功能takePicture。 --

  • 本文向大家介绍axios是什么?怎样使用它?怎么解决跨域的问题?相关面试题,主要包含被问及axios是什么?怎样使用它?怎么解决跨域的问题?时的应答技巧和注意事项,需要的朋友参考一下 axios 的是一种异步请求,用法和ajax类似,安装npm install axios --save 即可使用,请求中包括get,post,put, patch ,delete等五种请求方式,解决跨域可以在请求头中

  • 本文向大家介绍服务调用超时问题怎么解决?相关面试题,主要包含被问及服务调用超时问题怎么解决?时的应答技巧和注意事项,需要的朋友参考一下 dubbo在调用服务不成功时,默认是会重试两次的。  

  • webpack5 打包时候会报几个这种问题,怎么解决