mirror of
https://github.com/immich-app/immich.git
synced 2025-01-07 20:36:48 +01:00
fix(web): create face from video (#5544)
* fix: create face from video * fix: remove comment * fix: inaccurate bounding boxes
This commit is contained in:
parent
e4b24b6e04
commit
bc65bbfcc4
4 changed files with 30 additions and 9 deletions
|
@ -635,6 +635,7 @@
|
||||||
{#if showEditFaces}
|
{#if showEditFaces}
|
||||||
<PersonSidePanel
|
<PersonSidePanel
|
||||||
assetId={asset.id}
|
assetId={asset.id}
|
||||||
|
assetType={asset.type}
|
||||||
on:close={() => {
|
on:close={() => {
|
||||||
showEditFaces = false;
|
showEditFaces = false;
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -143,7 +143,7 @@
|
||||||
/>
|
/>
|
||||||
{#each getBoundingBox($boundingBoxesArray, $photoZoomState, $photoViewer) as boundingbox}
|
{#each getBoundingBox($boundingBoxesArray, $photoZoomState, $photoViewer) as boundingbox}
|
||||||
<div
|
<div
|
||||||
class="absolute border-solid border-white border-[3px] rounded-lg p-3"
|
class="absolute border-solid border-white border-[3px] rounded-lg"
|
||||||
style="top: {boundingbox.top}px; left: {boundingbox.left}px; height: {boundingbox.height}px; width: {boundingbox.width}px;"
|
style="top: {boundingbox.top}px; left: {boundingbox.left}px; height: {boundingbox.height}px; width: {boundingbox.width}px;"
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { api, type AssetFaceResponseDto, type PersonResponseDto } from '@api';
|
import { api, AssetTypeEnum, type AssetFaceResponseDto, type PersonResponseDto, ThumbnailFormat } from '@api';
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import { linear } from 'svelte/easing';
|
import { linear } from 'svelte/easing';
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
|
@ -14,6 +14,8 @@
|
||||||
export let peopleWithFaces: AssetFaceResponseDto[];
|
export let peopleWithFaces: AssetFaceResponseDto[];
|
||||||
export let allPeople: PersonResponseDto[];
|
export let allPeople: PersonResponseDto[];
|
||||||
export let editedPersonIndex: number;
|
export let editedPersonIndex: number;
|
||||||
|
export let assetType: AssetTypeEnum;
|
||||||
|
export let assetId: string;
|
||||||
|
|
||||||
// loading spinners
|
// loading spinners
|
||||||
let isShowLoadingNewPerson = false;
|
let isShowLoadingNewPerson = false;
|
||||||
|
@ -31,23 +33,38 @@
|
||||||
dispatch('close');
|
dispatch('close');
|
||||||
};
|
};
|
||||||
const zoomImageToBase64 = async (face: AssetFaceResponseDto): Promise<string | null> => {
|
const zoomImageToBase64 = async (face: AssetFaceResponseDto): Promise<string | null> => {
|
||||||
if ($photoViewer === null) {
|
let image: HTMLImageElement | null = null;
|
||||||
|
if (assetType === AssetTypeEnum.Image) {
|
||||||
|
image = $photoViewer;
|
||||||
|
} else if (assetType === AssetTypeEnum.Video) {
|
||||||
|
const data = await api.getAssetThumbnailUrl(assetId, ThumbnailFormat.Webp);
|
||||||
|
const img: HTMLImageElement = new Image();
|
||||||
|
img.src = data;
|
||||||
|
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
img.onload = () => resolve();
|
||||||
|
img.onerror = () => resolve();
|
||||||
|
});
|
||||||
|
|
||||||
|
image = img;
|
||||||
|
}
|
||||||
|
if (image === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const { boundingBoxX1: x1, boundingBoxX2: x2, boundingBoxY1: y1, boundingBoxY2: y2 } = face;
|
const { boundingBoxX1: x1, boundingBoxX2: x2, boundingBoxY1: y1, boundingBoxY2: y2 } = face;
|
||||||
|
|
||||||
const coordinates = {
|
const coordinates = {
|
||||||
x1: ($photoViewer.naturalWidth / face.imageWidth) * x1,
|
x1: (image.naturalWidth / face.imageWidth) * x1,
|
||||||
x2: ($photoViewer.naturalWidth / face.imageWidth) * x2,
|
x2: (image.naturalWidth / face.imageWidth) * x2,
|
||||||
y1: ($photoViewer.naturalHeight / face.imageHeight) * y1,
|
y1: (image.naturalHeight / face.imageHeight) * y1,
|
||||||
y2: ($photoViewer.naturalHeight / face.imageHeight) * y2,
|
y2: (image.naturalHeight / face.imageHeight) * y2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const faceWidth = coordinates.x2 - coordinates.x1;
|
const faceWidth = coordinates.x2 - coordinates.x1;
|
||||||
const faceHeight = coordinates.y2 - coordinates.y1;
|
const faceHeight = coordinates.y2 - coordinates.y1;
|
||||||
|
|
||||||
const faceImage = new Image();
|
const faceImage = new Image();
|
||||||
faceImage.src = $photoViewer.src;
|
faceImage.src = image.src;
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
faceImage.onload = resolve;
|
faceImage.onload = resolve;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { fly } from 'svelte/transition';
|
import { fly } from 'svelte/transition';
|
||||||
import { linear } from 'svelte/easing';
|
import { linear } from 'svelte/easing';
|
||||||
import { api, type PersonResponseDto, AssetFaceResponseDto } from '@api';
|
import { api, type PersonResponseDto, AssetFaceResponseDto, AssetTypeEnum } from '@api';
|
||||||
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
import ImageThumbnail from '../assets/thumbnail/image-thumbnail.svelte';
|
||||||
import { handleError } from '$lib/utils/handle-error';
|
import { handleError } from '$lib/utils/handle-error';
|
||||||
import { createEventDispatcher, onMount } from 'svelte';
|
import { createEventDispatcher, onMount } from 'svelte';
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
|
import { getPersonNameWithHiddenValue } from '$lib/utils/person';
|
||||||
|
|
||||||
export let assetId: string;
|
export let assetId: string;
|
||||||
|
export let assetType: AssetTypeEnum;
|
||||||
|
|
||||||
// keep track of the changes
|
// keep track of the changes
|
||||||
let numberOfPersonToCreate: string[] = [];
|
let numberOfPersonToCreate: string[] = [];
|
||||||
|
@ -271,6 +272,8 @@
|
||||||
{peopleWithFaces}
|
{peopleWithFaces}
|
||||||
{allPeople}
|
{allPeople}
|
||||||
{editedPersonIndex}
|
{editedPersonIndex}
|
||||||
|
{assetType}
|
||||||
|
{assetId}
|
||||||
on:close={() => (showSeletecFaces = false)}
|
on:close={() => (showSeletecFaces = false)}
|
||||||
on:createPerson={(event) => handleCreatePerson(event.detail)}
|
on:createPerson={(event) => handleCreatePerson(event.detail)}
|
||||||
on:reassign={(event) => handleReassignFace(event.detail)}
|
on:reassign={(event) => handleReassignFace(event.detail)}
|
||||||
|
|
Loading…
Reference in a new issue