react-native-mask-segment-c.../dist/utils/freqLayerPrep.js

178 lines
6.8 KiB
JavaScript
Raw Normal View History

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