-
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #293 from tomchm/feature-auto-take
Add new Auto-Take feature.
- Loading branch information
Showing
6 changed files
with
169 additions
and
1 deletion.
There are no files selected for viewing
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
(function() { | ||
"use strict"; | ||
// Imports | ||
const ToggleButton = require("../ui/ToggleButton"); | ||
const Features = require("./Features"); | ||
|
||
// HTML elements | ||
const preview = document.querySelector("#preview"); | ||
const hiddenPreview = document.querySelector("#hidden-preview"); | ||
const autoCaptureList = document.querySelector("#auto-capture-list"); | ||
|
||
/** | ||
* Class for automatically taking picture after detecting the connected DSLR video stream has taken a photo. | ||
* This is useful when DSLRs have their video output connected to an HDMI capture card, such as Cam Link. | ||
* While DSLRs are capturing a photo, their video stream becomes black. After the photo is taken, it is | ||
* previewed on the video output for around 1-2 seconds before switching back to the live view. This transition | ||
* can be detected, so the high-quality image preview can be captured off the video stream. | ||
*/ | ||
class AutoCapture { | ||
|
||
/** | ||
* Initializes the AutoCapture sidebar menu, and class variables. | ||
*/ | ||
static initialise() { | ||
AutoCapture.autoCaptureDaemon = null; // Timer Interval for detecting brightness change | ||
AutoCapture.enabled = true; // Is AutoCapture is enabled? | ||
AutoCapture.isDark = false; // Is the current video frame dark (brightness below threshold)? | ||
AutoCapture.brightnessThreshold = 1.2; // Brightness Threshold | ||
|
||
// Add a list item to settings dialog | ||
let autoCaptureItem = document.createElement("li"); | ||
autoCaptureList.appendChild(autoCaptureItem); | ||
|
||
// Item title | ||
let itemTitle = document.createElement("div"); | ||
let itemTitleText = document.createElement("div"); | ||
|
||
let itemToggleBtn = document.createElement("div"); // Item toggle button | ||
|
||
let itemSettingsContainer = document.createElement("div"); | ||
let brightnessThresholdInput = document.createElement("input"); // Brightness Threshold input | ||
let brightnessThresholdText = document.createElement("div"); | ||
|
||
// Set title | ||
autoCaptureItem.appendChild(itemTitle); | ||
itemTitle.classList.add("flex"); | ||
itemTitle.appendChild(itemTitleText); | ||
itemTitleText.innerText = "Auto-Capture"; | ||
|
||
// Add toggle button | ||
itemTitle.appendChild(itemToggleBtn); | ||
itemToggleBtn.setAttribute("style", "text-align: right"); | ||
itemToggleBtn.classList.add("auto-capture-toggle-btn"); | ||
new ToggleButton(itemToggleBtn, function() { | ||
if (AutoCapture.enabled) { | ||
clearInterval(AutoCapture.autoCaptureDaemon); | ||
AutoCapture.autoCaptureDaemon = null; | ||
AutoCapture.enabled = false; | ||
} else { | ||
// Start daemon to check webcam brightness 30 times per second. TODO: Make the interval time configurable | ||
AutoCapture.autoCaptureDaemon = setInterval(AutoCapture.checkBrightnessThreshold, 1000 / 30); // 30 FPS | ||
AutoCapture.enabled = true; | ||
} | ||
itemSettingsContainer.classList.toggle("hidden"); | ||
}); | ||
|
||
// Create item settings container to hold configurable inputs | ||
autoCaptureItem.appendChild(itemSettingsContainer); | ||
itemSettingsContainer.classList.add("flex"); | ||
itemSettingsContainer.setAttribute("style", "align-items: center"); | ||
|
||
// Create brightness threshold text label and input | ||
itemSettingsContainer.appendChild(brightnessThresholdText); | ||
brightnessThresholdText.classList.add("flex"); | ||
brightnessThresholdText.setAttribute("style", "flex-basis: 80%"); | ||
brightnessThresholdText.innerText = "Brightness Threshold:"; | ||
itemSettingsContainer.appendChild(brightnessThresholdInput); | ||
brightnessThresholdInput.style.margin = "0 0 0 0.5em"; | ||
brightnessThresholdInput.setAttribute("type", "number"); | ||
brightnessThresholdInput.setAttribute("min", "0"); | ||
brightnessThresholdInput.setAttribute("max", "255"); | ||
brightnessThresholdInput.setAttribute("step", "0.1"); | ||
brightnessThresholdInput.setAttribute("value", 1.2); | ||
|
||
// Listen to the input being updated | ||
brightnessThresholdInput.addEventListener("input", function(e) { | ||
let val = e.target.value; | ||
// Limit value to range [0-255] | ||
if (val > 255) { | ||
val = 255; | ||
} else if (val < 0) { | ||
val = 0; | ||
} | ||
e.target.value = val; // Set input back to corrected value. | ||
AutoCapture.brightnessThreshold = val; | ||
}); | ||
} | ||
|
||
static checkBrightnessThreshold() { | ||
let w = preview.videoWidth; | ||
let h = preview.videoHeight; | ||
|
||
if (w === 0 || h === 0) { | ||
return; // Return if video stream not setup | ||
} | ||
|
||
hiddenPreview.width = w; | ||
hiddenPreview.height = h; | ||
let context = hiddenPreview.getContext('2d'); | ||
|
||
context.drawImage(preview, 0, 0, w, h); | ||
|
||
let imgData = context.getImageData(0, 0, w, h) | ||
let data = imgData.data; | ||
let r, g, b, a, avg; | ||
let colorSum = 0; | ||
let totalPixels = 0; | ||
|
||
for (let x = 0, len = data.length; x < len; x += 4) { | ||
r = data[x]; | ||
g = data[x+1]; | ||
b = data[x+2]; | ||
a = data[x+3]; | ||
|
||
if (a > 0) { | ||
avg = (r + b + g) / 3; | ||
colorSum += avg; | ||
totalPixels += 1; | ||
} | ||
} | ||
|
||
let brightness = colorSum / totalPixels; | ||
|
||
if (brightness > AutoCapture.brightnessThreshold) { | ||
if (AutoCapture.isDark) { // Take picture if live image transitions from dark to light. | ||
Features.takePicture(); | ||
} | ||
AutoCapture.isDark = false; | ||
} else { | ||
AutoCapture.isDark = true; | ||
} | ||
} | ||
} | ||
|
||
module.exports = AutoCapture; | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters