Last active
May 29, 2024 20:38
-
-
Save AndrewHazelden/3147cdca2f35cc28c0869005136d4182 to your computer and use it in GitHub Desktop.
A Resolve Studio/Fusion Studio Lua script to apply camera sensor information to Camera3D, uCamera, CameraTracker, Renderer3D, and uRenderer nodes.
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
--[[-- | |
Camera Sensor Database v1 - 2024-05-29 05.30 PM (UTC -3) | |
Database By: Jacob Danell <[email protected]> | |
Fusion Studio GUI By: Andrew Hazelden <[email protected]> | |
Overview: | |
Apply camera sensor information to BMD Fusion Studio based Camera3D, uCamera, CameraTracker, Renderer3D, and uRenderer nodes. | |
Installation: | |
1. Copy the "Camera Sensor Database.lua" script to the "Reactor:/Deploy/Scripts/Comp/" PathMap folder. | |
2. If you are using this script offline, the "sensors.json" file is saved to the PathMap location of: | |
Reactor:/Deploy/Scripts/Support/sensors.json | |
For More Info: | |
To quickly view the data in a web browser, go to docs page: | |
https://emberlightvfx.github.io/Camera-Sensor-Database/ | |
The GitHub downloaded sensors.json file is accessed at: | |
https://raw.githubusercontent.com/EmberLightVFX/Camera-Sensor-Database/main/data/sensors.json | |
Sample record: | |
{ | |
mm = { | |
width = 23.76, | |
height = 13.37, | |
diagonal = 27.263 | |
}, | |
resolution = { | |
width = "", | |
height = "" | |
}, | |
["focal length"] = "", | |
inches = { | |
width = 0.935, | |
height = 0.526, | |
diagonal = 1.073 | |
} | |
} | |
Version History: | |
2024-05-29 - v1 Initial Release | |
By Jacob Danell & Andrew Hazelden | |
--]]-- | |
local json = require("dkjson") | |
function Download() | |
local sourceURL = [[https://raw.githubusercontent.com/EmberLightVFX/Camera-Sensor-Database/main/data/sensors.json]] | |
local destFolder = fusion:MapPath("Reactor:/Deploy/Scripts/Support/") | |
local destFile = fusion:MapPath(destFolder .. "sensors.json") | |
if not bmd.direxists(destFolder) then | |
bmd.createdir(destFolder) | |
if not bmd.direxists(destFolder) then | |
error(string.format("[Create Dir] Failed to create directory: %s", destFolder)) | |
end | |
end | |
ffi = require "ffi" | |
curl = require "lj2curl" | |
ezreq = require "lj2curl.CRLEasyRequest" | |
local req = ezreq(sourceURL) | |
local body = {} | |
req:setOption(curl.CURLOPT_SSL_VERIFYPEER, 0) | |
req:setOption(curl.CURLOPT_WRITEFUNCTION, ffi.cast("curl_write_callback", | |
function(buffer, size, nitems, userdata) | |
table.insert(body, ffi.string(buffer, size*nitems)) | |
return nitems | |
end)) | |
print("\n[Downloading JSON]\n" .. sourceURL) | |
ok, err = req:perform() | |
if ok then | |
local json_string = table.concat(body) | |
-- Write the file to disk | |
print("\n[Saving JSON]\n" .. destFile) | |
local file = io.open(destFile, "w") | |
file:write(json_string) | |
file:close(); | |
return json.decode(json_string) or {} | |
else | |
-- Fallback to a locally cached file | |
local file = io.open(destFile, "r") | |
if file then | |
print("\n[Loading Cached JSON]\n" .. destFile) | |
local json_string = file:read("*all") | |
file:close() | |
return json.decode(json_string) or {} | |
else | |
print("\n[No Cached JSON] Please download the sensors.json file.\n") | |
return {} | |
end | |
end | |
end | |
function ProgramInfo() | |
print("\n------------------------------------------") | |
print("Camera Sensor Database - v1") | |
print("A collection of camera sensor information.") | |
print("------------------------------------------\n") | |
end | |
function VendorInfo(tbl) | |
-- List the vendors and cameras | |
if tbl and type(tbl) == "table" then | |
print("\n[Vendors]") | |
for vendor,vendorTbl in pairs(tbl) do | |
print(vendor) | |
for camera,cameraTbl in pairs(vendorTbl) do | |
print("\t" , camera) | |
end | |
end | |
end | |
end | |
function GUI(tbl) | |
local ui = app.UIManager | |
local disp = bmd.UIDispatcher(ui) | |
local width,height = 880,95 | |
win = disp:AddWindow({ | |
ID = "CameraSensorDatabase", | |
TargetID = "CameraSensorDatabase", | |
WindowTitle = "Camera Sensor Database", | |
Geometry = {100, 200, width, height}, | |
ui:HGroup{ | |
ID = "root", | |
-- Add your GUI elements here: | |
ui:VGroup { | |
Weight = 1, | |
ui:HGroup { | |
Weight = 1, | |
ui:HGroup { | |
ui:Label{ | |
ID = "VendorLabel", | |
Weight = 0, | |
Text = "Vendor:", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:ComboBox{ | |
ID = "Vendor", | |
}, | |
}, | |
ui:HGroup { | |
ui:Label{ | |
ID = "CameraLabel", | |
Weight = 0, | |
Text = "Camera:", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:ComboBox{ | |
ID = "Camera", | |
}, | |
}, | |
ui:HGroup { | |
ui:Label{ | |
ID = "SensorLabel", | |
Weight = 0, | |
Text = "Sensor:", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:ComboBox{ | |
ID = "Sensor", | |
}, | |
}, | |
}, | |
ui:HGroup { | |
Weight = 0, | |
ui:Label{ | |
Weight = 0, | |
ID = "SensorDimensionsLabel", | |
Text = "Sensor Dimensions:", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:LineEdit{ | |
ID = "SensorDimensionsWidth", | |
Text = "", | |
PlaceholderText = "Width", | |
ReadOnly = true, | |
}, | |
ui:Label{ | |
Weight = 0, | |
ID = "SensorDimensionsXLabel", | |
Text = "X", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:LineEdit{ | |
ID = "SensorDimensionsHeight", | |
Text = "", | |
PlaceholderText = "Height", | |
ReadOnly = true, | |
}, | |
ui:Label{ | |
Weight = 0, | |
ID = "SensorDiagonalLabel", | |
Text = "Diagonal:", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:LineEdit{ | |
ID = "SensorDimensionsDiagonal", | |
Text = "", | |
PlaceholderText = "", | |
ReadOnly = true, | |
}, | |
ui:HGroup { | |
ui:ComboBox{ | |
Weight = 0.01, | |
ID = "Unit", | |
}, | |
}, | |
}, | |
ui:HGroup { | |
Weight = 0, | |
ui:HGroup { | |
Weight = 0.25, | |
ui:Label{ | |
ID = "FocalLengthLabel", | |
Weight = 0, | |
Text = "Focal Length:", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:LineEdit{ | |
ID = "FocalLength", | |
Text = "", | |
PlaceholderText = "", | |
ReadOnly = true, | |
}, | |
}, | |
ui:HGroup { | |
Weight = 0.75, | |
ui:Label{ | |
Weight = 0, | |
ID = "ResolutionLabel", | |
Text = "Resolution:", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:LineEdit{ | |
ID = "ResolutionWidth", | |
Text = "", | |
PlaceholderText = "Width", | |
ReadOnly = true, | |
}, | |
ui:Label{ | |
Weight = 0, | |
ID = "ResolutionXLabel", | |
Text = "X", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
ui:LineEdit{ | |
ID = "ResolutionHeight", | |
Text = "", | |
PlaceholderText = "Height", | |
ReadOnly = true, | |
}, | |
ui:Label{ | |
Weight = 0, | |
ID = "ResolutionPXLabel", | |
Text = "px", | |
Alignment = { | |
AlignHCenter = true, | |
AlignTop = true, | |
}, | |
}, | |
}, | |
}, | |
}, | |
ui:HGroup{ | |
Weight = 0, | |
ui:Button{ | |
Weight = 0, | |
ID = "ApplyButton", | |
Default = true, | |
Text = "Apply to Selection", | |
}, | |
}, | |
}, | |
}) | |
-- Add your GUI element based event functions here: | |
itm = win:GetItems() | |
-- The window was closed | |
function win.On.CameraSensorDatabase.Close(ev) | |
disp:ExitLoop() | |
end | |
vItemsTbl = {} | |
for vendor,vendorTbl in pairs(tbl) do | |
table.insert(vItemsTbl, vendor) | |
end | |
table.sort(vItemsTbl) | |
itm.Vendor:Clear() | |
itm.Vendor:AddItems(vItemsTbl) | |
itm.Unit:Clear() | |
itm.Unit:AddItem("mm") | |
itm.Unit:AddItem("inches") | |
-- The "Vendor" ComboBox value has changed | |
function win.On.Vendor.CurrentIndexChanged(ev) | |
local vendorName = itm.Vendor.CurrentText | |
local cItemsTbl = {} | |
for vendor,vendorTbl in pairs(tbl) do | |
if vendor == vendorName then | |
for camera,cameraTbl in pairs(vendorTbl) do | |
table.insert(cItemsTbl, camera) | |
end | |
end | |
end | |
table.sort(cItemsTbl) | |
itm.Camera:Clear() | |
itm.Camera:AddItems(cItemsTbl) | |
end | |
-- The "Camera" ComboBox value has changed | |
function win.On.Camera.CurrentIndexChanged(ev) | |
local vendorName = itm.Vendor.CurrentText | |
local cameraName = itm.Camera.CurrentText | |
local sensorName = itm.Sensor.CurrentText | |
sItemsTbl = {} | |
for vendor,vendorTbl in pairs(tbl) do | |
if vendor == vendorName then | |
for camera,cameraTbl in pairs(vendorTbl) do | |
if camera == cameraName then | |
sensorDimensionsTbl = cameraTbl["sensor dimensions"] or {} | |
for sensor,sensorTbl in pairs(sensorDimensionsTbl) do | |
table.insert(sItemsTbl, sensor) | |
end | |
end | |
end | |
end | |
end | |
table.sort(sItemsTbl) | |
itm.Sensor:Clear() | |
itm.Sensor:AddItems(sItemsTbl) | |
end | |
-- The "Sensor" ComboBox value has changed | |
function win.On.Sensor.CurrentIndexChanged(ev) | |
local vendorName = itm.Vendor.CurrentText | |
local cameraName = itm.Camera.CurrentText | |
local sensorName = itm.Sensor.CurrentText | |
sItemsTbl = {} | |
for vendor,vendorTbl in pairs(tbl) do | |
if vendor == vendorName then | |
for camera,cameraTbl in pairs(vendorTbl) do | |
if camera == cameraName then | |
sensorDimensionsTbl = cameraTbl["sensor dimensions"] or {} | |
for sensor,sensorTbl in pairs(sensorDimensionsTbl) do | |
if sensor == sensorName then | |
if sensorTbl["resolution"] and sensorTbl["focal length"] then | |
itm.FocalLength.Text = tostring(sensorTbl["focal length"]) | |
end | |
if sensorTbl["resolution"] and sensorTbl["resolution"]["width"] and sensorTbl["resolution"]["height"] then | |
itm.ResolutionWidth.Text = tostring(sensorTbl["resolution"]["width"]) | |
itm.ResolutionHeight.Text = tostring(sensorTbl["resolution"]["height"]) | |
end | |
if itm.Unit.CurrentText == "inches" and sensorTbl["inches"] and sensorTbl["inches"]["width"] and sensorTbl["inches"]["height"] and sensorTbl["inches"]["diagonal"] then | |
itm.SensorDimensionsWidth.Text = tostring(sensorTbl["inches"]["width"]) | |
itm.SensorDimensionsHeight.Text = tostring(sensorTbl["inches"]["height"]) | |
itm.SensorDimensionsDiagonal.Text = tostring(sensorTbl["inches"]["diagonal"]) | |
elseif itm.Unit.CurrentText == "mm" and sensorTbl["mm"] and sensorTbl["mm"]["width"] and sensorTbl["mm"]["height"] and sensorTbl["mm"]["diagonal"] then | |
itm.SensorDimensionsWidth.Text = tostring(sensorTbl["mm"]["width"]) | |
itm.SensorDimensionsHeight.Text = tostring(sensorTbl["mm"]["height"]) | |
itm.SensorDimensionsDiagonal.Text = tostring(sensorTbl["mm"]["diagonal"]) | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
-- The "Unit" ComboBox value has changed | |
function win.On.Unit.CurrentIndexChanged(ev) | |
local vendorName = itm.Vendor.CurrentText | |
local cameraName = itm.Camera.CurrentText | |
local sensorName = itm.Sensor.CurrentText | |
sItemsTbl = {} | |
for vendor,vendorTbl in pairs(tbl) do | |
if vendor == vendorName then | |
for camera,cameraTbl in pairs(vendorTbl) do | |
if camera == cameraName then | |
sensorDimensionsTbl = cameraTbl["sensor dimensions"] or {} | |
for sensor,sensorTbl in pairs(sensorDimensionsTbl) do | |
if sensor == sensorName then | |
if sensorTbl["resolution"] and sensorTbl["focal length"] then | |
itm.FocalLength.Text = tostring(sensorTbl["focal length"]) | |
end | |
if sensorTbl["resolution"] and sensorTbl["resolution"]["width"] and sensorTbl["resolution"]["height"] then | |
itm.ResolutionWidth.Text = tostring(sensorTbl["resolution"]["width"]) | |
itm.ResolutionHeight.Text = tostring(sensorTbl["resolution"]["height"]) | |
end | |
if itm.Unit.CurrentText == "inches" and sensorTbl["inches"] and sensorTbl["inches"]["width"] and sensorTbl["inches"]["height"] and sensorTbl["inches"]["diagonal"] then | |
itm.SensorDimensionsWidth.Text = tostring(sensorTbl["inches"]["width"]) | |
itm.SensorDimensionsHeight.Text = tostring(sensorTbl["inches"]["height"]) | |
itm.SensorDimensionsDiagonal.Text = tostring(sensorTbl["inches"]["diagonal"]) | |
elseif itm.Unit.CurrentText == "mm" and sensorTbl["mm"] and sensorTbl["mm"]["width"] and sensorTbl["mm"]["height"] and sensorTbl["mm"]["diagonal"] then | |
itm.SensorDimensionsWidth.Text = tostring(sensorTbl["mm"]["width"]) | |
itm.SensorDimensionsHeight.Text = tostring(sensorTbl["mm"]["height"]) | |
itm.SensorDimensionsDiagonal.Text = tostring(sensorTbl["mm"]["diagonal"]) | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
-- The "Apply to Selection" button was clicked | |
function win.On.ApplyButton.Clicked(ev) | |
print("\n[Apply to Selection]") | |
-- Undo Stack | |
comp:StartUndo("Camera Sensor Database") | |
local vendorName = itm.Vendor.CurrentText | |
local cameraName = itm.Camera.CurrentText | |
local sensorName = itm.Sensor.CurrentText | |
local sensorWidthMM | |
local sensorWidthInches | |
local sensorHeightMM | |
local sensorHeightInches | |
local focalLengthMM | |
local width | |
local height | |
sItemsTbl = {} | |
for vendor,vendorTbl in pairs(tbl) do | |
if vendor == vendorName then | |
for camera,cameraTbl in pairs(vendorTbl) do | |
if camera == cameraName then | |
sensorDimensionsTbl = cameraTbl["sensor dimensions"] or {} | |
for sensor,sensorTbl in pairs(sensorDimensionsTbl) do | |
if sensor == sensorName then | |
if sensorTbl["resolution"] and sensorTbl["focal length"] then | |
focalLengthMM = tostring(sensorTbl["focal length"]) | |
end | |
if sensorTbl["resolution"] and sensorTbl["resolution"]["width"] and sensorTbl["resolution"]["height"] then | |
width = tostring(sensorTbl["resolution"]["width"]) | |
height = tostring(sensorTbl["resolution"]["height"]) | |
end | |
if sensorTbl["inches"] and sensorTbl["inches"]["width"] and sensorTbl["inches"]["height"] and sensorTbl["inches"]["diagonal"] then | |
sensorWidthInches = tostring(sensorTbl["inches"]["width"]) | |
sensorHeightInches = tostring(sensorTbl["inches"]["height"]) | |
end | |
if sensorTbl["mm"] and sensorTbl["mm"]["width"] and sensorTbl["mm"]["height"] and sensorTbl["mm"]["diagonal"] then | |
sensorWidthMM = tostring(sensorTbl["mm"]["width"]) | |
sensorHeightMM = tostring(sensorTbl["mm"]["height"]) | |
end | |
end | |
end | |
end | |
end | |
end | |
end | |
Camera3DTools = comp:GetToolList(true, "Camera3D") | |
for k, tool in pairs(Camera3DTools) do | |
print(tool.Name) | |
tool["FilmGate"][fu.TIME_UNDEFINED] = "User" | |
-- Focal Length is in mm | |
if focalLengthMM ~= "" then | |
tool["FLength"][fu.TIME_UNDEFINED] = tonumber(focalLengthMM) | |
end | |
-- Aperture is in Inches | |
print("\t[Sensor Dimensions] ", sensorWidthInches, "x", sensorHeightInches, " inches") | |
tool["ApertureW"][fu.TIME_UNDEFINED] = tonumber(sensorWidthInches) | |
tool["ApertureH"][fu.TIME_UNDEFINED] = tonumber(sensorHeightInches) | |
end | |
uCameraTools = comp:GetToolList(true, "uCamera") | |
for k, tool in pairs(uCameraTools) do | |
print(tool.Name) | |
-- Focal Length is in mm | |
if focalLengthMM ~= "" then | |
tool["FocalLength"][fu.TIME_UNDEFINED] = tonumber(focalLengthMM) | |
end | |
-- Aperture is in mm | |
print("\t[Sensor Dimensions] ", sensorWidthMM, "x", sensorHeightMM, " mm") | |
tool["HorizontalAperture"][fu.TIME_UNDEFINED] = tonumber(sensorWidthMM) | |
tool["VerticalAperture"][fu.TIME_UNDEFINED] = tonumber(sensorHeightMM) | |
end | |
CameraTrackerTools = comp:GetToolList(true, "Dimension.CameraTracker") | |
for k, tool in pairs(CameraTrackerTools) do | |
print(tool.Name) | |
tool["Camera.FilmGate"][fu.TIME_UNDEFINED] = "User" | |
-- Focal Length is in mm | |
if focalLengthMM ~= "" then | |
tool["Camera.FocalLength"][fu.TIME_UNDEFINED] = tonumber(focalLengthMM) | |
end | |
-- Aperture is in Inches | |
print("\t[Sensor Dimensions] ", sensorWidthInches, "x", sensorHeightInches, " inches") | |
tool["Camera.ApertureW"][fu.TIME_UNDEFINED] = tonumber(sensorWidthInches) | |
tool["Camera.ApertureH"][fu.TIME_UNDEFINED] = tonumber(sensorHeightInches) | |
end | |
uRendererTools = comp:GetToolList(true, "uRenderer") | |
for k, tool in pairs(uRendererTools) do | |
print(tool.Name) | |
-- Image Size | |
if (type(width) == "string" and width ~= "") and (type(height) == "string" and height ~= "") then | |
print("\t[Image Size] ", width, "x", height, " px") | |
tool["Width"][fu.TIME_UNDEFINED] = tonumber(width) | |
tool["Height"][fu.TIME_UNDEFINED] = tonumber(height) | |
end | |
end | |
Renderer3DTools = comp:GetToolList(true, "Renderer3D") | |
for k, tool in pairs(Renderer3DTools) do | |
print(tool.Name) | |
-- Image Size | |
if (type(width) == "string" and width ~= "") and (type(height) == "string" and height ~= "") then | |
print("\t[Image Size] ", width, "x", height, " px") | |
tool["Width"][fu.TIME_UNDEFINED] = tonumber(width) | |
tool["Height"][fu.TIME_UNDEFINED] = tonumber(height) | |
end | |
end | |
-- Undo Stack | |
comp:EndUndo(true) | |
end | |
-- The app:AddConfig() command that will capture the "Control + W" or "Control + F4" hotkeys so they will close the window instead of closing the foreground composite. | |
app:AddConfig("CameraSensorDatabase", { | |
Target { | |
ID = "CameraSensorDatabase", | |
}, | |
Hotkeys { | |
Target = "CameraSensorDatabase", | |
Defaults = true, | |
CONTROL_W = "Execute{cmd = [[app.UIManager:QueueEvent(obj, 'Close', {})]]}", | |
CONTROL_F4 = "Execute{cmd = [[app.UIManager:QueueEvent(obj, 'Close', {})]]}", | |
ESCAPE = "Execute{cmd = [[app.UIManager:QueueEvent(obj, 'Close', {})]]}", | |
}, | |
}) | |
win:Show() | |
disp:RunLoop() | |
win:Hide() | |
app:RemoveConfig("CameraSensorDatabase") | |
collectgarbage() | |
end | |
function Main() | |
ProgramInfo() | |
-- Grab the latest sensor data | |
local tbl = Download() | |
-- Print the vendor data to the console | |
-- VendorInfo(tbl) | |
-- Show the GUI | |
if tbl and type(tbl) == "table" then | |
-- dump(tbl) | |
GUI(tbl) | |
else | |
print("No sensor data received") | |
end | |
print("[Done]") | |
end | |
-- Where the magic happens | |
Main() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Screenshots