Skip to content

Instantly share code, notes, and snippets.

@feklee
Last active August 29, 2015 14:09
Show Gist options
  • Save feklee/44ad96649efb4086e256 to your computer and use it in GitHub Desktop.
Save feklee/44ad96649efb4086e256 to your computer and use it in GitHub Desktop.
For remapping frames of a timelapse created with the Ricoh Theta
// Tested with Node.js 0.10 and with "nona" from Hugin 2013.
//
// Used to create: <https://vine.co/v/O5DI55QZnii>
//
// Original images from the Theta need to be in the sub directory `raw_frames`.
//
// Remaped images go into the sub directory `frames`.
//
// Remapping is parameterized, see the function `createPtoFile`.
//
// Written by Felix E. Klee <[email protected]>.
//
// To the extent possible under law, the author(s) have dedicated all copyright
// and related and neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty. You should
// have received a copy of the CC0 Public Domain Dedication along with this
// software. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
/*jslint node: true, maxlen: 79 */
'use strict';
var fs = require('fs'),
execFile = require('child_process').execFile,
remapFrames,
remapFrame,
ptoFilename = 'remap.pto',
createPtoFile,
exitWithOutput,
numberOfFiles;
exitWithOutput = function (dataToOutput, code) {
console.error(dataToOutput);
process.exit(code);
};
createPtoFile = function (n, onSuccess) {
var t = numberOfFiles * 2, // total number of frames
l = t - 1, // last frame
fieldOfView =
100 +
150 * (1 - Math.pow(n / l - 1, 4)) + // ease out
4 * l / (l - n), // warp
pitch =
10 + // some offset necessary to avoid unexpected flip
180 * n / t,
s = [
[
'p', 'f4', 'w1500', 'h1500',
'v' + fieldOfView,
'R0', 'nJPEG'
].join(' '),
[
'i', 'f4', 'w3584', 'h1792', 'v360',
'r90',
'p90' + pitch, // better use `p` instead of `p90` (bug)
'y90',
'n"input.jpg"'
].join(' ')
].join('\n');
fs.writeFile(ptoFilename, s, function (err) {
if (err) {
exitWithOutput(err, 1);
}
onSuccess();
});
};
remapFrame = function (filename, frameNumber, onSuccess) {
var outputFilename = String('000' + frameNumber).slice(-3);
execFile("nona", [
'-o',
'frames/' + outputFilename,
ptoFilename,
'raw_frames/' + filename
], function (err) {
if (err) {
exitWithOutput(err, 1);
}
onSuccess();
});
};
remapFrames = function (filenames, frameNumber) {
var filename;
if (filenames.length === 0) {
if (numberOfFiles > 0) {
process.stdout.write('\n');
}
return;
}
if (frameNumber === undefined) {
frameNumber = 0;
}
process.stdout.write('.');
// Here: Every file is used twice, for smoother animation:
filename = filenames[0];
if (frameNumber % 2 === 1) {
filenames.shift();
}
createPtoFile(frameNumber, function () {
remapFrame(filename, frameNumber, function () {
remapFrames(filenames, frameNumber + 1);
});
});
};
fs.readdir('raw_frames', function (err, filenames) {
if (err) {
exitWithOutput(err, err.errno);
}
numberOfFiles = filenames.length;
remapFrames(filenames);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment