react-native-mask-segment-c.../dist/utils/freqLayerPrep.js
a1518 70499176d6 Add splitWalls to subdivide wall regions by texture boundaries.
Enables optional wall-1/wall-2… sub-regions with independent paint and undo, plus example toggle and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 22:41:17 -07:00

178 lines
6.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import cv from './opencvAdapter';
import { getMaskSegmentRuntimeConfig } from './maskSegmentRuntime';
/** OpenCV 8-bit Lab L 通道BGR 输入,供单测与近似对照) */
export function bgrToLabL(b, g, r) {
return bgrToLab(b, g, r).l;
}
/** BGR → 8-bit LabL/a/b 均映射到 0255 */
export function bgrToLab(b, g, r) {
let rf = r / 255;
let gf = g / 255;
let bf = b / 255;
rf = rf > 0.04045 ? Math.pow((rf + 0.055) / 1.055, 2.4) : rf / 12.92;
gf = gf > 0.04045 ? Math.pow((gf + 0.055) / 1.055, 2.4) : gf / 12.92;
bf = bf > 0.04045 ? Math.pow((bf + 0.055) / 1.055, 2.4) : bf / 12.92;
const x = rf * 0.412453 + gf * 0.35758 + bf * 0.180423;
const y = rf * 0.212671 + gf * 0.71516 + bf * 0.072169;
const z = rf * 0.019334 + gf * 0.119193 + bf * 0.950227;
const xn = 0.950456;
const yn = 1;
const zn = 1.088754;
const delta = 6 / 29;
const delta3 = delta * delta * delta;
let fx = x / xn;
let fy = y / yn;
let fz = z / zn;
fx = fx > delta3 ? Math.cbrt(fx) : fx / (3 * delta * delta) + 4 / 29;
fy = fy > delta3 ? Math.cbrt(fy) : fy / (3 * delta * delta) + 4 / 29;
fz = fz > delta3 ? Math.cbrt(fz) : fz / (3 * delta * delta) + 4 / 29;
const L = fy * 116 - 16;
const a = 500 * (fx - fy);
const bLab = 200 * (fy - fz);
return {
l: Math.max(0, Math.min(255, Math.round((L * 255) / 100))),
a: Math.max(0, Math.min(255, Math.round(a + 128))),
b: Math.max(0, Math.min(255, Math.round(bLab + 128))),
};
}
export function bgrBufferToRgbaBuffer(bgr, cols, rows) {
const pixelCount = cols * rows;
const rgba = new Uint8Array(pixelCount * 4);
for (let i = 0; i < pixelCount; i++) {
const s = i * 3;
const d = i * 4;
rgba[d] = bgr[s + 2];
rgba[d + 1] = bgr[s + 1];
rgba[d + 2] = bgr[s];
rgba[d + 3] = 255;
}
return rgba;
}
export function releaseFreqLayerImages(layers) {
layers?.lowFreqImage.dispose();
layers?.highFreqImage.dispose();
}
/** 16-bit 有符号差分 → 8-bit 高频层detail * gain + 128 */
async function buildHighFreqMatNative(lMat, lLowMat, cols, rows, gain) {
const l16 = cv.createMat(cols, rows, 1);
const lLow16 = cv.createMat(cols, rows, 1);
const diff16 = cv.createMat(cols, rows, 1);
const high8 = cv.createMat(cols, rows, 1);
try {
cv.convertTo(lMat, l16, cv.CV_16SC1);
cv.convertTo(lLowMat, lLow16, cv.CV_16SC1);
await cv.subtract(l16, lLow16, diff16);
await cv.addWeighted(diff16, gain, null, 0, 128, diff16);
cv.convertTo(diff16, high8, cv.CV_8UC1);
return high8;
}
finally {
l16.release();
lLow16.release();
diff16.release();
}
}
async function downscaleMatForFreq(workMat, cols, rows) {
const maxLongSide = getMaskSegmentRuntimeConfig().pipeline.paintFreqMaxLongSide;
const longSide = Math.max(cols, rows);
if (longSide <= maxLongSide) {
return { mat: workMat, cols, rows, owned: false };
}
const scale = maxLongSide / longSide;
const freqCols = Math.floor(cols * scale);
const freqRows = Math.floor(rows * scale);
const resized = cv.createMat(freqCols, freqRows, 3);
await cv.resize(workMat, resized, { width: freqCols, height: freqRows }, cv.INTER_LINEAR);
return { mat: resized, cols: freqCols, rows: freqRows, owned: true };
}
/** 复用已上传的 BGR Mat避免重复 bufferToMat + JS↔原生往返 */
export async function prepareFreqLayersFromWorkMat(workMat, cols, rows) {
const paintCfg = getMaskSegmentRuntimeConfig().paint;
const blurStart = __DEV__ ? performance.now() : 0;
const scaled = await downscaleMatForFreq(workMat, cols, rows);
const freqMat = scaled.mat;
const freqCols = scaled.cols;
const freqRows = scaled.rows;
let labMat = null;
let lMat = null;
let lLowMat = null;
let lHighMat = null;
try {
labMat = cv.cvtColorBgr(freqMat, cv.COLOR_BGR2Lab);
lMat = cv.createMat(freqCols, freqRows, 1);
cv.extractChannel(labMat, lMat, 0);
labMat.release();
labMat = null;
lLowMat = cv.createMat(freqCols, freqRows, 1);
const kernel = paintCfg.lLowBlurKernel;
await cv.GaussianBlur(lMat, lLowMat, { width: kernel, height: kernel }, 0);
lHighMat = await buildHighFreqMatNative(lMat, lLowMat, freqCols, freqRows, paintCfg.lHighGain);
await cv.addWeighted(lLowMat, paintCfg.lLowContrast, null, 0, 128 * (1 - paintCfg.lLowContrast), lLowMat);
await cv.addWeighted(lLowMat, paintCfg.lLowBrightness, null, 0, 128 * (1 - paintCfg.lLowBrightness), lLowMat);
const lowFreqImage = cv.grayMatToSkiaImage(lLowMat);
const highFreqImage = cv.grayMatToSkiaImage(lHighMat);
if (!lowFreqImage || !highFreqImage) {
lowFreqImage?.dispose();
highFreqImage?.dispose();
return null;
}
return { lowFreqImage, highFreqImage };
}
finally {
if (scaled.owned) {
freqMat.release();
}
labMat?.release();
lMat?.release();
lLowMat?.release();
lHighMat?.release();
}
}
/** 单次 Mat 上传 → 高低频 + 原图 Skia并行高低频先就绪时可回调 */
export async function preparePaintResourcesFromWorkBuffer(bgrBuffer, cols, rows, onFreqLayersReady) {
const pixelCount = cols * rows;
if (bgrBuffer.length !== pixelCount * 3) {
return null;
}
const prepStart = __DEV__ ? performance.now() : 0;
const workMat = cv.bgrBufferToMat(bgrBuffer, cols, rows);
try {
const originPromise = cv.matToSkiaImage(workMat);
const freqPromise = prepareFreqLayersFromWorkMat(workMat, cols, rows);
const layers = await freqPromise;
if (!layers) {
const originImage = await originPromise;
originImage?.dispose();
return null;
}
onFreqLayersReady?.(layers);
const originImage = await originPromise;
if (!originImage) {
releaseFreqLayerImages(layers);
return null;
}
return { originImage, layers };
}
finally {
workMat.release();
}
}
/** @deprecated 测试兼容;生产路径请用 preparePaintResourcesFromWorkBuffer */
export async function prepareFreqLayersFromBgrBuffer(bgrBuffer, cols, rows) {
const pixelCount = cols * rows;
if (bgrBuffer.length !== pixelCount * 3) {
return null;
}
const workMat = cv.bgrBufferToMat(bgrBuffer, cols, rows);
try {
return await prepareFreqLayersFromWorkMat(workMat, cols, rows);
}
finally {
workMat.release();
}
}
/** 原图 BGR → Skia RGBAOpenCV cvtColor与 freq 并行) */
export async function originBgrBufferToSkiaImage(bgrBuffer, cols, rows) {
return cv.bgrBufferToSkiaImage(bgrBuffer, cols, rows);
}
//# sourceMappingURL=freqLayerPrep.js.map