用canvas切图展示及标记在原图片中的位置

1.切图

需要后端返回原图片地址及对应四个角的位置数组

TypeScript 复制代码
const getImageList = async (photos: string[]) => {
  try {
    const res = await getAiImage(photos)

    if (res.data && res.data.length) {
      // 处理结果
      const results: AIResultItem[] = []

      for (const item of res.data) {
        if (item.Num && item.Url && item.location) {
          const chars = []
          const resultItem: AIResultItem = {
            Num: item.Num,
            Url: item.Url,
            location: item.location,
            chars: chars
          }
          // 尝试裁剪图片
          try {
            const croppedUrl = await cropImage(item.Url, item.location)
            resultItem.croppedImageUrl = croppedUrl
          } catch (error) {
            console.warn('图片裁剪失败:', error)
            // 如果裁剪失败,使用原图
            resultItem.croppedImageUrl = item.Url.startsWith('http')
              ? item.Url
              : `${MINO}${item.Url}`
          }

          results.push(resultItem)
        }
      }

      currentImgList.value = results
      console.log('处理后的结果列表:', currentImgList.value)
    } else {
      showFailToast('未识别到')
      currentImgList.value = []
    }
  } catch (error) {
    console.error('AI识别失败:', error)
    showFailToast('识别失败,请重试')
  } finally {
    isidentify.value = false
  }
}

// 裁剪图片
const cropImage = (imageUrl: string, location: location): Promise<string> => {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.crossOrigin = 'anonymous'

    img.onload = () => {
      try {
        const rect = calculatePlateRect(location)
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')

        if (!ctx) {
          reject(new Error('Failed to get canvas context'))
          return
        }

        // 设置canvas尺寸
        canvas.width = rect.width
        canvas.height = rect.height

        // 绘制裁剪区域
        ctx.drawImage(img, rect.x, rect.y, rect.width, rect.height, 0, 0, rect.width, rect.height)

        // 转换为DataURL
        const croppedUrl = canvas.toDataURL('image/jpeg', 0.9)
        resolve(croppedUrl)
      } catch (error) {
        console.error('图片裁剪失败:', error)
        reject(error)
      }
    }

    img.onerror = () => {
      reject(new Error('Failed to load image'))
    }

    // 确保图片地址完整
    const fullImageUrl = imageUrl.startsWith('http') ? imageUrl : `${MINO}${imageUrl}`
    img.src = fullImageUrl
  })
}
// 根据location计算矩形区域
const calculatePlateRect = (location: location) => {
  const { leftTop, rightTop, rightBottom, leftBottom } = location

  // 计算矩形的坐标
  const x = Math.min(leftTop.x, rightTop.x, rightBottom.x, leftBottom.x)
  const y = Math.min(leftTop.y, rightTop.y, rightBottom.y, leftBottom.y)
  const width = Math.max(leftTop.x, rightTop.x, rightBottom.x, leftBottom.x) - x
  const height = Math.max(leftTop.y, rightTop.y, rightBottom.y, leftBottom.y) - y

  return { x, y, width, height }
}

2.标记位置

预览原图并标记出截图的位置重新绘制图片展示

TypeScript 复制代码
// 打开预览
const openPreview = async (item: AIResultItem) => {
  console.log('打开预览:', item)

  if (!item || !item.Url) {
    showToast('暂无照片')
    return
  }

  try {
    // 加载原始图片
    const originalImageUrl = item.Url.startsWith('http')
      ? item.Url
      : `${MINO}${item.Url}`

    // 在Canvas上绘制带框的图片
    const canvasImageUrl = await drawPlateLocationOnImage(originalImageUrl, item.plateLocation)

    // 显示预览
    if (canvasImageUrl) {
      showImagePreview([canvasImageUrl])
    } else {
      // 如果绘制失败,显示原始图片
      showImagePreview([originalImageUrl])
    }
  } catch (error) {
    console.error('预览图片失败:', error)
    // 出错时显示原始图片
    showImagePreview([`${MINO}${item.Url}`])
  }
}

// 在图片上绘制截图位置框
const drawPlateLocationOnImage = (
  imageUrl: string,
  plateLocation: PlateLocation
): Promise<string> => {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.crossOrigin = 'anonymous'

    img.onload = () => {
      try {
        const canvas = document.createElement('canvas')
        const ctx = canvas.getContext('2d')

        if (!ctx) {
          reject(new Error('Failed to get canvas context'))
          return
        }

        // 设置canvas尺寸与图片相同
        canvas.width = img.width
        canvas.height = img.height

        // 1. 绘制原始图片
        ctx.drawImage(img, 0, 0)

        // 2. 计算位置矩形
        const rect = calculatePlateRect(plateLocation)

        // 3. 绘制位置框
        drawPlateRect(ctx, rect)

        // 4. 转换为DataURL
        const imageWithRectUrl = canvas.toDataURL('image/jpeg', 0.9)
        resolve(imageWithRectUrl)
      } catch (error) {
        console.error('绘制位置框失败:', error)
        reject(error)
      }
    }

    img.onerror = () => {
      reject(new Error('Failed to load image'))
    }

    img.src = imageUrl
  })
}

