摄像头实时检查程序,插入设备,自动显示画面,支持多个摄像头,支持拍照,照片放大缩小

news2024/9/22 19:44:13

支持的特性

  • 插入摄像头设备后,无需手动选择,自动显示摄像头画面,需要预先授权
  • 支持多个摄像头切换显示
  • 多个摄像头时支持 默认显示特定名称的摄像头
  • 支持拍照
  • 支持照片放大,缩小

显示效果

在这里插入图片描述
在这里插入图片描述

完整代码

<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>摄像头实时检测,拍照</title>
    <style type="text/css">
      body {
        text-align: center;
      }

      .flex {
        display: flex;
        flex-direction: row;
        /* align-items: center; */
        justify-content: center;
      }

      .hidden {
        display: none;
      }
    </style>
  </head>

  <body>
    <h1>摄像头实时检测</h1>
    <div class="flex">
      <div>
        <video id="videoElement" autoplay playsinline controls></video>
        <p>
          <label for="videoSource">选择摄像头:</label>
          <select id="videoSource"></select>
          <span class="hidden" id="closeCamera" onclick="stopVideoStream()">关闭摄像头</span>
          <span id="cameraStatus">摄像头状态:未连接或已被占用</span>
          <button id="captureButton" onclick="takePicture()">拍照</button>
        </p>
      </div>
      <div style="margin-left: 20px">
        <div style="border: 1px solid #ddd; box-sizing: content-box">
          <canvas id="paperCanvas" width="640px" height="480px"></canvas>
        </div>
      </div>
    </div>
    <script src="./paper-full.min.js"></script>

    <script>
      const video = document.getElementById("videoElement");
      const cameraStatus = document.getElementById("cameraStatus");
      const captureButton = document.getElementById("captureButton");
      const videoSelect = document.getElementById("videoSource");
      let mediaStream;
      let raster;

      function gotDevices(deviceInfos) {
        videoSelect.value = "";
        videoSelect.innerHTML = "";
        for (let i = 0; i !== deviceInfos.length; ++i) {
          const deviceInfo = deviceInfos[i];
          const option = document.createElement("option");
          option.value = deviceInfo.deviceId;
          if (deviceInfo.kind === "videoinput") {
            option.text = deviceInfo.label || "摄像头 " + (videoSelect.length + 1);
            videoSelect.appendChild(option);
            if (deviceInfo.label.includes("TOOCAA")) {
              videoSelect.value = deviceInfo.deviceId;
            }
          }
        }
      }

      function getStream() {
        if (window.stream) {
          window.stream.getTracks().forEach((track) => {
            track.stop();
          });
        }

        const constraints = {
          video: {
            deviceId: { exact: videoSelect.value },
          },
        };

        navigator.mediaDevices.getUserMedia(constraints).then(gotStream).catch(handleError);
      }

      function gotStream(stream) {
        window.stream = stream;
        video.srcObject = stream;

        cameraStatus.textContent = "摄像头状态:已连接";
        const videoTrack = stream.getVideoTracks()[0];
        const settings = videoTrack.getSettings();
        captureButton.style.display = "inline-block";
        console.log(settings);
      }

      function startVideoStream() {
        if (navigator.mediaDevices) {
          navigator.mediaDevices
            .getUserMedia({ video: true })
            .then(function (stream) {
              mediaStream = stream; // 保存媒体流以便后续操作
              video.srcObject = stream;
              cameraStatus.textContent = "摄像头状态:已连接";
              const videoTrack = stream.getVideoTracks()[0];
              const settings = videoTrack.getSettings();
              captureButton.style.display = "inline-block";

              console.log(settings);
            })
            .catch(function (error) {
              console.error("无法获取摄像头:", error);
              cameraStatus.textContent = "摄像头状态:未连接或已被占用";
              captureButton.style.display = "none";
            });
        }
      }

      function stopVideoStream() {
        if (mediaStream) {
          mediaStream.getTracks().forEach((track) => track.stop()); // 停止所有媒体轨道
        }
        video.srcObject = null; // 清除视频源
        cameraStatus.textContent = "摄像头状态:已断开";
        captureButton.style.display = "none";
      }

      function initPaperCanvas() {
        paper.setup("paperCanvas");

        paper.view.element.addEventListener("wheel", function (event) {
          event.preventDefault();

          // 计算缩放因子
          var delta = event.deltaY > 0 ? 0.9 : 1.1; // 向下滚动缩小视图,向上滚动放大视图

          // 鼠标位置相对于视图的当前坐标
          var mousePosition = new paper.Point(event.offsetX, event.offsetY);
          var viewPosition = paper.view.viewToProject(mousePosition);

          // 应用缩放
          paper.view.scale(delta, viewPosition);
        });

        // 鼠标拖动事件处理移动
        const tool = new paper.Tool();
        var lastPoint = null; // 上一次鼠标位置
        var dragging = false;
        var lastViewCenter;

        tool.onMouseDown = (event) => {
          lastPoint = event.point;
          dragging = true;
        };

        tool.onMouseDrag = (event) => {
          if (dragging && lastPoint) {
            lastViewCenter = paper.view.center;
            const delta = lastPoint.subtract(event.point);
            paper.view.center = paper.view.center.add(delta);

            lastPoint = event.point.add(paper.view.center.subtract(lastViewCenter));
          }
        };

        tool.onMouseUp = () => {
          // 结束拖动
          dragging = false;
        };
      }

      window.onload = () => {
        if (navigator.mediaDevices) {
          navigator.mediaDevices.enumerateDevices().then(gotDevices).then(getStream).catch(handleError);
        }

        videoSelect.onchange = getStream;

        // 监听媒体设备变化事件
        if (navigator.mediaDevices) {
          navigator.mediaDevices.addEventListener("devicechange", function (event) {
            // 尝试重新获取媒体流以检查摄像头是否仍然可用
            navigator.mediaDevices
              .getUserMedia({ video: true })
              .then(function () {
                navigator.mediaDevices.enumerateDevices().then(gotDevices).then(getStream).catch(handleError);
                // startVideoStream(); // 摄像头已连接,重新开始视频流
              })
              .catch(function () {
                stopVideoStream(); // 摄像头已断开,停止视频流并更新状态
                // destoryCanvas();
              });
          });
        }

        initPaperCanvas();
      };

      // 拍照功能
      function takePicture() {
        let canvas = document.createElement("canvas");
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;

        const context = canvas.getContext("2d");
        context.drawImage(video, 0, 0, canvas.width, canvas.height);

        displayPictureOnPaper(canvas.toDataURL("image/png"));
        canvas = null;
      }

      function displayPictureOnPaper(imageData) {
        raster = new paper.Raster({
          source: imageData,
          position: paper.view.center,
        });
      }

      function destoryCanvas() {
        if (raster) {
          raster.remove();
        }
      }

      function handleError(error) {
        console.error("无法获取摄像头:", error);
        cameraStatus.textContent = "摄像头状态:未连接或已被占用";
        captureButton.style.display = "none";
      }
    </script>
  </body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2074792.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用 AMD GPUs 进行基于 Transformers 的时间序列预测

