lua-scripts icon indicating copy to clipboard operation
lua-scripts copied to clipboard

[FR] expose `dt_util_format_exposure` to Lua?

Open Geobert opened this issue 6 months ago • 18 comments

I’m writing a script and want to have exif_exposure in the fraction form and seen that the formatting function exists (dt_util_format_exposure) so maybe, if possible, to make it available in Lua?

Geobert avatar Jun 01 '25 18:06 Geobert

formatted_exposure = "1/" .. image.exif_exposure

wpferguson avatar Jun 01 '25 19:06 wpferguson

oh, thanks, so it changed since 4.8 https://docs.darktable.org/usermanual/4.8/en/lua/printing-labeled-images/

Geobert avatar Jun 01 '25 19:06 Geobert

I should look instead of answering off the top of my head :blush:

No, it hasn't changed but it should not be hard to code up the equivalent in Lua.

wpferguson avatar Jun 01 '25 22:06 wpferguson

Here's the code to do it

  local function toint(number)
    if number > 0 then
      return math.floor(number)
    elseif number < 0 then
      return math.ceil(number)
    else
      return 0
    end
  end

  local function nearbyintf(number)
    if number > 0 then
      if (number - toint(number)) > .5 then
        return toint(number) + 1
      else
        return toint(number)
      end
    else
      if (number + toint(number)) > .5 then
        return toint(number)
      else
        return toint(number) - 1
      end
    end
  end

  local function format_exposure(exposuretime)

    result = nil

    if exposuretime > 1.0 then
      if nearbyintf(exposuretime) == exposuretime then
        result = string.format("%.0f″", exposuretime)
      else
        result = string.format("%.1f″", exposuretime)
      end
    -- catch everything below 0.3 seconds
    elseif exposuretime < 0.2 then
      result = string.format("1/%.0f", 1.0 / exposuretime)
    -- catch 1/2, 1/3, ...
    elseif nearbyintf(1.0 / exposuretime) == (1.0 / exposuretime) then
      result = string.format("1/%.0f", 1.0 / exposuretime)
    -- catch 1/1.3, 1/1.6, ...
    elseif (10 * nearbyintf(10.0 / exposuretime)) == nearbyintf(100.0 / exposuretime) then
      result = string.format("1/%.1f", 1.0 / exposuretime)
    else
      result = string.format("%.1f″", exposuretime)
    end

    return result
  end


also in a file

dt_util_format_exposure.zip

I did not test this, but it's pretty much a copy and paste of the C code.

wpferguson avatar Jun 02 '25 01:06 wpferguson

I got around to testing today. The last else statement in nearbyintf needs an end statement.

I ran this in Lua interactively and tested random exposure values from my images and they all appeared to work correctly (after I added the end).

EDIT: I'll probably add this to the string library so it's available to all.

wpferguson avatar Jun 02 '25 20:06 wpferguson

I got around to testing today. The last else statement in nearbyintf needs an end statement.

I ran this in Lua interactively and tested random exposure values from my images and they all appeared to work correctly (after I added the end).

Thanks I replaced your impl of nearbyintf by:

local function nearbyintf(v)
	return math.floor(v + 0.5)
end

don’t know if that will do but so far so good

Geobert avatar Jun 02 '25 20:06 Geobert

I just ran a test comparing the two algorithms with shutters speeds from 30 seconds to 1/8000 in 1/3 stops and they both had the same (correct) answer across the range.

wpferguson avatar Jun 02 '25 21:06 wpferguson

The issue still stands though, would be better if we can expose the C impl, as it’s here already :)

Geobert avatar Jun 03 '25 06:06 Geobert

as it’s here already

so's the Lua one :)

would be better if we can expose the C impl

If we expose the C implementation

  • It wouldn't be available until the 5.4 release in December
  • If anything went wrong with the API call it wouldn't be fixed until 5.6, a year from now
  • The API exposes things you can't do or get from Lua such as information about the UI, images, program state, events, etc. It doesn't expose "convenience" functions.
  • I'd have to write the API code to expose the function.
  • Difficulty: moderate

If we add the above implementation to the string library

  • It could be available as early as today
  • If there is a problem it can be fixed and published in a day or two
  • It doesn't clutter up the darktable Lua API
  • Difficulty: trivial

wpferguson avatar Jun 03 '25 16:06 wpferguson

Maybe I’m lacking knowledge on Dartable’s Lua ecosystem: there’s a Lua library?

Geobert avatar Jun 03 '25 18:06 Geobert

https://docs.darktable.org/lua/stable

wpferguson avatar Jun 03 '25 20:06 wpferguson

Oh I see! this is separate from Darktable’s code then? How it is shipped?

Geobert avatar Jun 03 '25 20:06 Geobert

When you start darktable for the first time, down in the lower left there is a module called scripts_installer. It will install the scripts for you in the proper place with the proper settings so that script_manager will show up in the lower left allowing you to start and stop scripts.

script_manager has a function that checks for updates and automatically updates to the latest version of the scripts for the version of darktable you're running.

wpferguson avatar Jun 03 '25 20:06 wpferguson

When you start darktable for the first time, down in the lower left there is a module called scripts_installer. It will install the scripts for you in the proper place with the proper settings so that script_manager will show up in the lower left allowing you to start and stop scripts.

script_manager has a function that checks for updates and automatically updates to the latest version of the scripts for the version of darktable you're running.

and I discover the operation replace luarc altogether without creating a backup first :'(

Geobert avatar Jun 05 '25 09:06 Geobert

The code checks for a luarc and renames it to luarc.old.

    local function backup_luarc()
       debug_message("backuping up luarc file (if it exists)")
       local p = io.popen(_scripts_install.dir_cmd .. CONFIG_DIR)
       for line in p:lines() do 
         if string.match(line, "^luarc$") then
           debug_message("found the luarc file, renaming it to luarc.old")
           local success = false
           if _scripts_install.dt.configuration.running_os == "windows" then
             success = os_execute("rename " .. "\"" .. CONFIG_DIR .. PS .. "luarc\" \"" .. CONFIG_DIR .. PS .. "luarc.old\"")
           else
             success = os_execute("mv " .. CONFIG_DIR .. "/luarc " .. CONFIG_DIR .. "/luarc.old")
           end
           if not success then
             _scripts_install.dt.print(_("Unable to back up luarc file.  It will be overwritten"))
           end
         end
       end
       p:close()
     end

It's been tested hundreds of times, by me, and it's been released for years.

wpferguson avatar Jun 05 '25 15:06 wpferguson

I trust you, something went wrong (Windows 11) and I don’t know what ^^' I got the file back with Windows restore, luckily it had yesterday version

Geobert avatar Jun 05 '25 15:06 Geobert

I'll spin up a Win 11 VM and test.

wpferguson avatar Jun 05 '25 15:06 wpferguson

Also, I’m using portable version via scoop

Geobert avatar Jun 05 '25 15:06 Geobert