Hast du eine Frage?


How-to: Autor*innen Selfie


Willkommen zum How-to: Autor*innen Selfie.
Hier lernst du, wie du ein Werkzeug programmierst mit dem du
per Gesichtstracking dein eigenes Gesicht verfolgen und modifizieren kannst.

Falls du etwas nicht verstehst, nutze den Chatbot auf der rechten Seite.
Kopiere den Teil vom Code, den du erklärt haben möchtest und stelle deine Frage dazu.



Was brauchen wir für das Werkzeug:
→ Ordnerstruktur & Dateien (siehe How-to: Basic Setup)
→ Browser (funktioniert am besten mit Chrome)
→ Localhost (siehe How-to: Localhost)
→ Text-Editor (zb. SublimeText)
→ Faceapi und Models Folder (im Werkzeug Ordner ablegen) Download

HTML index.html

Die index.html beinhaltet u.A.:
→ <script> zum Einbinden des benötigten Scripts
→ <h1> Überschrift (optional)
→ <p> Anleitungstext (optional)
→ <button> zum Stoppen/Starten des Trackings
→ <video> zum Gesicht tracken
→ <div> zum Anzeigen der ausgeschnittenen Gesichtsteile

1. Öffne die index.html

Du kannst den Code kopieren und in deine Datei einfügen:

<!DOCTYPE html>
<html>
<head>
<title>Autor*in</title>
<link rel="stylesheet" type="text/css" href="css/style.css">
<script defer src="face-api.min.js"></script>
<script defer src="js/script.js"></script>
</head>
<body>

<h1>Autor*innen Selfie</h1>
<p>Bewege dein Gesicht und zeichne damit ein Bild von dir selbst.<br>
Dr&uuml;cke auf L&ouml;schen um die Canvas zu l&ouml;schen, auf Start oder Stopp um das Video anzuhalten.<br>
</p>

<button id="toggleButton">Stopp</button>
<button id="deleteButton">L&ouml;schen</button>

<video id="video" width="1200" height="800" autoplay muted></video>
<div id="markedPartsContainer"></div>

</body>
</html>

2. Speicher die index.html

CSS style.css

1. Öffne die style.css

Die style.css beinhaltet:
→ *{} Zücksetzen auf den Default Style
→ h1{}, p{}, button{}, div{} .slider{} basic Styling (optinal)
→ video{} macht das Input Video unsichtbar (wichtig)

Du kannst den Code kopieren und in deine style.css Datei einfügen:

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
width: 100vw;
height: 100vh;
}

h1,
p {
margin: 20px;
}

div {
pointer-events: none;
}

video {
opacity: 0;
position: absolute;
pointer-events: none;
z-index: -9999999999999 !important;
}

button {
margin-left: 20px;
z-index: 9999999999999 !important;
}


2. Speicher die style.css


JS script.js

Die script.js beinhaltet:
→ startVideo: Ermöglicht auf Kamera der Nutzer*innen zuzugreifen und startet den Video-Stream
→ toggleFunction: Starten und Stoppen der Gesichtserkennung
→ runFunction: Ist für die eigentliche Verarbeitung der Gesichtserkennung zuständig
→ detectFaces: Funktion um Gesichter im Video zu erkennen
→ extractMarkedParts: Zeichnet gefüllte Rechtecke um die markierten Gesichtsteile auf dem Canvas
→ extractMarkedParts: Extraktion der markierten Teile aus dem Video-Stream / Canvas
→ printMarkedParts: Fügt das Bild der markierten Teile dem Container-Element hinzu

1. Öffne die script.js

Du kannst den Code kopieren und in deine script.js Datei einfügen:

const video = document.getElementById('video');
const markedPartsContainer = document.getElementById('markedPartsContainer');
const deleteButton = document.getElementById('deleteButton');
const toggleButton = document.getElementById('toggleButton');

let imageIndex = 0;
let isProcessing = false;
let isRunning = true;
let isVideoLoaded = false; // Track video loading state

Promise.all([
faceapi.nets.tinyFaceDetector.loadFromUri('/models'),
faceapi.nets.faceLandmark68Net.loadFromUri('/models')
]).then(startVideo);

