<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Augmented Reality | Étienne Peillard</title><link>https://www.etiennepeillard.com/category/augmented-reality/</link><atom:link href="https://www.etiennepeillard.com/category/augmented-reality/index.xml" rel="self" type="application/rss+xml"/><description>Augmented Reality</description><generator>Wowchemy (https://wowchemy.com)</generator><language>en-us</language><lastBuildDate>Thu, 26 Feb 2026 00:00:00 +0000</lastBuildDate><image><url>https://www.etiennepeillard.com/media/sharing.png</url><title>Augmented Reality</title><link>https://www.etiennepeillard.com/category/augmented-reality/</link></image><item><title>Lab — Smartphone augmented reality</title><link>https://www.etiennepeillard.com/teaching/lab/ar-handheld/</link><pubDate>Thu, 26 Feb 2026 00:00:00 +0000</pubDate><guid>https://www.etiennepeillard.com/teaching/lab/ar-handheld/</guid><description>&lt;h2 id="objective">Objective&lt;/h2>
&lt;p>Create an augmented reality application capable of:&lt;/p>
&lt;ol>
&lt;li>detecting an image used as a marker,&lt;/li>
&lt;li>displaying a 3D object attached to this marker,&lt;/li>
&lt;li>detecting a plane in the environment,&lt;/li>
&lt;li>defining a play area,&lt;/li>
&lt;li>creating interactions between markers and the area.&lt;/li>
&lt;/ol>
&lt;h2 id="part-1--project-setup">Part 1 — Project setup&lt;/h2>
&lt;ol>
&lt;li>
&lt;p>Create a Unity project.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Switch the platform to &lt;strong>Android&lt;/strong> in &lt;em>Build Settings&lt;/em>.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>To access logs on the phone, install &lt;strong>Android Logcat&lt;/strong> from the Package Manager.&lt;/p>
&lt;/blockquote>
&lt;ol start="3">
&lt;li>
&lt;p>Install &lt;strong>AR Foundation&lt;/strong>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Install &lt;strong>ARCore XR Plugin&lt;/strong> (Android) or &lt;strong>ARKit XR Plugin&lt;/strong> (iOS).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Open &lt;strong>Project Settings → XR Plug-in Management&lt;/strong> and enable:&lt;/p>
&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>&lt;strong>ARCore&lt;/strong> in the Android tab&lt;/li>
&lt;li>&lt;strong>ARKit&lt;/strong> in the iOS tab (if needed)&lt;/li>
&lt;/ul>
&lt;ol start="6">
&lt;li>In &lt;strong>Project Settings → Player → Android&lt;/strong>:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>set &lt;strong>Minimum API Level: Android 10 / API 29&lt;/strong>&lt;/li>
&lt;li>in &lt;strong>Graphics APIs&lt;/strong>, disable &lt;strong>Vulkan&lt;/strong> and keep &lt;strong>OpenGLES3&lt;/strong> only&lt;/li>
&lt;/ul>
&lt;ol start="7">
&lt;li>Add to the scene:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>&lt;strong>AR Session&lt;/strong>&lt;/li>
&lt;li>&lt;strong>XR Origin&lt;/strong> (with &lt;strong>AR Camera&lt;/strong>)&lt;/li>
&lt;/ul>
&lt;ol start="8">
&lt;li>Mobile rendering configuration&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>In the project, open: &lt;code>Assets/Settings/Mobile_Renderer&lt;/code>&lt;/li>
&lt;li>In the inspector: &lt;strong>Add Renderer Feature → AR Background Renderer Feature&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>Verify that the application launches on the phone and that the camera feed is visible.&lt;/p>
&lt;p>When launching the app on the phone for the first time, &lt;strong>allow camera access&lt;/strong> when requested by the system.&lt;/p>
&lt;h2 id="part-2--image-detection">Part 2 — Image detection&lt;/h2>
&lt;ol>
&lt;li>Create a new &lt;strong>Reference Image Library&lt;/strong> asset.&lt;/li>
&lt;li>Add a marker image.&lt;/li>
&lt;li>Add an &lt;strong>ARTrackedImageManager&lt;/strong> component to the XR Origin.&lt;/li>
&lt;li>Assign the image library.&lt;/li>
&lt;li>Create a prefab for the 3D object to display on the marker.&lt;/li>
&lt;li>Assign this prefab to the &lt;strong>ARTrackedImageManager&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;blockquote>
&lt;p>You can use the markers provided for this lab. The &lt;strong>ImageLibrary&lt;/strong> asset is available &lt;a href="./Image%20Library%20small/ImageLibrary_small.unitypackage">here&lt;/a> and the markers are available &lt;a href="./Image%20Library%20small/markers_sheet.png">here&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;h2 id="part-3--plane-detection-selecting-a-plane--play-area">Part 3 — Plane detection (selecting a plane + play area)&lt;/h2>
&lt;h3 id="objective-1">Objective&lt;/h3>
&lt;ul>
&lt;li>detect horizontal/vertical planes using AR Foundation&lt;/li>
&lt;li>allow the user to &lt;strong>tap&lt;/strong> on a detected plane&lt;/li>
&lt;li>place a &lt;strong>play area&lt;/strong> (GameBoard) on this plane&lt;/li>
&lt;/ul>
&lt;h3 id="31-add-required-ar-components">3.1 Add required AR components&lt;/h3>
&lt;p>On the &lt;strong>XR Origin&lt;/strong> object:&lt;/p>
&lt;p>&lt;strong>ARPlaneManager&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Detects and maintains planes.&lt;/li>
&lt;li>Assign a &lt;strong>Plane Prefab&lt;/strong> to visualize planes.&lt;/li>
&lt;/ul>
&lt;h4 id="create-a-simple-plane-prefab">Create a simple Plane Prefab&lt;/h4>
&lt;ol>
&lt;li>&lt;strong>Create → Empty GameObject&lt;/strong>&lt;/li>
&lt;li>Name the object: &lt;code>ARPlanePrefab&lt;/code>&lt;/li>
&lt;li>Add the components:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>&lt;strong>AR Plane&lt;/strong>&lt;/li>
&lt;li>&lt;strong>AR Plane Mesh Visualizer&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Mesh Renderer&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Mesh Filter&lt;/strong>&lt;/li>
&lt;/ul>
&lt;ol start="4">
&lt;li>Create a simple material (e.g. &lt;strong>Unlit / Color&lt;/strong> transparent).&lt;/li>
&lt;li>Assign this material to the &lt;strong>Mesh Renderer&lt;/strong>.&lt;/li>
&lt;li>Drag the object into the &lt;em>Project&lt;/em> folder to create a prefab.&lt;/li>
&lt;li>Assign this prefab in &lt;strong>ARPlaneManager → Plane Prefab&lt;/strong>.&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>ARRaycastManager&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Used to cast a ray from the screen to detect planes.&lt;/li>
&lt;/ul>
&lt;p>Recommended setting:&lt;/p>
&lt;ul>
&lt;li>Detection Mode → &lt;strong>Horizontal&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h3 id="32-create-a-play-area-prefab-gameboard">3.2 Create a play area prefab (GameBoard)&lt;/h3>
&lt;p>Create a simple prefab:&lt;/p>
&lt;ul>
&lt;li>a &lt;strong>Quad&lt;/strong> lying flat&lt;/li>
&lt;li>a semi-transparent material&lt;/li>
&lt;li>scale around &lt;strong>0.5 m&lt;/strong>&lt;/li>
&lt;li>optional: &lt;strong>BoxCollider&lt;/strong>&lt;/li>
&lt;/ul>
&lt;h3 id="33-implement-tap-to-place-new-input-system">3.3 Implement tap-to-place (New Input System)&lt;/h3>
&lt;p>In this lab we use the &lt;strong>New Input System&lt;/strong> (Unity Input System Package) to detect taps.&lt;/p>
&lt;h4 id="prerequisites">Prerequisites&lt;/h4>
&lt;ol>
&lt;li>Open &lt;strong>Project Settings → Player&lt;/strong>&lt;/li>
&lt;li>Set &lt;strong>Active Input Handling → Input System Package (New)&lt;/strong>&lt;/li>
&lt;/ol>
&lt;h4 id="principle">Principle&lt;/h4>
&lt;p>Create a script &lt;code>BoardPlacement.cs&lt;/code>. The script should:&lt;/p>
&lt;ol>
&lt;li>enable &lt;strong>EnhancedTouch&lt;/strong> (at startup)&lt;/li>
&lt;li>retrieve the first active touch&lt;/li>
&lt;li>filter the &lt;strong>Began&lt;/strong> phase&lt;/li>
&lt;li>perform an &lt;strong>ARRaycast&lt;/strong> toward planes (&lt;code>PlaneWithinPolygon&lt;/code>)&lt;/li>
&lt;li>create or move the &lt;code>GameBoard&lt;/code> at the hit pose&lt;/li>
&lt;/ol>
&lt;p>Useful functions / APIs:&lt;/p>
&lt;ul>
&lt;li>&lt;code>EnhancedTouchSupport.Enable()&lt;/code> / &lt;code>Disable()&lt;/code>&lt;/li>
&lt;li>&lt;code>Touch.activeTouches&lt;/code>&lt;/li>
&lt;li>&lt;code>touch.phase&lt;/code> (New Input System)&lt;/li>
&lt;li>&lt;code>touch.screenPosition&lt;/code>&lt;/li>
&lt;li>&lt;code>ARRaycastManager.Raycast(...)&lt;/code>&lt;/li>
&lt;li>&lt;code>TrackableType.PlaneWithinPolygon&lt;/code>&lt;/li>
&lt;li>&lt;code>Pose&lt;/code>&lt;/li>
&lt;li>&lt;code>Instantiate(...)&lt;/code>&lt;/li>
&lt;li>&lt;code>transform.SetPositionAndRotation(...)&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="checkpoint">Checkpoint&lt;/h3>
&lt;p>You should be able to:&lt;/p>
&lt;ul>
&lt;li>see detected planes&lt;/li>
&lt;li>tap a plane&lt;/li>
&lt;li>place the play area&lt;/li>
&lt;/ul>
&lt;h2 id="part-4--interactions">Part 4 — Interactions&lt;/h2>
&lt;h3 id="41-basic-interaction">4.1 Basic interaction&lt;/h3>
&lt;p>Implement an interaction between:&lt;/p>
&lt;ul>
&lt;li>detected markers&lt;/li>
&lt;li>the play area&lt;/li>
&lt;/ul>
&lt;p>Possible examples:&lt;/p>
&lt;ul>
&lt;li>an object appears when the marker enters the area&lt;/li>
&lt;li>the marker moves an object within the area&lt;/li>
&lt;li>the marker orientation modifies a parameter&lt;/li>
&lt;/ul>
&lt;p>This section is intentionally open.&lt;/p>
&lt;h3 id="42-resources-for-going-further">4.2 Resources for going further&lt;/h3>
&lt;p>The &lt;strong>AR Foundation&lt;/strong> package provides several useful features to enrich your project.&lt;/p>
&lt;p>&lt;strong>Image tracking&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>ARTrackedImageManager&lt;/code>&lt;/li>
&lt;li>events: &lt;code>trackedImagesChanged&lt;/code>&lt;/li>
&lt;li>access to marker position&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Plane detection&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>ARPlaneManager&lt;/code>&lt;/li>
&lt;li>access to detected planes&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>AR Raycast&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>ARRaycastManager.Raycast()&lt;/code>&lt;/li>
&lt;li>allows interaction with the real environment&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>AR Anchors&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>ARAnchorManager&lt;/code>&lt;/li>
&lt;li>allows objects to remain fixed in space&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Light estimation&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>ARCameraManager&lt;/code>&lt;/li>
&lt;li>adapts virtual lighting to the real scene&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Face / body tracking&lt;/strong> (depending on device)&lt;/p>
&lt;ul>
&lt;li>Face Tracking&lt;/li>
&lt;li>Body Tracking&lt;/li>
&lt;/ul>
&lt;p>Official documentation:&lt;/p>
&lt;p>&lt;a href="https://docs.unity3d.com/Packages/com.unity.xr.arfoundation" target="_blank" rel="noopener">https://docs.unity3d.com/Packages/com.unity.xr.arfoundation&lt;/a>&lt;/p>
&lt;h2 id="part-5--scenario-design">Part 5 — Scenario design&lt;/h2>
&lt;p>You now have all the technical elements needed to create a complete AR experience.&lt;/p>
&lt;p>Invent a small application or mini-game using:&lt;/p>
&lt;ul>
&lt;li>one or several markers&lt;/li>
&lt;li>a zone&lt;/li>
&lt;li>at least one interaction.&lt;/li>
&lt;/ul>
&lt;h2 id="possible-extensions">Possible extensions&lt;/h2>
&lt;ul>
&lt;li>multiple markers&lt;/li>
&lt;li>interactions between markers&lt;/li>
&lt;li>visual effects&lt;/li>
&lt;li>AR user interface&lt;/li>
&lt;/ul>
&lt;h2 id="deliverables">Deliverables&lt;/h2>
&lt;ol>
&lt;li>Report (PDF) including:&lt;/li>
&lt;/ol>
&lt;ul>
&lt;li>description of the application&lt;/li>
&lt;li>screenshots&lt;/li>
&lt;li>technical explanations&lt;/li>
&lt;li>Git link to the Unity project&lt;/li>
&lt;/ul>
&lt;h2 id="grading">Grading&lt;/h2>
&lt;ul>
&lt;li>Project setup — &lt;strong>2 pts&lt;/strong>&lt;/li>
&lt;li>Image tracking — &lt;strong>2 pts&lt;/strong>&lt;/li>
&lt;li>Plane detection and placement — &lt;strong>4 pts&lt;/strong>&lt;/li>
&lt;li>Interactions — &lt;strong>4 pts&lt;/strong>&lt;/li>
&lt;li>Scenario design — &lt;strong>5 pts&lt;/strong>&lt;/li>
&lt;li>Technical quality — &lt;strong>2 pts&lt;/strong>&lt;/li>
&lt;li>Submission — &lt;strong>1 pt&lt;/strong>&lt;/li>
&lt;/ul></description></item><item><title>Lab — Projective augmented reality</title><link>https://www.etiennepeillard.com/teaching/lab/projective-ar/</link><pubDate>Thu, 19 Feb 2026 00:00:00 +0000</pubDate><guid>https://www.etiennepeillard.com/teaching/lab/projective-ar/</guid><description>&lt;h2 id="projective-augmented-reality">Projective Augmented Reality&lt;/h2>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/teaching/lab/projective-ar/image_hua7e3aa278d0be464fd894aab44259ad1_320305_0eeaf679062a2a085457c0452d37f088.webp 400w,
/teaching/lab/projective-ar/image_hua7e3aa278d0be464fd894aab44259ad1_320305_5ca371ff073ff534a10f2a375116c17d.webp 760w,
/teaching/lab/projective-ar/image_hua7e3aa278d0be464fd894aab44259ad1_320305_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/teaching/lab/projective-ar/image_hua7e3aa278d0be464fd894aab44259ad1_320305_0eeaf679062a2a085457c0452d37f088.webp"
width="760"
height="328"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>&lt;strong>▶ &lt;a href="code.zip">Base Code&lt;/a>&lt;/strong>&lt;/p>
&lt;h3 id="objectives">Objectives&lt;/h3>
&lt;p>This tutorial introduces the fundamentals of &lt;strong>projective augmented reality (AR)&lt;/strong>, from camera-based event detection to spatial augmentation using a projector.&lt;/p>
&lt;p>The reference application is a &lt;strong>tic-tac-toe game&lt;/strong>, but the architecture can be reused for any surface augmentation scenario (e.g., augmented board games, scientific visualization, research experiments).&lt;/p>
&lt;p>You will learn how to:&lt;/p>
&lt;ul>
&lt;li>Calibrate camera, projector, and physical surface&lt;/li>
&lt;li>Compute transition (homography) matrices between spaces&lt;/li>
&lt;li>Detect surface modifications&lt;/li>
&lt;li>Project consistent augmentations in real time&lt;/li>
&lt;/ul>
&lt;h3 id="system-overview">System Overview&lt;/h3>
&lt;p>We work in three different spaces:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Camera image space&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Projector image space&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Physical board space&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>These spaces are not aligned. Calibration is required to compute the transformations between them.&lt;/p>
&lt;p>The main goal is:&lt;/p>
&lt;ul>
&lt;li>Convert a detected move (camera space) into board coordinates&lt;/li>
&lt;li>Convert a board coordinate into projector coordinates&lt;/li>
&lt;/ul>
&lt;h3 id="installation">Installation&lt;/h3>
&lt;p>Make sure Python and &lt;code>pip&lt;/code> are installed.&lt;/p>
&lt;blockquote>
&lt;p>The tutorial has been tested with Python 3.12 and 3.13. Other versions may work, but adjustments might be necessary.&lt;/p>
&lt;/blockquote>
&lt;p>Install required dependencies:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">pip install &lt;span class="nv">numpy&lt;/span>&lt;span class="o">==&lt;/span>1.26.4
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install opencv-python&lt;span class="o">==&lt;/span>4.10.0.84
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">pip install opencv-contrib-python&lt;span class="o">==&lt;/span>4.6.0.66
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>&lt;strong>OpenCV&lt;/strong>: Computer vision library&lt;/li>
&lt;li>&lt;strong>Aruco&lt;/strong>: Marker generation and detection module (included in opencv-contrib)&lt;/li>
&lt;/ul>
&lt;p>Documentation: &lt;a href="https://docs.opencv.org/4.x/index.html" target="_blank" rel="noopener">https://docs.opencv.org/4.x/index.html&lt;/a>&lt;/p>
&lt;h3 id="marker-generation">Marker Generation&lt;/h3>
&lt;p>Run:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">python generateMarkers.py
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If correctly installed, four markers defining the playing area will be generated.&lt;/p>
&lt;p>Print and place them on the board.&lt;/p>
&lt;h3 id="coordinate-conventions">Coordinate Conventions&lt;/h3>
&lt;h4 id="matrix-representation-images">Matrix Representation (Images)&lt;/h4>
&lt;p>Images are NumPy matrices in &lt;strong>row-major order&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>First index → row (vertical, downward)&lt;/li>
&lt;li>Second index → column (horizontal, rightward)&lt;/li>
&lt;li>(0,0) is top-left&lt;/li>
&lt;/ul>
&lt;h4 id="point-representation">Point Representation&lt;/h4>
&lt;p>Points follow image convention:&lt;/p>
&lt;ul>
&lt;li>Origin: top-left&lt;/li>
&lt;li>X → right&lt;/li>
&lt;li>Y → down&lt;/li>
&lt;/ul>
&lt;p>Coordinate system:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">(0,0) - X →
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Y
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ↓
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Be extremely careful when switching between matrix indexing and geometric coordinates.&lt;/p>
&lt;h2 id="code-structure">Code Structure&lt;/h2>
&lt;p>Four main components:&lt;/p>
&lt;ul>
&lt;li>&lt;code>Camera&lt;/code>&lt;/li>
&lt;li>&lt;code>Projector&lt;/code>&lt;/li>
&lt;li>&lt;code>CoreAR&lt;/code>&lt;/li>
&lt;li>&lt;code>TicTacToe&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>All missing parts are marked with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">### TODO&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="1-initialization">1️⃣ Initialization&lt;/h2>
&lt;h3 id="camera">Camera&lt;/h3>
&lt;ul>
&lt;li>Automatically searches for available camera&lt;/li>
&lt;li>Exposure configurable&lt;/li>
&lt;li>Must release using:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">camera&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">release&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Complete all &lt;code>TODO&lt;/code> sections.&lt;/p>
&lt;h3 id="projector">Projector&lt;/h3>
&lt;p>Constructor: no modification required.&lt;/p>
&lt;p>Implement:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">drawBlack&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Understand how drawing works in projector space.&lt;/p>
&lt;h3 id="corear">CoreAR&lt;/h3>
&lt;p>Constructor only stores references.&lt;/p>
&lt;p>No modification required.&lt;/p>
&lt;h2 id="2-calibration">2️⃣ Calibration&lt;/h2>
&lt;h3 id="camera-calibration">Camera Calibration&lt;/h3>
&lt;p>Goal:&lt;/p>
&lt;ul>
&lt;li>Detect the four ArUco markers&lt;/li>
&lt;li>Match detected corners with board coordinates&lt;/li>
&lt;li>Compute homography between board and camera&lt;/li>
&lt;/ul>
&lt;p>Complete all &lt;code>TODO&lt;/code> sections in:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">calibrateCamera&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="projector-calibration">Projector Calibration&lt;/h3>
&lt;p>Principle:&lt;/p>
&lt;ol>
&lt;li>Project markers at known projector positions&lt;/li>
&lt;li>Detect them with camera&lt;/li>
&lt;li>Convert camera detections to board coordinates&lt;/li>
&lt;li>Compute projector → board homography&lt;/li>
&lt;/ol>
&lt;p>Complete:&lt;/p>
&lt;ul>
&lt;li>&lt;code>drawMarkers()&lt;/code> in projector&lt;/li>
&lt;li>&lt;code>calibrateProjector()&lt;/code> in CoreAR&lt;/li>
&lt;/ul>
&lt;h2 id="3-drawing-in-projector-space">3️⃣ Drawing in Projector Space&lt;/h2>
&lt;p>Implement:&lt;/p>
&lt;ul>
&lt;li>&lt;code>draw()&lt;/code>&lt;/li>
&lt;li>&lt;code>checkCalibration()&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>This allows:&lt;/p>
&lt;ul>
&lt;li>Displaying the game board&lt;/li>
&lt;li>Validating calibration visually&lt;/li>
&lt;/ul>
&lt;p>Test full calibration pipeline.&lt;/p>
&lt;h2 id="4-game-logic">4️⃣ Game Logic&lt;/h2>
&lt;p>The &lt;code>ticTacToe&lt;/code> class is already implemented.&lt;/p>
&lt;p>You now focus on detecting moves.&lt;/p>
&lt;h2 id="5-move-detection">5️⃣ Move Detection&lt;/h2>
&lt;p>Strategy:&lt;/p>
&lt;ol>
&lt;li>Capture reference frame&lt;/li>
&lt;li>Compare current frame with reference&lt;/li>
&lt;li>Compute difference image&lt;/li>
&lt;li>Apply threshold&lt;/li>
&lt;li>Apply morphological opening&lt;/li>
&lt;li>Extract blob position&lt;/li>
&lt;li>Validate stability over time&lt;/li>
&lt;li>Update reference frame&lt;/li>
&lt;/ol>
&lt;p>Complete all &lt;code>TODO&lt;/code> sections in:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">findMove&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Intermediate &lt;code>imshow()&lt;/code> lines can be uncommented to debug.&lt;/p>
&lt;h2 id="image-processing-pipeline">Image Processing Pipeline&lt;/h2>
&lt;ul>
&lt;li>Frame subtraction&lt;/li>
&lt;li>Thresholding&lt;/li>
&lt;li>Morphological opening&lt;/li>
&lt;li>Contour extraction&lt;/li>
&lt;/ul>
&lt;p>This isolates new moves placed on the board.&lt;/p>
&lt;h2 id="improvements--tuning">Improvements &amp;amp; Tuning&lt;/h2>
&lt;p>You may adjust:&lt;/p>
&lt;ul>
&lt;li>Camera exposure&lt;/li>
&lt;li>Projector brightness&lt;/li>
&lt;li>Threshold value&lt;/li>
&lt;/ul>
&lt;p>Fine-tuning greatly impacts detection robustness.&lt;/p>
&lt;h2 id="what-you-have-built">What You Have Built&lt;/h2>
&lt;p>At the end of this tutorial, you have implemented:&lt;/p>
&lt;ul>
&lt;li>Camera-projector calibration&lt;/li>
&lt;li>Multi-space homography estimation&lt;/li>
&lt;li>Real-time surface augmentation&lt;/li>
&lt;li>Basic event detection&lt;/li>
&lt;/ul>
&lt;p>This architecture can be reused for:&lt;/p>
&lt;ul>
&lt;li>Augmented board games&lt;/li>
&lt;li>Interactive surfaces&lt;/li>
&lt;li>Experimental setups&lt;/li>
&lt;li>Scientific visualization&lt;/li>
&lt;/ul>
&lt;h2 id="deliverables">Deliverables&lt;/h2>
&lt;ul>
&lt;li>Completed code (all TODOs filled)&lt;/li>
&lt;li>Functional calibration&lt;/li>
&lt;li>Fully working augmented tic-tac-toe&lt;/li>
&lt;/ul>
&lt;p>Optional:&lt;/p>
&lt;ul>
&lt;li>Detection improvements&lt;/li>
&lt;li>Graphical enhancements&lt;/li>
&lt;li>Additional gameplay features&lt;/li>
&lt;/ul>
&lt;h2 id="clarifications-based-on-student-questions">Clarifications (Based on Student Questions)&lt;/h2>
&lt;h3 id="opencv-installation-with-conda">OpenCV Installation with Conda&lt;/h3>
&lt;p>If &lt;code>pip&lt;/code> is not available, use:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">conda install -c conda-forge opencv
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In most cases, &lt;strong>Aruco&lt;/strong> is installed automatically with this command.&lt;/p>
&lt;p>If not, try:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">conda install -c conda-forge aruco
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If ArUco is still missing, you may need to install &lt;code>pip&lt;/code> and use the &lt;code>opencv-contrib-python&lt;/code> package.&lt;/p>
&lt;h3 id="camera--projector-projection-matrix-r2p">Camera → Projector Projection Matrix (R2P)&lt;/h3>
&lt;p>The projection matrix from the real board to the projector space (&lt;strong>R2P&lt;/strong>) is:&lt;/p>
&lt;ul>
&lt;li>Computed inside &lt;code>CoreAR&lt;/code>&lt;/li>
&lt;li>Assigned in &lt;code>main.py&lt;/code> via:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">proj&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">R2P&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">core&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">R2P&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There is no encapsulation mechanism here. The attribute is dynamically added to the &lt;code>Projector&lt;/code> instance.&lt;/p>
&lt;p>Inside the &lt;code>Projector&lt;/code> class, you simply use:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">R2P&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This works because &lt;code>draw()&lt;/code> is only called &lt;em>after&lt;/em> the attribute has been defined.&lt;/p>
&lt;h3 id="calibration-functions-and-transition-matrices">Calibration Functions and Transition Matrices&lt;/h3>
&lt;h4 id="calibratecamera">&lt;code>calibrateCamera()&lt;/code>&lt;/h4>
&lt;ul>
&lt;li>Detects &lt;strong>physical ArUco markers&lt;/strong> placed on the board&lt;/li>
&lt;li>⚠ Do &lt;strong>not&lt;/strong> place markers on a black background&lt;/li>
&lt;li>Computes the transformation matrix:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">R2C (Real → Camera)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="calibrateprojector">&lt;code>calibrateProjector()&lt;/code>&lt;/h4>
&lt;ol>
&lt;li>
&lt;p>The projector displays virtual markers&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The camera detects those projected markers&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Matching is performed between:&lt;/p>
&lt;ul>
&lt;li>Known projected marker positions&lt;/li>
&lt;li>Detected marker positions in camera space&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>A Camera → Projector transformation is computed&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>From this, we deduce:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">R2P (Real → Projector)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So the full transformation chain is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Real → Camera → Projector
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And finally:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">R2P = (C2P) ∘ (R2C)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>End of tutorial.&lt;/strong>&lt;/p></description></item><item><title>Lab — Marker tracking with OpenCV</title><link>https://www.etiennepeillard.com/teaching/lab/tp-tracking/</link><pubDate>Thu, 12 Feb 2026 00:00:00 +0000</pubDate><guid>https://www.etiennepeillard.com/teaching/lab/tp-tracking/</guid><description>&lt;h1 id="marker-tracking">Marker Tracking&lt;/h1>
&lt;p>&lt;strong>▶ &lt;a href="base-code.zip">Base Code&lt;/a>&lt;/strong>&lt;/p>
&lt;p>&lt;strong>Requirements&lt;/strong>:&lt;/p>
&lt;p>&lt;code>pip install opencv-python&lt;/code>&lt;/p>
&lt;p>&lt;code>pip install numpy&lt;/code>&lt;/p>
&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>The goal of this lab session is to introduce you to several fundamental concepts in computer vision through a concrete application: detecting and localizing a planar marker in a video stream.&lt;/p>
&lt;p>You will work on:&lt;/p>
&lt;ul>
&lt;li>detecting feature points (interest points),&lt;/li>
&lt;li>describing these points (local descriptors),&lt;/li>
&lt;li>matching points detected in two images,&lt;/li>
&lt;li>computing a homography (projective transformation),&lt;/li>
&lt;li>augmenting a video stream based on marker localization.&lt;/li>
&lt;/ul>
&lt;p>By the end of the lab, you will have a program that, given:&lt;/p>
&lt;ul>
&lt;li>a reference image (the marker),&lt;/li>
&lt;li>a video stream (webcam or video file),&lt;/li>
&lt;/ul>
&lt;p>automatically detects the presence of the marker in the scene and masks the marker area by replacing it with a white polygon (to visually validate detection).&lt;/p>
&lt;hr>
&lt;h2 id="application-principle">Application Principle&lt;/h2>
&lt;p>The overall idea of the system is the following:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Load the marker image (reference image).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Open a video stream (webcam or video file).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Detect a set of feature points in the marker image (FP_M).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Compute descriptors for these points (FD_M).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For each frame I_t of the video stream:&lt;/p>
&lt;ul>
&lt;li>detect a set of feature points (FP_t),&lt;/li>
&lt;li>compute descriptors (FD_t),&lt;/li>
&lt;li>match FD_M and FD_t to obtain correspondences,&lt;/li>
&lt;li>filter these correspondences to retain only “good matches”.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>Estimate the geometric transformation relating the marker plane to the frame: a homography H_{M-&amp;gt;t} robustly computed using RANSAC.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Use H_{M-&amp;gt;t} to:&lt;/p>
&lt;ul>
&lt;li>project the 4 corners of the marker into image I_t,&lt;/li>
&lt;li>mask the detected region by filling it in white.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;p>Important note: this lab performs localization via &amp;ldquo;re-detection + matching&amp;rdquo; at each frame. It is not a temporal tracking approach (e.g., KLT/optical flow), but rather a robust and simple method to implement.&lt;/p>
&lt;hr>
&lt;h2 id="feature-detection">Feature Detection&lt;/h2>
&lt;p>The objective of feature detection (or more generally &amp;ldquo;interest regions&amp;rdquo;) is to automatically select image elements that exhibit distinctive properties: corners, textured regions, high-contrast areas, etc.&lt;/p>
&lt;p>A feature detector takes an image as input and outputs a set of pixel coordinates corresponding to points considered &amp;ldquo;interesting&amp;rdquo; by the algorithm.&lt;/p>
&lt;p>Important: a detector provides positions (and sometimes scale/orientation) but not a descriptive signature.&lt;/p>
&lt;p>Examples of detectors: Harris, FAST, Shi-Tomasi, ORB, AKAZE, SIFT, etc.&lt;/p>
&lt;p>Resources (to read after the lab):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://fr.wikipedia.org/wiki/D%C3%A9tection_de_zones_d%27int%C3%A9r%C3%AAt" target="_blank" rel="noopener">https://fr.wikipedia.org/wiki/D%C3%A9tection_de_zones_d%27int%C3%A9r%C3%AAt&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Feature_detection_%28computer_vision%29" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Feature_detection_(computer_vision)&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="feature-description">Feature Description&lt;/h2>
&lt;p>A feature descriptor aims to numerically characterize the local appearance around an interest point.&lt;/p>
&lt;p>It takes as input:&lt;/p>
&lt;ul>
&lt;li>an image,&lt;/li>
&lt;li>a list of feature points,&lt;/li>
&lt;/ul>
&lt;p>and outputs a vector (or matrix) of descriptors, one per point.&lt;/p>
&lt;p>These descriptors represent a local “fingerprint” that enables comparison of points detected in different images.&lt;/p>
&lt;p>Desirable properties of a descriptor:&lt;/p>
&lt;ul>
&lt;li>invariance (or robustness) to rotation,&lt;/li>
&lt;li>invariance (or robustness) to scale changes,&lt;/li>
&lt;li>robustness to photometric variations (lighting),&lt;/li>
&lt;li>robustness to moderate geometric transformations.&lt;/li>
&lt;/ul>
&lt;p>Examples: ORB, AKAZE, BRISK, SIFT (detector + descriptor), etc.&lt;/p>
&lt;p>Resources (to read after the lab):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://fr.wikipedia.org/wiki/Extraction_de_caract%C3%A9ristique_en_vision_par_ordinateur" target="_blank" rel="noopener">https://fr.wikipedia.org/wiki/Extraction_de_caract%C3%A9ristique_en_vision_par_ordinateur&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Visual_descriptor" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Visual_descriptor&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Scale-invariant_feature_transform" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Scale-invariant_feature_transform&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="matching">Matching&lt;/h2>
&lt;p>Matching addresses the following question:&lt;/p>
&lt;blockquote>
&lt;p>Given a feature in the marker image, how can we find the most similar feature in the current video frame?&lt;/p>
&lt;/blockquote>
&lt;p>The general idea is:&lt;/p>
&lt;ol>
&lt;li>Define a distance or similarity measure between descriptors.&lt;/li>
&lt;li>For each descriptor of the marker, find the closest descriptor in the frame.&lt;/li>
&lt;/ol>
&lt;p>Possible measures: L1, L2, Hamming (for binary descriptors), ratio test, etc.&lt;/p>
&lt;p>Matching methods:&lt;/p>
&lt;ul>
&lt;li>Brute force: compares all descriptors pairwise (simple, potentially costly).&lt;/li>
&lt;li>FLANN: approximate nearest neighbors (faster for large datasets).&lt;/li>
&lt;/ul>
&lt;p>After matching, a filtering rule must be defined to retain only relevant correspondences. A classical approach (used in the provided base code) consists of using a threshold based on the minimum observed distance:&lt;/p>
&lt;p>threshold = alpha * minDist&lt;/p>
&lt;p>where alpha is a coefficient (typically between 3 and 20 depending on the method and expected quality).&lt;/p>
&lt;p>Resources (to read after the lab):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://docs.google.com/presentation/d/1_HFh3SdmdyZ_j-sFS4Tw17DmhKfjmYZqvSp7TmfdD_M/edit?slide=id.p#slide=id.p" target="_blank" rel="noopener">https://docs.google.com/presentation/d/1_HFh3SdmdyZ_j-sFS4Tw17DmhKfjmYZqvSp7TmfdD_M/edit?slide=id.p#slide=id.p&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.cs.toronto.edu/~urtasun/courses/CV/lecture04.pdf" target="_blank" rel="noopener">https://www.cs.toronto.edu/~urtasun/courses/CV/lecture04.pdf&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="homography-and-projection">Homography and Projection&lt;/h2>
&lt;p>Once correspondences between the marker (planar object) and the frame (image) have been established, we estimate the geometric transformation relating these two sets of points.&lt;/p>
&lt;p>For a planar object, this transformation is a homography (3x3 matrix). It models perspective effects and relates two views of the same plane.&lt;/p>
&lt;p>We estimate a matrix H from the good matches (after filtering). The estimation must be robust to outliers: we use RANSAC.&lt;/p>
&lt;p>Resources (to read after the lab):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Homography_%28computer_vision%29" target="_blank" rel="noopener">https://en.wikipedia.org/wiki/Homography_(computer_vision)&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://fr.wikipedia.org/wiki/Application_projective" target="_blank" rel="noopener">https://fr.wikipedia.org/wiki/Application_projective&lt;/a>&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="implementation-in-opencv">Implementation in OpenCV&lt;/h1>
&lt;p>The objective of the lab is to implement the previous steps in OpenCV starting from a provided code skeleton.&lt;/p>
&lt;h2 id="provided-base-code">Provided Base Code&lt;/h2>
&lt;p>You are given the following files:&lt;/p>
&lt;ul>
&lt;li>&lt;code>main.py&lt;/code>&lt;/li>
&lt;li>&lt;code>MyFeatureDetector.py&lt;/code>&lt;/li>
&lt;li>&lt;code>MyDescriptorExtractor.py&lt;/code>&lt;/li>
&lt;li>&lt;code>MyDescriptorMatcher.py&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>These files contain TODO sections guiding your implementation.&lt;/p>
&lt;p>Important: the current base code loads two static images. You must adapt it to handle:&lt;/p>
&lt;ul>
&lt;li>a reference image &lt;code>marker.jpg&lt;/code>,&lt;/li>
&lt;li>a video stream (webcam or file).&lt;/li>
&lt;/ul>
&lt;p>You will remove the final warped image generation part and replace it with an augmentation step (masking the marker) based on projected corners.&lt;/p>
&lt;hr>
&lt;h2 id="questions">Questions&lt;/h2>
&lt;h3 id="question-1-reading-and-understanding-the-provided-code">Question 1: Reading and Understanding the Provided Code&lt;/h3>
&lt;p>Read the provided code and identify:&lt;/p>
&lt;ul>
&lt;li>where images are loaded,&lt;/li>
&lt;li>where the detector, extractor, and matcher are instantiated,&lt;/li>
&lt;li>where detected points and descriptors are stored,&lt;/li>
&lt;li>how the list of best matches is built,&lt;/li>
&lt;li>how the homography is computed.&lt;/li>
&lt;/ul>
&lt;p>Explain in a few lines the role of each file.&lt;/p>
&lt;hr>
&lt;h3 id="question-2-create-a-feature-detector-orb">Question 2: Create a Feature Detector (ORB)&lt;/h3>
&lt;p>In &lt;code>MyFeatureDetector.py&lt;/code>, complete the &lt;code>changeFeatureDetector&lt;/code> function to create an ORB detector.&lt;/p>
&lt;p>Objective: fill the &lt;code>myFeatureDetector&lt;/code> attribute by calling the appropriate OpenCV constructor (e.g., &lt;code>cv.ORB_create(...)&lt;/code>).&lt;/p>
&lt;hr>
&lt;h3 id="question-3-display-detected-points">Question 3: Display Detected Points&lt;/h3>
&lt;p>In &lt;code>main.py&lt;/code>, display detected feature points:&lt;/p>
&lt;ul>
&lt;li>on the image &lt;code>marker.jpg&lt;/code>,&lt;/li>
&lt;li>on a video frame.&lt;/li>
&lt;/ul>
&lt;p>Hint: use the &lt;code>displayFeatures&lt;/code> method and the existing TODO sections in the skeleton.&lt;/p>
&lt;p>To do: add a screenshot showing results on the marker and on a frame.&lt;/p>
&lt;p>Optional: add parameters to &lt;code>cv.ORB_create(...)&lt;/code> (number of points, threshold, etc.) and comment on their impact.&lt;/p>
&lt;hr>
&lt;h3 id="question-4-instantiate-a-descriptor-extractor-orb">Question 4: Instantiate a Descriptor Extractor (ORB)&lt;/h3>
&lt;p>In &lt;code>MyDescriptorExtractor.py&lt;/code>, complete &lt;code>changeDescriptorExtractor&lt;/code> to create an ORB descriptor extractor.&lt;/p>
&lt;hr>
&lt;h3 id="question-5-compute-descriptors">Question 5: Compute Descriptors&lt;/h3>
&lt;p>Complete &lt;code>computeDescriptors()&lt;/code> in &lt;code>MyDescriptorExtractor.py&lt;/code> and complete the corresponding calls in &lt;code>main.py&lt;/code> to:&lt;/p>
&lt;ul>
&lt;li>compute descriptors for the marker,&lt;/li>
&lt;li>compute descriptors for a frame.&lt;/li>
&lt;/ul>
&lt;p>Verify that descriptors are properly computed (consistent dimensions, non-empty).&lt;/p>
&lt;hr>
&lt;h3 id="question-6-perform-matching">Question 6: Perform Matching&lt;/h3>
&lt;p>In &lt;code>MyDescriptorMatcher.py&lt;/code>, complete the &lt;code>match&lt;/code> method to retain only correspondences whose distance is below a threshold:&lt;/p>
&lt;p>threshold = alpha * minDist&lt;/p>
&lt;p>Selected matches must be stored in &lt;code>bestMatches&lt;/code>.&lt;/p>
&lt;hr>
&lt;h3 id="question-7-display-matching-results">Question 7: Display Matching Results&lt;/h3>
&lt;p>In &lt;code>main.py&lt;/code>, call &lt;code>drawMatchingResults()&lt;/code> and display the resulting image.&lt;/p>
&lt;p>To do: illustrate this result with a screenshot in your report.&lt;/p>
&lt;hr>
&lt;h3 id="question-8-understand-and-compute-the-homography">Question 8: Understand and Compute the Homography&lt;/h3>
&lt;p>From the best matches, build two lists of 2D points:&lt;/p>
&lt;ul>
&lt;li>points in the marker image,&lt;/li>
&lt;li>points in the video frame.&lt;/li>
&lt;/ul>
&lt;p>Then compute the homography using &lt;code>cv.findHomography(..., cv.RANSAC, epsilon)&lt;/code>.&lt;/p>
&lt;p>Explain briefly what this computation does and why RANSAC is necessary.&lt;/p>
&lt;hr>
&lt;h3 id="question-9-augmentation--mask-the-marker">Question 9: Augmentation — Mask the Marker&lt;/h3>
&lt;p>Once homography H is estimated, project the 4 marker corners into the frame.&lt;/p>
&lt;p>Expected steps:&lt;/p>
&lt;ol>
&lt;li>Define the 4 marker corners in its image coordinate system.&lt;/li>
&lt;li>Project them using &lt;code>cv.perspectiveTransform&lt;/code>.&lt;/li>
&lt;li>Draw the contour (recommended for debugging).&lt;/li>
&lt;li>Fill the projected polygon in white using &lt;code>cv.fillConvexPoly&lt;/code>.&lt;/li>
&lt;/ol>
&lt;p>Condition: perform augmentation only if H is valid and if the number of inliers is sufficient (threshold to define and justify).&lt;/p>
&lt;p>To do: provide a screenshot where the marker is effectively masked.&lt;/p>
&lt;hr>
&lt;h3 id="question-10-test-on-other-sequences">Question 10: Test on Other Sequences&lt;/h3>
&lt;p>Test your program with:&lt;/p>
&lt;ul>
&lt;li>another video (or another webcam scene),&lt;/li>
&lt;li>another marker (different reference image).&lt;/li>
&lt;/ul>
&lt;p>To do: illustrate at least one additional test and comment on robustness.&lt;/p>
&lt;hr>
&lt;h3 id="question-11-change-detectordescriptor-optional">Question 11: Change Detector/Descriptor (Optional)&lt;/h3>
&lt;p>Try another detector/descriptor combination (e.g., AKAZE) and compare:&lt;/p>
&lt;ul>
&lt;li>number of detected points,&lt;/li>
&lt;li>matching stability,&lt;/li>
&lt;li>number of inliers,&lt;/li>
&lt;li>robustness to scale/lighting variations,&lt;/li>
&lt;li>computational cost.&lt;/li>
&lt;/ul>
&lt;p>To do: illustrate and comment.&lt;/p>
&lt;hr>
&lt;h1 id="deliverables">Deliverables&lt;/h1>
&lt;p>You must submit:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>The completed code.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>A short report (PDF or Markdown) containing:&lt;/p>
&lt;ul>
&lt;li>the requested screenshots,&lt;/li>
&lt;li>your parameter choices (alpha, inlier threshold, etc.),&lt;/li>
&lt;li>a robustness analysis (tests, failure cases).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol></description></item><item><title>Course — Tracking in mixed reality</title><link>https://www.etiennepeillard.com/slides/tracking-for-xr/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://www.etiennepeillard.com/slides/tracking-for-xr/</guid><description>&lt;h1 id="tracking-in-mixed-reality">Tracking in Mixed Reality&lt;/h1>
&lt;h3 id="from-cameras-to-robust-visual-tracking">From Cameras to Robust Visual Tracking&lt;/h3>
&lt;p>&lt;strong>Étienne Peillard – IMT Atlantique&lt;/strong>&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image_hu657c80607dcafc9239866240ac93961f_2088632_bba09c0bf0dd64793df9611737e73e2d.webp 400w,
/slides/tracking-for-xr/image_hu657c80607dcafc9239866240ac93961f_2088632_22b59958a943684254f2a7765d0621a8.webp 760w,
/slides/tracking-for-xr/image_hu657c80607dcafc9239866240ac93961f_2088632_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image_hu657c80607dcafc9239866240ac93961f_2088632_bba09c0bf0dd64793df9611737e73e2d.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="-course-objectives">🎯 Course Objectives&lt;/h2>
&lt;p>By the end of this lecture, you should be able to:&lt;/p>
&lt;ul>
&lt;li>Explain what &lt;strong>tracking&lt;/strong> means in Mixed Reality&lt;/li>
&lt;li>Understand how cameras form digital images&lt;/li>
&lt;li>Connect image processing to feature detection&lt;/li>
&lt;li>Understand how features enable 6DoF tracking&lt;/li>
&lt;li>See the link with the practical lab (feature detection for tracking)&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h1 id="part-a--why-tracking-in-mixed-reality">PART A — Why Tracking in Mixed Reality?&lt;/h1>
&lt;hr>
&lt;h2 id="what-is-tracking-in-mixed-reality">What is Tracking in Mixed Reality?&lt;/h2>
&lt;p>Tracking = continuous estimation of &lt;strong>pose&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Position (x, y, z)&lt;/li>
&lt;li>Orientation (roll, pitch, yaw)&lt;/li>
&lt;/ul>
&lt;p>This applies to:&lt;/p>
&lt;ul>
&lt;li>The &lt;strong>camera/headset&lt;/strong>&lt;/li>
&lt;li>The &lt;strong>user’s body&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Objects&lt;/strong> in the environment&lt;/li>
&lt;li>Sometimes the &lt;strong>entire scene (SLAM)&lt;/strong>&lt;/li>
&lt;/ul>
&lt;!-- ILLUSTRATION: 6DoF pose visualization (axes + rotation) -->
&lt;hr>
&lt;h2 id="why-is-tracking-critical-in-mr">Why is Tracking Critical in MR?&lt;/h2>
&lt;p>Because MR requires &lt;strong>spatial alignment&lt;/strong> between:&lt;/p>
&lt;ul>
&lt;li>The real world (captured by sensors)&lt;/li>
&lt;li>The virtual world (rendered by the engine)&lt;/li>
&lt;/ul>
&lt;p>If tracking is wrong:&lt;/p>
&lt;ul>
&lt;li>Virtual objects drift&lt;/li>
&lt;li>Registration breaks&lt;/li>
&lt;li>The experience feels “fake” or unstable&lt;/li>
&lt;/ul>
&lt;!-- ILLUSTRATION: misaligned virtual object vs correctly aligned one -->
&lt;hr>
&lt;h2 id="the-registration-problem">The Registration Problem&lt;/h2>
&lt;p>Core question:&lt;/p>
&lt;blockquote>
&lt;p>Given an image from a moving camera, how do we estimate its 6DoF pose in the real world?&lt;/p>
&lt;/blockquote>
&lt;p>Conceptual pipeline:&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-1_hud7404c4a0de520c1916d1bf548343208_645972_15728914e37f7b475e8c7e28efd9d992.webp 400w,
/slides/tracking-for-xr/image-1_hud7404c4a0de520c1916d1bf548343208_645972_5ebc9a3a3b8cd7f10f57f10c8512a798.webp 760w,
/slides/tracking-for-xr/image-1_hud7404c4a0de520c1916d1bf548343208_645972_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-1_hud7404c4a0de520c1916d1bf548343208_645972_15728914e37f7b475e8c7e28efd9d992.webp"
width="760"
height="266"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="three-families-of-tracking-in-xr">Three Families of Tracking in XR&lt;/h2>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-2_huefa86478be04d1bd2f11c52245bfe6f5_1599205_95b8442883e42ef981f6011dce9234f8.webp 400w,
/slides/tracking-for-xr/image-2_huefa86478be04d1bd2f11c52245bfe6f5_1599205_b91e05387fb9fa90bd7f5d9d0a3de5c8.webp 760w,
/slides/tracking-for-xr/image-2_huefa86478be04d1bd2f11c52245bfe6f5_1599205_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-2_huefa86478be04d1bd2f11c52245bfe6f5_1599205_95b8442883e42ef981f6011dce9234f8.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="tracking-and-latency">Tracking and Latency&lt;/h2>
&lt;p>Good tracking must be:&lt;/p>
&lt;ul>
&lt;li>Accurate&lt;/li>
&lt;li>Low-latency&lt;/li>
&lt;li>Robust to occlusion&lt;/li>
&lt;li>Stable over time&lt;/li>
&lt;/ul>
&lt;p>Why? Because MR rendering depends on it in real time.&lt;/p>
&lt;!-- ILLUSTRATION: latency pipeline from sensor to render -->
&lt;hr>
&lt;h1 id="part-b--from-camera-to-digital-image">PART B — From Camera to Digital Image&lt;/h1>
&lt;hr>
&lt;h2 id="cameras-as-sensors-in-xr">Cameras as Sensors in XR&lt;/h2>
&lt;p>A camera is a physical sensor that:&lt;/p>
&lt;ul>
&lt;li>Captures light from the environment&lt;/li>
&lt;li>Converts it into a digital image&lt;/li>
&lt;li>Introduces limitations that affect tracking&lt;/li>
&lt;/ul>
&lt;p>Key issues:&lt;/p>
&lt;ul>
&lt;li>Discretization&lt;/li>
&lt;li>Aliasing&lt;/li>
&lt;li>Noise&lt;/li>
&lt;li>Limited dynamic range&lt;/li>
&lt;/ul>
&lt;!-- ILLUSTRATION: pinhole camera diagram + pixel grid -->
&lt;hr>
&lt;h2 id="discretization-of-images">Discretization of Images&lt;/h2>
&lt;p>The real world is continuous.&lt;br>
A digital image is &lt;strong>sampled on a grid of pixels&lt;/strong>.&lt;/p>
&lt;p>This creates:&lt;/p>
&lt;ul>
&lt;li>Loss of detail&lt;/li>
&lt;li>Potential aliasing&lt;/li>
&lt;li>Dependence on resolution&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-4_hue1583c921f588d4fec2b13b4c0664d2e_893837_0b952a856c728f5e50a6b18c49552330.webp 400w,
/slides/tracking-for-xr/image-4_hue1583c921f588d4fec2b13b4c0664d2e_893837_f450d5fd7409a9afebf221ec787eb318.webp 760w,
/slides/tracking-for-xr/image-4_hue1583c921f588d4fec2b13b4c0664d2e_893837_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-4_hue1583c921f588d4fec2b13b4c0664d2e_893837_0b952a856c728f5e50a6b18c49552330.webp"
width="760"
height="222"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h3 id="interpolation">Interpolation&lt;/h3>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-3_hue9bd438842195196530f7360e983aaf5_85187_f38899486762f901d0c69e163dfe304e.webp 400w,
/slides/tracking-for-xr/image-3_hue9bd438842195196530f7360e983aaf5_85187_454e64510a0b2021718835ad170cb09d.webp 760w,
/slides/tracking-for-xr/image-3_hue9bd438842195196530f7360e983aaf5_85187_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-3_hue9bd438842195196530f7360e983aaf5_85187_f38899486762f901d0c69e163dfe304e.webp"
width="760"
height="226"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="aliasing-and-moiré">Aliasing and Moiré&lt;/h2>
&lt;p>If sampling is too coarse:&lt;/p>
&lt;ul>
&lt;li>High-frequency patterns create artifacts (moiré)&lt;/li>
&lt;li>Edges look jagged (aliasing)&lt;/li>
&lt;/ul>
&lt;p>This matters for tracking:&lt;/p>
&lt;ul>
&lt;li>Artifacts can confuse feature detectors&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="aliasing">Aliasing&lt;/h3>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-6_hu05dee16ec3e6bfba1728dd2fe542cb2e_1549695_758596f852c1bdccbdcdddc24206ce0c.webp 400w,
/slides/tracking-for-xr/image-6_hu05dee16ec3e6bfba1728dd2fe542cb2e_1549695_2fd6c60c856af75a95a48944b42c9324.webp 760w,
/slides/tracking-for-xr/image-6_hu05dee16ec3e6bfba1728dd2fe542cb2e_1549695_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-6_hu05dee16ec3e6bfba1728dd2fe542cb2e_1549695_758596f852c1bdccbdcdddc24206ce0c.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h3 id="moiré">Moiré&lt;/h3>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-5_hucf154de5a2869db60081862a9b5ffdf0_670909_37468ae921139edeabac1e247a933111.webp 400w,
/slides/tracking-for-xr/image-5_hucf154de5a2869db60081862a9b5ffdf0_670909_192ef8db0565c10884251fa592ae7f52.webp 760w,
/slides/tracking-for-xr/image-5_hucf154de5a2869db60081862a9b5ffdf0_670909_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-5_hucf154de5a2869db60081862a9b5ffdf0_670909_37468ae921139edeabac1e247a933111.webp"
width="760"
height="458"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>&lt;a href="../../moire/index.html">Demo: effect of downsampling on camera&lt;/a>&lt;/p>
&lt;hr>
&lt;h2 id="nyquistshannon-theorem">Nyquist–Shannon Theorem&lt;/h2>
&lt;p>To properly sample a signal:
Sampling frequency ≥ 2 × highest frequency in the signal&lt;/p>
&lt;p>If violated → aliasing appears.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/Image1.gif"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
class="img-right"
>
&lt;h2 id="color-quantization">Color Quantization&lt;/h2>
&lt;p>Real light is continuous.&lt;br>
Digital images use a &lt;strong>finite number of values&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Typically 8-bit per channel (0–255)&lt;/li>
&lt;li>RGB representation&lt;/li>
&lt;/ul>
&lt;p>This limits precision in low-light conditions.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-7_hu45500e1b474c536d6ba0f3539e3c6b65_65082_4e0bc2f5a693fe621095adac0ba2fa75.webp 400w,
/slides/tracking-for-xr/image-7_hu45500e1b474c536d6ba0f3539e3c6b65_65082_ccc09920525539b1af529fc3bfc91b2d.webp 760w,
/slides/tracking-for-xr/image-7_hu45500e1b474c536d6ba0f3539e3c6b65_65082_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-7_hu45500e1b474c536d6ba0f3539e3c6b65_65082_4e0bc2f5a693fe621095adac0ba2fa75.webp"
width="760"
height="392"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
class="img-right"
>
&lt;h2 id="from-rgb-to-perception">From RGB to Perception&lt;/h2>
&lt;p>Different color spaces exist:&lt;/p>
&lt;ul>
&lt;li>RGB (device-oriented)&lt;/li>
&lt;li>HSV (more aligned with human perception)
&lt;ul>
&lt;li>Hue&lt;/li>
&lt;li>Saturation&lt;/li>
&lt;li>Value (brightness)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Useful in some tracking pipelines.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-8_hu2e62c38cd59d57cd9492ac70b6c3eefd_77187_e7aab376e72c0a0816c1021ac6517824.webp 400w,
/slides/tracking-for-xr/image-8_hu2e62c38cd59d57cd9492ac70b6c3eefd_77187_c25ccd1f7ee6db1a9a8b8c532ea6587b.webp 760w,
/slides/tracking-for-xr/image-8_hu2e62c38cd59d57cd9492ac70b6c3eefd_77187_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-8_hu2e62c38cd59d57cd9492ac70b6c3eefd_77187_e7aab376e72c0a0816c1021ac6517824.webp"
width="651"
height="380"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h1 id="part-c--projective-geometry-core-for-tracking">PART C — Projective Geometry (Core for Tracking)&lt;/h1>
&lt;hr>
&lt;h2 id="why-geometry-matters-for-tracking">Why Geometry Matters for Tracking&lt;/h2>
&lt;p>Tracking is fundamentally &lt;strong>projective geometry&lt;/strong>.&lt;/p>
&lt;p>We need to model:&lt;/p>
&lt;ul>
&lt;li>How 3D points project onto a 2D image&lt;/li>
&lt;li>How camera motion affects this projection&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-10_hu5314e135fcaa442bcf82ef6a15083de4_137754_3d29f2b00ff730b7b40b3e4d5f4d5cad.webp 400w,
/slides/tracking-for-xr/image-10_hu5314e135fcaa442bcf82ef6a15083de4_137754_95bfd16f4c9b685d2d73df9af7b8d652.webp 760w,
/slides/tracking-for-xr/image-10_hu5314e135fcaa442bcf82ef6a15083de4_137754_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-10_hu5314e135fcaa442bcf82ef6a15083de4_137754_3d29f2b00ff730b7b40b3e4d5f4d5cad.webp"
width="760"
height="358"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
class="img-right"
>
&lt;h2 id="intrinsic-parameters-k">Intrinsic Parameters (K)&lt;/h2>
&lt;p>Describe the &lt;strong>internal properties of the camera&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Focal length&lt;/li>
&lt;li>Principal point&lt;/li>
&lt;li>Pixel aspect ratio&lt;/li>
&lt;/ul>
&lt;p>Fixed for a calibrated camera.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-11_hu30dc3435330829e47cee8f7a615bfd5b_289449_9da94dd0b60c7ba9189edb23db1db8d4.webp 400w,
/slides/tracking-for-xr/image-11_hu30dc3435330829e47cee8f7a615bfd5b_289449_1af63bf6653e7ddcf1cf0c5e9e62e363.webp 760w,
/slides/tracking-for-xr/image-11_hu30dc3435330829e47cee8f7a615bfd5b_289449_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-11_hu30dc3435330829e47cee8f7a615bfd5b_289449_9da94dd0b60c7ba9189edb23db1db8d4.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="extrinsic-parameters-r-t">Extrinsic Parameters (R, t)&lt;/h2>
&lt;p>Describe the &lt;strong>pose of the camera in the world&lt;/strong>:&lt;/p>
&lt;ul>
&lt;li>Rotation matrix R&lt;/li>
&lt;li>Translation vector t&lt;/li>
&lt;/ul>
&lt;p>👉 This is what tracking estimates.&lt;/p>
&lt;!-- ILLUSTRATION: world frame vs camera frame -->
&lt;hr>
&lt;h2 id="the-projection-model-simplified">The Projection Model (Simplified)&lt;/h2>
&lt;p>The core equation:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">u = K [R | t] X
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Where:&lt;/p>
&lt;ul>
&lt;li>X = 3D point in the world&lt;/li>
&lt;li>u = 2D pixel in the image&lt;/li>
&lt;li>K = intrinsics&lt;/li>
&lt;li>[R|t] = camera pose (tracking result)&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-12_hub0ee1241b3d2691d0908aef3a4f8db61_97706_b03ca28a57617f0134e691d9ca08b437.webp 400w,
/slides/tracking-for-xr/image-12_hub0ee1241b3d2691d0908aef3a4f8db61_97706_edd916804235d08ab9a6a835d8889b29.webp 760w,
/slides/tracking-for-xr/image-12_hub0ee1241b3d2691d0908aef3a4f8db61_97706_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-12_hub0ee1241b3d2691d0908aef3a4f8db61_97706_b03ca28a57617f0134e691d9ca08b437.webp"
width="760"
height="125"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="intuition-of-the-projection">Intuition of the Projection&lt;/h2>
&lt;ol>
&lt;li>Transform 3D point from world to camera space (R, t)&lt;/li>
&lt;li>Project onto image plane (K)&lt;/li>
&lt;li>Obtain pixel coordinates (u)&lt;/li>
&lt;/ol>
&lt;p>This is the mathematical backbone of visual tracking.&lt;/p>
&lt;!-- ILLUSTRATION: step-by-step transformation diagram -->
&lt;hr>
&lt;h1 id="part-d--image-processing-as-a-precursor-to-tracking">PART D — Image Processing as a Precursor to Tracking&lt;/h1>
&lt;hr>
&lt;h2 id="why-image-processing-for-tracking">Why Image Processing for Tracking?&lt;/h2>
&lt;p>Three main goals:&lt;/p>
&lt;ol>
&lt;li>Reduce noise → more stable features&lt;/li>
&lt;li>Enhance structures → clearer edges/corners&lt;/li>
&lt;li>Normalize images → robustness across lighting changes&lt;/li>
&lt;/ol>
&lt;!-- ILLUSTRATION: raw vs processed image side by side -->
&lt;hr>
&lt;h2 id="grayscale-conversion">Grayscale Conversion&lt;/h2>
&lt;p>Most feature detectors work on grayscale images.&lt;/p>
&lt;p>Classic formula:&lt;/p>
&lt;blockquote>
&lt;p>Y = 0.299R + 0.587G + 0.114B&lt;/p>
&lt;/blockquote>
&lt;p>Reduces complexity while preserving structure.&lt;/p>
&lt;!-- ILLUSTRATION: RGB image vs grayscale -->
&lt;hr>
&lt;h2 id="histogram-equalization">Histogram Equalization&lt;/h2>
&lt;p>Improves contrast:&lt;/p>
&lt;ul>
&lt;li>Makes details more visible&lt;/li>
&lt;li>Helps feature detection in low-contrast images&lt;/li>
&lt;/ul>
&lt;p>Useful in challenging lighting conditions.&lt;/p>
&lt;hr>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-13_hu202ef62196320da610a9b7eedb497450_1129609_37d48ba4484fd84bca70ea24a47ea9dd.webp 400w,
/slides/tracking-for-xr/image-13_hu202ef62196320da610a9b7eedb497450_1129609_5681538d5cf6688688e9f72488387006.webp 760w,
/slides/tracking-for-xr/image-13_hu202ef62196320da610a9b7eedb497450_1129609_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-13_hu202ef62196320da610a9b7eedb497450_1129609_37d48ba4484fd84bca70ea24a47ea9dd.webp"
width="760"
height="611"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="contrast-and-brightness-adjustment">Contrast and Brightness Adjustment&lt;/h2>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-14_hu73f0806c12cb1a02ee9c3f4b489e95cb_373181_4b0db2b08f6fce0f9f097b44c01f1f32.webp 400w,
/slides/tracking-for-xr/image-14_hu73f0806c12cb1a02ee9c3f4b489e95cb_373181_3b4449ef2f242129840b9116d865d063.webp 760w,
/slides/tracking-for-xr/image-14_hu73f0806c12cb1a02ee9c3f4b489e95cb_373181_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-14_hu73f0806c12cb1a02ee9c3f4b489e95cb_373181_4b0db2b08f6fce0f9f097b44c01f1f32.webp"
width="760"
height="421"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
class="img-right"
>
&lt;h2 id="convolution--the-key-tool">Convolution — The Key Tool&lt;/h2>
&lt;p>A sliding kernel is applied over the image.&lt;/p>
&lt;p>Each pixel becomes a weighted sum of its neighbors.&lt;/p>
&lt;p>This enables:&lt;/p>
&lt;ul>
&lt;li>Blurring&lt;/li>
&lt;li>Edge detection&lt;/li>
&lt;li>Feature enhancement&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-15_hu1b222c9679880a6b7de5b5c53d3dec50_391704_9a51e055e417a4ff110e08c9e4097892.webp 400w,
/slides/tracking-for-xr/image-15_hu1b222c9679880a6b7de5b5c53d3dec50_391704_2c1cc332b54606765651cbd9d9d96e9b.webp 760w,
/slides/tracking-for-xr/image-15_hu1b222c9679880a6b7de5b5c53d3dec50_391704_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-15_hu1b222c9679880a6b7de5b5c53d3dec50_391704_9a51e055e417a4ff110e08c9e4097892.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-16_hu7cdc8b7257ce23ca4546193596f477af_367077_032336f8f054891b173e3d05140f2d11.webp 400w,
/slides/tracking-for-xr/image-16_hu7cdc8b7257ce23ca4546193596f477af_367077_d9fd3c591c21e11795eb669f6448484f.webp 760w,
/slides/tracking-for-xr/image-16_hu7cdc8b7257ce23ca4546193596f477af_367077_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-16_hu7cdc8b7257ce23ca4546193596f477af_367077_032336f8f054891b173e3d05140f2d11.webp"
width="760"
height="667"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="simple-blur">Simple Blur&lt;/h2>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-17_huac5ff651651a557ccd732f69812d421d_150881_c426515d9ddc54063dfa7f4a8de0b9d6.webp 400w,
/slides/tracking-for-xr/image-17_huac5ff651651a557ccd732f69812d421d_150881_7b93634bfdd8a5cec75e37f3a44bcb1a.webp 760w,
/slides/tracking-for-xr/image-17_huac5ff651651a557ccd732f69812d421d_150881_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-17_huac5ff651651a557ccd732f69812d421d_150881_c426515d9ddc54063dfa7f4a8de0b9d6.webp"
width="760"
height="212"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="gaussian-blur">Gaussian Blur&lt;/h2>
&lt;p>Used to:&lt;/p>
&lt;ul>
&lt;li>Reduce high-frequency noise&lt;/li>
&lt;li>Smooth the image before detecting features&lt;/li>
&lt;/ul>
&lt;p>Important for robust tracking.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-18_hu764831d096603a639571f2bcb5ae824e_185452_b71540038cc8251b4f254da8bd109984.webp 400w,
/slides/tracking-for-xr/image-18_hu764831d096603a639571f2bcb5ae824e_185452_eb9c5225c8b641cc8b7ad8808c9ba169.webp 760w,
/slides/tracking-for-xr/image-18_hu764831d096603a639571f2bcb5ae824e_185452_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-18_hu764831d096603a639571f2bcb5ae824e_185452_b71540038cc8251b4f254da8bd109984.webp"
width="720"
height="592"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-19_hua335938fb1a761bee4ac2631d7807c30_435878_af4f83d8ae1db64e9f74aa347fda37c0.webp 400w,
/slides/tracking-for-xr/image-19_hua335938fb1a761bee4ac2631d7807c30_435878_f953b7f90fea42d55bee8ce71c50fa50.webp 760w,
/slides/tracking-for-xr/image-19_hua335938fb1a761bee4ac2631d7807c30_435878_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-19_hua335938fb1a761bee4ac2631d7807c30_435878_af4f83d8ae1db64e9f74aa347fda37c0.webp"
width="760"
height="271"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
class="img-right"
>
&lt;h2 id="gradients-and-edges">Gradients and Edges&lt;/h2>
&lt;p>Edges correspond to strong intensity changes.&lt;/p>
&lt;p>We compute gradients using filters such as:&lt;/p>
&lt;ul>
&lt;li>Sobel&lt;/li>
&lt;li>Prewitt&lt;/li>
&lt;/ul>
&lt;p>Edges help locate meaningful structures.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-20_hu42daa86ba0c0ff338b3af58e58d958ed_116173_ee6696aaa6d6df8b8d39c574aaaaa733.webp 400w,
/slides/tracking-for-xr/image-20_hu42daa86ba0c0ff338b3af58e58d958ed_116173_6d3c773ffd4217602102796f0a611fad.webp 760w,
/slides/tracking-for-xr/image-20_hu42daa86ba0c0ff338b3af58e58d958ed_116173_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-20_hu42daa86ba0c0ff338b3af58e58d958ed_116173_ee6696aaa6d6df8b8d39c574aaaaa733.webp"
width="760"
height="477"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h3 id="sobel-filter">Sobel Filter&lt;/h3>
&lt;p>$f&amp;rsquo;(i)=\frac{f_{i+1}-f_{i-1}}{2}\ \text{is equivalent to}\ [\tfrac12,\ 0,\ -\tfrac12]$&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-21_hu3aef5e31b1908706f6a0a881b693d361_527012_661d9e11729388fea3a487297e2763d0.webp 400w,
/slides/tracking-for-xr/image-21_hu3aef5e31b1908706f6a0a881b693d361_527012_6b275bc90d6ba75b507c11f8ebe40281.webp 760w,
/slides/tracking-for-xr/image-21_hu3aef5e31b1908706f6a0a881b693d361_527012_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-21_hu3aef5e31b1908706f6a0a881b693d361_527012_661d9e11729388fea3a487297e2763d0.webp"
width="760"
height="272"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="laplacian-second-order-derivative">Laplacian (Second-Order Derivative)&lt;/h2>
&lt;p>Detects regions of rapid change:&lt;/p>
&lt;ul>
&lt;li>Useful for feature detection&lt;/li>
&lt;li>Highlights corners and fine details&lt;/li>
&lt;/ul>
&lt;p>The standard discrete Laplacian is written:&lt;/p>
&lt;p>$\nabla^2 f(i,j)=f_{i+1,j}+f_{i-1,j}+f_{i,j+1}+f_{i,j-1}-4f_{i,j}$&lt;/p>
&lt;p>This corresponds to the kernel:&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-22_hu8a4239d24eb6ed64e40e3c4847c0abef_5470_368abeffe7538496f366650903cd2469.webp 400w,
/slides/tracking-for-xr/image-22_hu8a4239d24eb6ed64e40e3c4847c0abef_5470_a457838eed85b93dd4f82b19f9f73548.webp 760w,
/slides/tracking-for-xr/image-22_hu8a4239d24eb6ed64e40e3c4847c0abef_5470_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-22_hu8a4239d24eb6ed64e40e3c4847c0abef_5470_368abeffe7538496f366650903cd2469.webp"
width="246"
height="226"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-23_hu0dcb2f66358540590e1dac26c8a13113_449718_affa82907961493d70f881d2d39f3cbc.webp 400w,
/slides/tracking-for-xr/image-23_hu0dcb2f66358540590e1dac26c8a13113_449718_56b2b5d26310e4e402b0fa8970f8bf3f.webp 760w,
/slides/tracking-for-xr/image-23_hu0dcb2f66358540590e1dac26c8a13113_449718_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-23_hu0dcb2f66358540590e1dac26c8a13113_449718_affa82907961493d70f881d2d39f3cbc.webp"
width="760"
height="270"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="from-image-processing-to-features">From Image Processing to Features&lt;/h2>
&lt;p>At this stage, we have a cleaned and enhanced image.&lt;/p>
&lt;p>Next step:
👉 Detect meaningful points for tracking.&lt;/p>
&lt;!-- ILLUSTRATION: pipeline arrow to feature detection -->
&lt;hr>
&lt;h1 id="part-e--feature-detection-for-visual-tracking">PART E — Feature Detection for Visual Tracking&lt;/h1>
&lt;hr>
&lt;h2 id="what-is-a-feature">What is a Feature?&lt;/h2>
&lt;p>A feature is a distinctive image pattern that can be reliably detected and matched across frames.&lt;/p>
&lt;p>Examples:&lt;/p>
&lt;ul>
&lt;li>Corners&lt;/li>
&lt;li>Blobs&lt;/li>
&lt;li>Edges&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-24_hu2c03cf802abf55c03cdc45fdf3799af5_819145_cdbd9a80852df622813699d05c15bf42.webp 400w,
/slides/tracking-for-xr/image-24_hu2c03cf802abf55c03cdc45fdf3799af5_819145_42e8d365a75a9d5917c6ea1802da6d44.webp 760w,
/slides/tracking-for-xr/image-24_hu2c03cf802abf55c03cdc45fdf3799af5_819145_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-24_hu2c03cf802abf55c03cdc45fdf3799af5_819145_cdbd9a80852df622813699d05c15bf42.webp"
width="760"
height="289"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="what-makes-a-good-feature">What Makes a Good Feature?&lt;/h2>
&lt;p>A good feature should be:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Local&lt;/strong> (robust to occlusion)&lt;/li>
&lt;li>&lt;strong>Invariant&lt;/strong> (to translation, rotation, scale)&lt;/li>
&lt;li>&lt;strong>Robust&lt;/strong> (to noise, lighting changes)&lt;/li>
&lt;li>&lt;strong>Distinctive&lt;/strong> (easy to match)&lt;/li>
&lt;li>&lt;strong>Repeatable&lt;/strong> (found again in next frame)&lt;/li>
&lt;/ul>
&lt;!-- ILLUSTRATION: same feature across frames -->
&lt;hr>
&lt;h2 id="why-corners-are-ideal">Why Corners are Ideal&lt;/h2>
&lt;p>Corners are:&lt;/p>
&lt;ul>
&lt;li>Stable across viewpoints&lt;/li>
&lt;li>Well-localized in both x and y&lt;/li>
&lt;li>Highly informative for motion estimation&lt;/li>
&lt;/ul>
&lt;!-- ILLUSTRATION: corner vs edge vs flat region -->
&lt;hr>
&lt;h2 id="moravec-corner-detector-1977">Moravec Corner Detector (1977)&lt;/h2>
&lt;p>Idea:&lt;/p>
&lt;ul>
&lt;li>Measure how much intensity changes when shifting a small window in different directions.&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-25_hufd181cc1059df49c29f2c918c90a2986_158196_39d68f741106fd94e1a05f773a4e39ee.webp 400w,
/slides/tracking-for-xr/image-25_hufd181cc1059df49c29f2c918c90a2986_158196_faa3d7276e0053b27bb2169172f8bdc5.webp 760w,
/slides/tracking-for-xr/image-25_hufd181cc1059df49c29f2c918c90a2986_158196_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-25_hufd181cc1059df49c29f2c918c90a2986_158196_39d68f741106fd94e1a05f773a4e39ee.webp"
width="760"
height="379"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;p>&lt;strong>Average intensity variation for a small displacement ((u,v))&lt;/strong>&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-26_hu89c5e361050d6b068d1bb185ec39fd4c_356435_b45ecefb3b86015627af3b0c4881fc8d.webp 400w,
/slides/tracking-for-xr/image-26_hu89c5e361050d6b068d1bb185ec39fd4c_356435_2ccea3210c8fe33161927fe605d6305a.webp 760w,
/slides/tracking-for-xr/image-26_hu89c5e361050d6b068d1bb185ec39fd4c_356435_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-26_hu89c5e361050d6b068d1bb185ec39fd4c_356435_b45ecefb3b86015627af3b0c4881fc8d.webp"
width="760"
height="372"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>$E(u,v)=\sum_{x,y} w(x,y),(I(x+u,y+v)-I(x,y))^2$&lt;/p>
&lt;ul>
&lt;li>(w) specifies the considered neighborhood (value 1 inside the window and 0 outside);&lt;/li>
&lt;li>(I(x,y)) is the intensity at pixel ((x,y)).&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h3 id="results">Results&lt;/h3>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-27_hu041e8836f49700a9a8ec23a7018c3a62_2392743_3390a0e7f7b7fb1caccdcfa3cef08b16.webp 400w,
/slides/tracking-for-xr/image-27_hu041e8836f49700a9a8ec23a7018c3a62_2392743_86b54a9a38e8b0cb247407d6468e111e.webp 760w,
/slides/tracking-for-xr/image-27_hu041e8836f49700a9a8ec23a7018c3a62_2392743_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-27_hu041e8836f49700a9a8ec23a7018c3a62_2392743_3390a0e7f7b7fb1caccdcfa3cef08b16.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;p>&lt;strong>Limitation:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>Not rotation invariant.&lt;/li>
&lt;/ul>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-28_hu6715a3123e952a77c984b7c44a631658_788025_a88e67894cb89ad7359b952d14b71031.webp 400w,
/slides/tracking-for-xr/image-28_hu6715a3123e952a77c984b7c44a631658_788025_8a39e6351fad087a60f2b3ee1c9e2ed0.webp 760w,
/slides/tracking-for-xr/image-28_hu6715a3123e952a77c984b7c44a631658_788025_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-28_hu6715a3123e952a77c984b7c44a631658_788025_a88e67894cb89ad7359b952d14b71031.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="harris-corner-detector-1988">Harris Corner Detector (1988)&lt;/h2>
&lt;p>Improvement over Moravec:&lt;/p>
&lt;ul>
&lt;li>Uses image gradients&lt;/li>
&lt;li>More robust&lt;/li>
&lt;li>Rotation invariant&lt;/li>
&lt;/ul>
&lt;p>Widely used in vision-based tracking.&lt;/p>
&lt;!-- ILLUSTRATION: Harris response map -->
&lt;hr>
&lt;h2 id="average-intensity-variation-for-a-small-displacement-uv">Average intensity variation for a small displacement $(u,v)$&lt;/h2>
&lt;p>$E(x,y)=\sum_{u,v} w(u,v),|I(x+u,y+v)-I(x,y)|^2$&lt;/p>
&lt;ul>
&lt;li>$w$ specifies the considered neighborhood (value 1 inside the window, 0 outside);&lt;/li>
&lt;li>$I(x,y)$ is the intensity at pixel $(x,y)$.&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="taylor-expansion">Taylor expansion&lt;/h2>
&lt;p>$I(x+u,y+v)=I(x,y)+u\frac{\partial I}{\partial x}(x,y)+v\frac{\partial I}{\partial y}(x,y)+o(u^2,v^2)$&lt;/p>
&lt;p>Neglecting $o(u^2,v^2)$:&lt;/p>
&lt;p>$E(x,y)=\sum_{u,v} w(u,v)\left|u\frac{\partial I}{\partial x}(x,y)+v\frac{\partial I}{\partial y}(x,y)\right|^2$&lt;/p>
&lt;hr>
&lt;h2 id="quadratic-form">Quadratic form&lt;/h2>
&lt;p>Average intensity variation for a small displacement ((u,v)):&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-29_hufa990c6ae1c53170e765c8ab77ff424a_116888_1e4b3dcc6a63fd84af8e4933bd6c1723.webp 400w,
/slides/tracking-for-xr/image-29_hufa990c6ae1c53170e765c8ab77ff424a_116888_ca58cd68b0e7f4b92d59b3e1d0cea07a.webp 760w,
/slides/tracking-for-xr/image-29_hufa990c6ae1c53170e765c8ab77ff424a_116888_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-29_hufa990c6ae1c53170e765c8ab77ff424a_116888_1e4b3dcc6a63fd84af8e4933bd6c1723.webp"
width="760"
height="291"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="for-small-displacements-uv">For small displacements $(u,v)$&lt;/h2>
&lt;p>$E(x,y)=[u\ v]M[u\ v]^T$&lt;/p>
&lt;p>$M$: symmetric, positive definite $\Rightarrow$ eigenvalue decomposition possible.&lt;/p>
&lt;hr>
&lt;h2 id="structure-of-the-second-moment-matrix">Structure of the second-moment matrix&lt;/h2>
&lt;p>$M=\begin{pmatrix}A,C;C,B\end{pmatrix}$&lt;/p>
&lt;p>with:&lt;/p>
&lt;p>$A=\left(\frac{\partial I}{\partial x}\right)^2 \otimes w ;
B=\left(\frac{\partial I}{\partial y}\right)^2 \otimes w ;
C=\left(\frac{\partial I}{\partial x}\frac{\partial I}{\partial y}\right)\otimes w$&lt;/p>
&lt;p>$w$: Gaussian window (isotropic).&lt;/p>
&lt;p>A corner is characterized by a large variation of $E$ in all directions of $(x,y)$.&lt;/p>
&lt;p>$\Rightarrow$ Compute the eigenvalues of $M$.&lt;/p>
&lt;hr>
&lt;h2 id="corner-response">Corner response&lt;/h2>
&lt;p>Instead of computing the eigenvalues, we can compute:&lt;/p>
&lt;p>$\det(M)=AB-C^2=\lambda_1\lambda_2$&lt;/p>
&lt;p>$\text{trace}(M)=A+B=\lambda_1+\lambda_2$&lt;/p>
&lt;p>and define the response:&lt;/p>
&lt;p>$R=\det(M)-k,\text{trace}(M)^2$&lt;/p>
&lt;p>Values of $R$:&lt;/p>
&lt;ul>
&lt;li>positive near corners,&lt;/li>
&lt;li>negative near edges,&lt;/li>
&lt;li>small in flat regions ($k=0.04$).&lt;/li>
&lt;/ul>
&lt;p>$\Rightarrow$ corners / interest points = local maxima of $R$.&lt;/p>
&lt;hr>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-30_hu216dce15d0fcc5f3a3ac63f86d2c87b5_1465677_04cc79c0d9b3e2122e7e450400051e48.webp 400w,
/slides/tracking-for-xr/image-30_hu216dce15d0fcc5f3a3ac63f86d2c87b5_1465677_632b3e9c6ab25321e98e16ae1cee9568.webp 760w,
/slides/tracking-for-xr/image-30_hu216dce15d0fcc5f3a3ac63f86d2c87b5_1465677_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-30_hu216dce15d0fcc5f3a3ac63f86d2c87b5_1465677_04cc79c0d9b3e2122e7e450400051e48.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-31_hu01d83725ecb01e7e0119ce4278fc5b6d_1422286_bfa6ee86f07956c29e9cc249f87642cd.webp 400w,
/slides/tracking-for-xr/image-31_hu01d83725ecb01e7e0119ce4278fc5b6d_1422286_a04aaea4607eff91882be0e1099bbef8.webp 760w,
/slides/tracking-for-xr/image-31_hu01d83725ecb01e7e0119ce4278fc5b6d_1422286_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-31_hu01d83725ecb01e7e0119ce4278fc5b6d_1422286_bfa6ee86f07956c29e9cc249f87642cd.webp"
width="760"
height="507"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;p>&lt;strong>Invariant by rotation&lt;/strong>: even after rotation, the matrix shape is unchanged, so the eigenvalues and the response $R$ are unchanged.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-32_hu166b3c8e49f5cf05dc477f1d9792aa51_23769_e833c05298c5c8d79d5d6e1a5af33085.webp 400w,
/slides/tracking-for-xr/image-32_hu166b3c8e49f5cf05dc477f1d9792aa51_23769_8cbf1c5d4a7468ef4cb4b040a51b41a2.webp 760w,
/slides/tracking-for-xr/image-32_hu166b3c8e49f5cf05dc477f1d9792aa51_23769_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-32_hu166b3c8e49f5cf05dc477f1d9792aa51_23769_e833c05298c5c8d79d5d6e1a5af33085.webp"
width="375"
height="200"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;p>&lt;strong>Not invariant by scale&lt;/strong>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-33_hu2b5a5fcfa7f457b6db52c3fedb019d43_17621_b0e6c8c91c47bc3da98da9a7bd37d895.webp 400w,
/slides/tracking-for-xr/image-33_hu2b5a5fcfa7f457b6db52c3fedb019d43_17621_4f2a97792138e78b6b44e6028088dcb9.webp 760w,
/slides/tracking-for-xr/image-33_hu2b5a5fcfa7f457b6db52c3fedb019d43_17621_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-33_hu2b5a5fcfa7f457b6db52c3fedb019d43_17621_b0e6c8c91c47bc3da98da9a7bd37d895.webp"
width="698"
height="211"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>Potential solution: compute Harris response at multiple scales (Harris–Laplace).&lt;/p>
&lt;hr>
&lt;h1 id="part-f--from-features-to-tracking">PART F — From Features to Tracking&lt;/h1>
&lt;hr>
&lt;h2 id="feature-based-tracking-pipeline">Feature-Based Tracking Pipeline&lt;/h2>
&lt;p>Frame t:&lt;/p>
&lt;ol>
&lt;li>Detect features&lt;/li>
&lt;li>Describe features&lt;/li>
&lt;li>Match with previous frame&lt;/li>
&lt;li>Estimate motion (R, t)&lt;/li>
&lt;/ol>
&lt;p>Repeat continuously.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-36_hu2ecf3a01792a4c095e141c34810564a3_536273_52afa4995f3527fd7bcb9c90034658ec.webp 400w,
/slides/tracking-for-xr/image-36_hu2ecf3a01792a4c095e141c34810564a3_536273_cb9d311e1df382c5170842f3b80f9e32.webp 760w,
/slides/tracking-for-xr/image-36_hu2ecf3a01792a4c095e141c34810564a3_536273_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-36_hu2ecf3a01792a4c095e141c34810564a3_536273_52afa4995f3527fd7bcb9c90034658ec.webp"
width="760"
height="305"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="feature-matching">Feature Matching&lt;/h2>
&lt;p>Features are compared using descriptors.&lt;/p>
&lt;p>Goal:&lt;/p>
&lt;ul>
&lt;li>Find correspondences between frames&lt;/li>
&lt;/ul>
&lt;p>This enables motion estimation.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-34_hu75cc9a76e069fc95dfd44a765d966e62_819434_782fafe89a575ba71442d5c3712bfc3b.webp 400w,
/slides/tracking-for-xr/image-34_hu75cc9a76e069fc95dfd44a765d966e62_819434_510e13e5972a07818f1cf3bd3ff6d674.webp 760w,
/slides/tracking-for-xr/image-34_hu75cc9a76e069fc95dfd44a765d966e62_819434_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-34_hu75cc9a76e069fc95dfd44a765d966e62_819434_782fafe89a575ba71442d5c3712bfc3b.webp"
width="760"
height="289"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="example-sift-scale-invariant-feature-transform">Example: SIFT (Scale-Invariant Feature Transform)&lt;/h2>
&lt;p>SIFT provides:&lt;/p>
&lt;ul>
&lt;li>Scale invariance&lt;/li>
&lt;li>Rotation invariance&lt;/li>
&lt;li>Robust descriptors&lt;/li>
&lt;/ul>
&lt;p>Widely used in classical computer vision.&lt;/p>
&lt;hr>
&lt;h2 id="sift-pipeline">SIFT Pipeline&lt;/h2>
&lt;ol>
&lt;li>Build Gaussian pyramid (multi-scale representation)&lt;/li>
&lt;li>Compute Difference of Gaussians (DoG)&lt;/li>
&lt;li>Detect keypoints&lt;/li>
&lt;li>Assign orientation&lt;/li>
&lt;li>Compute &lt;strong>descriptor&lt;/strong> from local gradients&lt;/li>
&lt;/ol>
&lt;hr>
&lt;h3 id="build-gaussian-pyramid-compute-dog-and-detect-keypoints">Build Gaussian pyramid, compute DoG and detect keypoints&lt;/h3>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-38_hufab470e83955287a8e08859ba977dd24_118860_d214533751c4b4704cb35b59df501602.webp 400w,
/slides/tracking-for-xr/image-38_hufab470e83955287a8e08859ba977dd24_118860_ca41e855db90fde5d5b1476b55eca26a.webp 760w,
/slides/tracking-for-xr/image-38_hufab470e83955287a8e08859ba977dd24_118860_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-38_hufab470e83955287a8e08859ba977dd24_118860_d214533751c4b4704cb35b59df501602.webp"
width="754"
height="344"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>To obtain scale-independent descriptors, the image is resampled multiple times (building a &amp;ldquo;pyramid&amp;rdquo;).&lt;/p>
&lt;p>The Gaussian difference is more efficient than a Laplacian calculation for each level.&lt;/p>
&lt;hr>
&lt;h3 id="assign-orientation-and-compute-descriptor">Assign orientation and compute descriptor&lt;/h3>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-39_hu5b41d703a472001977b405acd2da2ed9_958515_66239ca7c2b8907e8f52aab2724c6bda.webp 400w,
/slides/tracking-for-xr/image-39_hu5b41d703a472001977b405acd2da2ed9_958515_eeb3940fab622228d4cb4cb08a8ae901.webp 760w,
/slides/tracking-for-xr/image-39_hu5b41d703a472001977b405acd2da2ed9_958515_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-39_hu5b41d703a472001977b405acd2da2ed9_958515_66239ca7c2b8907e8f52aab2724c6bda.webp"
width="760"
height="288"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;p>The descriptor is a histogram of local gradients around the keypoint, weighted by a Gaussian window.&lt;/p>
&lt;hr>
&lt;video width="700" autoplay muted loop playsinline controls>
&lt;source src="media2.mp4" type="video/mp4">
&lt;/video>
&lt;hr>
&lt;h1 id="part-g--from-2d-features-to-6dof-tracking">PART G — From 2D Features to 6DoF Tracking&lt;/h1>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
class="img-right"
>
&lt;h2 id="the-pnp-problem-perspective-n-point">The PnP Problem (Perspective-n-Point)&lt;/h2>
&lt;p>If we know:&lt;/p>
&lt;ul>
&lt;li>3D world points&lt;/li>
&lt;li>Their 2D projections in the image&lt;/li>
&lt;/ul>
&lt;p>We can solve for camera pose (R, t).&lt;/p>
&lt;p>This is central to many AR systems.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-41_hude0c4097bf97c4a13a3891bb383e03ae_94304_fb1a895055a1afdc538e745ef0a3d34d.webp 400w,
/slides/tracking-for-xr/image-41_hude0c4097bf97c4a13a3891bb383e03ae_94304_d72f59258c3340627892cde92d5e787e.webp 760w,
/slides/tracking-for-xr/image-41_hude0c4097bf97c4a13a3891bb383e03ae_94304_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-41_hude0c4097bf97c4a13a3891bb383e03ae_94304_fb1a895055a1afdc538e745ef0a3d34d.webp"
width="428"
height="332"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="visual-odometry">Visual Odometry&lt;/h2>
&lt;p>If no 3D map is available:&lt;/p>
&lt;ul>
&lt;li>Track motion between consecutive frames&lt;/li>
&lt;li>Estimate relative movement of the camera&lt;/li>
&lt;/ul>
&lt;p>Used in SLAM systems.&lt;/p>
&lt;hr>
&lt;h2 id="slam-in-xr">SLAM in XR&lt;/h2>
&lt;p>Modern headsets use SLAM:&lt;/p>
&lt;ul>
&lt;li>Simultaneous Localization and Mapping&lt;/li>
&lt;li>Build a map of the environment&lt;/li>
&lt;li>Track the headset within that map&lt;/li>
&lt;/ul>
&lt;p>Examples:&lt;/p>
&lt;ul>
&lt;li>ARKit&lt;/li>
&lt;li>ARCore&lt;/li>
&lt;li>Meta Quest inside-out tracking&lt;/li>
&lt;/ul>
&lt;hr>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-40_hu4491781fb12c9b66fc8702f858d359ae_778202_64296ee21f39be1020c31192812276f2.webp 400w,
/slides/tracking-for-xr/image-40_hu4491781fb12c9b66fc8702f858d359ae_778202_1f731cfc0fca839128e4e463a24ca888.webp 760w,
/slides/tracking-for-xr/image-40_hu4491781fb12c9b66fc8702f858d359ae_778202_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-40_hu4491781fb12c9b66fc8702f858d359ae_778202_64296ee21f39be1020c31192812276f2.webp"
width="760"
height="440"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h1 id="part-h--tracking-in-modern-xr-systems">PART H — Tracking in Modern XR Systems&lt;/h1>
&lt;hr>
&lt;section data-noprocess data-shortcode-slide
class="img-right"
>
&lt;h2 id="inside-out-tracking-meta-quest">Inside-Out Tracking (Meta Quest)&lt;/h2>
&lt;p>Uses multiple cameras + IMU to:&lt;/p>
&lt;ul>
&lt;li>Track headset position&lt;/li>
&lt;li>Track controllers&lt;/li>
&lt;li>Map environment&lt;/li>
&lt;/ul>
&lt;p>Fully vision-based.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-42_hue872a9a954395fe8beef5ae27ddf9d61_494688_0ed5837127605d6c81aeda78fba5365e.webp 400w,
/slides/tracking-for-xr/image-42_hue872a9a954395fe8beef5ae27ddf9d61_494688_880a9496c6c781e9bc377e179699b985.webp 760w,
/slides/tracking-for-xr/image-42_hue872a9a954395fe8beef5ae27ddf9d61_494688_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-42_hue872a9a954395fe8beef5ae27ddf9d61_494688_0ed5837127605d6c81aeda78fba5365e.webp"
width="760"
height="441"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="optical-tracking-viconoptitrack">Optical Tracking (Vicon/OptiTrack)&lt;/h2>
&lt;p>Uses external cameras to track reflective markers.&lt;/p>
&lt;p>Very accurate but requires a dedicated setup.&lt;/p>
&lt;p>
&lt;figure >
&lt;div class="d-flex justify-content-center">
&lt;div class="w-100" >&lt;img alt="alt text" srcset="
/slides/tracking-for-xr/image-43_hu74a79043a395023f1a818122879f957e_600957_d1135368a0a34c38810b7408c55f4922.webp 400w,
/slides/tracking-for-xr/image-43_hu74a79043a395023f1a818122879f957e_600957_db8b943e2ff48043377be7d7709fa7b2.webp 760w,
/slides/tracking-for-xr/image-43_hu74a79043a395023f1a818122879f957e_600957_1200x1200_fit_q75_h2_lanczos_3.webp 1200w"
src="https://www.etiennepeillard.com/slides/tracking-for-xr/image-43_hu74a79043a395023f1a818122879f957e_600957_d1135368a0a34c38810b7408c55f4922.webp"
width="760"
height="428"
loading="lazy" data-zoomable />&lt;/div>
&lt;/div>&lt;/figure>
&lt;/p>
&lt;hr>
&lt;h2 id="hybrid-tracking-systems">Hybrid Tracking Systems&lt;/h2>
&lt;p>Combine:&lt;/p>
&lt;ul>
&lt;li>Vision&lt;/li>
&lt;li>IMU&lt;/li>
&lt;li>Depth sensors&lt;/li>
&lt;/ul>
&lt;p>To improve robustness and reduce drift.&lt;/p>
&lt;!-- ILLUSTRATION: sensor fusion diagram --></description></item></channel></rss>