Add a visual eye indicatore for eye ratio.
This commit is contained in:
parent
dd4dada65d
commit
5cbec95e74
2 changed files with 117 additions and 3 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { DEFAULT_CONFIG, TIMING } from '../constants.js';
|
import { DEFAULT_CONFIG, TIMING } from '../constants.js';
|
||||||
import { handleError } from '../errorHandler.js';
|
import { handleError } from '../errorHandler.js';
|
||||||
|
import EyeIndicator from './visual/EyeIndicator.svelte';
|
||||||
|
|
||||||
let { disabled = false } = $props();
|
let { disabled = false } = $props();
|
||||||
|
|
||||||
|
|
@ -290,10 +291,10 @@
|
||||||
{#if config.processing.eye_filter.enabled}
|
{#if config.processing.eye_filter.enabled}
|
||||||
<div class="setting-row sub-setting">
|
<div class="setting-row sub-setting">
|
||||||
<label for="min-ear">
|
<label for="min-ear">
|
||||||
<span class="setting-label">Min EAR</span>
|
<span class="setting-label">Minimum eye opening</span>
|
||||||
<span class="setting-hint">Eye Aspect Ratio threshold (lower = more closed)</span>
|
<span class="setting-hint">Discard image if Eye Aspect Ratio is bellow this value</span>
|
||||||
</label>
|
</label>
|
||||||
<div class="setting-control">
|
<div class="setting-control-with-visual">
|
||||||
<input
|
<input
|
||||||
id="min-ear"
|
id="min-ear"
|
||||||
type="range"
|
type="range"
|
||||||
|
|
@ -303,6 +304,9 @@
|
||||||
step="0.02"
|
step="0.02"
|
||||||
/>
|
/>
|
||||||
<span class="value">{config.processing.eye_filter.min_ear.toFixed(2)}</span>
|
<span class="value">{config.processing.eye_filter.min_ear.toFixed(2)}</span>
|
||||||
|
<div class="inline-visual-indicator">
|
||||||
|
<EyeIndicator ear={config.processing.eye_filter.min_ear} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
@ -724,4 +728,23 @@
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: #22c55e;
|
color: #22c55e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Visual indicator layout */
|
||||||
|
.setting-control-with-visual {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-visual-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
background: #0f0f0f;
|
||||||
|
border: 1px solid #333;
|
||||||
|
border-radius: 6px;
|
||||||
|
min-width: 60px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
91
frontend/src/lib/components/visual/EyeIndicator.svelte
Normal file
91
frontend/src/lib/components/visual/EyeIndicator.svelte
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* Minimalist eye graphic that opens/closes based on Eye Aspect Ratio (EAR)
|
||||||
|
* Uses simple white lines on transparent background
|
||||||
|
* EAR typically ranges from ~0.1 (closed) to ~0.4 (fully open)
|
||||||
|
*/
|
||||||
|
let { ear = 0.2 } = $props();
|
||||||
|
|
||||||
|
// Normalize EAR to a 0-1 range for animation
|
||||||
|
// 0.1 = fully closed (0), 0.4 = fully open (1)
|
||||||
|
const normalizedOpenness = $derived(Math.max(0, Math.min(1, (ear - 0.1) / 0.3)));
|
||||||
|
|
||||||
|
// Eye dimensions
|
||||||
|
const centerX = 50;
|
||||||
|
const centerY = 30;
|
||||||
|
const eyeWidth = 70;
|
||||||
|
const maxEyeHeight = 35;
|
||||||
|
const minEyeHeight = 2;
|
||||||
|
|
||||||
|
// Calculate eye height based on openness
|
||||||
|
const eyeHeight = $derived(minEyeHeight + normalizedOpenness * (maxEyeHeight - minEyeHeight));
|
||||||
|
|
||||||
|
const irisRadius = 12;
|
||||||
|
const pupilRadius = 3;
|
||||||
|
|
||||||
|
// Create almond-shaped eye outline using a path
|
||||||
|
// The path creates a pointed eye shape (yes, this is a Skyrim reference.)
|
||||||
|
const eyePath = $derived(() => {
|
||||||
|
const halfWidth = eyeWidth / 2;
|
||||||
|
const halfHeight = eyeHeight / 2;
|
||||||
|
|
||||||
|
// Start at left point, curve to top, to right point, curve to bottom, back to start
|
||||||
|
return `M ${centerX - halfWidth} ${centerY}
|
||||||
|
Q ${centerX - halfWidth/2} ${centerY - halfHeight}, ${centerX} ${centerY - halfHeight}
|
||||||
|
Q ${centerX + halfWidth/2} ${centerY - halfHeight}, ${centerX + halfWidth} ${centerY}
|
||||||
|
Q ${centerX + halfWidth/2} ${centerY + halfHeight}, ${centerX} ${centerY + halfHeight}
|
||||||
|
Q ${centerX - halfWidth/2} ${centerY + halfHeight}, ${centerX - halfWidth} ${centerY}
|
||||||
|
Z`;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 100 60"
|
||||||
|
class="eye-indicator"
|
||||||
|
role="img"
|
||||||
|
aria-label="Eye openness indicator"
|
||||||
|
>
|
||||||
|
<!-- Eye outline (almond shape) -->
|
||||||
|
<path
|
||||||
|
d={eyePath()}
|
||||||
|
fill="none"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="2.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="eye-outline"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Iris (circle outline) -->
|
||||||
|
<circle
|
||||||
|
cx={centerX}
|
||||||
|
cy={centerY}
|
||||||
|
r={Math.min(irisRadius, eyeHeight / 2 - 3)}
|
||||||
|
fill="none"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="2"
|
||||||
|
class="iris"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Pupil (filled circle) -->
|
||||||
|
<circle
|
||||||
|
cx={centerX}
|
||||||
|
cy={centerY}
|
||||||
|
r={Math.min(pupilRadius, eyeHeight / 2 - 8)}
|
||||||
|
fill="white"
|
||||||
|
class="pupil"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.eye-indicator {
|
||||||
|
width: 80px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eye-outline,
|
||||||
|
.iris,
|
||||||
|
.pupil {
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in a new issue