Added visualisation for face alignment setting
This commit is contained in:
parent
5cbec95e74
commit
b780dbad3e
4 changed files with 148 additions and 57 deletions
43
.github/workflows/docker-image.yml
vendored
43
.github/workflows/docker-image.yml
vendored
|
|
@ -1,43 +0,0 @@
|
|||
# name: Build and Publish image to Docker Hub
|
||||
# on:
|
||||
# push:
|
||||
# branches:
|
||||
# - master
|
||||
# tags:
|
||||
# - 'v*'
|
||||
|
||||
# jobs:
|
||||
# publish_images:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - name: checkout
|
||||
# uses: actions/checkout@v4
|
||||
|
||||
# - name: Extract metadata (tags, labels) for Docker
|
||||
# id: meta
|
||||
# uses: docker/metadata-action@v5
|
||||
# with:
|
||||
# images: arnaudcayrol/immich-selfie-timelapse
|
||||
# tags: |
|
||||
# type=ref,event=branch
|
||||
# type=ref,event=tag
|
||||
# type=semver,pattern={{version}}
|
||||
# type=semver,pattern={{major}}.{{minor}}
|
||||
# type=raw,value=latest,enable={{is_default_branch}}
|
||||
|
||||
# - name: Set up Docker Buildx
|
||||
# uses: docker/setup-buildx-action@v3
|
||||
|
||||
# - name: Login to Docker Hub
|
||||
# uses: docker/login-action@v3
|
||||
# with:
|
||||
# username: arnaudcayrol
|
||||
# password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
# - name: Build and push
|
||||
# uses: docker/build-push-action@v5
|
||||
# with:
|
||||
# context: .
|
||||
# push: true
|
||||
# tags: ${{ steps.meta.outputs.tags }}
|
||||
# labels: ${{ steps.meta.outputs.labels }}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
import { DEFAULT_CONFIG, TIMING } from '../constants.js';
|
||||
import { handleError } from '../errorHandler.js';
|
||||
import EyeIndicator from './visual/EyeIndicator.svelte';
|
||||
import AlignmentIndicator from './visual/AlignmentIndicator.svelte';
|
||||
|
||||
let { disabled = false } = $props();
|
||||
|
||||
|
|
@ -314,17 +315,12 @@
|
|||
|
||||
<!-- Alignment Section -->
|
||||
<div class="setting-section">
|
||||
<div class="section-header">
|
||||
<span class="section-title">Face Alignment</span>
|
||||
<span class="setting-hint" style="font-style: italic; color: var(--text-secondary);">Always enabled</span>
|
||||
</div>
|
||||
|
||||
<div class="setting-row sub-setting">
|
||||
<label for="eye-distance">
|
||||
<span class="setting-label">Eye Distance</span>
|
||||
<span class="setting-hint">Distance between eyes as % of image width (larger = zoom in)</span>
|
||||
</label>
|
||||
<div class="setting-control">
|
||||
<div class="setting-control-with-visual">
|
||||
<input
|
||||
id="eye-distance"
|
||||
type="range"
|
||||
|
|
@ -334,6 +330,9 @@
|
|||
step="0.01"
|
||||
/>
|
||||
<span class="value">{(config.processing.alignment.eye_distance * 100).toFixed(0)}%</span>
|
||||
<div class="inline-visual-indicator">
|
||||
<AlignmentIndicator eyeDistance={config.processing.alignment.eye_distance} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -740,11 +739,11 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.25rem 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: #0f0f0f;
|
||||
border: 1px solid #333;
|
||||
border-radius: 6px;
|
||||
min-width: 60px;
|
||||
height: 32px;
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
137
frontend/src/lib/components/visual/AlignmentIndicator.svelte
Normal file
137
frontend/src/lib/components/visual/AlignmentIndicator.svelte
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
<script>
|
||||
/**
|
||||
* Visual indicator for alignment settings showing head position and scale.
|
||||
* Displays an anatomically proportioned face with eyes positioned according to eye_distance.
|
||||
*
|
||||
* The eye_distance parameter controls zoom level:
|
||||
* - Smaller values (0.1): zoomed out, head appears smaller, shows more neck
|
||||
* - Larger values (0.5): zoomed in, head appears larger, tighter crop
|
||||
*
|
||||
* Proportions match the processing pipeline (alignment.rs):
|
||||
* - Eye positions calculated with FACE_CENTER_BELOW_EYES = 0.5
|
||||
* - Face center (hairline-to-chin midpoint + neck) positioned at frame center
|
||||
* - This creates a vertical offset showing more neck than forehead
|
||||
* - Inter-pupillary distance (IPD) is ~37% of face width
|
||||
*/
|
||||
let { eyeDistance = 0.3 } = $props();
|
||||
|
||||
// Frame dimensions (square output image)
|
||||
const frameSize = 120;
|
||||
|
||||
// Calculate eye positions using the same formulas as alignment.rs
|
||||
const FACE_CENTER_BELOW_EYES = 0.5;
|
||||
|
||||
const leftEyeX = $derived((1.0 - eyeDistance) / 2.0);
|
||||
const rightEyeX = $derived(1.0 - leftEyeX);
|
||||
const eyeY = $derived(0.5 - eyeDistance * FACE_CENTER_BELOW_EYES);
|
||||
|
||||
// Convert to pixel coordinates
|
||||
const leftEyePos = $derived({ x: leftEyeX * frameSize, y: eyeY * frameSize });
|
||||
const rightEyePos = $derived({ x: rightEyeX * frameSize, y: eyeY * frameSize });
|
||||
|
||||
// Calculate eye distance in pixels
|
||||
const eyeDistancePx = $derived(eyeDistance * frameSize);
|
||||
|
||||
// Calculate eye center point
|
||||
const eyeCenterX = $derived((leftEyePos.x + rightEyePos.x) / 2);
|
||||
const eyeCenterY = $derived(eyeY * frameSize);
|
||||
|
||||
// Face proportions based on inter-pupillary distance (IPD)
|
||||
// Per crop_utils.rs, IPD is ~0.37 of face width
|
||||
// Face center (hairline-to-chin midpoint + neck) is 0.5 * IPD below eyes
|
||||
|
||||
// Anatomical proportions relative to IPD:
|
||||
// - Eyes to top of head (forehead): ~0.5 IPD
|
||||
// - Eyes to face center: 0.5 IPD (by definition)
|
||||
// - Face center to bottom (lower face + neck): ~0.5 IPD
|
||||
// Total head height: ~1.5 IPD
|
||||
const foreheadHeight = $derived(eyeDistancePx * 0.5);
|
||||
const lowerFaceHeight = $derived(eyeDistancePx * 1.0);
|
||||
|
||||
// Head ellipse dimensions (head is taller than wide)
|
||||
const headCenter = $derived({
|
||||
x: eyeCenterX,
|
||||
y: eyeCenterY + (lowerFaceHeight - foreheadHeight) / 2
|
||||
});
|
||||
const headWidth = $derived(eyeDistancePx * 1.35); // Width relative to IPD
|
||||
const headHeight = $derived(eyeDistancePx * 1.85); // Height > width (ratio ~1.37)
|
||||
|
||||
// Eye rendering parameters (scale with IPD)
|
||||
const eyeRadius = $derived(Math.max(2.5, eyeDistancePx * 0.12));
|
||||
</script>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 {frameSize} {frameSize}"
|
||||
class="alignment-indicator"
|
||||
role="img"
|
||||
aria-label="Face alignment indicator"
|
||||
>
|
||||
<!-- Background frame -->
|
||||
<rect
|
||||
x="0"
|
||||
y="0"
|
||||
width={frameSize}
|
||||
height={frameSize}
|
||||
fill="rgba(0, 0, 0, 0.8)"
|
||||
stroke="none"
|
||||
/>
|
||||
|
||||
<!-- Head ellipse (face shape) -->
|
||||
<ellipse
|
||||
cx={headCenter.x}
|
||||
cy={headCenter.y}
|
||||
rx={headWidth}
|
||||
ry={headHeight}
|
||||
fill="none"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
class="head"
|
||||
/>
|
||||
|
||||
<!-- Left eye -->
|
||||
<circle
|
||||
cx={leftEyePos.x}
|
||||
cy={leftEyePos.y}
|
||||
r={eyeRadius}
|
||||
fill="none"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
class="eye"
|
||||
/>
|
||||
|
||||
<!-- Right eye -->
|
||||
<circle
|
||||
cx={rightEyePos.x}
|
||||
cy={rightEyePos.y}
|
||||
r={eyeRadius}
|
||||
fill="none"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
class="eye"
|
||||
/>
|
||||
|
||||
<!-- Simple mouth curve (positioned between eyes and chin) -->
|
||||
<path
|
||||
d="M {eyeCenterX - eyeDistancePx * 0.35} {eyeCenterY + eyeDistancePx * 0.65}
|
||||
Q {eyeCenterX} {eyeCenterY + eyeDistancePx * 0.75},
|
||||
{eyeCenterX + eyeDistancePx * 0.35} {eyeCenterY + eyeDistancePx * 0.65}"
|
||||
fill="none"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
class="mouth"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.alignment-indicator {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.head,
|
||||
.eye,
|
||||
.mouth {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
</style>
|
||||
|
|
@ -19,9 +19,7 @@
|
|||
|
||||
// Calculate eye height based on openness
|
||||
const eyeHeight = $derived(minEyeHeight + normalizedOpenness * (maxEyeHeight - minEyeHeight));
|
||||
|
||||
const irisRadius = 12;
|
||||
const pupilRadius = 3;
|
||||
const pupilRadius = 2;
|
||||
|
||||
// Create almond-shaped eye outline using a path
|
||||
// The path creates a pointed eye shape (yes, this is a Skyrim reference.)
|
||||
|
|
@ -60,7 +58,7 @@
|
|||
<circle
|
||||
cx={centerX}
|
||||
cy={centerY}
|
||||
r={Math.min(irisRadius, eyeHeight / 2 - 3)}
|
||||
r={eyeHeight / 2}
|
||||
fill="none"
|
||||
stroke="white"
|
||||
stroke-width="2"
|
||||
|
|
@ -71,7 +69,7 @@
|
|||
<circle
|
||||
cx={centerX}
|
||||
cy={centerY}
|
||||
r={Math.min(pupilRadius, eyeHeight / 2 - 8)}
|
||||
r={Math.max(0, Math.min(pupilRadius, eyeHeight / 2 - 8))}
|
||||
fill="white"
|
||||
class="pupil"
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue