403Webshell
Server IP : 217.154.3.148  /  Your IP : 216.73.216.90
Web Server : Apache
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/lib/mask/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/vhosts/gracious-boyd.217-154-3-148.plesk.page/nextjs/lib/mask/normalize.ts
import {
  otsuThreshold,
  percentile,
  clipRescale,
  binarize,
  morphOpen,
  morphClose,
  morphDilate,
  morphErode,
  sobel,
  removeBorderTouching,
  keepLargest,
  invert255,
  largestBorderConnected,
  dropSmall,
  subtractEdges,
  maskOr,
  maskAnd,
  maskMinus,
  stripBorder,
  gateByDistance,
  geodesicGrow,
  suppressGridEdges,
  suppressGridEdgesAdaptive,
  suppressThinLines,
  localStd,
  hysteresisEdges,
  meanStdMasked,
  illuminationNormalize,
} from '@/utils/maskOps'

export type MorphOp = {
  op: 'open' | 'close' | 'dilate' | 'erode'
  radius: number
}

export type EdgeGuardConfig = {
  enabled: boolean
  threshold?: number
  dilate?: number
}

export type EdgeFillConfig = {
  enabled: boolean
  threshold: number
  dilate: number
}

export type NormalizeOptions = {
  invertMode: 'auto' | 'true' | 'false' | boolean
  thresholdMode: 'otsu' | 'value'
  thresholdValue: number
  percentileLow: number
  percentileHigh: number
  morphOps: MorphOp[]
  keepLargest: number
  minArea: number
  removeBorder: boolean
  edgeGuard: EdgeGuardConfig
  edgeFill: EdgeFillConfig
  kDark: number
  kLight: number
  seedBorder: number
  seedGrow: number
  gridKill: string
  gridVFrac: number
  gridHFrac: number
  gridPad: number
  gridSigma: number
  thinWidth: number
  thinLengthFrac: number
  carveRadius: number
  edgeLowFrac: number
  localStdWin: number
  localStdK: number
  illuminationRadius: number
  illuminationAmount: number
  fallbackHigh: number
  fallbackLow: number
  bgMode: 'largest' | 'classic'
}

export type NormalizedStats = {
  originalCoverage: number
  originalMean: number
  percentileLow: number
  percentileHigh: number
  thresholdMode: string
  invertMode: string | boolean
  inverted: boolean
  clip?: { low: number; high: number }
  morphApplied: string[]
  backgroundMode?: string
  background?: { mode: string; coverage: number; fallback?: boolean; skipped?: boolean; occluderCoverage?: number }
  borderMinArea?: number
  dropSmall?: number
  keepLargest?: number
  edgeGuard?: { threshold: number; dilate: number }
  edgeFill?: { threshold?: number; low?: number; lowFrac?: number; dilate?: number; enabled?: boolean }
  gridKillMode?: { mode: string; sigma?: number; vFrac?: number; hFrac?: number; pad?: number }
  hybrid?: {
    kDark: number
    kLight: number
    thrDark: number
    thrLight: number
    std: number
    stdClamped?: number
    seedCoverage: number
    seedMask?: number
    borderCull?: number
    carveRadius?: number
    seedGrow?: number
    gateDist?: number
    gridKill?: string
    gridSigma?: number
    thinWidth?: number
    thinLength?: number
    textureMask?: number
    localStdWin?: number
    localStdK?: number
    localStdThresh?: number
    edgeLowFrac?: number
    illuminationRadius?: number
    illuminationAmount?: number
  }
  seedFallback?: boolean
  fallbackReason?: string | null
  highCoverage?: number
}

export type NormalizedResult = {
  data: Uint8Array
  coverage: number
  inverted: boolean
  threshold: number
  original: number
  fallback: boolean
  stats: NormalizedStats
}

export type MaskPresetName = 'tiles-default' | 'tiles-detail'

const defaultMorphOps: MorphOp[] = [
  { op: 'open', radius: 1 },
  { op: 'close', radius: 2 },
]

