Implement background blur.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
'use strict';
|
||||
|
||||
let imageSegmenter;
|
||||
|
||||
async function loadImageSegmenter() {
|
||||
let module = await import('/third-party/tasks-vision/vision_bundle.mjs');
|
||||
|
||||
let vision = await module.FilesetResolver.forVisionTasks(
|
||||
"/third-party/tasks-vision/wasm"
|
||||
);
|
||||
|
||||
imageSegmenter =
|
||||
await module.ImageSegmenter.createFromOptions(vision, {
|
||||
baseOptions: {
|
||||
modelAssetPath: '/third-party/tasks-vision/selfie_segmenter.tflite',
|
||||
},
|
||||
outputCategoryMask: true,
|
||||
outputConfidenceMasks: false,
|
||||
runningMode: 'VIDEO',
|
||||
});
|
||||
}
|
||||
|
||||
loadImageSegmenter();
|
||||
|
||||
onmessage = e => {
|
||||
if(!imageSegmenter) {
|
||||
// not ready yet
|
||||
postMessage(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let bitmap = e.data.bitmap;
|
||||
if(!(bitmap instanceof ImageBitmap))
|
||||
throw new Error('Bad type for worker data');
|
||||
let width = bitmap.width;
|
||||
let height = bitmap.height;
|
||||
imageSegmenter.segmentForVideo(
|
||||
bitmap, e.data.timestamp,
|
||||
result => {
|
||||
/** @type{Uint8Array} */
|
||||
let mask = result.categoryMask.getAsUint8Array();
|
||||
let id = new ImageData(width, height);
|
||||
for(let i = 0; i < mask.length; i++)
|
||||
id.data[4 * i + 3] = mask[i];
|
||||
createImageBitmap(id).then(ib => {
|
||||
postMessage({
|
||||
bitmap: bitmap,
|
||||
mask: ib,
|
||||
}, [bitmap, ib]);
|
||||
});
|
||||
},
|
||||
);
|
||||
} catch(e) {
|
||||
postMessage(e);
|
||||
}
|
||||
};
|
||||
+63
-2
@@ -1027,7 +1027,7 @@ function cancelReconsiderParameters() {
|
||||
* @property {string} [description]
|
||||
* @property {(this: Filter) => Promise<void>} [init]
|
||||
* @property {(this: Filter) => Promise<void>} [cleanup]
|
||||
* @property {(this: Filter, src: HTMLVideoElement, ctx: RenderingContext) => Promise<boolean>} draw
|
||||
* @property {(this: Filter, src: HTMLVideoElement, ctx: CanvasRenderingContext2D) => Promise<boolean>} draw
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1052,7 +1052,7 @@ function Filter(stream, definition) {
|
||||
/** @type {HTMLCanvasElement} */
|
||||
this.canvas = document.createElement('canvas');
|
||||
/** @type {any} */
|
||||
this.context = this.canvas.getContext('2d', {alpha: false});
|
||||
this.context = this.canvas.getContext('2d');
|
||||
/** @type {MediaStream} */
|
||||
this.captureStream = null;
|
||||
/** @type {MediaStream} */
|
||||
@@ -1223,6 +1223,67 @@ let filters = {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
'background-blur': {
|
||||
description: 'Background blur',
|
||||
init: async function(ctx) {
|
||||
if(!(this instanceof Filter))
|
||||
throw new Error('Bad type for this');
|
||||
if(this.userdata.worker)
|
||||
throw new Error("Worker already running (this shouldn't happen)")
|
||||
this.userdata.worker = new Worker('/background-blur-worker.js');
|
||||
},
|
||||
cleanup: async function() {
|
||||
if(this.userdata.worker.onmessage) {
|
||||
this.userdata.worker.onmessage(null);
|
||||
}
|
||||
this.userdata.worker.terminate();
|
||||
this.userdata.worker = null;
|
||||
},
|
||||
draw: async function(src, ctx) {
|
||||
let bitmap = await createImageBitmap(src);
|
||||
let p = new Promise((resolve, reject) => {
|
||||
this.userdata.worker.onmessage = e => {
|
||||
if(e && e.data) {
|
||||
if(e.data instanceof Error)
|
||||
reject(e.data);
|
||||
else
|
||||
resolve(e.data);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
};
|
||||
});
|
||||
this.userdata.worker.postMessage({
|
||||
bitmap: bitmap,
|
||||
timestamp: performance.now(),
|
||||
}, [bitmap]);
|
||||
let result = await p;
|
||||
|
||||
if(!result)
|
||||
return false;
|
||||
|
||||
let mask = result.mask;
|
||||
bitmap = result.bitmap;
|
||||
|
||||
if(ctx.canvas.width !== src.videoWidth ||
|
||||
ctx.canvas.height !== src.videoHeight) {
|
||||
ctx.canvas.width = src.videoWidth;
|
||||
ctx.canvas.height = src.videoHeight;
|
||||
}
|
||||
|
||||
ctx.globalCompositeOperation = 'copy';
|
||||
ctx.filter = `blur(${src.videoWidth / 256}px)`;
|
||||
ctx.drawImage(mask, 0, 0);
|
||||
ctx.globalCompositeOperation = 'source-in';
|
||||
ctx.filter = `blur(${src.videoWidth / 48}px)`;
|
||||
ctx.drawImage(result.bitmap, 0, 0);
|
||||
ctx.globalCompositeOperation = 'destination-atop';
|
||||
ctx.filter = 'none';
|
||||
ctx.drawImage(result.bitmap, 0, 0);
|
||||
ctx.globalCompositeOperation = 'source-over';
|
||||
return true;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function addFilters() {
|
||||
|
||||
+10
-6
@@ -88,13 +88,17 @@ func Serve(address string, dataDir string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func cspHeader(w http.ResponseWriter, connect string) {
|
||||
c := "connect-src ws: wss: 'self';"
|
||||
func cspHeader(w http.ResponseWriter, connect string, unsafeEval bool) {
|
||||
c := "connect-src ws: wss: 'self'; "
|
||||
if connect != "" {
|
||||
c = "connect-src " + connect + " ws: wss: 'self';"
|
||||
c = "connect-src " + connect + " ws: wss: 'self'; "
|
||||
}
|
||||
s := "script-src 'self'; "
|
||||
if unsafeEval {
|
||||
s = "script-src 'unsafe-eval' 'self'; "
|
||||
}
|
||||
w.Header().Add("Content-Security-Policy",
|
||||
c+" img-src data: 'self'; media-src blob: 'self'; default-src 'self'")
|
||||
c+s+"img-src data: 'self'; media-src blob: 'self'; default-src 'self'")
|
||||
|
||||
// Make browser stop sending referrer information
|
||||
w.Header().Add("Referrer-Policy", "no-referrer")
|
||||
@@ -207,7 +211,7 @@ func (fh *fileHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
cspHeader(w, "")
|
||||
cspHeader(w, "", r.URL.Path == "/blur-background-worker.js")
|
||||
p := r.URL.Path
|
||||
// this ensures any leading .. are removed by path.Clean below
|
||||
if !strings.HasPrefix(p, "/") {
|
||||
@@ -373,7 +377,7 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
status := g.Status(false, nil)
|
||||
cspHeader(w, status.AuthServer)
|
||||
cspHeader(w, status.AuthServer, false)
|
||||
serveFile(w, r, filepath.Join(StaticRoot, "galene.html"))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user