-
Notifications
You must be signed in to change notification settings - Fork 137
Expand file tree
/
Copy pathfujifilm_dynamic_range.lua
More file actions
156 lines (127 loc) · 5.64 KB
/
fujifilm_dynamic_range.lua
File metadata and controls
156 lines (127 loc) · 5.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
--[[ fujifilm_dynamic_range-0.1
Compensate for Fujifilm raw files made using "dynamic range".
Copyright (C) 2021, 2022 Dan Torop <dant@pnym.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
]]
--[[About this Plugin
Support for adjusting darktable exposure by Fujifilm raw exposure
bias. This corrects for a DR100/DR200/DR400 "dynamic range" setting.
Dependencies:
- exiftool (https://www.sno.phy.queensu.ca/~phil/exiftool/)
Based upon fujifilm_ratings by Ben Mendis
The relevant tag is RawExposureBias (0x9650). This appears to
represent the shift in EV for the chosen DR setting (whether manual or
automatic). Note that even at 100DR ("standard") there is an EV shift:
100 DR -> -0.72 EV
200 DR -> -1.72 EV
400 DR -> -2.72 EV
The ideal would be to use exiv2 to read this tag, as this is the same
code which darktable import uses. Unfortunately, exiv2 as of v0.27.3
can't read this tag. As it is encoded as a 4-byte ratio of two signed
shorts -- a novel data type -- it will require some attention to fix
this.
There is an exiv2-readable DevelopmentDynamicRange tag which maps to
RawExposureBias as above. DevelopmentDynamicRange is only present
when tag DynamicRangeSetting (0x1402) is Manual/Raw (0x0001). When it
is Auto (0x0000), the equivalent data is tag AutoDynamicRange
(0x140b). But exiv2 currently can't read that tag either.
Hence for now this code uses exiftool to read RawExposureBias, as a
more general solution. As exiftool is approx. 10x slower than exiv2
(Perl vs. C++), this may slow large imports.
These tags have been checked on a Fujifilm X100S and X100V. Other
cameras may behave in other ways.
--]]
local dt = require "darktable"
local du = require "lib/dtutils"
local df = require "lib/dtutils.file"
local dtsys = require "lib/dtutils.system"
local gettext = dt.gettext.gettext
du.check_min_api_version("7.0.0", "fujifilm_dynamic_range")
local function _(msgid)
return gettext(msgid)
end
-- return data structure for script_manager
local script_data = {}
script_data.metadata = {
name = _("fujifilm dynamic range"),
purpose = _("compensate for Fujifilm raw files made using \"dynamic range\""),
author = "Dan Torop <dant@pnym.net>",
help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/fujifilm_dynamic_range"
}
script_data.destroy = nil -- function to destory the script
script_data.destroy_method = nil -- set to hide for libs since we can't destroy them commpletely yet, otherwise leave as nil
script_data.restart = nil -- how to restart the (lib) script after it's been hidden - i.e. make it visible again
script_data.show = nil -- only required for libs since the destroy_method only hides them
local function detect_dynamic_range(event, image)
if image.exif_maker ~= "FUJIFILM" then
dt.print_log("[fujifilm_dynamic_range] ignoring non-Fujifilm image")
return
end
-- it would be nice to check image.is_raw but this appears to not yet be set
if not string.match(image.filename, "%.RAF$") then
dt.print_log("[fujifilm_dynamic_range] ignoring non-raw image")
return
end
local command = df.check_if_bin_exists("exiftool")
if not command then
dt.print_error("[fujifilm_dynamic_range] exiftool not found")
return
end
local RAF_filename = df.sanitize_filename(tostring(image))
-- without -n flag, exiftool will round to the nearest tenth
command = command .. " -RawExposureBias -n -t " .. RAF_filename
dt.print_log(command)
output = io.popen(command)
local raf_result = output:read("*all")
output:close()
if #raf_result == 0 then
dt.print_error("[fujifilm_dynamic_range] no output returned by exiftool")
return
end
raf_result = string.match(raf_result, "\t(.*)")
if not raf_result then
dt.print_error("[fujifilm_dynamic_range] could not parse exiftool output")
return
end
if image.exif_exposure_bias ~= image.exif_exposure_bias then
-- is NAN (this is unlikely as RAFs should have ExposureBiasValue set)
image.exif_exposure_bias = 0
end
-- this should be auto-applied if plugins/darkroom/workflow is scene-referred
-- note that scene-referred workflow exposure preset also pushes exposure up by 0.5 EV
image.exif_exposure_bias = image.exif_exposure_bias + tonumber(raf_result)
dt.print_log("[fujifilm_dynamic_range] raw exposure bias " .. tostring(raf_result))
-- handle any duplicates
if #image:get_group_members() > 1 then
local basename = df.get_basename(image.filename)
local grouped_images = image:get_group_members()
for _, img in ipairs(grouped_images) do
if string.match(img.filename, basename) and img.duplicate_index > 0 then
-- its a duplicate
img.exif_exposure_bias = img.exif_exposure_bias + tonumber(raf_result)
end
end
end
end
local function destroy()
dt.destroy_event("fujifilm_dr", "post-import-image")
end
if not df.check_if_bin_exists("exiftool") then
dt.print_log("Please install exiftool to use fujifilm_dynamic_range")
error "[fujifilm_dynamic_range] exiftool not found"
end
dt.register_event("fujifilm_dr", "post-import-image",
detect_dynamic_range)
dt.print_log("[fujifilm_dynamic_range] loaded")
script_data.destroy = destroy
return script_data