1

前言

我们的图像标注系统中,标注的时候,每人需要一个容器(docker),需要把数据推到去LabelStudio里面去标注,利用webhook回推标注好的数据。

Canvas的使用,当选中某个矩形框的时候,会变成蓝色。
canvas.gif

画布大小

设置的是,图片有多大,画布就有多大,然后以画布的中心点对齐。默认是左上角对齐

this.ctx.drawImage(this.image, -this.image.width / 2, -this.image.height / 2);

需要注意的是drawImage三种方法分别如下:

drawImage(image, dx, dy) 在画布指定位置绘制原图
drawImage(image, dx, dy, dw, dh) 在画布指定位置上按原图大小绘制指定大小的图
drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) 剪切图像,并在画布上定位被剪切的部分
描述参数
image规定要使用的图像、画布或视频
dx在画布上放置图像的 x 坐标位置
dy在画布上放置图像的 y 坐标位置
sx可选。开始剪切图片的 x 坐标位置
sy可选。开始剪切图片的 y 坐标位置
dw可选。要使用的图像的宽度(就是裁剪之后的图片高度,放大或者缩放)
dh可选。要使用的图像的高度(就是裁剪之后的图片高度,放大或者缩放)
sw可选。被剪切图像的宽度(就是裁剪之前的图片宽度,这里的宽度若小于图片的原宽。则图片多余部分被剪掉;若大于,则会以空白填充)
sh可选。被剪切图像的高度(就是裁剪之前的图片高度)

具体细节请点击官网查看

图片的旋转

调用rotate函数即可,this.rotation代表的是角度,例如this.rotation = 90,表示画布表示反转90度,即图片翻卷90度,Math.PI / 180将度数转换为弧度。
image.png

this.ctx.rotate(this.rotation * Math.PI / 180);

图片的缩放和移动

图片的缩放

缩放比较简单,一行代码就可以解决。第一个参数代表x轴缩放,第二个参数代表y轴缩放,绘图上下文会沿 x 轴和 y 轴都按 this.scale 的比例进行缩放。假设this.scale = 2,那么整体图片会被放大两倍
image.png

this.ctx.scale(this.scale, this.scale);

图片的移动

图片的移动相对比较困难,思想:

  • 右击代表是移动图片
  • 计算拖动的距离
  • 更新偏移量
  • 限制偏移量(不能超过画布的大小)
  • 记录当前鼠标的位置
  • 重新绘制图像
    核心代码:
      const dx = event.clientX - this.lastX;
      const dy = event.clientY - this.lastY;
      this.offsetX += dx;
      this.offsetY += dy;
      this.limitOffset();
      this.lastX = event.clientX;
      this.lastY = event.clientY;
      this.draw();

limitOffset() {
    const canvas = this.canvas.nativeElement;
    //计算缩放后的图像宽度和高度
    const scaledWidth = this.image.width * this.scale;
    const scaledHeight = this.image.height * this.scale;
    //计算偏移范围
    const minOffsetX = - (scaledWidth - canvas.width) / 2;
    const maxOffsetX = (scaledWidth - canvas.width) / 2;
    const minOffsetY = - (scaledHeight - canvas.height) / 2;
    const maxOffsetY = (scaledHeight - canvas.height) / 2;
    //图像的实际偏移量
    this.offsetX = Math.max(minOffsetX, Math.min(maxOffsetX, this.offsetX));
    this.offsetY = Math.max(minOffsetY, Math.min(maxOffsetY, this.offsetY));
  }

选中矩形框

只需要判断当前鼠标的位置再某个矩形框内,点击后即可被选中。因为是矩形框,只需要判断起始位和结束位置即可。
注意:必须是对象,而不是里面的坐标值,因为,当你更改被选中的矩形框后,annotations也会进行相应的更改。
核心代码:

findSelectedShape(x: number, y: number) {
    for (const annotation of this.annotations) {
      if (x >= annotation.x1 && x <= annotation.x2 && y >= annotation.y1 && y <= annotation.y2) {
        return annotation;
      }
    }
    return null;
  }

矩形框的移动、缩放和删除

移动

计算鼠标移动的距离,对选中的举行框进行修改,第一个参数是选中的矩形框,第二个是沿着水平线(x轴)的偏移量,第三个是垂直线(y轴)的偏移量。

moveShape(shape: { x1: number, y1: number, x2: number, y2: number }, dx: number, dy: number) {
  shape.x1 += dx;
  shape.y1 += dy;
  shape.x2 += dx;
  shape.y2 += dy;
}

缩放

缩放相比移动要比较复杂点,当选中一个矩形框后,点击矩形四个角的其中一个角,进行拖拽,达到缩放效果。
第一个参数,是当前的矩形,第二个和第三个是当前鼠标的位置。

resizeShape(shape: { x1: number, y1: number, x2: number, y2: number }, x: number, y: number) {
    switch (this.resizeCorner) {
      case 'top-left':
        shape.x1 = x;
        shape.y1 = y;
        break;
      case 'top-right':
        shape.x2 = x;
        shape.y1 = y;
        break;
      case 'bottom-left':
        shape.x1 = x;
        shape.y2 = y;
        break;
      case 'bottom-right':
        shape.x2 = x;
        shape.y2 = y;
        break;
    }
  }

删除

删除很简单,从矩形框数组中过滤掉被选中矩形框即可。

onDelete() {
    if (this.selectedShape) {
      this.annotations = this.annotations.filter(annotation => annotation !== this.selectedShape);
      this.selectedShape = null;
      this.draw();
    }
  }

总结

相对来说基础的实现了labelStudio的标注功能,还有一写功能,还需后续实现,比如自定义矩形框的颜色、值。

希望这篇文章对你有一定的帮助


zZ_jie
396 声望8 粉丝

虚心接受问题,砥砺前行。