mirror of
https://github.com/YuzuZensai/Pien-Studio.git
synced 2026-09-02 14:18:35 +00:00
77 lines
2.9 KiB
TypeScript
77 lines
2.9 KiB
TypeScript
import type { FaceDetectionOverlay, FacePreview } from "../hooks/use-face-detection";
|
|||
|
|
|
||
|
|
export async function loadImageFromUri(uri: string): Promise<HTMLImageElement | null> {
|
||
|
|
const image = new Image();
|
||
|
|
image.crossOrigin = "anonymous";
|
||
|
|
await new Promise<void>((resolve) => {
|
||
|
|
image.onload = () => resolve();
|
||
|
|
image.onerror = () => resolve();
|
||
|
|
image.src = uri;
|
||
|
|
});
|
||
|
|
if (!image.naturalWidth || !image.naturalHeight) return null;
|
||
|
|
return image;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function toFaceDetectionOverlays(
|
||
|
|
faces: Array<{ x: number; y: number; width: number; height: number; gender?: string; genderScore?: number }>,
|
||
|
|
image: HTMLImageElement,
|
||
|
|
layerWidth?: number,
|
||
|
|
layerHeight?: number,
|
||
|
|
): FaceDetectionOverlay[] {
|
||
|
|
const width = layerWidth ?? image.naturalWidth;
|
||
|
|
const height = layerHeight ?? image.naturalHeight;
|
||
|
|
const scaleX = width / image.naturalWidth;
|
||
|
|
const scaleY = height / image.naturalHeight;
|
||
|
|
|
||
|
|
return faces.map((face, index) => {
|
||
|
|
const genderLabel = face.gender ?? "unknown";
|
||
|
|
const scoreLabel = face.genderScore != null ? `${Math.round(face.genderScore * 100)}%` : "";
|
||
|
|
const label = scoreLabel ? `Person ${index + 1} - ${genderLabel} ${scoreLabel}` : `Person ${index + 1} - ${genderLabel}`;
|
||
|
|
|
||
|
|
return {
|
||
|
|
x: face.x * scaleX,
|
||
|
|
y: face.y * scaleY,
|
||
|
|
width: face.width * scaleX,
|
||
|
|
height: face.height * scaleY,
|
||
|
|
sourceWidth: image.naturalWidth,
|
||
|
|
sourceHeight: image.naturalHeight,
|
||
|
|
label,
|
||
|
|
gender: face.gender,
|
||
|
|
genderScore: face.genderScore,
|
||
|
|
};
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function buildFacePreviews(
|
||
|
|
image: HTMLImageElement,
|
||
|
|
faceDetections: FaceDetectionOverlay[],
|
||
|
|
layerWidth?: number,
|
||
|
|
layerHeight?: number,
|
||
|
|
): FacePreview[] {
|
||
|
|
const width = layerWidth ?? image.naturalWidth;
|
||
|
|
const height = layerHeight ?? image.naturalHeight;
|
||
|
|
const toImageScaleX = image.naturalWidth / Math.max(1, width);
|
||
|
|
const toImageScaleY = image.naturalHeight / Math.max(1, height);
|
||
|
|
|
||
|
|
return faceDetections
|
||
|
|
.map((face, index) => {
|
||
|
|
const sx = Math.max(0, Math.floor(face.x * toImageScaleX));
|
||
|
|
const sy = Math.max(0, Math.floor(face.y * toImageScaleY));
|
||
|
|
const sw = Math.max(1, Math.floor(face.width * toImageScaleX));
|
||
|
|
const sh = Math.max(1, Math.floor(face.height * toImageScaleY));
|
||
|
|
const ex = Math.min(image.naturalWidth, sx + sw);
|
||
|
|
const ey = Math.min(image.naturalHeight, sy + sh);
|
||
|
|
const cw = Math.max(1, ex - sx);
|
||
|
|
const ch = Math.max(1, ey - sy);
|
||
|
|
const canvas = document.createElement("canvas");
|
||
|
|
const targetWidth = 84;
|
||
|
|
const scale = targetWidth / cw;
|
||
|
|
canvas.width = targetWidth;
|
||
|
|
canvas.height = Math.max(1, Math.round(ch * scale));
|
||
|
|
const ctx = canvas.getContext("2d");
|
||
|
|
if (!ctx) return { id: `face-${index + 1}`, src: "" };
|
||
|
|
ctx.drawImage(image, sx, sy, cw, ch, 0, 0, canvas.width, canvas.height);
|
||
|
|
return { id: `face-${index + 1}`, src: canvas.toDataURL("image/jpeg", 0.9) };
|
||
|
|
})
|
||
|
|
.filter((preview) => preview.src);
|
||
|
|
}
|