Implement patterned glass and maximum-depth distance controls
Add gobang surface refraction and shared photo/depth distance adjustment. Compare V2 Small and Base at input 770 with maximum-depth masks and pixel diagnostics. Record browser QA and update runtime dependencies and model notices.
This commit is contained in:
+30
-16
@@ -1,14 +1,15 @@
|
||||
import { pipeline, type DepthEstimationPipelineOutput } from '@huggingface/transformers';
|
||||
import { pipeline, type DepthEstimationOutput as DepthEstimationPipelineOutput } from '@huggingface/transformers';
|
||||
import { AI_DEPTH, AI_DEPTH_MODELS, type AiDepthModel } from '../constants/depth';
|
||||
|
||||
const MODEL_ID = 'onnx-community/depth-anything-v2-small-ONNX';
|
||||
type Device = 'webgpu' | 'wasm';
|
||||
type RequestMessage = { type: 'infer'; id: number; image: ImageBitmap };
|
||||
type RequestMessage = { type: 'infer'; id: number; image: ImageBitmap; model: AiDepthModel };
|
||||
type WorkerReply =
|
||||
| { type: 'status'; id: number; message: string }
|
||||
| { type: 'result'; id: number; width: number; height: number; values: ArrayBuffer }
|
||||
| { type: 'result'; id: number; width: number; height: number; values: ArrayBuffer; normalizedValues: ArrayBuffer }
|
||||
| { type: 'error'; id: number; message: string };
|
||||
type Estimator = {
|
||||
(image: OffscreenCanvas): Promise<DepthEstimationPipelineOutput | DepthEstimationPipelineOutput[]>;
|
||||
processor: { image_processor: { size: { width: number; height: number } } };
|
||||
dispose(): Promise<void>;
|
||||
};
|
||||
type Progress = { status: string; progress?: number };
|
||||
@@ -24,6 +25,7 @@ const workerScope = self as unknown as {
|
||||
|
||||
let estimator: Estimator | null = null;
|
||||
let activeDevice: Device | null = null;
|
||||
let activeModel: AiDepthModel | null = null;
|
||||
|
||||
function reply(message: WorkerReply, transfer: Transferable[] = []): void {
|
||||
workerScope.postMessage(message, transfer);
|
||||
@@ -33,9 +35,9 @@ function setStatus(id: number, message: string): void {
|
||||
reply({ type: 'status', id, message });
|
||||
}
|
||||
|
||||
async function loadEstimator(device: Device, id: number): Promise<Estimator> {
|
||||
async function loadEstimator(device: Device, id: number, model: AiDepthModel): Promise<Estimator> {
|
||||
setStatus(id, 'AI 모델 준비 중…');
|
||||
return createPipeline('depth-estimation', MODEL_ID, {
|
||||
const loaded = await createPipeline('depth-estimation', AI_DEPTH_MODELS[model].id, {
|
||||
device,
|
||||
progress_callback: (progress) => {
|
||||
if (progress.status === 'progress') {
|
||||
@@ -45,13 +47,20 @@ async function loadEstimator(device: Device, id: number): Promise<Estimator> {
|
||||
}
|
||||
},
|
||||
});
|
||||
loaded.processor.image_processor.size = { width: AI_DEPTH.inputSize, height: AI_DEPTH.inputSize };
|
||||
return loaded;
|
||||
}
|
||||
|
||||
async function ensureEstimator(id: number): Promise<Estimator> {
|
||||
async function ensureEstimator(id: number, model: AiDepthModel): Promise<Estimator> {
|
||||
if (estimator && activeModel !== model) {
|
||||
await estimator.dispose();
|
||||
estimator = null;
|
||||
}
|
||||
if (estimator) return estimator;
|
||||
activeModel = model;
|
||||
if ('gpu' in navigator) {
|
||||
try {
|
||||
estimator = await loadEstimator('webgpu', id);
|
||||
estimator = await loadEstimator('webgpu', id, model);
|
||||
activeDevice = 'webgpu';
|
||||
return estimator;
|
||||
} catch {
|
||||
@@ -60,12 +69,12 @@ async function ensureEstimator(id: number): Promise<Estimator> {
|
||||
setStatus(id, 'AI 모델을 준비하는 중입니다…');
|
||||
}
|
||||
}
|
||||
estimator = await loadEstimator('wasm', id);
|
||||
estimator = await loadEstimator('wasm', id, model);
|
||||
activeDevice = 'wasm';
|
||||
return estimator;
|
||||
}
|
||||
|
||||
function normalizeDepth(output: DepthEstimationPipelineOutput): { values: Uint8Array; width: number; height: number } {
|
||||
function normalizeDepth(output: DepthEstimationPipelineOutput): { values: Uint8Array; normalizedValues: Float32Array; width: number; height: number } {
|
||||
const { predicted_depth: prediction } = output;
|
||||
const [height, width] = prediction.dims.slice(-2);
|
||||
const data = prediction.data;
|
||||
@@ -83,19 +92,23 @@ function normalizeDepth(output: DepthEstimationPipelineOutput): { values: Uint8A
|
||||
|
||||
const range = max - min;
|
||||
const values = new Uint8Array(width * height);
|
||||
const normalizedValues = new Float32Array(width * height);
|
||||
if (range <= 1e-8) {
|
||||
values.fill(128);
|
||||
normalizedValues.fill(0.5);
|
||||
} else {
|
||||
for (let i = 0; i < values.length; i += 1) {
|
||||
const value = Number(data[i]);
|
||||
// Depth Anything V2 predicts relative inverse depth: larger values are nearer.
|
||||
values[i] = Number.isFinite(value) ? Math.round(((value - min) / range) * 255) : 128;
|
||||
const normalized = Number.isFinite(value) ? (value - min) / range : 0.5;
|
||||
normalizedValues[i] = normalized;
|
||||
values[i] = Math.round(normalized * 255);
|
||||
}
|
||||
}
|
||||
return { values, width, height };
|
||||
return { values, normalizedValues, width, height };
|
||||
}
|
||||
|
||||
async function infer({ id, image }: RequestMessage): Promise<void> {
|
||||
async function infer({ id, image, model: modelKey }: RequestMessage): Promise<void> {
|
||||
try {
|
||||
const canvas = new OffscreenCanvas(image.width, image.height);
|
||||
const context = canvas.getContext('2d');
|
||||
@@ -103,7 +116,7 @@ async function infer({ id, image }: RequestMessage): Promise<void> {
|
||||
context.drawImage(image, 0, 0);
|
||||
image.close();
|
||||
|
||||
let model = await ensureEstimator(id);
|
||||
let model = await ensureEstimator(id, modelKey);
|
||||
setStatus(id, 'AI 깊이를 계산하는 중입니다…');
|
||||
let output: DepthEstimationPipelineOutput;
|
||||
try {
|
||||
@@ -116,7 +129,7 @@ async function infer({ id, image }: RequestMessage): Promise<void> {
|
||||
estimator = null;
|
||||
activeDevice = null;
|
||||
setStatus(id, 'AI 추론을 다시 시도하는 중입니다…');
|
||||
model = await loadEstimator('wasm', id);
|
||||
model = await loadEstimator('wasm', id, modelKey);
|
||||
estimator = model;
|
||||
activeDevice = 'wasm';
|
||||
const predicted = await model(canvas);
|
||||
@@ -127,7 +140,8 @@ async function infer({ id, image }: RequestMessage): Promise<void> {
|
||||
try {
|
||||
const normalized = normalizeDepth(output);
|
||||
const values = normalized.values.buffer as ArrayBuffer;
|
||||
reply({ type: 'result', id, width: normalized.width, height: normalized.height, values }, [values]);
|
||||
const normalizedValues = normalized.normalizedValues.buffer as ArrayBuffer;
|
||||
reply({ type: 'result', id, width: normalized.width, height: normalized.height, values, normalizedValues }, [values, normalizedValues]);
|
||||
} finally {
|
||||
output.predicted_depth.dispose();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user