Using AMD GPUs for Enhanced Time Series Forecasting with Transformers — ROCm Blogs 时间序列预测&#xff08;TSF&#xff09;是信号处理、数据科学和机器学习&#xff08;ML&#xff09;等领域的关键概念。TSF 通过分析系统的过去时间模式来预测其未来行为&#xff0c;利…

私域流量升级下的新机遇——“开源 AI 智能名片S2B2C 商城小程序”与新兴技术的融合

摘要&#xff1a;本文深入探讨了随着私域流量应用的进一步升级&#xff0c;智能对话式营销持续火爆的同时&#xff0c;CEM&#xff08;客户体验管理&#xff09;、MA&#xff08;营销自动化&#xff09;、CDP&#xff08;客户数据平台&#xff09;及 DAM&#xff08;数据资产管…

《黑神话:悟空》之光线追踪技术

8月20日&#xff0c;国产单机游戏《黑神话&#xff1a;悟空》终于上市&#xff0c;并以实力演绎了爆款游戏的“盛况空前”。 这款游戏的成功&#xff0c;不仅源自对经典文学《西游记》的深刻解读与创新演绎&#xff0c;更在于其背后强大的科技力量支撑。 空间计算功不可没 土…

游戏服务器架构:基于匿名函数的高性能异步定时器系统

