|
@@ -11,6 +11,7 @@ const system = useSystemStore()
|
|
|
|
|
|
const addDialogV = ref(false)
|
|
const addDialogV = ref(false)
|
|
const updDialogV = ref(false)
|
|
const updDialogV = ref(false)
|
|
|
|
+const outMatrix = ref<any>(null)
|
|
|
|
|
|
const tools = ref([
|
|
const tools = ref([
|
|
{},
|
|
{},
|
|
@@ -50,32 +51,26 @@ const naturalWH = {
|
|
width: 0,
|
|
width: 0,
|
|
height: 0,
|
|
height: 0,
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+const ShowWidthZoom = ref(1)
|
|
|
|
+const ShowHeightZoom = ref(1)
|
|
|
|
+const ShowWidthOffset = ref(0)
|
|
|
|
+const ShowHeightOffset = ref(0)
|
|
|
|
+
|
|
function imgSrcLoad() {
|
|
function imgSrcLoad() {
|
|
imgSrcLoading.value = false
|
|
imgSrcLoading.value = false
|
|
}
|
|
}
|
|
|
|
|
|
-interface MarkItemType { id: string, p: number, t: number, x: number, y: number, color: string }
|
|
|
|
-type MarkType = MarkItemType[]
|
|
|
|
-const mark = ref<MarkType>([])
|
|
|
|
-const markClone = ref<MarkType>([])
|
|
|
|
-const currentMark = ref<MarkItemType>({
|
|
|
|
- id: '',
|
|
|
|
- p: 0.0,
|
|
|
|
- t: 0.0,
|
|
|
|
- x: 0.0,
|
|
|
|
- y: 0.0,
|
|
|
|
- color: '#000',
|
|
|
|
-})
|
|
|
|
|
|
+const currentMark = ref('')
|
|
|
|
|
|
-const PTXYMap = new Map<string, { P: number, T: number, X: number, Y: number }>()
|
|
|
|
|
|
+const PTXYMap = ref(new Map<string, { P: number, T: number, X: number, Y: number }>())
|
|
|
|
|
|
-// opencv ==============start
|
|
|
|
|
|
+// 计算一个矩阵的逆矩阵
|
|
function calculateInverseMatrix(Matrix: number[]) {
|
|
function calculateInverseMatrix(Matrix: number[]) {
|
|
// 检查矩阵是否有效
|
|
// 检查矩阵是否有效
|
|
if (Matrix.length !== 9) {
|
|
if (Matrix.length !== 9) {
|
|
return []
|
|
return []
|
|
}
|
|
}
|
|
-
|
|
|
|
// 提取矩阵元素
|
|
// 提取矩阵元素
|
|
const a = Matrix[0]
|
|
const a = Matrix[0]
|
|
const b = Matrix[1]
|
|
const b = Matrix[1]
|
|
@@ -86,18 +81,14 @@ function calculateInverseMatrix(Matrix: number[]) {
|
|
const g = Matrix[6]
|
|
const g = Matrix[6]
|
|
const h = Matrix[7]
|
|
const h = Matrix[7]
|
|
const i = Matrix[8]
|
|
const i = Matrix[8]
|
|
-
|
|
|
|
// 计算行列式
|
|
// 计算行列式
|
|
const det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g)
|
|
const det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g)
|
|
-
|
|
|
|
// 检查矩阵是否可逆
|
|
// 检查矩阵是否可逆
|
|
if (det === 0) {
|
|
if (det === 0) {
|
|
return []
|
|
return []
|
|
}
|
|
}
|
|
-
|
|
|
|
// 计算行列式的倒数
|
|
// 计算行列式的倒数
|
|
const invDet = 1.0 / det
|
|
const invDet = 1.0 / det
|
|
-
|
|
|
|
return [
|
|
return [
|
|
(e * i - f * h) * invDet,
|
|
(e * i - f * h) * invDet,
|
|
(c * h - b * i) * invDet,
|
|
(c * h - b * i) * invDet,
|
|
@@ -110,12 +101,12 @@ function calculateInverseMatrix(Matrix: number[]) {
|
|
(a * e - b * d) * invDet,
|
|
(a * e - b * d) * invDet,
|
|
]
|
|
]
|
|
}
|
|
}
|
|
-
|
|
|
|
-function WarpingPtByHomography(matrix: number[], X: number, Y: number) {
|
|
|
|
|
|
+// 通过单应性矩阵计算映射后的坐标
|
|
|
|
+function WarpingPtByHomography(X: number, Y: number) {
|
|
let x, y
|
|
let x, y
|
|
- x = matrix[0] * X + matrix[1] * Y + 1.0 * matrix[2]
|
|
|
|
- y = matrix[3] * X + matrix[4] * Y + 1.0 * matrix[5]
|
|
|
|
- const z = matrix[6] * X + matrix[7] * Y + 1.0 * matrix[8]
|
|
|
|
|
|
+ x = outMatrix.value[0] * X + outMatrix.value[1] * Y + outMatrix.value[2]
|
|
|
|
+ y = outMatrix.value[3] * X + outMatrix.value[4] * Y + outMatrix.value[5]
|
|
|
|
+ const z = outMatrix.value[6] * X + outMatrix.value[7] * Y + outMatrix.value[8]
|
|
|
|
|
|
x /= z
|
|
x /= z
|
|
y /= z
|
|
y /= z
|
|
@@ -123,46 +114,31 @@ function WarpingPtByHomography(matrix: number[], X: number, Y: number) {
|
|
}
|
|
}
|
|
|
|
|
|
async function buildMatrix() {
|
|
async function buildMatrix() {
|
|
- if (PTXYMap.size < 4) {
|
|
|
|
|
|
+ if (PTXYMap.value.size < 4) {
|
|
system.msg('映射矩阵最少四个')
|
|
system.msg('映射矩阵最少四个')
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
- const PT: any[] = []; const XY: any[] = []
|
|
|
|
- PTXYMap.forEach((v: any) => {
|
|
|
|
|
|
+ const PT: any[] = []
|
|
|
|
+ const XY: any[] = []
|
|
|
|
+ PTXYMap.value.forEach((v: any) => {
|
|
PT.push(v.P, v.T)
|
|
PT.push(v.P, v.T)
|
|
XY.push(v.X, v.Y)
|
|
XY.push(v.X, v.Y)
|
|
})
|
|
})
|
|
// eslint-disable-next-line new-cap
|
|
// eslint-disable-next-line new-cap
|
|
- const srcPointMat = new window.cv.matFromArray(4, 1, window.cv.CV_64FC2, XY)
|
|
|
|
|
|
+ const srcPointMat = new window.cv.matFromArray(PT.length, 1, window.cv.CV_64FC2, XY)
|
|
// eslint-disable-next-line new-cap
|
|
// eslint-disable-next-line new-cap
|
|
- const dtsPointMat = new window.cv.matFromArray(4, 1, window.cv.CV_64FC2, PT)
|
|
|
|
|
|
+ const dtsPointMat = new window.cv.matFromArray(PT.length, 1, window.cv.CV_64FC2, PT)
|
|
|
|
|
|
// 计算单应性矩阵
|
|
// 计算单应性矩阵
|
|
- const outMatrix = window.cv.findHomography(srcPointMat, dtsPointMat)
|
|
|
|
- window.outMatrix = outMatrix.data64F
|
|
|
|
-
|
|
|
|
|
|
+ outMatrix.value = window.cv.findHomography(srcPointMat, dtsPointMat, window.cv.RANSAC).data64F
|
|
// 清理临时矩阵(OpenCV.js内存管理)
|
|
// 清理临时矩阵(OpenCV.js内存管理)
|
|
srcPointMat.delete()
|
|
srcPointMat.delete()
|
|
dtsPointMat.delete()
|
|
dtsPointMat.delete()
|
|
-
|
|
|
|
- // 应用矩阵到所有点
|
|
|
|
- PTXYMap.forEach((v: any) => {
|
|
|
|
- if (v.P !== undefined && v.T !== undefined) {
|
|
|
|
- const out = WarpingPtByHomography(outMatrix.data64F, v.X, v.Y)
|
|
|
|
- v.P = out.X
|
|
|
|
- v.T = out.Y
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-// opencv =============end
|
|
|
|
-
|
|
|
|
-function markClick(item: MarkItemType) {
|
|
|
|
- if (!isDisabled.value)
|
|
|
|
- return system.msg('正在装载,请稍后...')
|
|
|
|
|
|
+function markClick(item: string) {
|
|
currentMark.value = item
|
|
currentMark.value = item
|
|
- colorReset(item.id)
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// 计算原始坐标的函数
|
|
// 计算原始坐标的函数
|
|
@@ -172,95 +148,51 @@ function calculateOriginalCoords(x: number, y: number, originalWidth: number, or
|
|
return { originalX, originalY }
|
|
return { originalX, originalY }
|
|
}
|
|
}
|
|
|
|
|
|
-// 重置颜色
|
|
|
|
-function colorReset(id: string) {
|
|
|
|
- mark.value.forEach((v, i) => {
|
|
|
|
- v.color = '#000'
|
|
|
|
- markClone.value[i].color = '#000'
|
|
|
|
- })
|
|
|
|
- if (!id)
|
|
|
|
- return
|
|
|
|
- const current1 = mark.value.length ? mark.value.find(v => v.id === id) : ''
|
|
|
|
- const current2 = markClone.value.length ? markClone.value.find(v => v.id === id) : ''
|
|
|
|
- current1 && (current1.color = '#FFEB3B')
|
|
|
|
- current2 && (current2.color = '#FFEB3B')
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
// 删除标记
|
|
// 删除标记
|
|
function del(id: string) {
|
|
function del(id: string) {
|
|
if (!id)
|
|
if (!id)
|
|
return
|
|
return
|
|
- PTXYMap.delete(id)
|
|
|
|
- mark.value = mark.value.filter(v => v.id !== id)
|
|
|
|
- markClone.value = markClone.value.filter(v => v.id !== id)
|
|
|
|
- mark.value.length && (currentMark.value = mark.value[mark.value.length - 1])
|
|
|
|
- mark.value.length && colorReset(currentMark.value.id)
|
|
|
|
|
|
+ PTXYMap.value.delete(id)
|
|
}
|
|
}
|
|
// 清空标记
|
|
// 清空标记
|
|
function clear() {
|
|
function clear() {
|
|
- mark.value = []
|
|
|
|
- markClone.value = []
|
|
|
|
- currentMark.value = {
|
|
|
|
- id: '',
|
|
|
|
- p: 0.0,
|
|
|
|
- t: 0.0,
|
|
|
|
- x: 0.0,
|
|
|
|
- y: 0.0,
|
|
|
|
- color: '#000',
|
|
|
|
- }
|
|
|
|
- PTXYMap.clear()
|
|
|
|
|
|
+ currentMark.value = ''
|
|
|
|
+ PTXYMap.value.clear()
|
|
}
|
|
}
|
|
|
|
|
|
-/**
|
|
|
|
- * 在缩放后的图片上标点(考虑外边距)
|
|
|
|
- * @param {HTMLElement} imgElement - 图片元素
|
|
|
|
- */
|
|
|
|
-function markPointOnImageWithMargin(imgElement: HTMLImageElement) {
|
|
|
|
|
|
+function RefreshDisplayParameters(imgElement: HTMLImageElement) {
|
|
|
|
+ if (!imgElement) {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
const originalWidth = imgElement.naturalWidth // 原始宽度
|
|
const originalWidth = imgElement.naturalWidth // 原始宽度
|
|
const originalHeight = imgElement.naturalHeight // 原始高度
|
|
const originalHeight = imgElement.naturalHeight // 原始高度
|
|
const scaledWidth = imgElement.offsetWidth // 缩放后宽度
|
|
const scaledWidth = imgElement.offsetWidth // 缩放后宽度
|
|
const scaledHeight = imgElement.offsetHeight // 缩放后高度
|
|
const scaledHeight = imgElement.offsetHeight // 缩放后高度
|
|
const top = +(imgElement as HTMLImageElement).style.marginTop.replace('px', '') // 外边距上边距
|
|
const top = +(imgElement as HTMLImageElement).style.marginTop.replace('px', '') // 外边距上边距
|
|
const left = +(imgElement as HTMLImageElement).style.marginLeft.replace('px', '') // 外边距左边距
|
|
const left = +(imgElement as HTMLImageElement).style.marginLeft.replace('px', '') // 外边距左边距
|
|
- mark.value = markClone.value.map((v) => {
|
|
|
|
- return {
|
|
|
|
- id: v.id,
|
|
|
|
- x: v.x * (scaledWidth / originalWidth) + left - 12,
|
|
|
|
- y: v.y * (scaledHeight / originalHeight) + top - 12,
|
|
|
|
- p: v.p,
|
|
|
|
- t: v.t,
|
|
|
|
- color: v.color,
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- mark.value.length && colorReset(currentMark.value.id)
|
|
|
|
|
|
+
|
|
|
|
+ ShowWidthZoom.value = scaledWidth / originalWidth
|
|
|
|
+ ShowHeightZoom.value = scaledHeight / originalHeight
|
|
|
|
+ ShowWidthOffset.value = left
|
|
|
|
+ ShowHeightOffset.value = top
|
|
}
|
|
}
|
|
|
|
|
|
function dbClick(e: MouseEvent) {
|
|
function dbClick(e: MouseEvent) {
|
|
// 获取鼠标点击位置
|
|
// 获取鼠标点击位置
|
|
const { offsetX, offsetY } = e
|
|
const { offsetX, offsetY } = e
|
|
const { originalX, originalY } = calculateOriginalCoords(offsetX, offsetY, (imgElement as HTMLImageElement).naturalWidth, (imgElement as HTMLImageElement).naturalHeight, (imgElement as HTMLImageElement).offsetWidth, (imgElement as HTMLImageElement).offsetHeight)
|
|
const { originalX, originalY } = calculateOriginalCoords(offsetX, offsetY, (imgElement as HTMLImageElement).naturalWidth, (imgElement as HTMLImageElement).naturalHeight, (imgElement as HTMLImageElement).offsetWidth, (imgElement as HTMLImageElement).offsetHeight)
|
|
-
|
|
|
|
- const markV = {
|
|
|
|
- id: uuidv4(),
|
|
|
|
- x: originalX,
|
|
|
|
- y: originalY,
|
|
|
|
- p: 0.0,
|
|
|
|
- t: 0.0,
|
|
|
|
- color: '#000',
|
|
|
|
- }
|
|
|
|
- mark.value.push(markV)
|
|
|
|
- markClone.value.push(markV)
|
|
|
|
- currentMark.value = mark.value[mark.value.length - 1]
|
|
|
|
- PTXYMap.set(currentMark.value.id, { P: 0.0, T: 0.0, X: currentMark.value.x, Y: currentMark.value.y })
|
|
|
|
- markPointOnImageWithMargin(imgElement as HTMLImageElement)
|
|
|
|
|
|
+ const id = uuidv4()
|
|
|
|
+ PTXYMap.value.set(id, { P: 0.0, T: 0.0, X: originalX, Y: originalY })
|
|
|
|
+ currentMark.value = id
|
|
|
|
+ RefreshDisplayParameters(imgElement as HTMLImageElement)
|
|
}
|
|
}
|
|
|
|
|
|
-function getPTXY(id: string, x: number, y: number) {
|
|
|
|
|
|
+function getPTXY(id: string) {
|
|
if (!system.globalConfig.serverUrl) {
|
|
if (!system.globalConfig.serverUrl) {
|
|
system.msg('请先设置服务器地址')
|
|
system.msg('请先设置服务器地址')
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- if (!currentMark.value.id) {
|
|
|
|
|
|
+ if (!currentMark.value) {
|
|
system.msg('请双击创建标记')
|
|
system.msg('请双击创建标记')
|
|
return
|
|
return
|
|
}
|
|
}
|
|
@@ -268,19 +200,20 @@ function getPTXY(id: string, x: number, y: number) {
|
|
system.msg('请选择球机')
|
|
system.msg('请选择球机')
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // 获取已有的 X/Y
|
|
|
|
+ const existing = PTXYMap.value.get(currentMark.value)
|
|
|
|
+ const x = existing?.X ?? 0
|
|
|
|
+ const y = existing?.Y ?? 0
|
|
|
|
+
|
|
system.msg('正在装载,请稍后...')
|
|
system.msg('正在装载,请稍后...')
|
|
isDisabled.value = false
|
|
isDisabled.value = false
|
|
axios.get(`https://${system.globalConfig.serverUrl}/api/BallCamera/PtzGet?CameraId=${id}`).then((res) => {
|
|
axios.get(`https://${system.globalConfig.serverUrl}/api/BallCamera/PtzGet?CameraId=${id}`).then((res) => {
|
|
- PTXYMap.set(currentMark.value.id, { P: res.data.data.P, T: res.data.data.T, X: x, Y: y })
|
|
|
|
- mark.value.forEach((v, i) => {
|
|
|
|
- if (v.id === currentMark.value.id) {
|
|
|
|
- const p = system.mapping(system.positiveConfig.P_Start, system.positiveConfig.P_Max, res.data.data.P, system.positiveConfig.P_Direction, '')
|
|
|
|
- const t = system.mapping(system.positiveConfig.T_Start, system.positiveConfig.T_Max, res.data.data.T, system.positiveConfig.T_Direction, '')
|
|
|
|
- v.p = p
|
|
|
|
- v.t = t
|
|
|
|
- markClone.value[i].p = p
|
|
|
|
- markClone.value[i].t = t
|
|
|
|
- }
|
|
|
|
|
|
+ PTXYMap.value.set(currentMark.value, {
|
|
|
|
+ P: system.mapping(system.positiveConfig.P_Start, system.positiveConfig.P_Max, res.data.data.P, system.positiveConfig.P_Direction, ''),
|
|
|
|
+ T: system.mapping(system.positiveConfig.T_Start, system.positiveConfig.T_Max, res.data.data.T, system.positiveConfig.T_Direction, ''),
|
|
|
|
+ X: x,
|
|
|
|
+ Y: y,
|
|
})
|
|
})
|
|
isDisabled.value = true
|
|
isDisabled.value = true
|
|
system.msg('装载完成')
|
|
system.msg('装载完成')
|
|
@@ -290,43 +223,47 @@ function getPTXY(id: string, x: number, y: number) {
|
|
function loadOld() {
|
|
function loadOld() {
|
|
if (!system.ballCameraInfo)
|
|
if (!system.ballCameraInfo)
|
|
return
|
|
return
|
|
- PTXYMap.clear()
|
|
|
|
|
|
+ PTXYMap.value.clear()
|
|
const ps: any = system.ballCameraInfo.Matrix.PointSet
|
|
const ps: any = system.ballCameraInfo.Matrix.PointSet
|
|
|
|
|
|
for (const key in ps) {
|
|
for (const key in ps) {
|
|
- PTXYMap.set(key, {
|
|
|
|
|
|
+ PTXYMap.value.set(key, {
|
|
P: ps[key].P,
|
|
P: ps[key].P,
|
|
T: ps[key].T,
|
|
T: ps[key].T,
|
|
X: ps[key].X,
|
|
X: ps[key].X,
|
|
Y: ps[key].Y,
|
|
Y: ps[key].Y,
|
|
})
|
|
})
|
|
- mark.value.push({
|
|
|
|
- id: key,
|
|
|
|
- x: ps[key].X,
|
|
|
|
- y: ps[key].Y,
|
|
|
|
- p: ps[key].P,
|
|
|
|
- t: ps[key].T,
|
|
|
|
- color: '#000',
|
|
|
|
- })
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (!mark.value.length)
|
|
|
|
- return
|
|
|
|
- markClone.value = JSON.parse(JSON.stringify(mark.value))
|
|
|
|
- colorReset(mark.value[mark.value.length - 1].id)
|
|
|
|
- currentMark.value = mark.value[mark.value.length - 1]
|
|
|
|
- markPointOnImageWithMargin(imgElement as HTMLImageElement)
|
|
|
|
|
|
+ currentMark.value = ''
|
|
|
|
+ RefreshDisplayParameters(imgElement as HTMLImageElement)
|
|
}
|
|
}
|
|
|
|
|
|
-function jumpToPT(p: number, t: number) {
|
|
|
|
|
|
+function jumpToPT() {
|
|
if (!system.globalConfig.serverUrl) {
|
|
if (!system.globalConfig.serverUrl) {
|
|
system.msg('请先设置服务器地址')
|
|
system.msg('请先设置服务器地址')
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- const pV = system.mapping(system.positiveConfig.P_Start, system.positiveConfig.P_Max, p, system.positiveConfig.P_Direction, 'inv')
|
|
|
|
- const tV = system.mapping(system.positiveConfig.T_Start, system.positiveConfig.T_Max, t, system.positiveConfig.T_Direction, 'inv')
|
|
|
|
- axios.put(`https://${system.globalConfig.serverUrl}/api/BallCamera/PtzSet?CameraId=${system.currentBallCamera}&Action=1`, { P: pV, T: tV, Z: 0 }).then((res) => {
|
|
|
|
- console.log(res)
|
|
|
|
|
|
+ if (!PTXYMap.value.get(currentMark.value)) {
|
|
|
|
+ system.msg('未选中标记')
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Get the current mark's data from PTXYMap
|
|
|
|
+ const currentMarkData = PTXYMap.value.get(currentMark.value)
|
|
|
|
+
|
|
|
|
+ // Check if the data exists, if not, set pV and tV to default values
|
|
|
|
+ const pV = currentMarkData
|
|
|
|
+ ? system.mapping(system.positiveConfig.P_Start, system.positiveConfig.P_Max, currentMarkData.P, system.positiveConfig.P_Direction, 'inv')
|
|
|
|
+ : 0
|
|
|
|
+ const tV = currentMarkData
|
|
|
|
+ ? system.mapping(system.positiveConfig.T_Start, system.positiveConfig.T_Max, currentMarkData.T, system.positiveConfig.T_Direction, 'inv')
|
|
|
|
+ : 0
|
|
|
|
+ axios.put(`https://${system.globalConfig.serverUrl}/api/BallCamera/PtzSet?CameraId=${system.currentBallCamera}&Action=1`, {
|
|
|
|
+ P: pV,
|
|
|
|
+ T: tV,
|
|
|
|
+ Z: 0,
|
|
|
|
+ }).then((res) => {
|
|
|
|
+ console.warn(res)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
@@ -335,16 +272,9 @@ function ptByMatrix() {
|
|
system.msg('请先设置服务器地址')
|
|
system.msg('请先设置服务器地址')
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- const out: any = WarpingPtByHomography(window.outMatrix, currentMark.value.x, currentMark.value.y)
|
|
|
|
- PTXYMap.set(currentMark.value.id, { P: out.P, T: out.T, X: currentMark.value.x, Y: currentMark.value.y })
|
|
|
|
- mark.value.forEach((v, i) => {
|
|
|
|
- if (v.id === currentMark.value.id) {
|
|
|
|
- v.p = out.P
|
|
|
|
- v.t = out.T
|
|
|
|
- markClone.value[i].p = out.P
|
|
|
|
- markClone.value[i].t = out.T
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
|
|
+ const v = PTXYMap.value.get(currentMark.value)
|
|
|
|
+ const out: any = WarpingPtByHomography(v ? v.X : 0, v ? v.Y : 0)
|
|
|
|
+ PTXYMap.value.set(currentMark.value, { P: out.X, T: out.Y, X: v ? v.X : 0, Y: v ? v.Y : 0 })
|
|
}
|
|
}
|
|
|
|
|
|
function updateMatrix(callback?: () => void) {
|
|
function updateMatrix(callback?: () => void) {
|
|
@@ -357,7 +287,7 @@ function updateMatrix(callback?: () => void) {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
const pointSet: any = {}
|
|
const pointSet: any = {}
|
|
- PTXYMap.forEach((v, k) => {
|
|
|
|
|
|
+ PTXYMap.value.forEach((v, k) => {
|
|
pointSet[k] = {
|
|
pointSet[k] = {
|
|
P: v.P,
|
|
P: v.P,
|
|
T: v.T,
|
|
T: v.T,
|
|
@@ -372,7 +302,7 @@ function updateMatrix(callback?: () => void) {
|
|
T_Start: system.positiveConfig.T_Start,
|
|
T_Start: system.positiveConfig.T_Start,
|
|
T_Max: system.positiveConfig.T_Max,
|
|
T_Max: system.positiveConfig.T_Max,
|
|
T_Positive_Direction: system.positiveConfig.T_Direction,
|
|
T_Positive_Direction: system.positiveConfig.T_Direction,
|
|
- Matrix: window.outMatrix || system.ballCameraInfo?.Matrix.Matrix || [],
|
|
|
|
|
|
+ Matrix: outMatrix.value || system.ballCameraInfo?.Matrix.Matrix || [],
|
|
InvMatrix: system.ballCameraInfo?.Matrix.InvMatrix || [],
|
|
InvMatrix: system.ballCameraInfo?.Matrix.InvMatrix || [],
|
|
PointSet: JSON.stringify(pointSet) === '{}' ? system.ballCameraInfo?.Matrix.PointSet : pointSet,
|
|
PointSet: JSON.stringify(pointSet) === '{}' ? system.ballCameraInfo?.Matrix.PointSet : pointSet,
|
|
}
|
|
}
|
|
@@ -383,11 +313,11 @@ function updateMatrix(callback?: () => void) {
|
|
}
|
|
}
|
|
|
|
|
|
function saveBtn() {
|
|
function saveBtn() {
|
|
- if (!window.outMatrix) {
|
|
|
|
|
|
+ if (!outMatrix.value) {
|
|
system.msg('请先生成映射矩阵')
|
|
system.msg('请先生成映射矩阵')
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- const invMatrix = calculateInverseMatrix(window.outMatrix)
|
|
|
|
|
|
+ const invMatrix = calculateInverseMatrix(outMatrix.value)
|
|
system.ballCameraInfo!.Matrix.InvMatrix = invMatrix
|
|
system.ballCameraInfo!.Matrix.InvMatrix = invMatrix
|
|
updateMatrix(() => {
|
|
updateMatrix(() => {
|
|
system.msg('保存成功')
|
|
system.msg('保存成功')
|
|
@@ -428,10 +358,10 @@ function initViewer() {
|
|
naturalWH.height = e.detail.image.naturalHeight
|
|
naturalWH.height = e.detail.image.naturalHeight
|
|
},
|
|
},
|
|
zoomed() {
|
|
zoomed() {
|
|
- markPointOnImageWithMargin(imgElement as HTMLImageElement)
|
|
|
|
|
|
+ RefreshDisplayParameters(imgElement as HTMLImageElement)
|
|
},
|
|
},
|
|
moved() {
|
|
moved() {
|
|
- markPointOnImageWithMargin(imgElement as HTMLImageElement)
|
|
|
|
|
|
+ RefreshDisplayParameters(imgElement as HTMLImageElement)
|
|
},
|
|
},
|
|
})
|
|
})
|
|
}
|
|
}
|
|
@@ -449,6 +379,19 @@ watch(() => system.globalConfig.imgUrl, (file) => {
|
|
watch(() => system.positiveConfig, () => {
|
|
watch(() => system.positiveConfig, () => {
|
|
updateMatrix()
|
|
updateMatrix()
|
|
}, { deep: true })
|
|
}, { deep: true })
|
|
|
|
+// 监听窗口变化
|
|
|
|
+window.addEventListener('resize', () => {
|
|
|
|
+ RefreshDisplayParameters(imgElement as HTMLImageElement)
|
|
|
|
+})
|
|
|
|
+
|
|
|
|
+document.addEventListener('visibilitychange', () => {
|
|
|
|
+ if (document.hidden) {
|
|
|
|
+ RefreshDisplayParameters(imgElement as HTMLImageElement)
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ RefreshDisplayParameters(imgElement as HTMLImageElement)
|
|
|
|
+ }
|
|
|
|
+})
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<template>
|
|
<template>
|
|
@@ -564,9 +507,17 @@ watch(() => system.positiveConfig, () => {
|
|
<!-- 视频播放 -->
|
|
<!-- 视频播放 -->
|
|
<div class="p-1">
|
|
<div class="p-1">
|
|
<v-card
|
|
<v-card
|
|
- :loading="system.canvasVideoLoading" title="画面预览"
|
|
|
|
- :subtitle="`当前P ${system.PT.p},当前T ${system.PT.t},当前标定点位的个数0`" text=""
|
|
|
|
|
|
+ :loading="system.canvasVideoLoading" title="画面预览" text=""
|
|
>
|
|
>
|
|
|
|
+ <template #subtitle>
|
|
|
|
+ <div class="d-flex align-center justify-between">
|
|
|
|
+ <span>当前P {{ system.PT.p }},当前T {{ system.PT.t }},当前标定点位的个数{{ PTXYMap.size }}</span>
|
|
|
|
+ <v-btn
|
|
|
|
+ v-tooltip:top="'刷新PT位置'" size="large" rounded="0" variant="flat" icon="mdi-refresh"
|
|
|
|
+ @click="system.GetPT(system.currentBallCamera)"
|
|
|
|
+ />
|
|
|
|
+ </div>
|
|
|
|
+ </template>
|
|
<div class="relative aspect-ratio-video b-1 b-#ccc b-solid bg-black">
|
|
<div class="relative aspect-ratio-video b-1 b-#ccc b-solid bg-black">
|
|
<v-icon
|
|
<v-icon
|
|
icon="mdi-plus" color="#ef4444" class="absolute left-50% top-50% transform-translate--50%" :style="{
|
|
icon="mdi-plus" color="#ef4444" class="absolute left-50% top-50% transform-translate--50%" :style="{
|
|
@@ -640,19 +591,20 @@ watch(() => system.positiveConfig, () => {
|
|
<img id="uploadImage" class="w-full" :src="imgSrc" alt="图片预览" @load="imgSrcLoad">
|
|
<img id="uploadImage" class="w-full" :src="imgSrc" alt="图片预览" @load="imgSrcLoad">
|
|
<v-empty-state v-if="!imgSrcLoading && !imgSrc" icon="mdi-image-broken-variant" title="暂无上传图片" />
|
|
<v-empty-state v-if="!imgSrcLoading && !imgSrc" icon="mdi-image-broken-variant" title="暂无上传图片" />
|
|
<v-icon
|
|
<v-icon
|
|
- v-for="(item, index) in mark" :key="index" :style="{
|
|
|
|
- top: `${item.y}px`,
|
|
|
|
- left: `${item.x}px`,
|
|
|
|
|
|
+ v-for="(item) in Array.from(PTXYMap)" :key="item[0]"
|
|
|
|
+ :style="{
|
|
|
|
+ top: `${(item[1].Y * ShowHeightZoom + ShowHeightOffset) - 12}px`,
|
|
|
|
+ left: `${(item[1].X * ShowWidthZoom + ShowWidthOffset) - 12}px`,
|
|
textShadow: `0 -1px #fff, 1px 0px #fff, 0 1px #fff, -1px 0 #fff,
|
|
textShadow: `0 -1px #fff, 1px 0px #fff, 0 1px #fff, -1px 0 #fff,
|
|
-1px -1px #fff, 1px 1px #fff, 1px -1px #fff, -1px 1px #fff`,
|
|
-1px -1px #fff, 1px 1px #fff, 1px -1px #fff, -1px 1px #fff`,
|
|
- }" :color="item.color" icon="mdi-plus-thick" class="absolute bottom-0" variant="tonal"
|
|
|
|
- @click="markClick(item)"
|
|
|
|
|
|
+ }" :color="currentMark === item[0] ? '#FFEB3B' : '#000000'" icon="mdi-plus-thick" class="absolute bottom-0" variant="tonal"
|
|
|
|
+ @click="markClick(item[0])"
|
|
/>
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-col items-center">
|
|
<div class="flex flex-col items-center">
|
|
<v-chip class="ma-2" color="blue" label>
|
|
<v-chip class="ma-2" color="blue" label>
|
|
<v-icon icon="mdi-map-marker-radius" start />
|
|
<v-icon icon="mdi-map-marker-radius" start />
|
|
- 选中的标记 P: {{ currentMark.p }}, T: {{ currentMark.t }}
|
|
|
|
|
|
+ 选中的标记 P: {{ PTXYMap.get(currentMark)?.P }}, T: {{ PTXYMap.get(currentMark)?.T }}
|
|
</v-chip>
|
|
</v-chip>
|
|
<v-card min-width="90%">
|
|
<v-card min-width="90%">
|
|
<v-card-text>
|
|
<v-card-text>
|
|
@@ -664,7 +616,7 @@ watch(() => system.positiveConfig, () => {
|
|
</div>
|
|
</div>
|
|
<v-row justify="center">
|
|
<v-row justify="center">
|
|
<v-col>
|
|
<v-col>
|
|
- <v-btn width="100%" @click="getPTXY(system.currentBallCamera, currentMark.x, currentMark.y)">
|
|
|
|
|
|
+ <v-btn width="100%" @click="getPTXY(system.currentBallCamera)">
|
|
当前PT位置装载PT
|
|
当前PT位置装载PT
|
|
</v-btn>
|
|
</v-btn>
|
|
</v-col>
|
|
</v-col>
|
|
@@ -680,7 +632,7 @@ watch(() => system.positiveConfig, () => {
|
|
</v-btn>
|
|
</v-btn>
|
|
</v-col>
|
|
</v-col>
|
|
<v-col>
|
|
<v-col>
|
|
- <v-btn width="100%" @click="jumpToPT(currentMark.p, currentMark.t)">
|
|
|
|
|
|
+ <v-btn width="100%" @click="jumpToPT">
|
|
跳转到PT
|
|
跳转到PT
|
|
</v-btn>
|
|
</v-btn>
|
|
</v-col>
|
|
</v-col>
|
|
@@ -689,7 +641,7 @@ watch(() => system.positiveConfig, () => {
|
|
<v-btn color="#FFA726" @click="clear">
|
|
<v-btn color="#FFA726" @click="clear">
|
|
清空
|
|
清空
|
|
</v-btn>
|
|
</v-btn>
|
|
- <v-btn color="#F44336" @click="del(currentMark.id)">
|
|
|
|
|
|
+ <v-btn color="#F44336" @click="del(currentMark)">
|
|
删除选中
|
|
删除选中
|
|
</v-btn>
|
|
</v-btn>
|
|
</div>
|
|
</div>
|