bugfixes
This commit is contained in:
parent
93b51978c5
commit
4484fb978d
BIN
backend/dist/nsfwapp-linux-amd64
vendored
BIN
backend/dist/nsfwapp-linux-amd64
vendored
Binary file not shown.
BIN
backend/dist/nsfwapp.exe
vendored
BIN
backend/dist/nsfwapp.exe
vendored
Binary file not shown.
BIN
backend/dist/nsfwapp_amd64.deb
vendored
BIN
backend/dist/nsfwapp_amd64.deb
vendored
Binary file not shown.
@ -4523,10 +4523,14 @@ export default function TrainingTab(props: {
|
|||||||
const poseFaceImagePanCenterRef = useRef<{ x: number; y: number } | null>(null)
|
const poseFaceImagePanCenterRef = useRef<{ x: number; y: number } | null>(null)
|
||||||
const poseFaceImagePanGestureRef = useRef<PoseFaceImagePanGesture | null>(null)
|
const poseFaceImagePanGestureRef = useRef<PoseFaceImagePanGesture | null>(null)
|
||||||
const poseFaceImageDisplayCameraRef = useRef<PoseFaceImageCamera | null>(null)
|
const poseFaceImageDisplayCameraRef = useRef<PoseFaceImageCamera | null>(null)
|
||||||
|
const poseFaceImageCameraRef = useRef<PoseFaceImageCamera | null>(null)
|
||||||
const poseFaceImageCameraAnimationFrameRef = useRef<number | null>(null)
|
const poseFaceImageCameraAnimationFrameRef = useRef<number | null>(null)
|
||||||
|
const poseFaceImageZoomAnimationResolveRef = useRef<(() => void) | null>(null)
|
||||||
|
const poseFaceImageZoomAppliedRef = useRef(false)
|
||||||
const poseFaceImageZoomOutActiveRef = useRef(false)
|
const poseFaceImageZoomOutActiveRef = useRef(false)
|
||||||
const correctionRef = useRef<CorrectionState>(correction)
|
const correctionRef = useRef<CorrectionState>(correction)
|
||||||
const hasManualCorrectionRef = useRef(hasManualCorrection)
|
const hasManualCorrectionRef = useRef(hasManualCorrection)
|
||||||
|
const saveFeedbackInFlightRef = useRef(false)
|
||||||
const latestGestureBoxRef = useRef<TrainingBox | null>(null)
|
const latestGestureBoxRef = useRef<TrainingBox | null>(null)
|
||||||
const pendingPointerMoveRef = useRef<{ clientX: number; clientY: number } | null>(null)
|
const pendingPointerMoveRef = useRef<{ clientX: number; clientY: number } | null>(null)
|
||||||
const pointerMoveRafRef = useRef<number | null>(null)
|
const pointerMoveRafRef = useRef<number | null>(null)
|
||||||
@ -4634,8 +4638,14 @@ export default function TrainingTab(props: {
|
|||||||
cancelAnimationFrame(poseFaceImageCameraAnimationFrameRef.current)
|
cancelAnimationFrame(poseFaceImageCameraAnimationFrameRef.current)
|
||||||
poseFaceImageCameraAnimationFrameRef.current = null
|
poseFaceImageCameraAnimationFrameRef.current = null
|
||||||
}
|
}
|
||||||
|
if (poseFaceImageZoomAnimationResolveRef.current !== null) {
|
||||||
|
poseFaceImageZoomAnimationResolveRef.current()
|
||||||
|
poseFaceImageZoomAnimationResolveRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
|
poseFaceImageCameraRef.current = null
|
||||||
poseFaceImageDisplayCameraRef.current = null
|
poseFaceImageDisplayCameraRef.current = null
|
||||||
|
poseFaceImageZoomAppliedRef.current = false
|
||||||
poseFaceImageZoomOutActiveRef.current = false
|
poseFaceImageZoomOutActiveRef.current = false
|
||||||
setPoseFaceImageDisplayCamera(null)
|
setPoseFaceImageDisplayCamera(null)
|
||||||
setPoseFaceImageCameraAnimating(false)
|
setPoseFaceImageCameraAnimating(false)
|
||||||
@ -4653,6 +4663,87 @@ export default function TrainingTab(props: {
|
|||||||
setPoseKeypointMapAnimating(false)
|
setPoseKeypointMapAnimating(false)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const zoomOutPoseFaceImageToFullImage = useCallback(() => {
|
||||||
|
if (poseFaceImageZoomAnimationResolveRef.current !== null) {
|
||||||
|
poseFaceImageZoomAnimationResolveRef.current()
|
||||||
|
poseFaceImageZoomAnimationResolveRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (poseFaceImageCameraAnimationFrameRef.current !== null) {
|
||||||
|
cancelAnimationFrame(poseFaceImageCameraAnimationFrameRef.current)
|
||||||
|
poseFaceImageCameraAnimationFrameRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const fromCamera =
|
||||||
|
poseFaceImageDisplayCameraRef.current ??
|
||||||
|
poseFaceImageCameraRef.current ??
|
||||||
|
poseFaceImageFullCamera()
|
||||||
|
const toCamera = poseFaceImageFullCamera()
|
||||||
|
const shouldAnimate =
|
||||||
|
poseFaceImageZoomAppliedRef.current ||
|
||||||
|
poseFaceImageZoomOutActiveRef.current ||
|
||||||
|
Math.abs(fromCamera.scale - toCamera.scale) > 0.001 ||
|
||||||
|
Math.abs(fromCamera.translateX - toCamera.translateX) > 0.5 ||
|
||||||
|
Math.abs(fromCamera.translateY - toCamera.translateY) > 0.5
|
||||||
|
|
||||||
|
switchPoseKeypointMapView('body')
|
||||||
|
|
||||||
|
if (!shouldAnimate) {
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise<void>((resolve) => {
|
||||||
|
let settled = false
|
||||||
|
const settle = () => {
|
||||||
|
if (settled) return
|
||||||
|
settled = true
|
||||||
|
if (poseFaceImageZoomAnimationResolveRef.current === settle) {
|
||||||
|
poseFaceImageZoomAnimationResolveRef.current = null
|
||||||
|
}
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
const finish = () => {
|
||||||
|
poseFaceImageCameraAnimationFrameRef.current = null
|
||||||
|
poseFaceImageDisplayCameraRef.current = null
|
||||||
|
poseFaceImageZoomOutActiveRef.current = false
|
||||||
|
poseFaceImageZoomAppliedRef.current = false
|
||||||
|
setPoseFaceImageDisplayCamera(null)
|
||||||
|
setPoseFaceImageCameraAnimating(false)
|
||||||
|
setPoseFaceImageZoomOutActive(false)
|
||||||
|
settle()
|
||||||
|
}
|
||||||
|
|
||||||
|
poseFaceImagePanGestureRef.current = null
|
||||||
|
poseFaceImagePanCenterRef.current = null
|
||||||
|
setPoseFaceImagePanCenter(null)
|
||||||
|
setPoseFaceImagePanning(false)
|
||||||
|
poseFaceImageZoomAnimationResolveRef.current = settle
|
||||||
|
poseFaceImageZoomOutActiveRef.current = true
|
||||||
|
setPoseFaceImageZoomOutActive(true)
|
||||||
|
setPoseFaceImageCameraAnimating(true)
|
||||||
|
poseFaceImageDisplayCameraRef.current = fromCamera
|
||||||
|
setPoseFaceImageDisplayCamera(fromCamera)
|
||||||
|
|
||||||
|
const startedAt = performance.now()
|
||||||
|
const animateZoomOut = (now: number) => {
|
||||||
|
const progress = Math.min(1, (now - startedAt) / POSE_FACE_IMAGE_ZOOM_DURATION_MS)
|
||||||
|
const nextCamera = interpolatePoseFaceImageCamera(fromCamera, toCamera, progress)
|
||||||
|
|
||||||
|
poseFaceImageDisplayCameraRef.current = nextCamera
|
||||||
|
setPoseFaceImageDisplayCamera(nextCamera)
|
||||||
|
|
||||||
|
if (progress < 1) {
|
||||||
|
poseFaceImageCameraAnimationFrameRef.current = requestAnimationFrame(animateZoomOut)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
poseFaceImageCameraAnimationFrameRef.current = requestAnimationFrame(animateZoomOut)
|
||||||
|
})
|
||||||
|
}, [switchPoseKeypointMapView])
|
||||||
|
|
||||||
const labelsRef = useRef<TrainingLabels>(emptyLabels)
|
const labelsRef = useRef<TrainingLabels>(emptyLabels)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -4790,13 +4881,14 @@ export default function TrainingTab(props: {
|
|||||||
)
|
)
|
||||||
const activePosePerson =
|
const activePosePerson =
|
||||||
activePosePersonIndex !== null ? posePersons[activePosePersonIndex] : undefined
|
activePosePersonIndex !== null ? posePersons[activePosePersonIndex] : undefined
|
||||||
const activePosePersonVisible = Boolean(
|
const activePosePersonSelectable = Boolean(
|
||||||
activePosePerson &&
|
activePosePersonIndex !== null &&
|
||||||
(hasVisiblePoseBox(activePosePerson) ||
|
activePosePersonIndex >= 0 &&
|
||||||
activePosePerson.keypoints?.some(isPoseKeypointVisible))
|
activePosePersonIndex < posePersons.length &&
|
||||||
|
activePosePerson
|
||||||
)
|
)
|
||||||
const poseCorrectionPersonIndex =
|
const poseCorrectionPersonIndex =
|
||||||
activePosePersonIndex !== null && activePosePersonVisible
|
activePosePersonIndex !== null && activePosePersonSelectable
|
||||||
? activePosePersonIndex
|
? activePosePersonIndex
|
||||||
: visiblePosePersonCount === 1
|
: visiblePosePersonCount === 1
|
||||||
? firstVisiblePosePersonIndex
|
? firstVisiblePosePersonIndex
|
||||||
@ -7111,15 +7203,18 @@ export default function TrainingTab(props: {
|
|||||||
negative?: boolean
|
negative?: boolean
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
if (!sample || trainingRunning) return
|
if (!sample || trainingRunning || saveFeedbackInFlightRef.current) return
|
||||||
|
|
||||||
|
saveFeedbackInFlightRef.current = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await zoomOutPoseFaceImageToFullImage()
|
||||||
resetPoseUiToBoxes()
|
resetPoseUiToBoxes()
|
||||||
setSavingOverlayText(editingFeedback ? 'Feedback wird aktualisiert…' : 'Feedback wird gespeichert…')
|
setSavingOverlayText(editingFeedback ? 'Feedback wird aktualisiert…' : 'Feedback wird gespeichert…')
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
setMessage(null)
|
setMessage(null)
|
||||||
|
|
||||||
try {
|
|
||||||
const normalizedBoxes = (correction.boxes ?? [])
|
const normalizedBoxes = (correction.boxes ?? [])
|
||||||
.map(normalizeBox)
|
.map(normalizeBox)
|
||||||
.filter((box) => box.label && box.w > 0 && box.h > 0)
|
.filter((box) => box.label && box.w > 0 && box.h > 0)
|
||||||
@ -7262,6 +7357,7 @@ export default function TrainingTab(props: {
|
|||||||
|
|
||||||
setError(`Feedback konnte nicht gespeichert werden. ${short}`)
|
setError(`Feedback konnte nicht gespeichert werden. ${short}`)
|
||||||
} finally {
|
} finally {
|
||||||
|
saveFeedbackInFlightRef.current = false
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
setSavingOverlayText('')
|
setSavingOverlayText('')
|
||||||
}
|
}
|
||||||
@ -7276,6 +7372,7 @@ export default function TrainingTab(props: {
|
|||||||
loadNextImportedQueuedSample,
|
loadNextImportedQueuedSample,
|
||||||
resetPoseUiToBoxes,
|
resetPoseUiToBoxes,
|
||||||
trainingRunning,
|
trainingRunning,
|
||||||
|
zoomOutPoseFaceImageToFullImage,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -8676,6 +8773,8 @@ export default function TrainingTab(props: {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}, [imageBoxSize.height, imageBoxSize.width, imageLayerStyle, poseFaceImageEffectiveZoomRegion, poseFaceImageZoomActive])
|
}, [imageBoxSize.height, imageBoxSize.width, imageLayerStyle, poseFaceImageEffectiveZoomRegion, poseFaceImageZoomActive])
|
||||||
|
poseFaceImageCameraRef.current = poseFaceImageCamera
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (poseFaceImageZoomOutActiveRef.current) {
|
if (poseFaceImageZoomOutActiveRef.current) {
|
||||||
return
|
return
|
||||||
@ -8753,6 +8852,8 @@ export default function TrainingTab(props: {
|
|||||||
}
|
}
|
||||||
}, [poseFaceImageRenderedCamera])
|
}, [poseFaceImageRenderedCamera])
|
||||||
const poseFaceImageZoomApplied = Boolean(poseFaceImageZoomStyle)
|
const poseFaceImageZoomApplied = Boolean(poseFaceImageZoomStyle)
|
||||||
|
poseFaceImageZoomAppliedRef.current = poseFaceImageZoomApplied
|
||||||
|
|
||||||
const setClampedPoseFaceImagePanCenter = useCallback((
|
const setClampedPoseFaceImagePanCenter = useCallback((
|
||||||
centerX: number,
|
centerX: number,
|
||||||
centerY: number,
|
centerY: number,
|
||||||
@ -8907,54 +9008,8 @@ export default function TrainingTab(props: {
|
|||||||
}, [setClampedPoseFaceImagePanCenter])
|
}, [setClampedPoseFaceImagePanCenter])
|
||||||
|
|
||||||
const zoomOutPoseFaceImageToBody = useCallback(() => {
|
const zoomOutPoseFaceImageToBody = useCallback(() => {
|
||||||
if (poseFaceImageCameraAnimationFrameRef.current !== null) {
|
void zoomOutPoseFaceImageToFullImage()
|
||||||
cancelAnimationFrame(poseFaceImageCameraAnimationFrameRef.current)
|
}, [zoomOutPoseFaceImageToFullImage])
|
||||||
poseFaceImageCameraAnimationFrameRef.current = null
|
|
||||||
}
|
|
||||||
|
|
||||||
const fromCamera =
|
|
||||||
poseFaceImageDisplayCameraRef.current ??
|
|
||||||
poseFaceImageCamera ??
|
|
||||||
poseFaceImageFullCamera()
|
|
||||||
const toCamera = poseFaceImageFullCamera()
|
|
||||||
const startedAt = performance.now()
|
|
||||||
|
|
||||||
poseFaceImagePanGestureRef.current = null
|
|
||||||
poseFaceImagePanCenterRef.current = null
|
|
||||||
setPoseFaceImagePanCenter(null)
|
|
||||||
setPoseFaceImagePanning(false)
|
|
||||||
poseFaceImageZoomOutActiveRef.current = true
|
|
||||||
setPoseFaceImageZoomOutActive(true)
|
|
||||||
setPoseFaceImageCameraAnimating(true)
|
|
||||||
poseFaceImageDisplayCameraRef.current = fromCamera
|
|
||||||
setPoseFaceImageDisplayCamera(fromCamera)
|
|
||||||
|
|
||||||
// Die rechte Map darf parallel zurueckfahren; die Bildkamera bleibt durch
|
|
||||||
// den Override sichtbar, bis der Zoom-Out fertig ist.
|
|
||||||
switchPoseKeypointMapView('body')
|
|
||||||
|
|
||||||
const animateZoomOut = (now: number) => {
|
|
||||||
const progress = Math.min(1, (now - startedAt) / POSE_FACE_IMAGE_ZOOM_DURATION_MS)
|
|
||||||
const nextCamera = interpolatePoseFaceImageCamera(fromCamera, toCamera, progress)
|
|
||||||
|
|
||||||
poseFaceImageDisplayCameraRef.current = nextCamera
|
|
||||||
setPoseFaceImageDisplayCamera(nextCamera)
|
|
||||||
|
|
||||||
if (progress < 1) {
|
|
||||||
poseFaceImageCameraAnimationFrameRef.current = requestAnimationFrame(animateZoomOut)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
poseFaceImageCameraAnimationFrameRef.current = null
|
|
||||||
poseFaceImageDisplayCameraRef.current = null
|
|
||||||
poseFaceImageZoomOutActiveRef.current = false
|
|
||||||
setPoseFaceImageDisplayCamera(null)
|
|
||||||
setPoseFaceImageCameraAnimating(false)
|
|
||||||
setPoseFaceImageZoomOutActive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
poseFaceImageCameraAnimationFrameRef.current = requestAnimationFrame(animateZoomOut)
|
|
||||||
}, [poseFaceImageCamera, switchPoseKeypointMapView])
|
|
||||||
|
|
||||||
const deletePosePerson = useCallback((personIndex: number) => {
|
const deletePosePerson = useCallback((personIndex: number) => {
|
||||||
const shouldNavigateAfterDelete =
|
const shouldNavigateAfterDelete =
|
||||||
@ -10426,7 +10481,7 @@ export default function TrainingTab(props: {
|
|||||||
.filter(isPoseKeypointVisible)
|
.filter(isPoseKeypointVisible)
|
||||||
.map((point) => poseKeypointId(point.name))
|
.map((point) => poseKeypointId(point.name))
|
||||||
)
|
)
|
||||||
const disabled = uiLocked || frameBusy || trainingRunning || saving || !targetPerson
|
const disabled = uiLocked || frameBusy || trainingRunning || saving || !poseTrainingAllowed
|
||||||
const faceVisibleCount = POSE_FACE_KEYPOINT_SHAPES.filter((shape) =>
|
const faceVisibleCount = POSE_FACE_KEYPOINT_SHAPES.filter((shape) =>
|
||||||
visibleKeypointIds.has(shape.keypointName)
|
visibleKeypointIds.has(shape.keypointName)
|
||||||
).length
|
).length
|
||||||
@ -10459,10 +10514,29 @@ export default function TrainingTab(props: {
|
|||||||
const showMapParts = !poseKeypointMapAnimating
|
const showMapParts = !poseKeypointMapAnimating
|
||||||
|
|
||||||
const selectPoseKeypoint = (keypointName: string) => {
|
const selectPoseKeypoint = (keypointName: string) => {
|
||||||
if (disabled || targetIndex === null) return
|
if (disabled) return
|
||||||
|
|
||||||
|
let selectionPersonIndex = targetIndex
|
||||||
|
|
||||||
|
if (selectionPersonIndex === null || !targetPerson) {
|
||||||
|
const persons = clonePosePersons(correctionRef.current.posePersons)
|
||||||
|
selectionPersonIndex = persons.length
|
||||||
|
const next: CorrectionState = {
|
||||||
|
...correctionRef.current,
|
||||||
|
posePersons: [...persons, createEmptyPosePerson()],
|
||||||
|
}
|
||||||
|
|
||||||
|
markManualCorrection()
|
||||||
|
correctionRef.current = next
|
||||||
|
setCorrection(next)
|
||||||
|
resetBoxDrawingMode()
|
||||||
|
setShowPoseSkeleton(true)
|
||||||
|
setActivePosePersonIndex(selectionPersonIndex)
|
||||||
|
setMobilePanel('labels')
|
||||||
|
}
|
||||||
|
|
||||||
const pending =
|
const pending =
|
||||||
pendingPoseKeypoint?.personIndex === targetIndex &&
|
pendingPoseKeypoint?.personIndex === selectionPersonIndex &&
|
||||||
pendingPoseKeypoint.keypointName === keypointName
|
pendingPoseKeypoint.keypointName === keypointName
|
||||||
|
|
||||||
if (pending) {
|
if (pending) {
|
||||||
@ -10472,10 +10546,10 @@ export default function TrainingTab(props: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setShowPoseSkeleton(true)
|
setShowPoseSkeleton(true)
|
||||||
setActivePosePersonIndex(targetIndex)
|
setActivePosePersonIndex(selectionPersonIndex)
|
||||||
setSelectedPoseKeypointAction(null)
|
setSelectedPoseKeypointAction(null)
|
||||||
setPendingPoseKeypoint({
|
setPendingPoseKeypoint({
|
||||||
personIndex: targetIndex,
|
personIndex: selectionPersonIndex,
|
||||||
keypointName,
|
keypointName,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -10492,9 +10566,9 @@ export default function TrainingTab(props: {
|
|||||||
<div className="mt-0.5 text-[10px] text-gray-500 dark:text-gray-400">
|
<div className="mt-0.5 text-[10px] text-gray-500 dark:text-gray-400">
|
||||||
{targetPerson
|
{targetPerson
|
||||||
? `${targetPoseLabel}: Punkt wählen und im Bild setzen.`
|
? `${targetPoseLabel}: Punkt wählen und im Bild setzen.`
|
||||||
: visiblePosePersonCount > 1
|
: poseTrainingAllowed
|
||||||
? 'Links erst eine Pose auswählen.'
|
? 'Körperteil wählen, neue Pose wird angelegt.'
|
||||||
: 'Keine Pose vorhanden.'}
|
: 'Für Unknown wird keine Pose trainiert.'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -10530,7 +10604,7 @@ export default function TrainingTab(props: {
|
|||||||
height="1386"
|
height="1386"
|
||||||
preserveAspectRatio="xMidYMid meet"
|
preserveAspectRatio="xMidYMid meet"
|
||||||
pointerEvents="none"
|
pointerEvents="none"
|
||||||
className={targetPerson ? 'opacity-90' : 'opacity-35'}
|
className={disabled ? 'opacity-35' : 'opacity-90'}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{showMapParts && poseKeypointMapView === 'head' ? (
|
{showMapParts && poseKeypointMapView === 'head' ? (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user