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()
}