@scienjoy/sj-ai-plus 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/ui/es/packages/components/src/sjDigitalHumanAssistant/src/index.vue.ts +2 -2
- package/ui/es/packages/components/src/sjDigitalHumanAssistant/src/index.vue2.ts +316 -314
- package/ui/es/packages/components/src/utils/videoBackgroundRemoval.ts +114 -142
- package/ui/es/sj-ai-plus.css +1 -1
- package/ui/lib/packages/components/src/sjDigitalHumanAssistant/src/index.vue.ts +1 -1
- package/ui/lib/packages/components/src/sjDigitalHumanAssistant/src/index.vue2.ts +1 -1
- package/ui/lib/packages/components/src/utils/videoBackgroundRemoval.ts +10 -13
- package/ui/lib/sj-ai-plus.css +1 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ref as
|
|
2
|
-
function
|
|
3
|
-
const
|
|
4
|
-
let
|
|
5
|
-
const
|
|
1
|
+
import { ref as x } from "vue";
|
|
2
|
+
function B() {
|
|
3
|
+
const g = x(null), o = x(null), u = x(!1), a = x(!1);
|
|
4
|
+
let c = null, e = null, l = null, p = null, v = null, s = null, d = null;
|
|
5
|
+
const y = `
|
|
6
6
|
struct Uniforms {
|
|
7
7
|
targetColor1: vec3f,
|
|
8
8
|
threshold: f32,
|
|
@@ -23,7 +23,6 @@ function E() {
|
|
|
23
23
|
|
|
24
24
|
@vertex
|
|
25
25
|
fn vertexMain(@builtin(vertex_index) vertexIndex: u32) -> VertexOutput {
|
|
26
|
-
// 全屏四边形顶点
|
|
27
26
|
var pos = array<vec2f, 6>(
|
|
28
27
|
vec2f(-1.0, -1.0),
|
|
29
28
|
vec2f(1.0, -1.0),
|
|
@@ -35,7 +34,6 @@ function E() {
|
|
|
35
34
|
|
|
36
35
|
var output: VertexOutput;
|
|
37
36
|
output.position = vec4f(pos[vertexIndex], 0.0, 1.0);
|
|
38
|
-
// 翻转Y坐标以修正视频倒置问题
|
|
39
37
|
output.texCoord = vec2f(
|
|
40
38
|
pos[vertexIndex].x * 0.5 + 0.5,
|
|
41
39
|
1.0 - (pos[vertexIndex].y * 0.5 + 0.5)
|
|
@@ -56,12 +54,12 @@ function E() {
|
|
|
56
54
|
let rgb = color.rgb;
|
|
57
55
|
let pixelSize = 1.0 / uniforms.resolution;
|
|
58
56
|
|
|
59
|
-
//
|
|
57
|
+
// 主颜色匹配:直接透明
|
|
60
58
|
if (matchesTargetColor(rgb)) {
|
|
61
59
|
return vec4f(0.0, 0.0, 0.0, 0.0);
|
|
62
60
|
}
|
|
63
61
|
|
|
64
|
-
//
|
|
62
|
+
// 8邻域检测:边缘像素透明(第一层侵蚀)
|
|
65
63
|
let left = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(-pixelSize.x, 0.0)).rgb;
|
|
66
64
|
let right = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(pixelSize.x, 0.0)).rgb;
|
|
67
65
|
let top = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(0.0, -pixelSize.y)).rgb;
|
|
@@ -71,7 +69,6 @@ function E() {
|
|
|
71
69
|
let bottomLeft = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(-pixelSize.x, pixelSize.y)).rgb;
|
|
72
70
|
let bottomRight = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(pixelSize.x, pixelSize.y)).rgb;
|
|
73
71
|
|
|
74
|
-
// 第一层侵蚀:如果任意邻居匹配背景色,当前像素也变透明
|
|
75
72
|
if (matchesTargetColor(left) || matchesTargetColor(right) ||
|
|
76
73
|
matchesTargetColor(top) || matchesTargetColor(bottom) ||
|
|
77
74
|
matchesTargetColor(topLeft) || matchesTargetColor(topRight) ||
|
|
@@ -79,147 +76,123 @@ function E() {
|
|
|
79
76
|
return vec4f(0.0, 0.0, 0.0, 0.0);
|
|
80
77
|
}
|
|
81
78
|
|
|
82
|
-
//
|
|
83
|
-
let left2 = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(-pixelSize.x *
|
|
84
|
-
let right2 = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(pixelSize.x *
|
|
85
|
-
let top2 = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(0.0, -pixelSize.y *
|
|
86
|
-
let bottom2 = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(0.0, pixelSize.y *
|
|
79
|
+
// 2像素邻域检测:第二层侵蚀,优化边缘
|
|
80
|
+
let left2 = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(-pixelSize.x * 4.0, 0.0)).rgb;
|
|
81
|
+
let right2 = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(pixelSize.x * 4.0, 0.0)).rgb;
|
|
82
|
+
let top2 = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(0.0, -pixelSize.y * 4.0)).rgb;
|
|
83
|
+
let bottom2 = textureSampleBaseClampToEdge(videoTexture, videoSampler, input.texCoord + vec2f(0.0, pixelSize.y * 4.0)).rgb;
|
|
87
84
|
|
|
88
85
|
if (matchesTargetColor(left2) || matchesTargetColor(right2) ||
|
|
89
86
|
matchesTargetColor(top2) || matchesTargetColor(bottom2)) {
|
|
90
87
|
return vec4f(0.0, 0.0, 0.0, 0.0);
|
|
91
88
|
}
|
|
92
89
|
|
|
93
|
-
//
|
|
90
|
+
// 非背景/非边缘像素:完全保留
|
|
94
91
|
return vec4f(rgb, 1.0);
|
|
95
92
|
}
|
|
96
93
|
`;
|
|
97
|
-
function
|
|
98
|
-
const
|
|
99
|
-
return
|
|
100
|
-
r: parseInt(
|
|
101
|
-
g: parseInt(
|
|
102
|
-
b: parseInt(
|
|
94
|
+
function S(t) {
|
|
95
|
+
const r = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);
|
|
96
|
+
return r ? {
|
|
97
|
+
r: parseInt(r[1], 16),
|
|
98
|
+
g: parseInt(r[2], 16),
|
|
99
|
+
b: parseInt(r[3], 16)
|
|
103
100
|
} : { r: 0, g: 0, b: 0 };
|
|
104
101
|
}
|
|
105
|
-
|
|
106
|
-
if (
|
|
107
|
-
|
|
102
|
+
const T = () => {
|
|
103
|
+
if (c && (cancelAnimationFrame(c), c = null), s && (s.destroy(), s = null), p && (p = null), d && (d = null), v && (v = null), l) {
|
|
104
|
+
try {
|
|
105
|
+
l.unconfigure();
|
|
106
|
+
} catch {
|
|
107
|
+
}
|
|
108
|
+
l = null;
|
|
109
|
+
}
|
|
110
|
+
e && (e.destroy(), e = null), u.value = !1, a.value = !1, console.log("✅ WebGPU资源已彻底销毁,可重新初始化");
|
|
111
|
+
};
|
|
112
|
+
async function h(t, r = 3) {
|
|
113
|
+
if (T(), !navigator.gpu)
|
|
114
|
+
return console.error("❌ WebGPU不受支持:请使用Chrome113+/Edge113+,或在chrome://flags中启用WebGPU"), !1;
|
|
115
|
+
if (!t)
|
|
116
|
+
return console.error("❌ WebGPU初始化失败:canvas节点不存在"), !1;
|
|
108
117
|
try {
|
|
109
|
-
const
|
|
110
|
-
if (!
|
|
111
|
-
|
|
112
|
-
if (
|
|
113
|
-
|
|
114
|
-
const i = navigator.gpu.getPreferredCanvasFormat();
|
|
118
|
+
const i = await navigator.gpu.requestAdapter({ powerPreference: "low-power" });
|
|
119
|
+
if (!i) throw new Error("获取GPU适配器失败");
|
|
120
|
+
if (e = await i.requestDevice(), !e) throw new Error("获取GPU设备失败");
|
|
121
|
+
if (l = t.getContext("webgpu"), !l) throw new Error("获取canvas的WebGPU上下文失败");
|
|
122
|
+
const n = navigator.gpu.getPreferredCanvasFormat();
|
|
115
123
|
l.configure({
|
|
116
|
-
device:
|
|
117
|
-
format:
|
|
124
|
+
device: e,
|
|
125
|
+
format: n,
|
|
118
126
|
alphaMode: "premultiplied"
|
|
127
|
+
// 透明背景核心配置
|
|
119
128
|
});
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
});
|
|
123
|
-
f = t.createSampler({
|
|
124
|
-
magFilter: "linear",
|
|
125
|
-
minFilter: "linear"
|
|
126
|
-
}), n = t.createBuffer({
|
|
129
|
+
const f = e.createShaderModule({ code: y });
|
|
130
|
+
v = e.createSampler({ magFilter: "linear", minFilter: "linear" }), s = e.createBuffer({
|
|
127
131
|
size: 48,
|
|
128
132
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
|
|
129
|
-
}),
|
|
133
|
+
}), d = e.createBindGroupLayout({
|
|
130
134
|
entries: [
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
sampler: {}
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
binding: 1,
|
|
138
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
139
|
-
externalTexture: {}
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
binding: 2,
|
|
143
|
-
visibility: GPUShaderStage.FRAGMENT,
|
|
144
|
-
buffer: { type: "uniform" }
|
|
145
|
-
}
|
|
135
|
+
{ binding: 0, visibility: GPUShaderStage.FRAGMENT, sampler: {} },
|
|
136
|
+
{ binding: 1, visibility: GPUShaderStage.FRAGMENT, externalTexture: {} },
|
|
137
|
+
{ binding: 2, visibility: GPUShaderStage.FRAGMENT, buffer: { type: "uniform" } }
|
|
146
138
|
]
|
|
147
139
|
});
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
layout: c,
|
|
153
|
-
vertex: {
|
|
154
|
-
module: a,
|
|
155
|
-
entryPoint: "vertexMain"
|
|
156
|
-
},
|
|
140
|
+
const C = e.createPipelineLayout({ bindGroupLayouts: [d] });
|
|
141
|
+
return p = e.createRenderPipeline({
|
|
142
|
+
layout: C,
|
|
143
|
+
vertex: { module: f, entryPoint: "vertexMain" },
|
|
157
144
|
fragment: {
|
|
158
|
-
module:
|
|
145
|
+
module: f,
|
|
159
146
|
entryPoint: "fragmentMain",
|
|
160
147
|
targets: [{
|
|
161
|
-
format:
|
|
148
|
+
format: n,
|
|
162
149
|
blend: {
|
|
163
|
-
color: {
|
|
164
|
-
|
|
165
|
-
dstFactor: "one-minus-src-alpha"
|
|
166
|
-
},
|
|
167
|
-
alpha: {
|
|
168
|
-
srcFactor: "one",
|
|
169
|
-
dstFactor: "one-minus-src-alpha"
|
|
170
|
-
}
|
|
150
|
+
color: { srcFactor: "src-alpha", dstFactor: "one-minus-src-alpha" },
|
|
151
|
+
alpha: { srcFactor: "one", dstFactor: "one-minus-src-alpha" }
|
|
171
152
|
}
|
|
172
153
|
}]
|
|
173
154
|
},
|
|
174
|
-
primitive: {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
} catch (e) {
|
|
179
|
-
return console.error("WebGPU初始化失败:", e), !1;
|
|
155
|
+
primitive: { topology: "triangle-list" }
|
|
156
|
+
}), u.value = !0, console.log("✅ WebGPU初始化成功"), !0;
|
|
157
|
+
} catch (i) {
|
|
158
|
+
return console.error(`❌ WebGPU初始化失败(剩余重试${r - 1}次):`, i), r > 1 ? (await new Promise((n) => setTimeout(n, 500)), h(t, r - 1)) : (u.value = !1, !1);
|
|
180
159
|
}
|
|
181
160
|
}
|
|
182
|
-
function
|
|
183
|
-
if (!
|
|
161
|
+
function b() {
|
|
162
|
+
if (!u.value || !g.value || !o.value || !e || !l || !p || !v || !s || !d) {
|
|
163
|
+
a.value = !1;
|
|
184
164
|
return;
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
165
|
+
}
|
|
166
|
+
const t = g.value, r = o.value;
|
|
167
|
+
if (t.readyState < t.HAVE_CURRENT_DATA) {
|
|
168
|
+
c = requestAnimationFrame(b);
|
|
188
169
|
return;
|
|
189
170
|
}
|
|
190
|
-
(
|
|
171
|
+
(r.width !== t.videoWidth || r.height !== t.videoHeight) && (r.width = t.videoWidth, r.height = t.videoHeight);
|
|
191
172
|
try {
|
|
192
|
-
const i =
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
a.g / 255,
|
|
197
|
-
a.b / 255,
|
|
198
|
-
// targetColor1
|
|
173
|
+
const i = e.importExternalTexture({ source: t }), n = S("#0AFFFF"), f = S("#0AFFFF"), C = new Float32Array([
|
|
174
|
+
n.r / 255,
|
|
175
|
+
n.g / 255,
|
|
176
|
+
n.b / 255,
|
|
199
177
|
0.4,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
c.b / 255,
|
|
204
|
-
// targetColor2
|
|
178
|
+
f.r / 255,
|
|
179
|
+
f.g / 255,
|
|
180
|
+
f.b / 255,
|
|
205
181
|
0,
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
e.height,
|
|
209
|
-
// resolution
|
|
182
|
+
r.width,
|
|
183
|
+
r.height,
|
|
210
184
|
0,
|
|
211
185
|
0
|
|
212
|
-
// padding2
|
|
213
186
|
]);
|
|
214
|
-
|
|
215
|
-
const
|
|
216
|
-
layout:
|
|
187
|
+
e.queue.writeBuffer(s, 0, C);
|
|
188
|
+
const U = e.createBindGroup({
|
|
189
|
+
layout: d,
|
|
217
190
|
entries: [
|
|
218
|
-
{ binding: 0, resource:
|
|
191
|
+
{ binding: 0, resource: v },
|
|
219
192
|
{ binding: 1, resource: i },
|
|
220
|
-
{ binding: 2, resource: { buffer:
|
|
193
|
+
{ binding: 2, resource: { buffer: s } }
|
|
221
194
|
]
|
|
222
|
-
}),
|
|
195
|
+
}), P = e.createCommandEncoder(), m = P.beginRenderPass({
|
|
223
196
|
colorAttachments: [{
|
|
224
197
|
view: l.getCurrentTexture().createView(),
|
|
225
198
|
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
@@ -227,49 +200,48 @@ function E() {
|
|
|
227
200
|
storeOp: "store"
|
|
228
201
|
}]
|
|
229
202
|
});
|
|
230
|
-
|
|
203
|
+
m.setPipeline(p), m.setBindGroup(0, U), m.draw(6), m.end(), e.queue.submit([P.finish()]);
|
|
231
204
|
} catch (i) {
|
|
232
|
-
console.error("WebGPU渲染帧错误:", i);
|
|
205
|
+
console.error("❌ WebGPU渲染帧错误:", i), a.value = !1;
|
|
206
|
+
return;
|
|
233
207
|
}
|
|
234
|
-
|
|
208
|
+
c = requestAnimationFrame(b);
|
|
235
209
|
}
|
|
236
|
-
async function
|
|
237
|
-
if (!
|
|
238
|
-
|
|
239
|
-
|
|
210
|
+
async function G() {
|
|
211
|
+
if (!g.value || !o.value) {
|
|
212
|
+
console.error("❌ processVideo失败:video/canvas节点未绑定");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (a.value) return;
|
|
216
|
+
if (a.value = !0, !await h(o.value)) {
|
|
217
|
+
console.error("❌ WebGPU初始化重试耗尽,无法处理视频"), a.value = !1, u.value = !1;
|
|
240
218
|
return;
|
|
241
219
|
}
|
|
242
|
-
|
|
220
|
+
c = requestAnimationFrame(b);
|
|
243
221
|
}
|
|
244
|
-
function
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
clearValue: { r: 0, g: 0, b: 0, a: 0 },
|
|
252
|
-
loadOp: "clear",
|
|
253
|
-
storeOp: "store"
|
|
254
|
-
}]
|
|
255
|
-
}).end(), t.queue.submit([i.finish()]);
|
|
256
|
-
}
|
|
257
|
-
const e = r.value.getContext("2d");
|
|
258
|
-
if (e) {
|
|
259
|
-
e.clearRect(0, 0, r.value.width, r.value.height);
|
|
260
|
-
const i = r.value.width, a = r.value.height;
|
|
261
|
-
r.value.width = i, r.value.height = a;
|
|
222
|
+
function w() {
|
|
223
|
+
if (a.value = !1, u.value = !1, T(), o.value) {
|
|
224
|
+
const t = o.value.getContext("2d");
|
|
225
|
+
if (t) {
|
|
226
|
+
t.clearRect(0, 0, o.value.width, o.value.height);
|
|
227
|
+
const r = o.value.width, i = o.value.height;
|
|
228
|
+
o.value.width = r, o.value.height = i;
|
|
262
229
|
}
|
|
263
230
|
}
|
|
264
|
-
|
|
231
|
+
}
|
|
232
|
+
async function F() {
|
|
233
|
+
return o.value ? await h(o.value, 3) : (console.error("❌ reInitWebGPU失败:canvas节点未绑定"), !1);
|
|
265
234
|
}
|
|
266
235
|
return {
|
|
267
|
-
videoRef:
|
|
268
|
-
canvasRef:
|
|
269
|
-
|
|
270
|
-
|
|
236
|
+
videoRef: g,
|
|
237
|
+
canvasRef: o,
|
|
238
|
+
isInitSuccess: u,
|
|
239
|
+
isProcessing: a,
|
|
240
|
+
processVideo: G,
|
|
241
|
+
stopProcessing: w,
|
|
242
|
+
reInitWebGPU: F
|
|
271
243
|
};
|
|
272
244
|
}
|
|
273
245
|
export {
|
|
274
|
-
|
|
246
|
+
B as default
|
|
275
247
|
};
|