export const baseNormalizeOptions = (): NormalizeOptions => ({
  invertMode: 'auto',
  thresholdMode: 'otsu',
  thresholdValue: 127,
  percentileLow: 1,
  percentileHigh: 99,
  morphOps: [...defaultMorphOps],
  keepLargest: 0,
  minArea: 900,
  removeBorder: true,
  edgeGuard: { enabled: false, threshold: 48, dilate: 1 },
  edgeFill: { enabled: true, threshold: 96, dilate: 1 },
  kDark: 1.25,
  kLight: 0.5,
  seedBorder: 6,
  seedGrow: 6,
  gridKill: 'auto',
  gridVFrac: 0.55,
  gridHFrac: 0.55,
  gridPad: 1,
  gridSigma: 2.5,
  thinWidth: 3,
  thinLengthFrac: 0.25,
  carveRadius: 2,
  edgeLowFrac: 0.5,
  localStdWin: 7,
  localStdK: 1.2,
  illuminationRadius: 0,
  illuminationAmount: 0,
  fallbackHigh: 0.98,
  fallbackLow: 0.02,
  bgMode: 'largest',
})

export function applyPreset(config: NormalizeOptions, presetName: MaskPresetName): NormalizeOptions {
  const next = { ...config }
  const ensureEdgeFill = (threshold: number) => {
    const prev = next.edgeFill || { enabled: true, threshold, dilate: 1 }
    next.edgeFill = {
      enabled: true,
      threshold,
      dilate: Number.isFinite(prev.dilate) ? Math.max(0, Math.round(prev.dilate!)) : 1,
    }
  }
  switch (presetName) {
    case 'tiles-default':
      next.kDark = 0.40
      next.kLight = 0.16
      ensureEdgeFill(84)
      next.edgeLowFrac = 0.30
      next.seedBorder = 2
      next.seedGrow = 28
      next.carveRadius = 6
      next.gridKill = 'auto'
      next.gridSigma = 2.8
      next.gridPad = 3
      next.thinWidth = 2
      next.thinLengthFrac = 0.30
      next.minArea = 400
      next.edgeGuard = { enabled: false, threshold: 48, dilate: 1 }
      next.localStdWin = 7
      next.localStdK = 1.2
      next.illuminationRadius = 32
      next.illuminationAmount = 0.75
      return next
    case 'tiles-detail':
      next.kDark = 0.45
      next.kLight = 0.18
      ensureEdgeFill(80)
      next.edgeLowFrac = 0.30
      next.seedBorder = 3
      next.seedGrow = 32
      next.carveRadius = 5
      next.gridKill = 'auto'
      next.gridSigma = 3.0
      next.gridPad = 3
      next.thinWidth = 3
      next.thinLengthFrac = 0.25
      next.minArea = 600
      next.edgeGuard = { enabled: false, threshold: 48, dilate: 1 }
      next.localStdWin = 7
      next.localStdK = 1.2
      next.illuminationRadius = 28
      next.illuminationAmount = 0.7
      return next
    default:
      return next
  }
}

export function resolvePresetName(preset?: string | null): MaskPresetName {
  if (!preset) return 'tiles-default'
  const lower = preset.trim().toLowerCase()
  return lower === 'tiles-detail' ? 'tiles-detail' : 'tiles-default'
}

