| Server IP : 217.154.3.148 / Your IP : 216.73.216.90 Web Server : nginx/1.30.3 System : Linux blissful-wright.217-154-3-148.plesk.page 6.8.0-124-generic #124-Ubuntu SMP PREEMPT_DYNAMIC Tue May 26 13:00:45 UTC 2026 x86_64 User : gracious-boyd_hfhh1h8vo9n ( 10000) PHP Version : 8.3.31 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/scripts/ |
Upload File : |
import fs from 'node:fs/promises'
import path from 'node:path'
import crypto from 'node:crypto'
import sharp from 'sharp'
import { morphDilate, morphErode } from '../utils/maskOps'
type AnalyzeResponse = {
ok: boolean
depth_debug?: {
occluderUrl?: string | null
occluderCoverage?: number | null
wallDepth?: number | null
occlusionThreshold?: number | null
sensitivityUsed?: number | null
} | null
tile_mask_meta?: {
coverage?: Record<string, number>
} | null
mask_metrics?: {
coverage?: number | null
warnings?: string[] | null
} | null
}
function safeSlug(name: string): string {
return name
.replace(/\.[a-z0-9]+$/i, '')
.replace(/[^a-z0-9]+/gi, '_')
.replace(/^_+|_+$/g, '')
.slice(0, 80)
.toLowerCase()
}
function fromDataUrl(dataUrl: string): Buffer {
const idx = dataUrl.indexOf('base64,')
if (idx < 0) throw new Error('unsupported dataUrl')
return Buffer.from(dataUrl.slice(idx + 'base64,'.length), 'base64')
}
async function decodeMaskPng(dataUrl: string): Promise<{ mask: Uint8Array; width: number; height: number }> {
const png = fromDataUrl(dataUrl)
const { data, info } = await sharp(png, { failOnError: false }).greyscale().raw().toBuffer({ resolveWithObject: true })
const width = info.width ?? 0
const height = info.height ?? 0
if (!(width > 0 && height > 0)) throw new Error('invalid mask size')
return { mask: new Uint8Array(data), width, height }
}
function coverageOf(mask: Uint8Array): number {
if (!mask.length) return 0
let solid = 0
for (let i = 0; i < mask.length; i++) if (mask[i] > 127) solid += 1
return solid / mask.length
}
async function writeMaskPng(outPath: string, mask: Uint8Array, width: number, height: number): Promise<void> {
const buf = await sharp(Buffer.from(mask), { raw: { width, height, channels: 1 } })
.png({ compressionLevel: 6 })
.toBuffer()
await fs.writeFile(outPath, buf)
}
function haloRingMetric(mask: Uint8Array, width: number, height: number, r = 2): number {
const dil = morphDilate(mask, width, height, r)
const ero = morphErode(mask, width, height, r)
let ring = 0
for (let i = 0; i < mask.length; i++) {
const v = (dil[i] > 0 ? 1 : 0) - (ero[i] > 0 ? 1 : 0)
if (v > 0) ring += 1
}
return ring / Math.max(1, mask.length)
}
async function main() {
const basePath = process.env.NEXT_PUBLIC_BASE_PATH?.replace(/\/$/, '') ?? '/nextjs'
const host = process.env.REGRESS_HOST ?? 'http://127.0.0.1:3001'
const endpoint = `${host}${basePath}/api/analyze-mix`
const screenshotsDir = path.resolve(process.cwd(), 'screenshots')
const entries = await fs.readdir(screenshotsDir).catch(() => [])
const images = entries
.filter((f) => /\.(png|jpe?g|webp)$/i.test(f))
.map((f) => path.join(screenshotsDir, f))
.sort()
if (!images.length) {
throw new Error(`no images found in ${screenshotsDir}`)
}
const stamp = new Date().toISOString().replace(/[:.]/g, '-')
const outDir = path.resolve(process.cwd(), 'reports', 'regression', stamp)
await fs.mkdir(outDir, { recursive: true })
const results: any[] = []
for (const imgPath of images) {
const buf = await fs.readFile(imgPath)
const name = path.basename(imgPath)
const slug = safeSlug(name) || crypto.createHash('sha1').update(name).digest('hex').slice(0, 8)
const form = new FormData()
form.append('file', new Blob([new Uint8Array(buf)]), name)
const params = new URLSearchParams({
model: 'unet_latest',
ransac: 'auto',
maskPreset: 'tiles-default',
maskLattice: '1',
maskIllumination: '1',
maskAutotune: '1',
maskAutotuneMs: '150',
wu: '1.0',
wh: '0.9',
fblur: '1',
})
const res = await fetch(`${endpoint}?${params.toString()}`, {
method: 'POST',
body: form as any,
})
const text = await res.text()
if (!res.ok) {
throw new Error(`analyze-mix failed for ${name}: ${res.status} ${text}`)
}
const json = JSON.parse(text) as AnalyzeResponse
let occluder = null as null | { mask: Uint8Array; width: number; height: number; cov: number; halo: number }
if (json.depth_debug?.occluderUrl) {
const decoded = await decodeMaskPng(json.depth_debug.occluderUrl)
const cov = coverageOf(decoded.mask)
const halo = haloRingMetric(decoded.mask, decoded.width, decoded.height, 2)
occluder = { ...decoded, cov, halo }
await writeMaskPng(path.join(outDir, `${slug}_occluder.png`), decoded.mask, decoded.width, decoded.height)
}
const row = {
file: name,
ok: json.ok,
tilesCoverage: json.tile_mask_meta?.coverage?.tiles ?? null,
fgCoverage: json.mask_metrics?.coverage ?? null,
warnings: json.mask_metrics?.warnings ?? null,
depth: json.depth_debug
? {
occluderCoverage: json.depth_debug.occluderCoverage ?? null,
wallDepth: json.depth_debug.wallDepth ?? null,
occlusionThreshold: json.depth_debug.occlusionThreshold ?? null,
sensitivityUsed: json.depth_debug.sensitivityUsed ?? null,
}
: null,
occluder: occluder
? {
cov: Number(occluder.cov.toFixed(4)),
haloRingFrac: Number(occluder.halo.toFixed(5)),
width: occluder.width,
height: occluder.height,
}
: null,
}
results.push(row)
await fs.writeFile(path.join(outDir, `${slug}.json`), JSON.stringify(json, null, 2))
}
await fs.writeFile(path.join(outDir, `summary.json`), JSON.stringify({ endpoint, results }, null, 2))
console.log(`wrote ${results.length} reports to ${outDir}`)
}
main().catch((err) => {
console.error(err)
process.exit(1)
})