// 绘制位置矩形框
const drawPlateRect = (
  ctx: CanvasRenderingContext2D,
  rect: { x: number; y: number; width: number; height: number }
) => {
  const { x, y, width, height } = rect

  // 设置绘制样式
  ctx.save()

  // 绘制半透明背景填充
  ctx.fillStyle = 'rgba(255, 0, 0, 0.1)'
  ctx.fillRect(x, y, width, height)

  // 绘制边框
  ctx.lineWidth = 3
  ctx.strokeStyle = '#FF0000'
  ctx.setLineDash([8, 4]) // 虚线边框

  // 绘制圆角矩形
  const borderRadius = 8
  drawRoundedRect(ctx, x, y, width, height, borderRadius)
  ctx.stroke()

  // 绘制四个角的标记
  const cornerSize = 12
  const cornerLineWidth = 3
  ctx.setLineDash([]) // 实线
  ctx.lineWidth = cornerLineWidth
  ctx.strokeStyle = 'FF0000'

  // 左上角
  ctx.beginPath()
  ctx.moveTo(x, y + cornerSize)
  ctx.lineTo(x, y)
  ctx.lineTo(x + cornerSize, y)
  ctx.stroke()

  // 右上角
  ctx.beginPath()
  ctx.moveTo(x + width - cornerSize, y)
  ctx.lineTo(x + width, y)
  ctx.lineTo(x + width, y + cornerSize)
  ctx.stroke()

  // 右下角
  ctx.beginPath()
  ctx.moveTo(x + width, y + height - cornerSize)
  ctx.lineTo(x + width, y + height)
  ctx.lineTo(x + width - cornerSize, y + height)
  ctx.stroke()

  // 左下角
  ctx.beginPath()
  ctx.moveTo(x + cornerSize, y + height)
  ctx.lineTo(x, y + height)
  ctx.lineTo(x, y + height - cornerSize)
  ctx.stroke()

  // 添加位置标签
  ctx.fillStyle = '#47A0FF'
  ctx.font = 'bold 16px Arial'
  ctx.textAlign = 'left'
  ctx.textBaseline = 'top'
  ctx.fillText('', x, y - 25)

  ctx.restore()
}

// 绘制圆角矩形
const drawRoundedRect = (
  ctx: CanvasRenderingContext2D,
  x: number,
  y: number,
  width: number,
  height: number,
  radius: number
) => {
  ctx.beginPath()
  // 左上角
  ctx.moveTo(x + radius, y)
  ctx.arcTo(x + width, y, x + width, y + height, radius)
  // 右上角
  ctx.arcTo(x + width, y + height, x, y + height, radius)
  // 右下角
  ctx.arcTo(x, y + height, x, y, radius)
  // 左下角
  ctx.arcTo(x, y, x + width, y, radius)
  ctx.closePath()
}
相关推荐
wearegogog1232 小时前
基于 MATLAB 的卡尔曼滤波器实现,用于消除噪声并估算信号
前端·算法·matlab
molaifeng2 小时前
Go 语言如何实现高性能网络 I/O:Netpoller 模型揭秘
开发语言·网络·golang
Drawing stars2 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
崇山峻岭之间2 小时前
Matlab学习记录33
开发语言·学习·matlab
品克缤2 小时前
Element UI MessageBox 增加第三个按钮(DOM Hack 方案)
前端·javascript·vue.js
Evand J2 小时前
【2026课题推荐】DOA定位——MUSIC算法进行多传感器协同目标定位。附MATLAB例程运行结果
开发语言·算法·matlab
小二·2 小时前
Python Web 开发进阶实战:性能压测与调优 —— Locust + Prometheus + Grafana 构建高并发可观测系统
前端·python·prometheus
小沐°3 小时前
vue-设置不同环境的打包和运行
前端·javascript·vue.js
jllllyuz3 小时前
基于MATLAB的二维波场模拟程序(含PML边界条件)
开发语言·matlab
忆锦紫3 小时前
图像增强算法:Gamma映射算法及MATLAB实现
开发语言·算法·matlab