export function normalizeMaskBinary(
  mask: Uint8Array,
  width: number,
  height: number,
  options: NormalizeOptions,
  graySource?: Uint8Array,
): NormalizedResult {
  const total = mask.length
  if (total === 0) {
    return {
      data: mask,
      coverage: 0,
      inverted: false,
      threshold: 0,
      original: 0,
      fallback: false,
      stats: {
        originalCoverage: 0,
        originalMean: 0,
        percentileLow: options.percentileLow,
        percentileHigh: options.percentileHigh,
        thresholdMode: options.thresholdMode,
        invertMode: options.invertMode,
        inverted: false,
        morphApplied: [],
        backgroundMode: options.bgMode,
      },
    }
  }

  const gray = Uint8Array.from(mask)
  let solid = 0
  let sum = 0
  for (let i = 0; i < total; i++) {
    if (mask[i] >= 128) solid += 1
    sum += mask[i]
  }

  const originalCoverage = solid / total
  const originalMean = sum / (total * 255)

  const cfg: NormalizeOptions = {
    invertMode: options.invertMode,
    thresholdMode: options.thresholdMode,
    thresholdValue: options.thresholdValue,
    percentileLow: options.percentileLow,
    percentileHigh: options.percentileHigh,
    morphOps: options.morphOps,
    keepLargest: options.keepLargest,
    minArea: options.minArea,
    edgeGuard: options.edgeGuard,
    edgeFill: options.edgeFill ?? { enabled: true, threshold: 96, dilate: 1 },
    kDark: Number.isFinite(options.kDark) ? options.kDark : 1.25,
    kLight: Number.isFinite(options.kLight) ? options.kLight : 0.5,
    seedBorder: Number.isFinite(options.seedBorder) ? options.seedBorder : 6,
    seedGrow: Number.isFinite(options.seedGrow) ? options.seedGrow : 6,
    gridKill: options.gridKill ?? 'auto',
    gridVFrac: Number.isFinite(options.gridVFrac) ? options.gridVFrac : 0.55,
    gridHFrac: Number.isFinite(options.gridHFrac) ? options.gridHFrac : 0.55,
    gridPad: Number.isFinite(options.gridPad) ? options.gridPad : 1,
    gridSigma: Number.isFinite(options.gridSigma) ? options.gridSigma : 2.5,
    thinWidth: Number.isFinite(options.thinWidth) ? Math.max(0, options.thinWidth) : 3,
    thinLengthFrac: Number.isFinite(options.thinLengthFrac)
      ? Math.max(0, Math.min(1, options.thinLengthFrac))
      : 0.25,
    carveRadius: Number.isFinite(options.carveRadius)
      ? Math.max(0, Math.round(options.carveRadius))
      : 2,
    edgeLowFrac: Number.isFinite(options.edgeLowFrac) ? Math.max(0, Math.min(1, options.edgeLowFrac)) : 0.5,
    localStdWin: Number.isFinite(options.localStdWin) ? Math.max(1, Math.round(options.localStdWin)) : 7,
    localStdK: Number.isFinite(options.localStdK) ? options.localStdK : 1.2,
    illuminationRadius: Number.isFinite(options.illuminationRadius)
      ? Math.max(0, options.illuminationRadius)
      : 0,
    illuminationAmount: Number.isFinite(options.illuminationAmount)
      ? Math.max(0, Math.min(1, options.illuminationAmount))
      : 0,
    fallbackHigh: options.fallbackHigh,
    fallbackLow: options.fallbackLow,
    bgMode: options.bgMode,
  }
  const imageGray = graySource && graySource.length === total ? graySource : undefined
  const baseIntensity = imageGray ?? gray
  const intensity =
    cfg.illuminationRadius > 0 && cfg.illuminationAmount > 0
      ? illuminationNormalize(
          baseIntensity,
          width,
          height,
          cfg.illuminationRadius,
          cfg.illuminationAmount,
        )
      : baseIntensity

  const coverageOf = (bin: Uint8Array | null | undefined): number => {
    if (!bin || bin.length === 0) return 0
    let filled = 0
    for (let i = 0; i < Math.min(bin.length, total); i++) if (bin[i]) filled += 1
    return filled / total
  }

  const stats: NormalizedStats = {
    originalCoverage,
    originalMean,
    percentileLow: cfg.percentileLow,
    percentileHigh: cfg.percentileHigh,
    thresholdMode: cfg.thresholdMode,
    invertMode: cfg.invertMode,
    inverted: false,
    morphApplied: [],
    backgroundMode: cfg.bgMode,
  }

  let fallback = false
  let fallbackReason: string | null = null
  let highCoverageFlag = false
  if (originalCoverage > cfg.fallbackHigh) {
    highCoverageFlag = true
  } else if (originalCoverage < cfg.fallbackLow) {
    fallback = true
    fallbackReason = 'coverage-low'
  }

  let working: Uint8Array = gray
  if (!fallback) {
    const lo = percentile(working, cfg.percentileLow)
    const hi = percentile(working, cfg.percentileHigh)
    if (hi > lo) {
      working = clipRescale(working, lo, hi) as Uint8Array
      stats.clip = { low: lo, high: hi }
    }
  }

  let threshold = cfg.thresholdMode === 'otsu' ? otsuThreshold(working) : cfg.thresholdValue
  if (!Number.isFinite(threshold)) threshold = 127
  threshold = Math.max(0, Math.min(255, Math.round(threshold)))

  let inverted: boolean
  if (cfg.invertMode === 'auto') {
    inverted = originalMean < 0.5 && originalCoverage < 0.8
  } else if (cfg.invertMode === 'true') {
    inverted = true
  } else if (cfg.invertMode === 'false') {
    inverted = false
  } else {
    inverted = Boolean(cfg.invertMode)
  }
  stats.inverted = inverted

  const fgBase = binarize(working, threshold, inverted) as Uint8Array
  stats.originalCoverage = originalCoverage
  stats.originalMean = originalMean

  let fg = fgBase
  if (!fallback) {
    const morphApplied: string[] = []
    for (const entry of cfg.morphOps || []) {
      const radius = Math.max(1, Math.round(entry.radius || 1))
      switch (entry.op) {
        case 'open':
          fg = morphOpen(fg, width, height, radius) as Uint8Array
          morphApplied.push(`open:${radius}`)
          break
        case 'close':
          fg = morphClose(fg, width, height, radius) as Uint8Array
          morphApplied.push(`close:${radius}`)
          break
        case 'dilate':
          fg = morphDilate(fg, width, height, radius) as Uint8Array
          morphApplied.push(`dilate:${radius}`)
          break
        case 'erode':
          fg = morphErode(fg, width, height, radius) as Uint8Array
          morphApplied.push(`erode:${radius}`)
          break
        default:
          break
      }
    }
    if (morphApplied.length) stats.morphApplied = morphApplied

    if (cfg.bgMode === 'largest') {
      const background = largestBorderConnected(invert255(fg), width, height)
      const bgCoverage = coverageOf(background)
      const occluderBase = invert255(background)

      if (bgCoverage > 0.01) {
        const statsMasked = meanStdMasked(intensity, background)
        const { mean, std } = statsMasked
        const stdClamped = Math.min(std, 32)
        const kDark = Math.max(0, cfg.kDark)
        const kLight = Math.max(0, cfg.kLight)
        const thrDark = Math.max(0, Math.min(255, mean - stdClamped * kDark))
        const thrLight = Math.max(0, Math.min(255, mean + stdClamped * kLight))

        const borderCull = cfg.seedBorder > 0 ? cfg.seedBorder : 0
        const mDark = new Uint8Array(total)
        const mLight = new Uint8Array(total)
        for (let i = 0; i < total; i++) {
          const v = intensity[i]
          if (v <= thrDark) mDark[i] = 255
          if (v >= thrLight) mLight[i] = 255
        }

        const localStdWin = Number.isFinite(cfg.localStdWin) ? Math.max(1, Math.round(cfg.localStdWin)) : 7
        const localStdK = Number.isFinite(cfg.localStdK) ? cfg.localStdK : 1.2
        let textureMask = new Uint8Array(total)
        let localStdThresh = 0
        if (localStdK > 0 && localStdWin > 0) {
          const stdMap = localStd(intensity, width, height, localStdWin)
          localStdThresh = localStdK * std
          for (let i = 0; i < total; i++) {
            if (stdMap[i] >= localStdThresh) textureMask[i] = 255
          }
        }

        const textureCoverage = coverageOf(textureMask)

        const seedBase = maskOr(maskOr(mDark, mLight), textureMask) as Uint8Array
        let seed: Uint8Array = (borderCull > 0
          ? (stripBorder(seedBase, width, height, cfg.seedBorder) as Uint8Array)
          : seedBase) as Uint8Array
        seed = morphClose(seed, width, height, 2) as Uint8Array

        const notBackground = occluderBase
        const gateDist = cfg.seedBorder > 0 ? cfg.seedBorder : 8
        let edges: Uint8Array = new Uint8Array(seed.length)
        if (cfg.edgeFill.enabled) {
          const edgeThr = Number.isFinite(cfg.edgeFill.threshold) ? cfg.edgeFill.threshold : 96
          const edgeDilate = Math.max(0, Math.round(cfg.edgeFill.dilate || 0))
          const grad = sobel(intensity, width, height)
          const lowFrac = Number.isFinite(cfg.edgeLowFrac) ? cfg.edgeLowFrac : 0.5
          const lowThr = Math.max(0, Math.round(edgeThr * Math.max(0, Math.min(1, lowFrac))))
          edges = hysteresisEdges(grad, width, height, edgeThr, lowThr) as Uint8Array
          if (cfg.gridKill !== 'off') {
            const pad = Number.isFinite(cfg.gridPad) ? cfg.gridPad : 1
            if (cfg.gridKill === 'auto' || cfg.gridKill === 'adaptive') {
              const sigma = Number.isFinite(cfg.gridSigma) ? cfg.gridSigma : 2.5
              edges = suppressGridEdgesAdaptive(edges, width, height, sigma, pad) as Uint8Array
              stats.gridKillMode = { mode: 'adaptive', sigma: Number(sigma.toFixed(2)), pad }
            } else {
              const vFrac = Number.isFinite(cfg.gridVFrac) ? cfg.gridVFrac : 0.55
              const hFrac = Number.isFinite(cfg.gridHFrac) ? cfg.gridHFrac : 0.55
              edges = suppressGridEdges(edges, width, height, vFrac, hFrac, pad) as Uint8Array
              stats.gridKillMode = { mode: cfg.gridKill, vFrac, hFrac, pad }
            }
          } else {
            stats.gridKillMode = { mode: 'off' }
          }
          if (edgeDilate > 0) edges = morphDilate(edges, width, height, edgeDilate) as Uint8Array
          edges = gateByDistance(edges, seed, width, height, gateDist) as Uint8Array
          stats.edgeFill = {
            enabled: true,
            threshold: edgeThr,
            low: lowThr,
            lowFrac: Number(lowFrac.toFixed(2)),
            dilate: edgeDilate,
          }
        } else {
          stats.edgeFill = { enabled: false }
          stats.gridKillMode = { mode: 'skipped' }
        }

        const edgeGrowMask: Uint8Array = edges.length
          ? (morphDilate(edges, width, height, 1) as Uint8Array)
          : edges
        const growMask = maskOr(notBackground, edgeGrowMask) as Uint8Array
        seed = maskAnd(seed, growMask) as Uint8Array
        let fgSeed: Uint8Array = maskOr(seed, edges) as Uint8Array
        if (cfg.seedGrow > 0) {
          fgSeed = geodesicGrow(fgSeed, growMask, width, height, cfg.seedGrow) as Uint8Array
        } else {
          fgSeed = maskAnd(fgSeed, growMask) as Uint8Array
        }

        const seedOnBackground = maskAnd(fgSeed, background) as Uint8Array
        const seedBgCoverage = coverageOf(seedOnBackground)
        const carveRadius = Math.max(0, Number.isFinite(cfg.carveRadius) ? Math.round(cfg.carveRadius) : 0)
        const carveMask =
          carveRadius > 0
            ? (morphDilate(fgSeed, width, height, carveRadius) as Uint8Array)
            : fgSeed
        const bgCarved = maskMinus(background, carveMask) as Uint8Array
        let fgCandidate: Uint8Array = invert255(bgCarved) as Uint8Array
        fgCandidate = maskAnd(fgCandidate, growMask) as Uint8Array

        if (cfg.thinWidth > 0 && width > 0 && height > 0) {
          const thinWidth = Math.max(1, Math.round(cfg.thinWidth))
          const thinLength = Math.max(0, Math.min(1, Number(cfg.thinLengthFrac)))
          fgCandidate = suppressThinLines(fgCandidate, width, height, thinWidth, thinLength)
        }

        fgCandidate = dropSmall(fgCandidate, width, height, cfg.minArea > 0 ? cfg.minArea : 1) as Uint8Array
        let seedCoverage = coverageOf(fgCandidate)
        if (cfg.keepLargest > 0) {
          fgCandidate = keepLargest(fgCandidate, width, height, cfg.keepLargest) as Uint8Array
          seedCoverage = coverageOf(fgCandidate)
        }

        if (seedCoverage === 0) {
          stats.seedFallback = true
          fgCandidate = dropSmall(notBackground, width, height, cfg.minArea > 0 ? cfg.minArea : 1) as Uint8Array
          if (cfg.keepLargest > 0)
            fgCandidate = keepLargest(fgCandidate, width, height, cfg.keepLargest) as Uint8Array
          seedCoverage = coverageOf(fgCandidate)
        }

        const bgFinalCoverage = coverageOf(bgCarved)
        const occluderCoverage = coverageOf(fgCandidate)
        stats.background = { mode: cfg.bgMode, coverage: bgFinalCoverage, occluderCoverage }

        stats.hybrid = {
          kDark,
          kLight,
          thrDark: Number(thrDark.toFixed(1)),
          thrLight: Number(thrLight.toFixed(1)),
          std: Number(std.toFixed(2)),
          stdClamped: Number(stdClamped.toFixed(2)),
          seedCoverage: Number(seedCoverage.toFixed(4)),
          seedMask: Number(seedBgCoverage.toFixed(4)),
          borderCull,
          carveRadius,
          seedGrow: cfg.seedGrow,
          gateDist,
          gridKill: cfg.gridKill,
          gridSigma: Number.isFinite(cfg.gridSigma) ? Number(cfg.gridSigma.toFixed(2)) : undefined,
          thinWidth: cfg.thinWidth,
          thinLength: Number(cfg.thinLengthFrac.toFixed(3)),
          textureMask: Number(textureCoverage.toFixed(4)),
          localStdWin,
          localStdK: Number(localStdK.toFixed(3)),
          localStdThresh: Number(localStdThresh.toFixed(2)),
          edgeLowFrac: Number((Number.isFinite(cfg.edgeLowFrac) ? cfg.edgeLowFrac : 0.5).toFixed(2)),
          illuminationRadius: cfg.illuminationRadius,
          illuminationAmount: Number(cfg.illuminationAmount.toFixed(2)),
        }
        if (cfg.minArea > 0) stats.dropSmall = cfg.minArea
        if (cfg.keepLargest > 0) stats.keepLargest = cfg.keepLargest
        if (highCoverageFlag) stats.highCoverage = originalCoverage

        fg = fgCandidate
      } else {
        stats.background = { mode: cfg.bgMode, coverage: bgCoverage, fallback: true }
        fg = dropSmall(fg, width, height, cfg.minArea > 0 ? cfg.minArea : 1)
        if (cfg.keepLargest > 0) fg = keepLargest(fg, width, height, cfg.keepLargest)
        if (cfg.minArea > 0) stats.dropSmall = cfg.minArea
        if (cfg.keepLargest > 0) stats.keepLargest = cfg.keepLargest
      }
    } else {
      if (cfg.minArea > 0 && cfg.removeBorder !== false) {
        fg = removeBorderTouching(fg, width, height, cfg.minArea, true)
        stats.borderMinArea = cfg.minArea
      }
      if (cfg.keepLargest > 0) {
        fg = keepLargest(fg, width, height, cfg.keepLargest)
        stats.keepLargest = cfg.keepLargest
      }
    }

    if (cfg.edgeGuard.enabled) {
      const thresholdRaw =
        typeof cfg.edgeGuard.threshold === 'number' && Number.isFinite(cfg.edgeGuard.threshold)
          ? cfg.edgeGuard.threshold
          : 48
      const edgeThreshold = Math.max(0, Math.min(255, Math.round(thresholdRaw)))
      const dilateRadius = Math.max(0, Math.round(cfg.edgeGuard.dilate || 0))
      fg = subtractEdges(fg, intensity, width, height, { thr: edgeThreshold, dilate: dilateRadius })
      stats.edgeGuard = { threshold: edgeThreshold, dilate: dilateRadius }
    }
  } else if (fallback) {
    stats.fallbackReason = fallbackReason
    stats.morphApplied = []
    stats.background = { mode: cfg.bgMode, coverage: 0, skipped: true }
  } else if (highCoverageFlag) {
    stats.highCoverage = originalCoverage
  }

  const coverage = coverageOf(fg)

  return {
    data: fg,
    coverage,
    threshold,
    inverted,
    original: originalCoverage,
    fallback,
    stats,
  }
}

