mirror of
https://github.com/publiclab/image-sequencer.git
synced 2025-12-06 16:30:01 +01:00
Compare commits
33 Commits
MargaretAN
...
codeclimat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ec3345f5b | ||
|
|
180bdb8a42 | ||
|
|
89345f32b3 | ||
|
|
4e5f8728fa | ||
|
|
c2477bc3f7 | ||
|
|
ef4a4d3055 | ||
|
|
20d49456fd | ||
|
|
2aa7e9b96c | ||
|
|
52c9c35398 | ||
|
|
a522e13613 | ||
|
|
f41f564bd9 | ||
|
|
1a98247858 | ||
|
|
4369137336 | ||
|
|
595d66ecd5 | ||
|
|
6d9f656a1e | ||
|
|
69ed689ad7 | ||
|
|
107b4c22e4 | ||
|
|
e5d36ca317 | ||
|
|
eb595d6139 | ||
|
|
2bdd532848 | ||
|
|
d104ea744b | ||
|
|
bc122af258 | ||
|
|
11476d5084 | ||
|
|
a2157be2da | ||
|
|
b71e9dec12 | ||
|
|
aad1f823d3 | ||
|
|
b520622bfd | ||
|
|
6028520197 | ||
|
|
a60acd5a72 | ||
|
|
0fc8007e79 | ||
|
|
5bdaea3688 | ||
|
|
e353465dce | ||
|
|
39c70d1304 |
@@ -1,16 +1,21 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '4'
|
||||
- '5'
|
||||
- '6'
|
||||
- '7' # Node.js 7 is most stable version
|
||||
- '8'
|
||||
- '10'
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
before_script:
|
||||
- npm install grunt-cli -g # for "grunt build"
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
- ./cc-test-reporter before-build
|
||||
script:
|
||||
- npm test
|
||||
- grunt build
|
||||
after_script:
|
||||
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
|
||||
8600
dist/image-sequencer.js
vendored
8600
dist/image-sequencer.js
vendored
File diff suppressed because it is too large
Load Diff
2
dist/image-sequencer.min.js
vendored
2
dist/image-sequencer.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -128,8 +128,8 @@ h1 {
|
||||
}
|
||||
#save-seq {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
min-width: 250px;
|
||||
margin: 0px 10px 0px 0px;
|
||||
width: 250px;
|
||||
}
|
||||
.info {
|
||||
padding: 8px;
|
||||
@@ -137,8 +137,8 @@ h1 {
|
||||
}
|
||||
#gif {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
min-width: 250px;
|
||||
margin: 0px 0px 0px 10px;
|
||||
width: 250px;
|
||||
}
|
||||
#dwnld {
|
||||
max-width: 500px;
|
||||
@@ -199,8 +199,15 @@ h1 {
|
||||
|
||||
.radio-group {
|
||||
margin-bottom: 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
||||
.savesequencemsg{
|
||||
display: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#stepRemovedNotification {
|
||||
background-color: #808b96;
|
||||
padding:4px;
|
||||
@@ -208,11 +215,16 @@ h1 {
|
||||
border-radius:3px;
|
||||
font-size:2rem;
|
||||
position:fixed;
|
||||
bottom:8px;
|
||||
left:45%;
|
||||
bottom:2px;
|
||||
left:50%;
|
||||
min-width:14rem;
|
||||
text-align:center;
|
||||
display:none;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
#resetButton {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.no-border {
|
||||
@@ -230,3 +242,9 @@ h1 {
|
||||
.i-small {
|
||||
left: 25px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 767px) {
|
||||
.center-align {
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +65,15 @@ window.onload = function() {
|
||||
sequencer.loadImage("images/tulips.png", ui.onLoad);
|
||||
}
|
||||
|
||||
var resetSequence = function(){
|
||||
var r=confirm("Do you want to reset the sequence?");
|
||||
if (r)
|
||||
window.location = "/";
|
||||
}
|
||||
|
||||
$("#addStep select").on("change", ui.selectNewStepUi);
|
||||
$("#addStep #add-step-btn").on("click", ui.addStepUi);
|
||||
$("#resetButton").on("click",resetSequence);
|
||||
|
||||
//Module button radio selection
|
||||
$('.radio-group .radio').on("click", function() {
|
||||
@@ -86,11 +93,23 @@ window.onload = function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
function displayMessageOnSaveSequence(){
|
||||
$(".savesequencemsg").fadeIn();
|
||||
setTimeout(function() {
|
||||
$(".savesequencemsg").fadeOut();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
$('body').on('click', 'button.remove', ui.removeStepUi);
|
||||
$('#save-seq').click(() => {
|
||||
sequencer.saveSequence(window.prompt("Please give a name to your sequence..."), sequencer.toString());
|
||||
sequencer.loadModules();
|
||||
refreshOptions();
|
||||
var result = window.prompt("Please give a name to your sequence... (Saved sequence will only be available in this browser).");
|
||||
if(result){
|
||||
result = result + " (local)";
|
||||
sequencer.saveSequence(result, sequencer.toString());
|
||||
sequencer.loadModules();
|
||||
displayMessageOnSaveSequence();
|
||||
refreshOptions();
|
||||
}
|
||||
});
|
||||
|
||||
var isWorkingOnGifGeneration = false;
|
||||
@@ -188,48 +207,14 @@ window.onload = function() {
|
||||
}
|
||||
});
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('sw.js', { scope: '/examples/' })
|
||||
.then(function(registration) {
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
console.log(installingWorker)
|
||||
if (installingWorker.state === 'installed') {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
console.log('Registration successful, scope is:', registration.scope);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log('Service worker registration failed, error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
caches.keys().then(function(cacheNames) {
|
||||
cacheNames.forEach(function(cacheName) {
|
||||
$("#clear-cache").append(" " + cacheName);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$("#clear-cache").click(function() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
caches.keys().then(function(cacheNames) {
|
||||
cacheNames.forEach(function(cacheName) {
|
||||
caches.delete(cacheName);
|
||||
});
|
||||
});
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
|
||||
setupCache();
|
||||
|
||||
function updatePreviews(src) {
|
||||
$('#addStep img').remove();
|
||||
|
||||
var previewSequencerSteps = {
|
||||
"brightness": "20",
|
||||
"saturation": "5",
|
||||
"brightness": "175",
|
||||
"saturation": "0.5",
|
||||
"rotate": 90,
|
||||
"contrast": 90,
|
||||
"crop": {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<script src="lib/defaultHtmlStepUi.js" charset="utf-8"></script>
|
||||
<script src="lib/defaultHtmlSequencerUi.js" charset="utf-8"></script>
|
||||
<script src="lib/intermediateHtmlStepUi.js" charset="utf-8"></script>
|
||||
<script src="lib/cache.js" charset="utf-8"></script>
|
||||
<script src="demo.js" charset="utf-8"></script>
|
||||
<!-- for crop module: -->
|
||||
<script src="../node_modules/imgareaselect/jquery.imgareaselect.dev.js"></script>
|
||||
@@ -80,6 +81,7 @@
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
<p class = "alert alert-success savesequencemsg">Saved Sequence Success. Sequence can be found among other modules.</p>
|
||||
<div class="row">
|
||||
<div class="col-sm-8">
|
||||
<section id="addStep" class="panel panel-primary">
|
||||
@@ -133,8 +135,8 @@
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="row center-align">
|
||||
<button class="btn btn-primary btn-lg" name="save-sequence" id="save-seq">Save Sequence</button>
|
||||
<button class="btn btn-primary btn-lg js-view-as-gif" id="gif">View GIF</button>
|
||||
<button class="btn btn-primary btn-block btn-lg" name="save-sequence" id="save-seq">Save Sequence</button>
|
||||
<button class="btn btn-primary btn-block btn-lg js-view-as-gif" id="gif">View GIF</button>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -169,6 +171,7 @@
|
||||
<div class="panel-body">
|
||||
<div style="text-align:center;">
|
||||
<button class="btn btn-success btn-lg" id="download-btn" name="download">Download PNG</button>
|
||||
<button id="resetButton" class="btn btn-default btn-lg">Clear All Steps</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -176,13 +179,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form class="move-up" action="#up">
|
||||
<button><i class="fa fa-arrow-circle-o-up"></i></button>
|
||||
</form>
|
||||
|
||||
<footer>
|
||||
<hr style="margin:20px;"><center><a class="color:grey;" id="clear-cache">Clear offline cache</a></center>
|
||||
</footer>
|
||||
<form class="move-up" action="#up">
|
||||
<button><i class="fa fa-arrow-circle-o-up"></i></button>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
|
||||
37
examples/lib/cache.js
Normal file
37
examples/lib/cache.js
Normal file
@@ -0,0 +1,37 @@
|
||||
var setupCache = function() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('sw.js', { scope: '/examples/' })
|
||||
.then(function(registration) {
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
console.log(installingWorker)
|
||||
if (installingWorker.state === 'installed') {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
console.log('Registration successful, scope is:', registration.scope);
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log('Service worker registration failed, error:', error);
|
||||
});
|
||||
}
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
caches.keys().then(function(cacheNames) {
|
||||
cacheNames.forEach(function(cacheName) {
|
||||
$("#clear-cache").append(" " + cacheName);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$("#clear-cache").click(function() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
caches.keys().then(function(cacheNames) {
|
||||
cacheNames.forEach(function(cacheName) {
|
||||
caches.delete(cacheName);
|
||||
});
|
||||
});
|
||||
}
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
@@ -57,6 +57,7 @@ function DefaultHtmlSequencerUi(_sequencer, options) {
|
||||
_sequencer
|
||||
.addSteps(newStepName, options)
|
||||
.run({ index: _sequencer.images.image1.steps.length - sequenceLength - 1 });
|
||||
$(addStepSel + " .info").html("Select a new module to add to your sequence.");
|
||||
|
||||
//enable save-sequence button if disabled initially
|
||||
handleSaveSequence();
|
||||
|
||||
@@ -31,6 +31,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
'\
|
||||
<div class="container">\
|
||||
<div class="row step">\
|
||||
<form class="input-form">\
|
||||
<div class="col-md-4 details">\
|
||||
<h3>' +
|
||||
step.name +
|
||||
@@ -39,6 +40,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
(step.description || "") +
|
||||
'</i></p>\
|
||||
</div>\
|
||||
</form>\
|
||||
<div class="col-md-8">\
|
||||
<div class="load" style="display:none;"><i class="fa fa-circle-o-notch fa-spin"></i></div>\
|
||||
<a><img alt="" style="max-width=100%" class="img-thumbnail step-thumbnail"/></a>\
|
||||
@@ -113,7 +115,6 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
var description = inputs[paramName].desc || paramName;
|
||||
div.innerHTML =
|
||||
"<div class='det'>\
|
||||
<form class='input-form'>\
|
||||
<label for='" +
|
||||
paramName +
|
||||
"'>" +
|
||||
@@ -122,44 +123,15 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
" +
|
||||
html +
|
||||
"\
|
||||
</form>\
|
||||
</div>";
|
||||
step.ui.querySelector("div.details").appendChild(div);
|
||||
}
|
||||
|
||||
function toggleSaveButton() {
|
||||
$(step.ui.querySelector("div.details .btn-save")).prop("disabled", false);
|
||||
focusInput();
|
||||
}
|
||||
|
||||
$(step.ui.querySelectorAll(".target")).on('change', toggleSaveButton);
|
||||
|
||||
$(step.ui.querySelector("div.details")).append(
|
||||
"<p><button class='btn btn-default btn-save' disabled = 'true' >Apply</button><span> Press apply to see changes</span></p>"
|
||||
"<p><button type='submit' class='btn btn-default btn-save' disabled = 'true' >Apply</button><span> Press apply to see changes</span></p>"
|
||||
);
|
||||
|
||||
function focusInput() {
|
||||
$(step.ui.querySelector("div.details .target")).focus();
|
||||
}
|
||||
|
||||
function saveOptions(e) {
|
||||
e.preventDefault();
|
||||
$(step.ui.querySelector("div.details"))
|
||||
.find("input,select")
|
||||
.each(function(i, input) {
|
||||
step.options[$(input).attr("name")] = input.value;
|
||||
});
|
||||
_sequencer.run({ index: step.index - 1 });
|
||||
|
||||
// modify the url hash
|
||||
setUrlHashParameter("steps", _sequencer.toString());
|
||||
// disable the save button
|
||||
$(step.ui.querySelector("div.details .btn-save")).prop("disabled", true);
|
||||
}
|
||||
|
||||
// on clicking Save in the details pane of the step
|
||||
$(step.ui.querySelector("div.details .btn-save")).click(saveOptions);
|
||||
$(step.ui.querySelector("div.details .input-form")).on('submit', saveOptions);
|
||||
|
||||
}
|
||||
|
||||
if (step.name != "load-image") {
|
||||
@@ -174,7 +146,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
if (stepOptions.index == _sequencer.images.image1.steps.length) {
|
||||
stepsEl.appendChild(step.ui);
|
||||
$("#steps .container:nth-last-child(1) .insert-step").prop('disabled',true);
|
||||
if($("#steps .container:nth-last-child(2)"))
|
||||
if($("#steps .container:nth-last-child(2)"))
|
||||
$("#steps .container:nth-last-child(2) .insert-step").prop('disabled',false);
|
||||
} else {
|
||||
stepsEl.insertBefore(step.ui, $(stepsEl).children()[stepOptions.index]);
|
||||
@@ -183,13 +155,69 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
else {
|
||||
$("#load-image").append(step.ui);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function saveOptions(e) {
|
||||
e.preventDefault();
|
||||
if (optionsChanged){
|
||||
$(step.ui.querySelector("div.details"))
|
||||
.find("input,select")
|
||||
.each(function(i, input) {
|
||||
$(input)
|
||||
.data('initValue', $(input).val())
|
||||
.data('hasChangedBefore', false);
|
||||
step.options[$(input).attr("name")] = $(input).val();
|
||||
});
|
||||
_sequencer.run({ index: step.index - 1 });
|
||||
|
||||
// modify the url hash
|
||||
setUrlHashParameter("steps", _sequencer.toString());
|
||||
|
||||
// disable the save button
|
||||
$(step.ui.querySelector('.btn-save')).prop('disabled', true);
|
||||
optionsChanged = false;
|
||||
changedInputs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputValueChange(currentValue, initValue, hasChangedBefore) {
|
||||
var inputChanged = !(isNaN(initValue) || isNaN(currentValue) ? currentValue === initValue : currentValue - initValue === 0);
|
||||
changedInputs += hasChangedBefore ? inputChanged ? 0 : -1 : inputChanged ? 1 : 0;
|
||||
optionsChanged = changedInputs > 0;
|
||||
|
||||
$(step.ui.querySelector('.btn-save')).prop('disabled', !optionsChanged);
|
||||
return inputChanged;
|
||||
}
|
||||
|
||||
var
|
||||
changedInputs = 0,
|
||||
optionsChanged = false;
|
||||
$(step.ui.querySelector('.input-form')).on('submit', saveOptions);
|
||||
$(step.ui.querySelectorAll('.target')).each(function(i, input) {
|
||||
$(input)
|
||||
.data('initValue', $(input).val())
|
||||
.data('hasChangedBefore', false)
|
||||
.on('input', function() {
|
||||
$(this)
|
||||
.focus()
|
||||
.data('hasChangedBefore',
|
||||
handleInputValueChange(
|
||||
$(this).val(),
|
||||
$(this).data('initValue'),
|
||||
$(this).data('hasChangedBefore')
|
||||
)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
|
||||
$('input[type="range"]').on('input', function() {
|
||||
$(this).next().html($(this).val());
|
||||
})
|
||||
}
|
||||
|
||||
var inputs = document.querySelectorAll('input[type="range"]')
|
||||
for (i in inputs)
|
||||
inputs[i].oninput = function(e) {
|
||||
e.target.nextSibling.innerHTML = e.target.value;
|
||||
}
|
||||
|
||||
function onDraw(step) {
|
||||
$(step.ui.querySelector(".load")).show();
|
||||
@@ -224,17 +252,19 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
for (var i in inputs) {
|
||||
if (step.options[i] !== undefined) {
|
||||
if (inputs[i].type.toLowerCase() === "input")
|
||||
step.ui.querySelector('div[name="' + i + '"] input').value =
|
||||
step.options[i];
|
||||
$(step.ui.querySelector('div[name="' + i + '"] input'))
|
||||
.val(step.options[i])
|
||||
.data('initValue', step.options[i]);
|
||||
if (inputs[i].type.toLowerCase() === "select")
|
||||
step.ui.querySelector('div[name="' + i + '"] select').value =
|
||||
step.options[i];
|
||||
$(step.ui.querySelector('div[name="' + i + '"] select'))
|
||||
.val(step.options[i])
|
||||
.data('initValue', step.options[i]);
|
||||
}
|
||||
}
|
||||
for (var i in outputs) {
|
||||
if (step[i] !== undefined)
|
||||
step.ui.querySelector('div[name="' + i + '"] input').value =
|
||||
step[i];
|
||||
$(step.ui.querySelector('div[name="' + i + '"] input'))
|
||||
.val(step[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,6 +272,7 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
function onRemove(step) {
|
||||
step.ui.remove();
|
||||
$("#steps .container:nth-last-child(1) .insert-step").prop('disabled',true);
|
||||
$('div[class^=imgareaselect-]').remove();
|
||||
}
|
||||
|
||||
function getPreview() {
|
||||
@@ -255,4 +286,4 @@ function DefaultHtmlStepUi(_sequencer, options) {
|
||||
onRemove: onRemove,
|
||||
onDraw: onDraw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
examples/lib/sw.js
Normal file
35
examples/lib/sw.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const staticCacheName = 'image-sequencer-static-v3';
|
||||
|
||||
self.addEventListener('install', event => {
|
||||
console.log('Attempting to install service worker');
|
||||
});
|
||||
|
||||
self.addEventListener('activate', function(e) {
|
||||
console.log('[ServiceWorker] Activate');
|
||||
e.waitUntil(
|
||||
caches.keys().then(function(cacheNames) {
|
||||
return Promise.all(
|
||||
cacheNames.filter(function(cacheName){
|
||||
return cacheName.startsWith('image-sequencer-') &&
|
||||
cacheName != staticCacheName;
|
||||
}).map(function(cacheName){
|
||||
return caches.delete(cacheName);
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', function(event) {
|
||||
event.respondWith(
|
||||
caches.open(staticCacheName).then(function(cache) {
|
||||
return cache.match(event.request).then(function (response) {
|
||||
return response || fetch(event.request).then(function(response) {
|
||||
if(event.request.method == "GET")
|
||||
cache.put(event.request, response.clone());
|
||||
return response;
|
||||
});
|
||||
});
|
||||
})
|
||||
);
|
||||
});
|
||||
3027
package-lock.json
generated
3027
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "image-sequencer",
|
||||
"version": "2.2.3",
|
||||
"version": "2.3.1",
|
||||
"description": "A modular JavaScript image manipulation library modeled on a storyboard.",
|
||||
"main": "src/ImageSequencer.js",
|
||||
"scripts": {
|
||||
@@ -35,7 +35,7 @@
|
||||
"imagejs": "0.0.9",
|
||||
"imgareaselect": "git://github.com/jywarren/imgareaselect.git#v1.0.0-rc.2",
|
||||
"jquery": "^3.3.1",
|
||||
"jsqr": "^0.2.2",
|
||||
"jsqr": "^1.1.1",
|
||||
"lodash": "^4.17.5",
|
||||
"ndarray-gaussian-filter": "^1.0.0",
|
||||
"ora": "^3.0.0",
|
||||
@@ -54,10 +54,10 @@
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"image-filter-core": "~2.0.2",
|
||||
"image-filter-threshold": "~2.0.1",
|
||||
"looks-same": "^4.1.0",
|
||||
"looks-same": "^5.0.1",
|
||||
"matchdep": "^2.0.0",
|
||||
"tap-spec": "^5.0.0",
|
||||
"tape": ">=4.7.0",
|
||||
"tape": "^4.9.2",
|
||||
"tape-run": "^5.0.0",
|
||||
"uglify-es": "^3.3.7"
|
||||
},
|
||||
|
||||
@@ -455,7 +455,7 @@ ImageSequencer = function ImageSequencer(options) {
|
||||
createMetaModule: createMetaModule,
|
||||
saveSequence: saveSequence,
|
||||
loadModules: loadModules,
|
||||
|
||||
|
||||
//other functions
|
||||
log: log,
|
||||
objTypeOf: objTypeOf,
|
||||
|
||||
@@ -13,6 +13,8 @@ module.exports = {
|
||||
'convolution': require('./modules/Convolution'),
|
||||
'crop': require('./modules/Crop'),
|
||||
'decode-qr': require('./modules/DecodeQr'),
|
||||
'dither': require('./modules/Dither'),
|
||||
'draw-rectangle': require('./modules/DrawRectangle'),
|
||||
'dynamic': require('./modules/Dynamic'),
|
||||
'edge-detect': require('./modules/EdgeDetect'),
|
||||
'fisheye-gl': require('./modules/FisheyeGl'),
|
||||
@@ -26,5 +28,6 @@ module.exports = {
|
||||
'overlay': require('./modules/Overlay'),
|
||||
'resize': require('./modules/Resize'),
|
||||
'rotate': require('./modules/Rotate'),
|
||||
'saturation': require('./modules/Saturation')
|
||||
'saturation': require('./modules/Saturation'),
|
||||
'white-balance': require('./modules/WhiteBalance')
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@
|
||||
|
||||
module.exports = function Brightness(options,UI){
|
||||
|
||||
|
||||
var output;
|
||||
|
||||
function draw(input,callback,progressObj){
|
||||
|
||||
options.brightness = parseInt(options.brightness) || 100;
|
||||
var val = (options.brightness)/100.0;
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
@@ -20,13 +23,10 @@ module.exports = function Brightness(options,UI){
|
||||
var step = this;
|
||||
|
||||
function changePixel(r, g, b, a){
|
||||
options.brightness =
|
||||
options.brightness || 100
|
||||
var val = (options.brightness)/100.0
|
||||
|
||||
r = val*r<255?val*r:255
|
||||
g = val*g<255?val*g:255
|
||||
b = val*b<255?val*b:255
|
||||
r = Math.min(val*r, 255)
|
||||
g = Math.min(val*g, 255)
|
||||
b = Math.min(val*b, 255)
|
||||
return [r, g, b, a]
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"brightness": {
|
||||
"type": "range",
|
||||
"desc": "% brightness for the new image",
|
||||
"default": "100",
|
||||
"default": "175",
|
||||
"min": "0",
|
||||
"max": "200",
|
||||
"step": "1"
|
||||
|
||||
@@ -53,7 +53,7 @@ function colormap(segments) {
|
||||
|
||||
var colormaps = {
|
||||
greyscale: colormap([
|
||||
[0, [0, 0, 0], [220, 20, 60] ],
|
||||
[0, [0, 0, 0], [255, 255, 255] ],
|
||||
[1, [255, 255, 255], [255, 255, 255] ]
|
||||
]),
|
||||
|
||||
@@ -177,7 +177,7 @@ var colormaps = {
|
||||
fastie: colormap([
|
||||
[0, [255, 255, 255], [0, 0, 0] ],
|
||||
[0.167, [0, 0, 0], [255, 255, 255] ],
|
||||
[0.33, [2, 0, 226], [2, 0, 226] ],
|
||||
[0.33, [255, 255, 255], [0, 0, 0] ],
|
||||
[0.5, [0, 0, 0], [140, 140, 255] ],
|
||||
[0.55, [140, 140, 255], [0, 255, 0] ],
|
||||
[0.63, [0, 255, 0], [255, 255, 0] ],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"type": "select",
|
||||
"desc": "Name of the Colormap",
|
||||
"default": "default",
|
||||
"values": ["default","greyscale","stretched","fastie","brntogrn","blutoredjet","colors16"]
|
||||
"values": ["default","greyscale","bluwhtgrngis","stretched","fastie","brntogrn","blutoredjet","colors16"]
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
"description": "Change the contrast of the image by given value",
|
||||
"inputs": {
|
||||
"contrast": {
|
||||
"type": "Number",
|
||||
"type": "range",
|
||||
"desc": "contrast for the new image, typically -100 to 100",
|
||||
"default": 70
|
||||
"default": "70",
|
||||
"min": "-100",
|
||||
"max": "100",
|
||||
"step": "1"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
|
||||
|
||||
@@ -9,14 +9,25 @@ module.exports = function Crop(input,options,callback) {
|
||||
getPixels(input.src,function(err,pixels){
|
||||
options.w = parseInt(options.w) || Math.floor(pixels.shape[0]);
|
||||
options.h = parseInt(options.h) || Math.floor(pixels.shape[1]);
|
||||
options.backgroundColor = options.backgroundColor || '255 255 255 255';
|
||||
var ox = options.x;
|
||||
var oy = options.y;
|
||||
var w = options.w;
|
||||
var h = options.h;
|
||||
var iw = pixels.shape[0]; //Width of Original Image
|
||||
var ih = pixels.shape[1]; //Height of Original Image
|
||||
var backgroundArray = [];
|
||||
backgroundColor = options.backgroundColor.split(" ");
|
||||
for(var i = 0; i < w ; i++){
|
||||
backgroundArray = backgroundArray.concat([backgroundColor[0],backgroundColor[1],backgroundColor[2],backgroundColor[3]]);
|
||||
}
|
||||
var newarray = new Uint8Array(4*w*h);
|
||||
for (var n = oy; n < oy + h; n++) {
|
||||
if(n<ih){
|
||||
newarray.set(pixels.data.slice(n*4*iw + ox, n*4*iw + ox + 4*w),4*w*(n-oy));
|
||||
} else {
|
||||
newarray.set(backgroundArray,4*w*(n-oy));
|
||||
}
|
||||
}
|
||||
pixels.data = newarray;
|
||||
pixels.shape = [w,h,4];
|
||||
|
||||
@@ -19,7 +19,7 @@ module.exports = function CropModule(options, UI) {
|
||||
// add our custom in-module html ui:
|
||||
if (options.step.inBrowser && !options.noUI) var ui = require('./Ui.js')(options.step, UI);
|
||||
var output,
|
||||
setupComplete = false;
|
||||
setupComplete = false;
|
||||
|
||||
// This function is caled everytime the step has to be redrawn
|
||||
function draw(input,callback) {
|
||||
@@ -29,8 +29,23 @@ module.exports = function CropModule(options, UI) {
|
||||
// save the input image;
|
||||
// TODO: this should be moved to module API to persist the input image
|
||||
options.step.input = input.src;
|
||||
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||
|
||||
require('./Crop')(input, options, function(out, format){
|
||||
//parse the inputs
|
||||
parseCornerCoordinateInputs(options,{
|
||||
src: input.src,
|
||||
x: { valInp: options.x, type: 'horizontal' },
|
||||
y: { valInp: options.y, type: 'vertical' },
|
||||
w: { valInp: options.w, type: 'horizontal' },
|
||||
h: { valInp: options.h, type: 'vertical' },
|
||||
}, function (options, coord) {
|
||||
options.x = parseInt(coord.x.valInp);
|
||||
options.y = parseInt(coord.y.valInp);
|
||||
options.w = coord.w.valInp;
|
||||
options.h = coord.h.valInp;
|
||||
});
|
||||
|
||||
require('./Crop')(input, options, function (out, format) {
|
||||
|
||||
// This output is accessible to Image Sequencer
|
||||
step.output = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Crop",
|
||||
"description": "Crop image to given x, y, w, h in pixels, measured from top left",
|
||||
"description": "Crop image to given x, y, w, h in pixels or % , measured from top left",
|
||||
"url": "https://github.com/publiclab/image-sequencer/tree/master/MODULES.md",
|
||||
"inputs": {
|
||||
"x": {
|
||||
@@ -16,13 +16,19 @@
|
||||
"w": {
|
||||
"type": "integer",
|
||||
"desc": "Width of crop",
|
||||
"default": "(100%)"
|
||||
"default": "(50%)"
|
||||
},
|
||||
"h": {
|
||||
"type": "integer",
|
||||
"desc": "Height of crop",
|
||||
"default": "(100%)"
|
||||
"default": "(50%)"
|
||||
},
|
||||
"backgroundColor": {
|
||||
"type": "String",
|
||||
"desc": "Background Color (Four space separated RGBA values)",
|
||||
"default": "255 255 255 255",
|
||||
"placeholder": "255 255 255 255"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
|
||||
}
|
||||
}
|
||||
|
||||
70
src/modules/Dither/Dither.js
Normal file
70
src/modules/Dither/Dither.js
Normal file
@@ -0,0 +1,70 @@
|
||||
module.exports = function Dither(pixels, type) {
|
||||
type = type || "none";
|
||||
var bayerThresholdMap = [
|
||||
[ 15, 135, 45, 165 ],
|
||||
[ 195, 75, 225, 105 ],
|
||||
[ 60, 180, 30, 150 ],
|
||||
[ 240, 120, 210, 90 ]
|
||||
];
|
||||
|
||||
var lumR = [];
|
||||
var lumG = [];
|
||||
var lumB = [];
|
||||
for (var i=0; i<256; i++) {
|
||||
lumR[i] = i*0.299;
|
||||
lumG[i] = i*0.587;
|
||||
lumB[i] = i*0.114;
|
||||
}
|
||||
var threshold = 129;
|
||||
var imageDataLength = pixels.data.length; //imageData.data.length;
|
||||
|
||||
// Greyscale luminance (sets r pixels to luminance of rgb)
|
||||
for (var i = 0; i <= imageDataLength; i += 4) {
|
||||
pixels.data[i] = Math.floor(lumR[pixels.data[i]] + lumG[pixels.data[i+1]] + lumB[pixels.data[i+2]]);
|
||||
}
|
||||
|
||||
var w = pixels.shape[0];
|
||||
var newPixel, err;
|
||||
|
||||
for (var currentPixel = 0; currentPixel <= imageDataLength; currentPixel+=4) {
|
||||
|
||||
if (type === "none") {
|
||||
// No dithering
|
||||
pixels.data[currentPixel] = pixels.data[currentPixel] < threshold ? 0 : 255;
|
||||
} else if (type === "bayer") {
|
||||
// 4x4 Bayer ordered dithering algorithm
|
||||
var x = currentPixel/4 % w;
|
||||
var y = Math.floor(currentPixel/4 / w);
|
||||
var map = Math.floor( (pixels.data[currentPixel] + bayerThresholdMap[x%4][y%4]) / 2 );
|
||||
pixels.data[currentPixel] = (map < threshold) ? 0 : 255;
|
||||
} else if (type === "floydsteinberg") {
|
||||
// Floyd–Steinberg dithering algorithm
|
||||
newPixel = pixels.data[currentPixel] < 129 ? 0 : 255;
|
||||
err = Math.floor((pixels.data[currentPixel] - newPixel) / 16);
|
||||
pixels.data[currentPixel] = newPixel;
|
||||
|
||||
pixels.data[currentPixel + 4 ] += err*7;
|
||||
pixels.data[currentPixel + 4*w - 4 ] += err*3;
|
||||
pixels.data[currentPixel + 4*w ] += err*5;
|
||||
pixels.data[currentPixel + 4*w + 4 ] += err*1;
|
||||
} else {
|
||||
// Bill Atkinson's dithering algorithm
|
||||
newPixel = pixels.data[currentPixel] < threshold ? 0 : 255;
|
||||
err = Math.floor((pixels.data[currentPixel] - newPixel) / 8);
|
||||
pixels.data[currentPixel] = newPixel;
|
||||
|
||||
pixels.data[currentPixel + 4 ] += err;
|
||||
pixels.data[currentPixel + 8 ] += err;
|
||||
pixels.data[currentPixel + 4*w - 4 ] += err;
|
||||
pixels.data[currentPixel + 4*w ] += err;
|
||||
pixels.data[currentPixel + 4*w + 4 ] += err;
|
||||
pixels.data[currentPixel + 8*w ] += err;
|
||||
}
|
||||
|
||||
// Set g and b pixels equal to r
|
||||
pixels.data[currentPixel + 1] = pixels.data[currentPixel + 2] = pixels.data[currentPixel];
|
||||
}
|
||||
return pixels;
|
||||
|
||||
}
|
||||
|
||||
37
src/modules/Dither/Module.js
Normal file
37
src/modules/Dither/Module.js
Normal file
@@ -0,0 +1,37 @@
|
||||
module.exports = function Dither(options, UI){
|
||||
|
||||
var output;
|
||||
|
||||
function draw(input,callback,progressObj){
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
var step = this;
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
pixels = require('./Dither')(pixels, options.dither)
|
||||
return pixels
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype){
|
||||
// This output is accessible by Image Sequencer
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
callback: callback
|
||||
});
|
||||
}
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
}
|
||||
4
src/modules/Dither/index.js
Normal file
4
src/modules/Dither/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
]
|
||||
13
src/modules/Dither/info.json
Normal file
13
src/modules/Dither/info.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "Dither",
|
||||
"description": "Approximates a color from a mixture of other colors when the required color is not available, creating illusions of the color that is not present actually.<a href='https://en.wikipedia.org/wiki/Dither'>Read more</a>",
|
||||
"inputs": {
|
||||
"dither": {
|
||||
"type": "select",
|
||||
"desc": "Name of the Dithering Algorithm",
|
||||
"default": "none",
|
||||
"values": ["none","floydsteinberg","bayer","Atkinson"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
48
src/modules/DrawRectangle/DrawRectangle.js
Normal file
48
src/modules/DrawRectangle/DrawRectangle.js
Normal file
@@ -0,0 +1,48 @@
|
||||
module.exports = exports = function(pixels, options){
|
||||
options.startingX = options.startingX || 0;
|
||||
options.startingY = options.startingY || 0;
|
||||
var ox = Number(options.startingX);
|
||||
var oy = Number(options.startingY);
|
||||
var iw = pixels.shape[0];
|
||||
var ih = pixels.shape[1];
|
||||
options.endX = Number(options.endX) || iw - 1;
|
||||
options.endY = Number(options.endY) || ih - 1;
|
||||
var ex = options.endX;
|
||||
var ey = options.endY;
|
||||
var thickness = Number(options.thickness) || 1;
|
||||
var color = options.color || "0 0 0 255";
|
||||
color = color.split(" ");
|
||||
for(var i = 0; i<thickness; i++){
|
||||
for(var n = (oy+i)*4*iw + 4*ox ; n < (oy+i)*4*iw + 4*(ex); n = n+4){
|
||||
pixels.data[n] = color[0];
|
||||
pixels.data[n+1] = color[1];
|
||||
pixels.data[n+2] = color[2];
|
||||
pixels.data[n+3] = color[3];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i<thickness; i++){
|
||||
for(var n = (ey-i)*4*iw + 4*ox ; n < (ey-i)*4*iw + 4*(ex); n = n+4){
|
||||
pixels.data[n] = color[0];
|
||||
pixels.data[n+1] = color[1];
|
||||
pixels.data[n+2] = color[2];
|
||||
pixels.data[n+3] = color[3];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < thickness; i++){
|
||||
for(var n = oy*4*iw + 4*(ox+i) ; n < ey*4*iw + 4*(ox+i); n = n+ 4*iw){
|
||||
pixels.data[n] = color[0];
|
||||
pixels.data[n+1] = color[1];
|
||||
pixels.data[n+2] = color[2];
|
||||
pixels.data[n+3] = color[3];
|
||||
}
|
||||
}
|
||||
for(var i = 0; i < thickness; i++){
|
||||
for(var n = oy*4*iw + 4*(ex - i) ; n < ey*4*iw + 4*(ex - i); n = n+ 4*iw){
|
||||
pixels.data[n] = color[0];
|
||||
pixels.data[n+1] = color[1];
|
||||
pixels.data[n+2] = color[2];
|
||||
pixels.data[n+3] = color[3];
|
||||
}
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
44
src/modules/DrawRectangle/Module.js
Normal file
44
src/modules/DrawRectangle/Module.js
Normal file
@@ -0,0 +1,44 @@
|
||||
module.exports = function DrawRectangle(options, UI) {
|
||||
|
||||
|
||||
var output;
|
||||
|
||||
function draw(input, callback, progressObj) {
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
var step = this;
|
||||
|
||||
function changePixel(r, g, b, a) {
|
||||
return [r, g, b, a]
|
||||
}
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
pixels = require('./DrawRectangle')(pixels, options)
|
||||
return pixels
|
||||
}
|
||||
|
||||
function output(image, datauri, mimetype) {
|
||||
|
||||
step.output = { src: datauri, format: mimetype };
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
callback: callback
|
||||
});
|
||||
|
||||
}
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
}
|
||||
4
src/modules/DrawRectangle/index.js
Normal file
4
src/modules/DrawRectangle/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
]
|
||||
41
src/modules/DrawRectangle/info.json
Normal file
41
src/modules/DrawRectangle/info.json
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "Draw Rectangle",
|
||||
"description": "It draws a rectangle on the image",
|
||||
"inputs": {
|
||||
"startingX":{
|
||||
"type": "Number",
|
||||
"desc": "starting x position of the rectangle",
|
||||
"default": 0
|
||||
},
|
||||
|
||||
"startingY": {
|
||||
"type": "Number",
|
||||
"desc": "starting y position of the rectangle",
|
||||
"default": 0
|
||||
},
|
||||
|
||||
"endX":{
|
||||
"type": "integer",
|
||||
"desc": "last x position of the rectangle",
|
||||
"default": "width"
|
||||
},
|
||||
|
||||
"endY":{
|
||||
"type": "integer",
|
||||
"desc": "last y position of the rectangle",
|
||||
"default": "height"
|
||||
},
|
||||
|
||||
"thickness":{
|
||||
"type": "integer",
|
||||
"desc": "thickness of border",
|
||||
"default": 1
|
||||
},
|
||||
|
||||
"color":{
|
||||
"type": "String",
|
||||
"desc": "RGBA values separated by a space",
|
||||
"default": "0 0 0 255"
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
{
|
||||
"name": "Detect Edges",
|
||||
"description": "this module detects edges using the Canny method, which first Gaussian blurs the image to reduce noise (amount of blur configurable in settings as `options.blur`), then applies a number of steps to highlight edges, resulting in a greyscale image where the brighter the pixel, the stronger the detected edge.<a href='https://en.wikipedia.org/wiki/Canny_edge_detector'> Read more. </a>",
|
||||
"description": "This module detects edges using the Canny method, which first Gaussian blurs the image to reduce noise (amount of blur configurable in settings as `options.blur`), then applies a number of steps to highlight edges, resulting in a greyscale image where the brighter the pixel, the stronger the detected edge.<a href='https://en.wikipedia.org/wiki/Canny_edge_detector'> Read more. </a>",
|
||||
"inputs": {
|
||||
"blur": {
|
||||
"type": "integer",
|
||||
"desc": "amount of gaussian blur(Less blur gives more detail, typically 0-5)",
|
||||
"default": 2
|
||||
"type": "range",
|
||||
"desc": "Amount of gaussian blur(Less blur gives more detail, typically 0-5)",
|
||||
"default": "2",
|
||||
"min": "0",
|
||||
"max": "5",
|
||||
"step": "0.25"
|
||||
},
|
||||
"highThresholdRatio":{
|
||||
"type": "float",
|
||||
|
||||
@@ -15,13 +15,25 @@ module.exports = function Dynamic(options, UI, util) {
|
||||
|
||||
var step = this;
|
||||
|
||||
var parseCornerCoordinateInputs = require('../../util/ParseInputCoordinates');
|
||||
|
||||
//parse the inputs
|
||||
parseCornerCoordinateInputs(options, {
|
||||
src: input.src,
|
||||
x: { valInp: options.x, type: 'horizontal' },
|
||||
y: { valInp: options.y, type: 'vertical' },
|
||||
}, function (options, input) {
|
||||
options.x = parseInt(input.x.valInp);
|
||||
options.y = parseInt(input.y.valInp);
|
||||
});
|
||||
|
||||
// save the pixels of the base image
|
||||
var baseStepImage = this.getStep(options.offset).image;
|
||||
var baseStepOutput = this.getOutput(options.offset);
|
||||
|
||||
var getPixels = require('get-pixels');
|
||||
|
||||
getPixels(input.src, function(err, pixels) {
|
||||
getPixels(input.src, function (err, pixels) {
|
||||
options.secondImagePixels = pixels;
|
||||
|
||||
function changePixel(r1, g1, b1, a1, x, y) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Overlay",
|
||||
"description": "Overlays an Image over another at a given position(x,y)",
|
||||
"description": "Overlays an Image over another at a given position(x,y) in pixels or in %",
|
||||
"inputs": {
|
||||
"x": {
|
||||
"type": "integer",
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
"description": "Rotates image by specified degrees",
|
||||
"inputs": {
|
||||
"rotate": {
|
||||
"type": "integer",
|
||||
"type": "range",
|
||||
"desc": "Angular value for rotation in degrees",
|
||||
"default": 0
|
||||
"default": "0",
|
||||
"min": "0",
|
||||
"max": "360",
|
||||
"step": "1"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
"description": "Change the saturation of the image by given value, from 0-1, with 1 being 100% saturated.",
|
||||
"inputs": {
|
||||
"saturation": {
|
||||
"type": "integer",
|
||||
"type": "range",
|
||||
"desc": "saturation for the new image between 0 and 2, 0 being black and white and 2 being highly saturated",
|
||||
"default": 0
|
||||
"default": "0.5",
|
||||
"min": "0",
|
||||
"max": "2",
|
||||
"step": "0.1"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
|
||||
|
||||
86
src/modules/WhiteBalance/Module.js
Normal file
86
src/modules/WhiteBalance/Module.js
Normal file
@@ -0,0 +1,86 @@
|
||||
module.exports = function Balance(options, UI) {
|
||||
|
||||
var output;
|
||||
|
||||
function draw (input, callback, progressObj) {
|
||||
|
||||
options.temperature = (options.temperature > "40000") ? "40000" : options.temperature
|
||||
|
||||
progressObj.stop(true);
|
||||
progressObj.overrideFlag = true;
|
||||
|
||||
var step = this;
|
||||
|
||||
function changePixel(r, g, b, a) {
|
||||
return [r, g, b ,a]
|
||||
}
|
||||
|
||||
function extraManipulation(pixels) {
|
||||
|
||||
let temp = parseInt(options.temperature)
|
||||
temp /= 100
|
||||
|
||||
let r, g, b;
|
||||
|
||||
if (temp <= 66) {
|
||||
r = 255;
|
||||
g = Math.min(Math.max(99.4708025861 * Math.log(temp) - 161.1195681661, 0), 255);
|
||||
} else {
|
||||
r = Math.min(Math.max(329.698727446 * Math.pow(temp - 60, -0.1332047592), 0), 255);
|
||||
g = Math.min(Math.max(288.1221695283 * Math.pow(temp - 60, -0.0755148492), 0), 255);
|
||||
}
|
||||
|
||||
if (temp >= 66) {
|
||||
b = 255;
|
||||
} else if (temp <= 19) {
|
||||
b = 0;
|
||||
} else {
|
||||
b = temp - 10;
|
||||
b = Math.min(Math.max(138.5177312231 * Math.log(b) - 305.0447927307, 0), 255);
|
||||
}
|
||||
|
||||
for(let i=0; i<pixels.shape[0]; i++) {
|
||||
for (let j=0; j<pixels.shape[1]; j++) {
|
||||
|
||||
r_data = pixels.get(i,j,0)
|
||||
r_new_data = (255/r) * r_data
|
||||
pixels.set(i,j,0,r_new_data)
|
||||
|
||||
g_data = pixels.get(i,j,1)
|
||||
g_new_data = (255/g) * g_data
|
||||
pixels.set(i,j,1,g_new_data)
|
||||
|
||||
b_data = pixels.get(i,j,2)
|
||||
b_new_data = (255/b) * b_data
|
||||
pixels.set(i,j,2,b_new_data)
|
||||
}
|
||||
}
|
||||
|
||||
return pixels
|
||||
}
|
||||
|
||||
function output (image, datauri, mimetype){
|
||||
|
||||
step.output = {src:datauri,format:mimetype};
|
||||
|
||||
}
|
||||
|
||||
return require('../_nomodule/PixelManipulation.js')(input, {
|
||||
output: output,
|
||||
changePixel: changePixel,
|
||||
extraManipulation: extraManipulation,
|
||||
format: input.format,
|
||||
image: options.image,
|
||||
inBrowser: options.inBrowser,
|
||||
callback: callback
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
options: options,
|
||||
draw: draw,
|
||||
output: output,
|
||||
UI: UI
|
||||
}
|
||||
|
||||
}
|
||||
4
src/modules/WhiteBalance/index.js
Normal file
4
src/modules/WhiteBalance/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = [
|
||||
require('./Module'),
|
||||
require('./info.json')
|
||||
]
|
||||
12
src/modules/WhiteBalance/info.json
Normal file
12
src/modules/WhiteBalance/info.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "White Balance",
|
||||
"description": "Change the colour balance of the image by adjusting the colour temperature.",
|
||||
"inputs": {
|
||||
"temperature": {
|
||||
"type": "string",
|
||||
"desc": "Temperature between 0 - 40,000 Kelvin",
|
||||
"default": "6000"
|
||||
}
|
||||
},
|
||||
"docs-link":"https://github.com/publiclab/image-sequencer/blob/main/docs/MODULES.md"
|
||||
}
|
||||
24
src/util/ParseInputCoordinates.js
Normal file
24
src/util/ParseInputCoordinates.js
Normal file
@@ -0,0 +1,24 @@
|
||||
module.exports = function parseCornerCoordinateInputs(options,coord,callback) {
|
||||
var getPixels = require('get-pixels');
|
||||
getPixels(coord.src, function(err, pixels) {
|
||||
var iw = pixels.shape[0],
|
||||
ih = pixels.shape[1];
|
||||
if (!coord.x.valInp) {
|
||||
return
|
||||
}
|
||||
else {
|
||||
Object.keys(coord).forEach(convert);
|
||||
function convert(key) {
|
||||
var val = coord[key];
|
||||
if (val.valInp && val.valInp.slice(-1) === "%") {
|
||||
val.valInp = parseInt(val.valInp, 10);
|
||||
if (val.type === 'horizontal')
|
||||
val.valInp = val.valInp * iw / 100;
|
||||
else
|
||||
val.valInp = val.valInp * ih / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
callback(options, coord);
|
||||
})
|
||||
}
|
||||
17
test/util/parse-input.js
Normal file
17
test/util/parse-input.js
Normal file
@@ -0,0 +1,17 @@
|
||||
var test = require('tape');
|
||||
|
||||
var red = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAQABADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAf/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAABgj/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABykX//Z";
|
||||
|
||||
var parseCornerCoordinateInputs = require('../../src/util/ParseInputCoordinates');
|
||||
|
||||
|
||||
test('parseCornerCoordinateInputs works.', function (t) {
|
||||
var options = { x: '10%' },
|
||||
coord = { src: red, x: { valInp: options.x, type: 'horizontal' } }
|
||||
callback = function (options, coord) {
|
||||
options.x = parseInt(coord.x.valInp);
|
||||
t.equal(options.x, 1, 'parseCornerCoordinateInputs works.');
|
||||
t.end();
|
||||
};
|
||||
parseCornerCoordinateInputs(options, coord, callback);
|
||||
});
|
||||
22
yarn.lock
22
yarn.lock
@@ -3185,10 +3185,10 @@ jsprim@^1.2.2:
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
jsqr@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-0.2.2.tgz#8c1f0279fb7c94542aaa9e8aeb7c722d523ea092"
|
||||
integrity sha1-jB8Ceft8lFQqqp6K63xyLVI+oJI=
|
||||
jsqr@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.1.1.tgz#a0d7f95e6c3b0bec913dfef2ca64a877f28ed05f"
|
||||
integrity sha512-FVoMU2ncTyjaOqN/vwvDnZ7jaAVvFzM3LK3vG3jvQZFWJQlAwJ1XTCOgAEKo+4Rkd6ydMXTTvqGV/4w5VunmTw==
|
||||
|
||||
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
|
||||
version "3.2.2"
|
||||
@@ -3313,10 +3313,10 @@ longest@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||
integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=
|
||||
|
||||
looks-same@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/looks-same/-/looks-same-4.1.0.tgz#fa9593350dcddc79999fe130689860f9fb6afff2"
|
||||
integrity sha512-9dvYQrWpMhQzyyR25xRtFMq6TSXhduTcKKvYtlMf9IHzb+r/fvF+MQG6+hChC4MrLVlA+MRTOt6fhvUkbZ9IpA==
|
||||
looks-same@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/looks-same/-/looks-same-5.0.1.tgz#60c283a65d24240c407d0fa737db51b1f2981a07"
|
||||
integrity sha512-BkQ0wWh+tRehLdP2DiQ4xLwjGd/OCnX0D5SZg7y7ViFnQOR2EOxGkcRS63Vw7RZdczGdpPYR/8RFRhEOGVNN5Q==
|
||||
dependencies:
|
||||
color-diff "^1.1.0"
|
||||
concat-stream "^1.6.2"
|
||||
@@ -5345,9 +5345,9 @@ tape-run@^5.0.0:
|
||||
throughout "0.0.0"
|
||||
|
||||
tape@>=4.7.0:
|
||||
version "4.9.1"
|
||||
resolved "https://registry.yarnpkg.com/tape/-/tape-4.9.1.tgz#1173d7337e040c76fbf42ec86fcabedc9b3805c9"
|
||||
integrity sha512-6fKIXknLpoe/Jp4rzHKFPpJUHDHDqn8jus99IfPnHIjyz78HYlefTGD3b5EkbQzuLfaEvmfPK3IolLgq2xT3kw==
|
||||
version "4.9.2"
|
||||
resolved "https://registry.yarnpkg.com/tape/-/tape-4.9.2.tgz#f233e40f09dc7e00fcf9b26755453c3822ad28c0"
|
||||
integrity sha512-lPXKRKILZ1kZaUy5ynWKs8ATGSUO7HAFHCFnBam6FaGSqPdOwMWbxXHq4EXFLE8WRTleo/YOMXkaUTRmTB1Fiw==
|
||||
dependencies:
|
||||
deep-equal "~1.0.1"
|
||||
defined "~1.0.0"
|
||||
|
||||
Reference in New Issue
Block a user