function startVideo() {
navigator.mediaDevices.getUserMedia({ video: true })
.then((stream) => {
video.srcObject = stream;
video.onloadedmetadata = () => {
video.play();
isVideoLoaded = true;
runFunction();
};
})
.catch((err) => console.error(err));
}

deleteButton.addEventListener('click', () => {
markedPartsContainer.innerHTML = '';
imageIndex = 0;
});

toggleButton.addEventListener('click', toggleFunction);

function toggleFunction() {
isRunning = !isRunning;
toggleButton.textContent = isRunning ? 'Stop' : 'Start';

if (isRunning) {
runFunction();
}
}

function runFunction() {
if (!isVideoLoaded) {
setTimeout(runFunction, 200);
return;
}

const displaySize = { width: video.width, height: video.height };
const canvas = faceapi.createCanvasFromMedia(video);
markedPartsContainer.appendChild(canvas);
faceapi.matchDimensions(canvas, displaySize);

async function detectFaces() {
if (!isProcessing) {
isProcessing = true;

const detections = await faceapi
.detectAllFaces(video, new faceapi.TinyFaceDetectorOptions({ inputSize: 320 }))
.withFaceLandmarks(false);
const resizedDetections = faceapi.resizeResults(detections, displaySize);

if (resizedDetections && resizedDetections.length > 0) {
const landmarks = resizedDetections[0].landmarks;
const eyeLandmarks = [landmarks.getLeftEye(), landmarks.getRightEye()];
const noseLandmarks = [landmarks.getNose()];
const mouthLandmarks = [landmarks.getMouth()];

const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';

drawFilledSurfaces(ctx, eyeLandmarks, 1.5, 10, 70, 40);
drawFilledSurfaces(ctx, noseLandmarks, 2, 10, 50, 60);
drawFilledSurfaces(ctx, mouthLandmarks, 1, 0, 150, 70);

const markedParts = extractMarkedParts(video, ctx);

if (markedParts) {
printMarkedParts(markedPartsContainer, markedParts, imageIndex);
imageIndex++;
}
}

isProcessing = false;
}

if (isRunning) {
setTimeout(detectFaces, 100);
}
}

detectFaces();
}

function drawFilledSurfaces(ctx, landmarkGroups, sizeMultiplier, padding, width, height) {
landmarkGroups.forEach((landmarks) => {
const minX = Math.min(...landmarks.map((pt) => pt.x)) - padding;
const minY = Math.min(...landmarks.map((pt) => pt.y)) - padding;
const maxWidth = Math.max(...landmarks.map((pt) => pt.x)) + padding;
const maxHeight = Math.max(...landmarks.map((pt) => pt.y)) + padding;
const scaledWidth = width * sizeMultiplier;
const scaledHeight = height * sizeMultiplier;
const offsetX = (maxWidth - minX - scaledWidth) / 2;
const offsetY = (maxHeight - minY - scaledHeight) / 2;
const startX = minX + offsetX;
const startY = minY + offsetY;

ctx.beginPath();
ctx.rect(startX, startY, scaledWidth, scaledHeight);
ctx.closePath();
ctx.fill();
});
}

function extractMarkedParts(video, ctx) {
const { width, height } = video;
const padding = 200;
const canvas = document.createElement('canvas');
canvas.width = width + padding * 2;
canvas.height = height + padding * 2;
const canvasCtx = canvas.getContext('2d');
canvasCtx.fillStyle = 'white';
canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
canvasCtx.drawImage(video, padding, padding, width, height);
canvasCtx.globalCompositeOperation = 'destination-in';
canvasCtx.drawImage(ctx.canvas, padding, padding);
return canvas.toDataURL();
}

function printMarkedParts(container, markedParts, index) {
const image = document.createElement('img');
image.src = markedParts;
image.alt = `Marked Parts ${index}`;
image.style.position = 'fixed';
image.style.top = '0';
image.style.left = '0';

container.appendChild(image);
}

2. Speicher die script.js


Du kannst jetzt den Ordner deines Werkzeuges mit einem Localhost öffnen und in deinem Browser das Tool testen :)