update readme and repo structure

This commit is contained in:
Yohan Chalier
2022-08-12 10:32:03 +02:00
parent f82791afc6
commit 4e32d444ee
6 changed files with 312 additions and 304 deletions
+4
View File
@@ -18,3 +18,7 @@ Here are some videos made using those scripts (click on the thumbnails to see th
Drop h264 I-Frames | Optical Flow Transfer
------------------ | ---------------------
[![](drop-h264-iframes/example.gif)](https://i.imgur.com/bOHT26q.mp4) | [![](optical-flow-transfer/example.gif)](https://i.imgur.com/pt6Sq7A.mp4)
## Demo
Here is a [web demonstration](https://ychalier.github.io/datamoshing/optical-flow-transfer/www/) where you may try different video sources (such as the webcam) and image sources to perform optical flow transfer.
+4
View File
@@ -36,3 +36,7 @@ python optical_flow_transfer.py <source-video> <source-image> <output-video>
[![](example.gif)](https://i.imgur.com/pt6Sq7A.mp4)
I wrote some details [on my blog](https://chalier.fr/blog/datamoshing#opticalflowtransfer).
## Demo
Here is a [web demonstration](www/) where you may try different video sources (such as the webcam) and image sources to perform optical flow transfer.
@@ -1,106 +1,106 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf8">
<title>Optical Flow Transfer</title>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/ychalier/pifekit/pifekit.min.css" />
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/ychalier/pifekit/pifekit.min.js"></script>
<script src="opencv.js"></script>
<script src="master.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body class="dark-blue">
<div class="container text-center">
<h1>Optical Flow Transfer</h1>
<div class="xcolumns">
<div class="xcolumn-left">
<div class="media-container" id="container-video">
<button class="btn btn-primary" onclick="showModal('modal-load-video')" id="button-video-modal">Load video</button>
</div>
<div style="height: .4em"></div>
<div class="media-container" id="container-image">
<button class="btn btn-primary" onclick="showModal('modal-load-image')" id="button-image-modal">Load image</button>
</div>
</div>
<div class="xcolumn-right">
<div id="container-canvas" rowspan="2">
<canvas id="canvas" width="256" height="256"></canvas>
</div>
<div class="mt-4">
<button class="btn" id="button-reset" disabled>Reset</button>
<button class="btn" id="button-download" disabled>Download</button>
</div>
</div>
</div>
</div>
<footer class="text-center">
<p>
More details on my <a href="https://chalier.fr/blog/datamoshing">blog</a> · Images from <a href="https://picsum.photos/">Lorem Picsum</a>
</p>
</footer>
<div class="modal" id="modal-load-video">
<span class="modal-overlay" onclick="closeModal('modal-load-video')"></span>
<div class="modal-container">
<div class="modal-header">
<div class="modal-title h5">Load video</div>
<div class="modal-subtitle text-gray">Load a video from a URL, a local file, or your webcam</div>
</div>
<div class="modal-body">
<div class="form-group pb-2">
<label class="form-label" for="input-video-url">URL</label>
<input class="form-input" type="url" id="input-video-url" placeholder="https://example.org/axis-cgi/mjpg/video.cgi" />
</div>
<div class="divider text-center" data-content="OR"></div>
<div class="form-group">
<label class="form-label" for="input-video-file">File</label>
<input class="form-input" type="file" id="input-video-file" accept="video/*" />
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="button-video-webcam">Webcam</button>
<button class="btn btn-primary" id="button-video-load">Load</button>
<button class="btn" onclick="closeModal('modal-load-video')">Cancel</button>
</div>
</div>
</div>
<div class="modal" id="modal-load-image">
<span class="modal-overlay" onclick="closeModal('modal-load-image')"></span>
<div class="modal-container">
<div class="modal-header">
<div class="modal-title h5">Load image</div>
<div class="modal-subtitle text-gray">Load an image from a URL or a local file</div>
</div>
<div class="modal-body">
<div class="form-group pb-2">
<label class="form-label" for="input-image-url">URL</label>
<input class="form-input" type="url" id="input-image-url" placeholder="https://example.org/image.jpg" />
</div>
<div class="divider text-center" data-content="OR"></div>
<div class="form-group">
<label class="form-label" for="input-image-file">File</label>
<input class="form-input" type="file" id="input-image-file" accept="image/*" />
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="button-image-random">Random</button>
<button class="btn btn-primary" id="button-image-load">Load</button>
<button class="btn" onclick="closeModal('modal-load-image')">Cancel</button>
</div>
</div>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf8">
<title>Optical Flow Transfer</title>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/gh/ychalier/pifekit/pifekit.min.css" />
<script type="text/javascript" src="https://cdn.jsdelivr.net/gh/ychalier/pifekit/pifekit.min.js"></script>
<script src="opencv.js"></script>
<script src="master.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body class="dark-blue">
<div class="container text-center">
<h1>Optical Flow Transfer</h1>
<div class="xcolumns">
<div class="xcolumn-left">
<div class="media-container" id="container-video">
<button class="btn btn-primary" onclick="showModal('modal-load-video')" id="button-video-modal">Load video</button>
</div>
<div style="height: .4em"></div>
<div class="media-container" id="container-image">
<button class="btn btn-primary" onclick="showModal('modal-load-image')" id="button-image-modal">Load image</button>
</div>
</div>
<div class="xcolumn-right">
<div id="container-canvas" rowspan="2">
<canvas id="canvas" width="256" height="256"></canvas>
</div>
<div class="mt-4">
<button class="btn" id="button-reset" disabled>Reset</button>
<button class="btn" id="button-download" disabled>Download</button>
</div>
</div>
</div>
</div>
<footer class="text-center">
<p>
More details on my <a href="https://chalier.fr/blog/datamoshing">blog</a> · Images from <a href="https://picsum.photos/">Lorem Picsum</a>
</p>
</footer>
<div class="modal" id="modal-load-video">
<span class="modal-overlay" onclick="closeModal('modal-load-video')"></span>
<div class="modal-container">
<div class="modal-header">
<div class="modal-title h5">Load video</div>
<div class="modal-subtitle text-gray">Load a video from a URL, a local file, or your webcam</div>
</div>
<div class="modal-body">
<div class="form-group pb-2">
<label class="form-label" for="input-video-url">URL</label>
<input class="form-input" type="url" id="input-video-url" placeholder="https://example.org/axis-cgi/mjpg/video.cgi" />
</div>
<div class="divider text-center" data-content="OR"></div>
<div class="form-group">
<label class="form-label" for="input-video-file">File</label>
<input class="form-input" type="file" id="input-video-file" accept="video/*" />
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="button-video-webcam">Webcam</button>
<button class="btn btn-primary" id="button-video-load">Load</button>
<button class="btn" onclick="closeModal('modal-load-video')">Cancel</button>
</div>
</div>
</div>
<div class="modal" id="modal-load-image">
<span class="modal-overlay" onclick="closeModal('modal-load-image')"></span>
<div class="modal-container">
<div class="modal-header">
<div class="modal-title h5">Load image</div>
<div class="modal-subtitle text-gray">Load an image from a URL or a local file</div>
</div>
<div class="modal-body">
<div class="form-group pb-2">
<label class="form-label" for="input-image-url">URL</label>
<input class="form-input" type="url" id="input-image-url" placeholder="https://example.org/image.jpg" />
</div>
<div class="divider text-center" data-content="OR"></div>
<div class="form-group">
<label class="form-label" for="input-image-file">File</label>
<input class="form-input" type="file" id="input-image-file" accept="image/*" />
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="button-image-random">Random</button>
<button class="btn btn-primary" id="button-image-load">Load</button>
<button class="btn" onclick="closeModal('modal-load-image')">Cancel</button>
</div>
</div>
</div>
</body>
</html>
@@ -1,198 +1,198 @@
navigator.getMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
async function load_effect() {
var video_loaded = false;
var image_loaded = false;
const size = 256;
const width = size;
const height = size;
const video_container = document.getElementById("container-video");
const video = document.createElement("video");
video.muted = true;
video.loop = true;
video.style.objectFit = "cover";
video.setAttribute("width", size);
video.setAttribute("height", size);
video.addEventListener("canplay", () => {
if (!video_loaded) {
console.log("Video is loaded");
video_container.innerHTML = "";
video_container.appendChild(video);
if (image_loaded) start_effect();
}
video_loaded = true;
}, false);
const image_container = document.getElementById("container-image");
const image = new Image();
const image_canvas = document.createElement("canvas");
image_canvas.width = width;
image_canvas.height = height;
const image_canvas_context = image_canvas.getContext("2d");
image.crossOrigin = "anonymous";
image.addEventListener("load", () => {
if (!image_loaded) {
console.log("Image is loaded");
image_container.innerHTML = "";
image_container.appendChild(image_canvas);
const target_ratio = width / height;
const current_ratio = image.width / image.height;
var cropped_width = image.width;
var cropped_height = image.height;
if (target_ratio > current_ratio) {
// Crop vertically
cropped_height = cropped_width / target_ratio;
} else if (target_ratio < current_ratio) {
// Crop horizontaly
cropped_width = target_ratio * cropped_height;
}
image_canvas_context.drawImage(image, (image.width - cropped_width) / 2, (image.height - cropped_height) / 2, cropped_width, cropped_height, 0, 0, width, height);
context.drawImage(image, (image.width - cropped_width) / 2, (image.height - cropped_height) / 2, cropped_width, cropped_height, 0, 0, width, height);
if (video_loaded) start_effect();
}
image_loaded = true;
}, false);
const canvas = document.getElementById("canvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext("2d");
var image_data;
var cap;
async function start_effect() {
console.log("Both media loaded, starting the effect");
document.getElementById("button-reset").removeAttribute("disabled");
document.getElementById("button-download").removeAttribute("disabled");
function takepicture() {
let frame = new cv.Mat(height, width, cv.CV_8UC4);
let frame_gray = new cv.Mat();
cap.read(frame);
cv.cvtColor(frame, frame_gray, cv.COLOR_RGBA2GRAY);
return frame_gray;
}
cap = new cv.VideoCapture(video);
let reference = takepicture();
image_data = context.getImageData(0, 0, width, height);
while (true) {
let current = takepicture();
let flow = new cv.Mat();
cv.calcOpticalFlowFarneback(reference, current, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
let image_data_copy = [...image_data.data];
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
let k = (i * width + j);
let ii = i + Math.round(flow.data32F[k * 2]);
let jj = j + Math.round(flow.data32F[k * 2 + 1]);
image_data.data[(ii * width + jj) * 4] = image_data_copy[(i * width + j) * 4];
image_data.data[(ii * width + jj) * 4 + 1] = image_data_copy[(i * width + j) * 4 + 1];
image_data.data[(ii * width + jj) * 4 + 2] = image_data_copy[(i * width + j) * 4 + 2];
image_data.data[(ii * width + jj) * 4 + 3] = image_data_copy[(i * width + j) * 4 + 3];
}
}
context.putImageData(image_data, 0, 0);
await new Promise(x => requestAnimationFrame(x));
current.copyTo(reference);
}
}
async function load_video_webcam() {
console.log("Loading video from the webcam");
navigator.getMedia({ video: true, audio: false }, (stream) => {
if (navigator.mozGetUserMedia) {
video.srcObject = stream;
} else {
video.src = vendorURL.createObjectURL(stream);
}
video.play();
}, (err) => {
alert(err);
console.error(err);
document.getElementById("button-video-modal").disabled = false;
});
while (!video_loaded) { await new Promise(resolve => setTimeout(resolve, 10)); }
}
async function load_video_url(video_url) {
console.log("Loading video from a URL:", video_url);
video.src = video_url;
video.play();
}
async function load_video_file(video_file) {
console.log("Loading video from a file:", video_file);
video.src = URL.createObjectURL(video_file);
video.play();
}
document.getElementById("button-video-webcam").addEventListener("click", () => {
document.getElementById("button-video-modal").disabled = true;
closeModal("modal-load-video");
load_video_webcam();
});
document.getElementById("button-video-load").addEventListener("click", () => {
let video_url = document.getElementById("input-video-url").value;
let video_files = document.getElementById("input-video-file").files;
if (video_url != "") {
load_video_url(video_url);
} else if (video_files.length > 0) {
load_video_file(video_files[0]);
} else {
alert("Please specify one source!");
return;
}
document.getElementById("button-video-modal").disabled = true;
closeModal("modal-load-video");
});
document.getElementById("button-image-random").addEventListener("click", () => {
console.log("Loading random image");
image.src = `https://picsum.photos/${ width }/${ height }`;
if (document.getElementById("button-image-modal")) {
document.getElementById("button-image-modal").disabled = true;
}
closeModal("modal-load-image");
});
document.getElementById("button-image-load").addEventListener("click", () => {
let image_url = document.getElementById("input-image-url").value;
let image_files = document.getElementById("input-image-file").files;
if (image_url != "") {
console.log("Loading image from a URL:", image_url);
image.src = image_url;
} else if (image_files.length > 0) {
console.log("Loading image from a file:", image_files[0]);
image.src = URL.createObjectURL(image_files[0]);
} else {
alert("Please specify one source!");
return;
}
document.getElementById("button-image-modal").disabled = true;
closeModal("modal-load-image");
});
document.getElementById("button-reset").addEventListener("click", () => {
image_data = image_canvas_context.getImageData(0, 0, width, height);
});
document.getElementById("button-download").addEventListener("click", () => {
const link = document.createElement("a");
link.download = `${ Date.now() }.png`;
link.href = canvas.toDataURL();
link.click();
});
}
window.addEventListener("load", load_effect);
navigator.getMedia = (navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia);
async function load_effect() {
var video_loaded = false;
var image_loaded = false;
const size = 256;
const width = size;
const height = size;
const video_container = document.getElementById("container-video");
const video = document.createElement("video");
video.muted = true;
video.loop = true;
video.style.objectFit = "cover";
video.setAttribute("width", size);
video.setAttribute("height", size);
video.addEventListener("canplay", () => {
if (!video_loaded) {
console.log("Video is loaded");
video_container.innerHTML = "";
video_container.appendChild(video);
if (image_loaded) start_effect();
}
video_loaded = true;
}, false);
const image_container = document.getElementById("container-image");
const image = new Image();
const image_canvas = document.createElement("canvas");
image_canvas.width = width;
image_canvas.height = height;
const image_canvas_context = image_canvas.getContext("2d");
image.crossOrigin = "anonymous";
image.addEventListener("load", () => {
if (!image_loaded) {
console.log("Image is loaded");
image_container.innerHTML = "";
image_container.appendChild(image_canvas);
const target_ratio = width / height;
const current_ratio = image.width / image.height;
var cropped_width = image.width;
var cropped_height = image.height;
if (target_ratio > current_ratio) {
// Crop vertically
cropped_height = cropped_width / target_ratio;
} else if (target_ratio < current_ratio) {
// Crop horizontaly
cropped_width = target_ratio * cropped_height;
}
image_canvas_context.drawImage(image, (image.width - cropped_width) / 2, (image.height - cropped_height) / 2, cropped_width, cropped_height, 0, 0, width, height);
context.drawImage(image, (image.width - cropped_width) / 2, (image.height - cropped_height) / 2, cropped_width, cropped_height, 0, 0, width, height);
if (video_loaded) start_effect();
}
image_loaded = true;
}, false);
const canvas = document.getElementById("canvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext("2d");
var image_data;
var cap;
async function start_effect() {
console.log("Both media loaded, starting the effect");
document.getElementById("button-reset").removeAttribute("disabled");
document.getElementById("button-download").removeAttribute("disabled");
function takepicture() {
let frame = new cv.Mat(height, width, cv.CV_8UC4);
let frame_gray = new cv.Mat();
cap.read(frame);
cv.cvtColor(frame, frame_gray, cv.COLOR_RGBA2GRAY);
return frame_gray;
}
cap = new cv.VideoCapture(video);
let reference = takepicture();
image_data = context.getImageData(0, 0, width, height);
while (true) {
let current = takepicture();
let flow = new cv.Mat();
cv.calcOpticalFlowFarneback(reference, current, flow, 0.5, 3, 15, 3, 5, 1.2, 0);
let image_data_copy = [...image_data.data];
for (let i = 0; i < height; i++) {
for (let j = 0; j < width; j++) {
let k = (i * width + j);
let ii = i + Math.round(flow.data32F[k * 2]);
let jj = j + Math.round(flow.data32F[k * 2 + 1]);
image_data.data[(ii * width + jj) * 4] = image_data_copy[(i * width + j) * 4];
image_data.data[(ii * width + jj) * 4 + 1] = image_data_copy[(i * width + j) * 4 + 1];
image_data.data[(ii * width + jj) * 4 + 2] = image_data_copy[(i * width + j) * 4 + 2];
image_data.data[(ii * width + jj) * 4 + 3] = image_data_copy[(i * width + j) * 4 + 3];
}
}
context.putImageData(image_data, 0, 0);
await new Promise(x => requestAnimationFrame(x));
current.copyTo(reference);
}
}
async function load_video_webcam() {
console.log("Loading video from the webcam");
navigator.getMedia({ video: true, audio: false }, (stream) => {
if (navigator.mozGetUserMedia) {
video.srcObject = stream;
} else {
video.src = vendorURL.createObjectURL(stream);
}
video.play();
}, (err) => {
alert(err);
console.error(err);
document.getElementById("button-video-modal").disabled = false;
});
while (!video_loaded) { await new Promise(resolve => setTimeout(resolve, 10)); }
}
async function load_video_url(video_url) {
console.log("Loading video from a URL:", video_url);
video.src = video_url;
video.play();
}
async function load_video_file(video_file) {
console.log("Loading video from a file:", video_file);
video.src = URL.createObjectURL(video_file);
video.play();
}
document.getElementById("button-video-webcam").addEventListener("click", () => {
document.getElementById("button-video-modal").disabled = true;
closeModal("modal-load-video");
load_video_webcam();
});
document.getElementById("button-video-load").addEventListener("click", () => {
let video_url = document.getElementById("input-video-url").value;
let video_files = document.getElementById("input-video-file").files;
if (video_url != "") {
load_video_url(video_url);
} else if (video_files.length > 0) {
load_video_file(video_files[0]);
} else {
alert("Please specify one source!");
return;
}
document.getElementById("button-video-modal").disabled = true;
closeModal("modal-load-video");
});
document.getElementById("button-image-random").addEventListener("click", () => {
console.log("Loading random image");
image.src = `https://picsum.photos/${ width }/${ height }`;
if (document.getElementById("button-image-modal")) {
document.getElementById("button-image-modal").disabled = true;
}
closeModal("modal-load-image");
});
document.getElementById("button-image-load").addEventListener("click", () => {
let image_url = document.getElementById("input-image-url").value;
let image_files = document.getElementById("input-image-file").files;
if (image_url != "") {
console.log("Loading image from a URL:", image_url);
image.src = image_url;
} else if (image_files.length > 0) {
console.log("Loading image from a file:", image_files[0]);
image.src = URL.createObjectURL(image_files[0]);
} else {
alert("Please specify one source!");
return;
}
document.getElementById("button-image-modal").disabled = true;
closeModal("modal-load-image");
});
document.getElementById("button-reset").addEventListener("click", () => {
image_data = image_canvas_context.getImageData(0, 0, width, height);
});
document.getElementById("button-download").addEventListener("click", () => {
const link = document.createElement("a");
link.download = `${ Date.now() }.png`;
link.href = canvas.toDataURL();
link.click();
});
}
window.addEventListener("load", load_effect);