作者&#xff1a;码客&#xff08;ygluu 卢益贵&#xff09; 关键词&#xff1a;游戏服务器架构、匿名函数、高性能、异步定时器。 一、前言 本文主要介绍适用于MMO/RPG游戏服务端的、基于匿名函数做定时器回调函数的、高性能异步触发的定时器系统的设计方案&#xff0c;以解决…

《深入浅出WPF》读书笔记.7依赖属性和附加属性

《深入浅出WPF》读书笔记.7依赖属性和附加属性 背景 总结一下wpf依赖属性和附加属性的底层逻辑&#xff0c;方便更好的理解使用。 属性 CLR属性由来 static属性和非static属性的区别 static属性:对类有意义&#xff0c;内存只有一个实例&#xff1b; 非static属性:对类实…

WPF—LiveCharts图表

LiveCharts图表 LiveCharts是一个简单灵活、交互式以及功能强大的跨平台图表库&#xff0c;支持wpf、winform...应用程序。 快速入门 安装 在应用程序中右键引用​&#xff0c;点击管理NuGet程序包​&#xff0c;选择浏览​&#xff0c;搜索LiveChartsCore.SkiaSharpView.W…

自动驾驶-机器人-slam-定位面经和面试知识系列10之高频面试题(04)

这个博客系列会分为C STL-面经、常考公式推导和SLAM面经面试题等三个系列进行更新&#xff0c;基本涵盖了自己秋招历程被问过的面试内容&#xff08;除了实习和学校项目相关的具体细节&#xff09;。在知乎和牛客也会同步更新&#xff0c;全网同号&#xff08;lonely-stone或者…

Cortex-A7的GIC(通用中断控制器):专有名词简介

0 资料 ARM Generic Interrupt Controller Architecture version 2.0 Architecture Specification1 专有名词简介 1.1 中断状态 说明&#xff1a; Inactive&#xff1a;未激活&#xff0c;中断无效。中断非挂起或非激活。 Pending&#xff1a;挂起&#xff0c;中断有效。等待…

【Web】NepCTF 2024题解

目录 PHP_MASTER!! NepDouble 蹦蹦炸弹&#xff08;boom_it&#xff09; NepRouter-白给 Always RCE First PHP_MASTER!! PHP反序列化键值逃逸mb_strpos与mb_substr连用导致的字符注入 https://www.cnblogs.com/EddieMurphy-blogs/p/18310518 flag在phpinfo里 payloa…

1/f噪声影响及解决措施

在将6位半数字万用表输入短接时&#xff0c;观察其输出。在逐渐增加均值次数后&#xff0c;噪声开始下降&#xff0c;达到一定程度后便停止下降&#xff0c;随着时间的推移&#xff0c;停止下降的噪声在逐渐增加&#xff0c;该部分主要是1/f噪声影响。 这种1/f噪声&#xff08;…

mPLUG-Owl3环境搭建推理测试

mPLUG-Owl3环境搭建&推理测试 引子 多模态的大模型也写了很多篇&#xff0c;阿里系的之前有一篇Qwen-VL的相关部署&#xff0c;感兴趣的童鞋请移步&#xff08;Qwen-VL环境搭建&推理测试-CSDN博客&#xff09;。今天这个mPLUG-Qwl3&#xff0c;更新换代也很快&#x…

