游客无体验权限,请先登录。
授权微博登录
授权Github登录
···

标注图像多边形区域

已开放源代码

项目源代码仓库:airglass-label源代码在Github的仓库地址
项目名称:Airglass Label
授权许可协议:MIT License

在线体验作品

在线体验入口:Canvas标注多边形区域与切割图像的实验项目入口

多边形标注与图像切割的演示

创作背景

我又重新实现了一版依赖Airglass.js库的基于Canvas技术的功能包括多边形区域标注以及图像切割的实验项目,这篇想法记录开发过程。

加载待标注图像后

实际应用场景中,根据需要加载特定图像。在本实验项目中,为了体现对多样性的尊重,缓解什么疲劳,每次刷新都会加载不同的图像作为示例图像。我特意从自己的相册中选取了几张有东西可标注的照片。因为我选择的图片的宽、高不固定,因此我需要将图片们按一定规则缩放,以适配画布大小。

目前实例化一个Airglass对象可以指定四个参数。一是作为Glass放置容器的DOM元素,二是画布的样式宽度,三是画布的样式高度,最后是和渲染清晰度有关的设备像素比。

按照我的预期设想,固定画布的样式高度,等到图像加载完毕后确定画布的样式宽度。所以初始化Airglass是在图像加载完之后。得到的两张已加载图像img1和img2。

let agHeight = 280;
let DPR = window.devicePixelRatio;
let img1ResizeWidth = img1.width / img1.height * agHeight;
let img2ResizeWidth = img2.width / img2.height * agHeight;
ag = new airglass.Airglass({
  element: document.querySelector('#wrap'),
  width: img1ResizeWidth + img2ResizeWidth,
  height: agHeight,
  DPR: DPR
});
imageRenderer = ag.addRenderer();
polygonRenderer = ag.addRenderer();
controllerRenderer = ag.addRenderer();

关于缩放图像以适配画布这一块的算法,其实和角度与弧度之间的转换算法很类似。图像的原始宽度做分子,原始高度做分母,就得到了图像等比缩放的宽、高比例值。使用这个比值与一个数相乘,就得到了那个数按照这个比值算出的对应分子的数值。

等比缩放图像的算法公式

准备第2个Airglass

该实验项目用到了两个Airglass实例。如上方演示动图所示,左侧的Airglass实例用于显示待标注图像、标注多边形,右侧的Airglass实例用于呈现图像的标注结果。

clipAg = new airglass.Airglass({
  element: document.querySelector('#clip'),
  width: img1ResizeWidth + img2ResizeWidth,
  height: agHeight,
  DPR: DPR
});

按照我的想法,另一个Airglass的大小和第一个的一样。第一个Airglass的大小在加载完待标注图像后初始化已经获得了。直接使用现成的画布宽度与高度数值初始化第2个Airglass。

canvas图像标注结果与切割

第二个Airglass存在的必要性在于呈现图像的标注结果。至于何为标注结果,多边形控制点在图像上的坐标是标注结果,将标注区域作为图像的切割依据以得到切割后的图像也是标注结果。

如果要获得多边形各控制点在图像上的位置坐标,不要忘了图像是经过缩放后渲染到画布上的,如果没有缩放图像而是按原始图像大小直接渲染到画布上,则不需要考虑按比例值反求出各控制点在图像上的真实位置坐标。

let Polygon = airglass.extend(airglass.Renderable, {
  _constructor: function (params) {
    this.path = null;
    this.points = params.points || [];
  },
  updatePath: function () {
    let path = new Path2D;
    for (let i = 0; i < this.points.length; i++) {
      let point = this.points[i];
      if (i == 0) {
        path.moveTo(point.x, point.y);
        continue;
      }
      path.lineTo(point.x, point.y);
    }
    this.path = path;
  },
  addPoint: function (point) {
    this.points.push(point);
  },
  draw: function (ctx) {
    ctx.strokeStyle = this.stroke;
    ctx.lineWidth = this.line;
    ctx.fillStyle = this.fill;
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';
    ctx.fill(this.path);
    ctx.stroke(this.path);
  }
})

我专门定义了Ploygon类,表示多边形。它存储至少三个点,因为至少需要三个点才能连成一个最少顶点的面。在Three.js中,最小面也是由3个顶点组成。

触摸与拖拽

最开始Airglass内置了基础图像,比如圆形、矩形和多边形。在我实际应用Airglass的过程中发现基础的图形不能满足变化多样的需要。我删掉了Airglass中的基础图形,保留下Renderable和Effect这样的抽象类,方便扩展出多种多样的形状和丰富的效果。

我也将Airglass中关于事件处理方面的代码保留了下来,其中就包括触摸事件。Airglass制作的界面技能使用鼠标交互,也能使用手指在触摸屏上交互。我将所有事件名称统一按他们在触摸屏上的名称称呼。比如mousedown和touchstart在Airglass中是一种事件类型,但有些是鼠标特有的事件类型我也保留了下来,比如mousemove。

Airglass的实例能订阅这些主要的事件,我常常在一个函数中就能把所有的代码逻辑写清楚,只需判断事件类型即可。

ag.subscribe(event => {
  if(event.type == 'touchstart'){
    touchstart: {
      /* 处理鼠标按下或手指在触摸屏上按下时的逻辑 */
    }
  }
  
  if(event.type == 'touchmove'){
    touchmove: {
      /* 处理鼠标按下并移动或手指在触摸屏上滑动时的逻辑 */
    }
  }
  
  /* 其他事件类型的处理 */
})
视频加载中...
《标注图像多边形区域》使用指南
雷达扫描动效

基于Airglass.js

我是我的作品

个人简历

一直在路上

心上到路上的“旅行”

待办事项

由原生JS实现的视图组件

项目管理平台

基于Vue.js

甘特图在线编辑

基于Airglass.js

Blender3D模型渲染

基于Three.js渲染

元胞自动机

基于Airglass.js

JS原型链可视化

基于Airglass.js

重构图像在线标注

基于Airglass.js

图像在线标注工具

基于原生Canvas实现

极坐标地图

Canvas缓动动画实验

类星系关系可视化

Canvas交互事件触发

趣玩周易64卦

国学小项目

日程计划组件

基于React.js

家谱可视化

基于Airglass.js

Airglass.js官方API

我的Canvas库使用参考