Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
Expand All @@ -98,7 +98,7 @@ ipython_config.py
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# uv.lock
uv.lock

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,21 @@ Scan the following QR code with your GoPro to apply all required camera settings

![Camera settings QR code](assets/camera_settings_qr.jpeg)

Once the settings are saved, the GoPro screen will show **"Labs-Trumi"** with the applied configuration:

![Camera settings saved confirmation](assets/camera_settings_saved_check.jpg)

**Note:** When using with ultra-wide lens, disable auto lens detection and manually set the lens to standard (Preferences > Lens Mod > Standard). This prevents GoPro from applying additional distortion correction, ensuring raw undistorted frames are saved.

### Step 1: Timecode Sync

**Note:** This step is only required for bi-manual (two-gripper) data collection. Skip it if you are using a single gripper.

Open the [timecode sync link](https://umi-gripper.github.io/qrlocal/) and scan the refreshing QR code using your GoPro (requires Labs firmware). Precise synchronization between the two grippers is critical for bi-manual data collection.
Serve the timecode sync page locally and open the printed URL in a browser, then scan the refreshing QR code using your GoPro (requires Labs firmware). Precise synchronization between the two grippers is critical for bi-manual data collection.

```bash
uv run python scripts/serve_timecode_sync.py
```

**Tip:** Adjust your distance to the QR code if you have difficulty scanning.

Expand Down
Binary file added assets/camera_settings_saved_check.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
216 changes: 216 additions & 0 deletions assets/timecode_sync/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@

<!DOCTYPE html>
<html lang="en-US">
<head>


<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#157878">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<link rel="stylesheet" href="./style.css">
<link rel="icon" href="data:,">
</head>
<body>
<main id="content" class="main-content" role="main">
<h1 id="precision-date-and-time-local">Precision Date and Time (Local)</h1>

<script src="./qrcodeborder.js"></script>

<style>
#qrcode{
width: 100%;
}
div{
width: 100%;
display: inline-block;
}
</style>

<p><strong>Refresh Rate</strong> <input type="range" id="fpsid" name="fpsid" min="10" max="120" value="30" style="width: 300px;" /><label for="fpsid"></label> <b id="fpstext"></b> FPS</p>

<p><strong>Timezone</strong> <input type="range" id="tzid" name="tzid" min="-48" max="56" value="0" style="width: 300px;" /><label for="tzid"></label> <b id="tztext"></b> hour <b id="tzmin"></b> minute<br /></p>

<p><strong>Daylight Saving Time</strong> <input type="checkbox" id="tdid" name="tdid" /> <label for="tdid">Active</label><br /></p>

<p><strong>QR Command Set Time</strong> <input type="checkbox" id="qrid" name="qrid" checked="true"/> <label for="qrid">Active</label><br /></p>

<p>Simply point your Labs enabled camera at this animated QR Code, to set your date and time very accurately to local time. This is particularly useful for multi-camera shoots, as it helps synchronize the timecode between cameras. As the camera’s internal clock will drift slowly over time, use this QR Code just before your multi-camera shoot for the best synchronization.</p>

<center>
<div id="qrcode"></div>
<br />
</center>
<p>QR Command: <b id="qrtext"></b></p>

<p><strong>Compatibility:</strong> Labs enabled HERO5 Session, HERO7, HERO8, HERO9, HERO10, HERO11, HERO12, HERO13, MAX and BONES</p>

<script>
var once = true;
var qrcode;
var cmd = "";
var id = 0;
var fps = 30;


function id5() { // 5 characters, so up to 17-bit ID
return ([1111]+1).replace(/1/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] % 10 >> c / 4).toString()
);
}

function getMachineId()
{
let machineId = localStorage.getItem('MachineId');
if (!machineId) {
//machineId = crypto.randomUUID();
machineId = id5();
localStorage.setItem('MachineId', machineId);
}
return machineId;
}

function isDST(d) {
let jan = new Date(d.getFullYear(), 0, 1).getTimezoneOffset();
let jul = new Date(d.getFullYear(), 6, 1).getTimezoneOffset();
return d.getTimezoneOffset() < Math.max(jan, jul);
}

function setTZ() {
var today = new Date();
var tz,td = 0;
tz = today.getTimezoneOffset();

if(isDST(today))
td = 1;

Comment thread
abhichothani42 marked this conversation as resolved.
if(document.getElementById("tzid") !== null)
{
document.getElementById("tzid").value = -tz/15;

var h = Math.trunc(tz/60);
var m = tz - h*60;
document.getElementById("tztext").innerHTML = -h;
document.getElementById("tzmin").innerHTML = -m;
}


if(document.getElementById("tdid") !== null)
{
if(td) {
document.getElementById("tdid").checked = true;
}
}
}

function makeQR() {
if(once === true)
{
id = getMachineId(); // 5 character 10-base, so up to 17-bit ID
qrcode = new QRCode(document.getElementById("qrcode"),
{
text : "oT0",
width : 360,
height : 360,
correctLevel : QRCode.CorrectLevel.M
});
once = false;
}
}
function padTime(i) {
if (i < 10) {i = "0" + i;} // add zero in front of numbers < 10
return i;
}
function padTime1000(i) {
if (i >= 10 && i < 100) {i = "0" + i;} // add zero in front of numbers < 100
else if (i < 10) {i = "00" + i;} // add zero in front of numbers < 10
return i;
}
function timeLoop(prevTime)
{

if(document.getElementById("fpsid") !== null)
{
fps = parseInt(document.getElementById("fpsid").value);
document.getElementById("fpstext").innerHTML = fps;
}

var currTime = Date.now()
// exactly 60fps
if ((currTime - prevTime) < 1000./fps){
requestAnimationFrame(function (){timeLoop(prevTime)});
Comment thread
abhichothani42 marked this conversation as resolved.
return;
}

var today;
var yy,mm,dd,h,m,s;
var ms,tz;

today = new Date();
yy = today.getFullYear() - 2000;
mm = today.getMonth() + 1;
dd = today.getDate();
h = today.getHours();
m = today.getMinutes();
s = today.getSeconds();
ms = today.getMilliseconds();
yy = padTime(yy);
mm = padTime(mm);
dd = padTime(dd);
h = padTime(h);
m = padTime(m);
s = padTime(s);
//ms = Math.floor(ms / 10); // hundredths
ms = padTime1000(ms);

if(document.getElementById("tzid") !== null)
{
tz = parseInt(document.getElementById("tzid").value) * 15;

var H = Math.trunc(tz/60);
var M = tz - H*60;
document.getElementById("tztext").innerHTML = H;
document.getElementById("tzmin").innerHTML = M;

if(Math.trunc(tz/60) == tz/60)
tz = tz/60; // only need hours when precise.
}

var td = 0;
if(document.getElementById("tdid") !== null)
{
if(document.getElementById("tdid").checked) {
td = 1;
}
}

var set_time = true;
if(document.getElementById("qrid") !== null)
{
set_time = document.getElementById("qrid").checked;
}
cmd = "#" + yy + mm + dd + h + m + s + "." + ms;
if(set_time){
cmd = "oT" + yy + mm + dd + h + m + s + "." + ms + "oTD" + td + "oTZ" + tz + "oTI" + id;
}
qrcode.clear();
qrcode.makeCode(cmd);
document.getElementById("qrtext").innerHTML = cmd;

requestAnimationFrame(function (){timeLoop(currTime)});
}

function myReloadFunction() {
location.reload();
}

makeQR();
setTZ();
timeLoop(Date.now());

</script>

</main>
</body>
</html>
Loading