径向透镜畸变:鱼眼摄像头照片的矫正,纯前端js矫正鱼眼摄像头

最近体验了一下lightburn的摄像头辅助定位, 在使用摄像头进行定位时,你需要先校准相机镜头,然后在校准相机对齐,由于我们上位机后续也需要类似的功能,所以就需要先抽时间预研一下,以便后续开张工作。

先说一下校准相机镜头,这一步做的目的是将相机拍的照片进行处理,如果相机拍的比较斜,或者那种鱼眼相机,拍的照片是中间被放大,四周被压缩。

类似下面的。

在这一步我们需要将其处理成正常的二维平面。

恢复成下面这样子。

这就是这一步的目的,将变形的图片处理成正常的,以便后续建立坐标系,其他流程的处理。

废话不多说,直接上硬菜。

要解决上文所述的,图片失真的情况,我找到了一个库 这就是 fisheye.js

https://github.com/ericleong/fisheye.js

官方体验地址

https://ericleong.me/fisheye.js/

该库支持 普通图片的的径向透镜畸变,还支持gif的畸变。此外还支持不同通道,不同值的设置。

在该库内部使用WebGL 来实现畸变和恢复。使用着色器程序来处理纹理和是失真的参数。

使用方法

js 复制代码
var canvas = document.getElementById('canvas');
var fisheye = new Fisheye(canvas);

var img;

var red = document.getElementById('red');
var green = document.getElementById('green');
var blue = document.getElementById('blue');

var update = function () {
  fisheye.setViewport(canvas.width, canvas.height);
  fisheye.setDistortion(red.value, green.value, blue.value);
  fisheye.clear();
  fisheye.draw(img);
};

向构造函数传入一个canvas节点,设置视图的尺寸和三个通道的失真参数就可以看到失真的效果。非常的简单。

由于我们的场景是,摄像机安装在机器的一个固定位置,位置固定,画面尺寸固定,

所有只要得到失真的系数,就能将照片还原正常。

失真参数是一个数字,或者三个数字,表示三个通道。0表示没有失真。

下面是案例源码

html 复制代码
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1">

  <title>fisheye.js</title>

  <style>
    html,
    body {
      margin: 0;
      padding: 0;
    }

    a {
      text-decoration: none;
    }

    body>h1 {
      text-align: center;
    }

    h1 {
      padding-left: 1em;
      padding-right: 1em;
    }

    h1,
    h2 {
      font-family: sans-serif;
      font-weight: lighter;
      font-style: normal;
    }

    h3 {
      font-family: sans-serif;
      font-style: bold;
      font-variant: small-caps;
    }

    #sandbox {
      display: flex;
      justify-content: space-around;

      display: -webkit-flex;
      -webkit-justify-content: space-around;

      width: 100%;
    }

    #controls {
      box-sizing: border-box;
      flex-basis: 20vw;
      -webkit-flex-basis: 20vw;

      padding: 0 1em 0 1em;
    }

    #controls input {
      width: 100%;
    }

    #picker-container {
      position: relative;

      width: 100%;
      height: 3em;

      font-size: 1.25em;
      font-family: sans-serif;

      margin-top: 1em;
    }

    #picker-container::before {
      position: absolute;
      top: 0;
      left: 0;
      bottom: 0;
      right: 0;

      padding: 1em;

      content: 'pick another image';

      text-align: center;
    }

    #picker-container>input {
      width: 100%;
      height: 100%;

      cursor: pointer;

      opacity: 0 !important;
    }

    @media (max-width: 768px) {

      canvas {
        height: 100vw;
      }

      #sandbox {
        flex-direction: column;
        -webkit-flex-direction: column;
      }

      #controls {
        width: 100vw;
        padding: 0 10% 10% 10%;

        flex-basis: auto;
        -webkit-flex-basis: auto;
      }
    }
  </style>

  <script src="fisheye/omggif.js"></script>
  <script src="fisheye/glif.js"></script>
  <script src="fisheye/viewer.js"></script>
  <script src="fisheye/droppick.js"></script>
  <script src="fisheye/grab.js"></script>
  <script src="fisheye/fisheye.js"></script>
</head>
</head>