Windows下线程的竞争与资源保护(win32-API)

一、前言 在线程编程中&#xff0c;资源共享与保护是一个核心议题&#xff0c;尤其当多个线程试图同时访问同一份资源时&#xff0c;如果不采取适当的措施&#xff0c;就会引发一系列的问题&#xff0c;如数据不一致、竞态条件、死锁等。为了确保数据的一致性和线程安全&#…

【游戏速递】 小猪冲刺:萌动指尖的极速挑战,小虎鲸Scratch资源站独家献映!

在线玩&#xff1a;Scratch小猪冲刺&#xff1a;全新挑战的几何冒险游戏-小虎鲸Scratch资源站 想象一下&#xff0c;一群憨态可掬的小猪&#xff0c;穿上炫酷的装备&#xff0c;踏上了追逐梦想的赛道。它们或跳跃、或滑行&#xff0c;灵活躲避各种障碍&#xff0c;只为那终点的…

微软亚研院哈佛:同行评议互一致的rStar

本来想将近期另一篇DeepSeek的“DeepSeek-Prover-V1.5: Harnessing Proof Assistant Feedback for Reinforcement Learning and Monte-Carlo Tree Search”与这篇同样基于强化学习思想的小型清爽型推理模型放在一个笔记中相互对比借鉴一下&#xff0c;考虑虽然两者有着一些共通…

论文3解析(复现):六自由度机械臂轨迹规划研究+机器人基础知识-部分1

论文&#xff1a;六自由度机械臂轨迹规划研究&#xff0c;马强 机器人一些关于数学基础的知识&#xff0c;简单的说一下&#xff1a; 向量 在机器人中&#xff0c;向量的含义并不是算算数那样子&#xff0c;而是在空间的本质含义。 向量叉乘&#xff1a;ab |a|*|b|*sin&am…

燃烧控制模型

加热炉燃烧控制 主要功能&#xff1a; 1&#xff0e; 把要轧制的钢坯加热的规定温度&#xff0c;即出炉目标温度&#xff0c;并尽量减少黑印。 2&#xff0e; 协调加热炉及轧机的生产能力&#xff0c;以提高轧机总的生产效率。 3&#xff0e; 节省燃料 在轧钢生产过程中&#x…

s3c2440移植Linux内核之引导

最近想尝试把新的Linux内核移植到tq2440的开发板上&#xff0c;看看还能不能顺利的跑起来。我的基础版本是买板子的时候提供的2.6.30版本&#xff0c;编译器版本是4.3.3.。 下载源码和编译器 下载linux源码&#xff0c;源码的官方网站是The Linux Kernel Archives&#xff0c…

沉积层的厚度为自振周期波长的1/4

要理解为什么是1/4&#xff0c;需要明白如下两点。 &#xff08;1&#xff09;自振周期&#xff08;fundamental model, or first harmonic&#xff09;取决于在某边界条件下可以出现驻波&#xff08;standing wave&#xff09;的最短距离。Standing wave, also known as a st…

AI助力水体保护区无人值守垂钓智能预警,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建水体保护区场景下无人值守垂钓智能检测预警系统

保护我们赖以生存的自然生态环境&#xff0c;无疑是一项意义深远且需要长期坚持的任务。自然界的生态系统&#xff0c;由水、气、森林、土壤等多要素组成&#xff0c;它们相互依存、相互影响&#xff0c;共同维系着地球的生态平衡。然而&#xff0c;在人类活动的影响下&#xf…

浅谈进程,线程,协程以及服务端高并发的处理

进程、线程、协程 进程&#xff1a;独立的程序实例&#xff0c;资源开销较大&#xff0c;适合隔离性要求高的任务。 独立性&#xff1a;进程具有独立的内存空间和资源&#xff0c;互不干扰。 资源开销大&#xff1a;由于每个进程都需要分配独立的内存和资源&#xff0c;创建和…