1
0
Fork 0
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:
martin 2023-12-08 05:18:33 +01:00 committed by GitHub
parent e4b24b6e04
commit bc65bbfcc4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 30 additions and 9 deletions

View file

@ -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;
}} }}

View file

@ -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}

View file

@ -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;

View file

@ -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)}