<body>
  <!-- <h1>radial lens distortion with <a href="https://github.com/ericleong/fisheye.js">fisheye.js</a></h1> -->
  <h1>径向透镜畸变:鱼眼摄像头照片的矫正</h1>
  <div id="sandbox">
    <canvas id="canvas" width="640" height="640"></canvas>
    <div id="controls">
      <h2>distortion</h2>
      <div><input id="all" type="range" min="-10" max="10" value="5" step="0.1" /></div>
      <h2>color channels</h2>
      <div>
        <h3>red</h3><input id="red" type="range" min="-10" max="10" value="5" step="0.1" />
      </div>
      <div>
        <h3>green</h3><input id="green" type="range" min="-10" max="10" value="5" step="0.1" />
      </div>
      <div>
        <h3>blue</h3><input id="blue" type="range" min="-10" max="10" value="5" step="0.1" />
      </div>
      <div id="picker-container"><input id="picker" type="file" accept="image/*" /></div>
    </div>
  </div>

  <script type="text/javascript">
    var canvas = document.getElementById('canvas');
    var fisheye = new Fisheye(canvas);

    var img;

    var red = document.getElementById('red');
    var green = document.getElementById('green');
    var blue = document.getElementById('blue');

    var update = function () {
      fisheye.setViewport(canvas.width, canvas.height);
      fisheye.setDistortion(red.value, green.value, blue.value);
      fisheye.clear();
      fisheye.draw(img);
    };

    var ie11 = !(window.ActiveXObject) && "ActiveXObject" in window;
    var eventType = ie11 ? 'change' : 'input';

    red.addEventListener(eventType, update);
    green.addEventListener(eventType, update);
    blue.addEventListener(eventType, update);

    var all = document.getElementById('all');
    all.addEventListener(eventType, function () {
      console.log(all.value, 'value')
      red.value = all.value;
      green.value = all.value;
      blue.value = all.value;

      update();
    });

    img = new Image();
    img.addEventListener('load', update, false);
    // img.src = 'fisheye/rainbow-glass.jpg';
    img.src = 'fisheye/grib.png';

    var gifTimeout;

    // drag and drop
    droppick(document.getElementById('sandbox'), document.getElementById('picker'), function (items) {
      grab(items, function (item) {
        debugger
        clearTimeout(gifTimeout);

        function setSize(width, height) {
          if (width > 0.8 * window.innerWidth) {
            if (window.matchMedia('(max-width: 768px)').matches) {
              canvas.width = Math.round(window.innerWidth * window.devicePixelRatio);
              canvas.height = Math.round(canvas.width * (height / width));

              canvas.style.width = '100vw'
              canvas.style.height = 100 * (height / width) + 'vw';
            } else {
              canvas.width = Math.round(0.8 * window.innerWidth * window.devicePixelRatio);
              canvas.height = Math.round(canvas.width * (height / width));

              canvas.style.width = '80vw'
              canvas.style.height = 80 * (height / width) + 'vw';
            }
          } else {
            canvas.width = width;
            canvas.height = height;

            canvas.style.width = canvas.width + 'px';
            canvas.style.height = canvas.height + 'px';
          }
        }

        if (item instanceof ArrayBuffer) {
          img = document.createElement('canvas');

          var view = viewer(img, item, function (timeoutId) {
            gifTimeout = timeoutId;

            setSize(img.width, img.height);
            update();
          });

          gifTimeout = view();
        } else if (item instanceof HTMLImageElement) {

          img = item;

          setSize(item.naturalWidth, item.naturalHeight);
          update();
        }
      })
    });
  </script>
</body>

</html>
相关推荐
半开半落3 分钟前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt
百事老饼干11 分钟前
Java[面试题]-真实面试
java·开发语言·面试
理想不理想v31 分钟前
vue经典前端面试题
前端·javascript·vue.js
不收藏找不到我32 分钟前
浏览器交互事件汇总
前端·交互
小阮的学习笔记1 小时前
Vue3中使用LogicFlow实现简单流程图
javascript·vue.js·流程图
YBN娜1 小时前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=1 小时前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css
杨荧1 小时前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
minDuck1 小时前
ruoyi-vue集成tianai-captcha验证码
java·前端·vue.js