Sapphire项目日志(十五)

本周任务

为模型编写工具框,适配数据标注环境。接入图片和embeeding数据的传入和标注数据的构造。

状态解释

设计一些全局状态便于在各层次组件获取当前工作台状态。

    click: [click, setClick], //当前的点击位置(追踪鼠标位置)
    clicks: [clicks, setClicks], //全部的点击位置数组
    image: [image, setImage], //需要被推理的数组
    prevImage: [prevImage, setPrevImage], //此前的图片,用于重置操作
    svg: [, setSVG], //蒙版svg
    svgs: [svgs, setSVGs], //全部的蒙版svg
    allsvg: [, setAllsvg],
    isErased: [, setIsErased], //是否被擦除
    isModelLoaded: [, setIsModelLoaded], //模型是否加载
    isLoading: [, setIsLoading], //是否正在加载图片和embedding
    segmentTypes: [, setSegmentTypes], //当前的分割类型
    maskImg: [, setMaskImg], //蒙版图片
    isErasing: [isErasing, setIsErasing], //是否正在擦除
    stickerTabBool: [stickerTabBool, setStickerTabBool],
    isMultiMaskMode: [isMultiMaskMode, setIsMultiMaskMode], //
    isHovering: [isHovering, setIsHovering],
    showLoadingModal: [showLoadingModal, setShowLoadingModal],
    eraserText: [eraserText, setEraserText],
    predMask: [predMask, setPredMask], //之前的蒙版
    predMasks: [predMasks, setPredMasks],
    predMasksHistory: [predMasksHistory, setPredMasksHistory],
    isToolBarUpload: [isToolBarUpload, setIsToolBarUpload],
    stickers: [stickers, setStickers], //所有扣完的元素

添加工具栏

在这里插入图片描述

工具栏包含点选模式和框选模式。

点选模式通过修改clicks来添加一个正点(负点)。框选模式框住一个区域,框选区域的左上和右下坐标会被装入clicks中,用于后续推理。

Undo会撤销最后一次点击操作,Redo会加回撤销的操作,重置会清空蒙版,然后重置所有状态。

点击确认会将推理结果加入stickers中,形成抠图数据。

添加结果栏

添加结果栏,用于展示当前已经抠出的区域。

在这里插入图片描述

这里只要遍历stickers,然后将stickers中的canvas内容展示出来即可。 当用户点击确认时,需要计算出一个图片加入到stickers中去,这里需要编写一个计算方式。

 const cropImageFromCanvasTS = (ref: any) => {
      let newCanvas = null;
      let stickerData = null;
      try {
        const canvas = ref!.toCanvas().getContext("2d");

        let w = ref.width();
        let h = ref.height();
        const pix: { x: number[]; y: number[] } = { x: [], y: [] };
        const imageData = canvas.getImageData(0, 0, w, h);
        let x;
        let y;
        let index;

        for (y = 0; y < h; y++) {
          for (x = 0; x < w; x++) {
            index = (y * w + x) * 4;
            if (imageData.data[index + 3] > 0) {
              pix.x.push(x);
              pix.y.push(y);
            }
          }
        }
        pix.x.sort(function (a: number, b: number) {
          return a - b;
        });
        pix.y.sort(function (a: number, b: number) {
          return a - b;
        });
        const n = pix.x.length - 1;

        stickerData = {
          center_x: (pix.x[n] + pix.x[0]) / 2 / w,
          center_y: (pix.y[n] + pix.y[0]) / 2 / h,
          w: (1 + pix.x[n] - pix.x[0]) / w,
          h: (1 + pix.y[n] - pix.y[0]) / h,
        };
        // console.log({
        //   center_x: ((pix.x[n] + pix.x[0]) / 2)/w,
        //   center_y: ((pix.y[n] + pix.y[0]) / 2)/h,
        //   w:(1 + pix.x[n] - pix.x[0])/w,
        //   h:(1 + pix.y[n] - pix.y[0])/h
        // })

        w = 1 + pix.x[n] - pix.x[0];
        h = 1 + pix.y[n] - pix.y[0];
        const cut = canvas.getImageData(pix.x[0], pix.y[0], w, h);

        canvas.width = w;
        canvas.height = h;
        canvas.putImageData(cut, 0, 0);
        newCanvas = document.createElement("canvas");
        newCanvas.width = w;
        newCanvas.height = h;
        newCanvas.getContext("2d")!.putImageData(cut, 0, 0);

这个函数根据蒙版信息计算抠图区域,蒙版中为0的区域是没有被框选的,如果不是0,说明是被抠出的部分,那就加入到新的canvas中去。最终分别找到x轴和y轴的最大和最小值,这样得到一个矩形区域,从蒙版中抠出这个区域即可。

同时我们还需要计算出一个可以交由后端保存,此后用于数据训练的数据。以yolo为例,训练的数据集需要提供抠图区域的中心相对坐标、相对宽度、相对高度。所有的相对值都是相对于总高度和总宽度而言的,所以都是0~1之间的值,我们把这个数据保存到stikerData中,一同返回。

添加图片切换

然后我们还需要添加图片切换功能。

首先在工具栏下添加上一张和下一张按钮。用于单张切换图片。

在这里插入图片描述

切换图片时,只要切换image和embedding即可,通过useEffect来监听这两者的变化,当他们发生变化时,我们会清空所有数据,然后将新图片交由模型进行推理。

  useEffect(() => {
    if (imgUrl && embeddingUrl)
      handleSelectedImage(new URL(imgUrl), embeddingUrl, {
        shouldDownload: false,
        shouldNotFetchAllModel: true,
      });
    setStickers([]);
  }, [imgUrl, embeddingUrl]);	

使用同样的方法,我们添加一个全部任务的展示栏,用于展示全局的任务。

在这里插入图片描述

切换图片时,修改方式与之前上下张切换类似,我们使用一个pos状态记录当前的任务位置,然后点击某张图片的时候我们修改pos的值即可。

根据用户习惯,在这里滚动滚轮,理论上应该会横向滚动才对,所以我们添加一个监听器,监听这个位置的滚轮状态,组织默认行为,使用js修改当前容器的y轴。

  useEffect(() => {
    const tabsContainer = imgListContainer.current;

    const handleWheel = (event: any) => {
      event.preventDefault();
      tabsContainer!.scrollLeft += event.deltaY;
    };

    if (tabsContainer) {
      tabsContainer.addEventListener("wheel", handleWheel);
    }

    return () => {
      if (tabsContainer) {
        tabsContainer.removeEventListener("wheel", handleWheel);
      }
    };
  }, []);

抠图结果控制

对于抠图结果也需要控制,用户需要删除已抠的图,或者为抠出来的图选择id(类型)。

所以这里我们添加两个按钮

在这里插入图片描述

用于删除当前选中的sticker或者为当前的sticker选择类型。

删除的时候,我们只要重新设置stickers状态即可。

 const handleDelete = () => {
    if (stickers === null || stickers.length === 0) {
      enqueueSnackbar("请先创建一个贴纸", { variant: "error" });
      return;
    }
    if (
      activeSticker === null ||
      activeSticker < 0 ||
      activeSticker >= stickers!.length
    ) {
      enqueueSnackbar("请选择一个贴纸", { variant: "error" });
      return;
    }
    setStickers(stickers.filter((_, index) => index !== activeSticker));
    setActiveSticker(0);
  };

这里通过filter来删除选中的贴纸。

选择类型留坑待填。。。。

效果

目前抠图效果如下,此处功能基本完成。

在这里插入图片描述

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值