export type NormalizeMaskRequest = {
  mask: Uint8Array
  width: number
  height: number
  preset?: string | null
  gray?: Uint8Array
  overrides?: Partial<NormalizeOptions>
}

export type NormalizeMaskResponse = {
  preset: MaskPresetName
  options: NormalizeOptions
  result: NormalizedResult
}

export function normalizeWithPreset(request: NormalizeMaskRequest): NormalizeMaskResponse {
  const base = baseNormalizeOptions()
  const preset = resolvePresetName(request.preset)
  const config = applyPreset(base, preset)

  if (request.overrides) {
    const { overrides } = request
    if (overrides.invertMode !== undefined) config.invertMode = overrides.invertMode
    if (overrides.thresholdMode !== undefined) config.thresholdMode = overrides.thresholdMode
    if (overrides.thresholdValue !== undefined) config.thresholdValue = overrides.thresholdValue
    if (overrides.percentileLow !== undefined) config.percentileLow = overrides.percentileLow
    if (overrides.percentileHigh !== undefined) config.percentileHigh = overrides.percentileHigh
    if (overrides.keepLargest !== undefined) config.keepLargest = overrides.keepLargest
    if (overrides.minArea !== undefined) config.minArea = overrides.minArea
    if (overrides.removeBorder !== undefined) config.removeBorder = overrides.removeBorder
    if (overrides.edgeGuard !== undefined) config.edgeGuard = overrides.edgeGuard
    if (overrides.edgeFill !== undefined) config.edgeFill = overrides.edgeFill
    if (overrides.kDark !== undefined) config.kDark = overrides.kDark
    if (overrides.kLight !== undefined) config.kLight = overrides.kLight
    if (overrides.seedBorder !== undefined) config.seedBorder = overrides.seedBorder
    if (overrides.seedGrow !== undefined) config.seedGrow = overrides.seedGrow
    if (overrides.gridKill !== undefined) config.gridKill = overrides.gridKill
    if (overrides.gridVFrac !== undefined) config.gridVFrac = overrides.gridVFrac
    if (overrides.gridHFrac !== undefined) config.gridHFrac = overrides.gridHFrac
    if (overrides.gridPad !== undefined) config.gridPad = overrides.gridPad
    if (overrides.gridSigma !== undefined) config.gridSigma = overrides.gridSigma
    if (overrides.thinWidth !== undefined) config.thinWidth = overrides.thinWidth
    if (overrides.thinLengthFrac !== undefined) config.thinLengthFrac = overrides.thinLengthFrac
    if (overrides.carveRadius !== undefined) config.carveRadius = overrides.carveRadius
    if (overrides.edgeLowFrac !== undefined) config.edgeLowFrac = overrides.edgeLowFrac
    if (overrides.localStdWin !== undefined) config.localStdWin = overrides.localStdWin
    if (overrides.localStdK !== undefined) config.localStdK = overrides.localStdK
    if (overrides.illuminationRadius !== undefined) config.illuminationRadius = overrides.illuminationRadius
    if (overrides.illuminationAmount !== undefined) config.illuminationAmount = overrides.illuminationAmount
    if (overrides.fallbackHigh !== undefined) config.fallbackHigh = overrides.fallbackHigh
    if (overrides.fallbackLow !== undefined) config.fallbackLow = overrides.fallbackLow
    if (overrides.bgMode !== undefined) config.bgMode = overrides.bgMode
    if (overrides.morphOps !== undefined) config.morphOps = overrides.morphOps
  }

  const result = normalizeMaskBinary(request.mask, request.width, request.height, config, request.gray)
  return { preset, options: config, result }
}

Youez - 2016 - github.com/yon3zu
LinuXploit