face_recognition.lua script face some stability and performance issues on an i5-6600, 32 GB and GTX 1060 6GB:
1. Taking to much time – almost a day for 3000 images
2. Crashing due to lines malformation in facerecognition.txt – some times face_recognition Python script concatenates two output lines in just once, what is not expected by face_recognition.lua script
3. Crashing the whole system in Linux – I keep receiving “bash: fork: resource temporarily unavailable” trying to process a too big (starting from some hundreds) image list. Some other applications starts to crash at this point. It seems related to the attach tag call because commenting this instruction (for debugging purposes) eliminates the crashes.
After debugging the Lua script and checking Lua and SQL darktable out logs, I decided to make the following changes:
1. Remove an inner loop in the processing results step – helps preventing issue 1
2. Remove a loop in the export step – helps preventing issue 1
3. Remove duplicate tags (when there are more more than a reference face for each person) while facerecognition.txt is read – helps preventing issue 1 and 3
4. Make a sanity check with processing images, skip any malformation data – helps preventing issue 2
5. Reduces the sqlite access by caching tags that where already created for a previous image – helps preventing issue 3
Those changes allowed may thousands (more than 30K) of images to be processed in half a day instead of several days.
This commit is contained in:
@@ -63,7 +63,7 @@ du.check_min_api_version("5.0.0", "face_recognition")
|
||||
gettext.bindtextdomain("face_recognition", dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("face_recognition", msgid)
|
||||
return gettext.dgettext("face_recognition", msgid)
|
||||
end
|
||||
|
||||
-- preferences
|
||||
@@ -92,8 +92,10 @@ local function build_image_table(images)
|
||||
end
|
||||
|
||||
for _,img in ipairs(images) do
|
||||
image_table[img] = tmp_dir .. df.get_basename(img.filename) .. file_extension
|
||||
cnt = cnt + 1
|
||||
if img ~= nil then
|
||||
image_table[tmp_dir .. df.get_basename(img.filename) .. file_extension] = img
|
||||
cnt = cnt + 1
|
||||
end
|
||||
end
|
||||
|
||||
return image_table, cnt
|
||||
@@ -103,17 +105,12 @@ local function stop_job(job)
|
||||
job.valid = false
|
||||
end
|
||||
|
||||
local function do_export(img_tbl)
|
||||
local function do_export(img_tbl, images)
|
||||
local exporter = nil
|
||||
local upsize = false
|
||||
local upscale = false
|
||||
local ff = fc.export_format.value
|
||||
local height = dt.preferences.read(MODULE, "max_height", "integer")
|
||||
local width = dt.preferences.read(MODULE, "max_width", "integer")
|
||||
local images = 0
|
||||
for k,v in pairs(img_tbl) do
|
||||
images = images + 1
|
||||
end
|
||||
|
||||
-- get the export format parameters
|
||||
if string.match(ff, "JPEG") then
|
||||
@@ -134,7 +131,7 @@ local function do_export(img_tbl)
|
||||
local exp_cnt = 0
|
||||
local percent_step = 1.0 / images
|
||||
job.percent = 0.0
|
||||
for img,export in pairs(img_tbl) do
|
||||
for export,img in pairs(img_tbl) do
|
||||
exp_cnt = exp_cnt + 1
|
||||
dt.print(string.format(_("Exporting image %i of %i images"), exp_cnt, images))
|
||||
exporter:write_image(img, export, upsize)
|
||||
@@ -188,7 +185,7 @@ local function ignoreByTag (image, ignoreTags)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return ignoreImage
|
||||
end
|
||||
|
||||
@@ -221,25 +218,25 @@ local function face_recognition ()
|
||||
if nrCores < 1 then
|
||||
nrCores = -1
|
||||
end
|
||||
|
||||
|
||||
-- Split ignore tags (if any)
|
||||
ignoreTags = {}
|
||||
for tag in string.gmatch(ignoreTagString, '([^,]+)') do
|
||||
table.insert (ignoreTags, tag)
|
||||
dt.print_log ("Face recognition: Ignore tag: " .. tag)
|
||||
end
|
||||
|
||||
|
||||
-- list of exported images
|
||||
|
||||
local image_table, cnt = build_image_table(dt.gui.action_images)
|
||||
|
||||
if cnt > 0 then
|
||||
local success = do_export(image_table)
|
||||
local success = do_export(image_table, cnt)
|
||||
if success then
|
||||
-- do the face recognition
|
||||
local img_list = {}
|
||||
|
||||
for img,v in pairs(image_table) do
|
||||
for v,_ in pairs(image_table) do
|
||||
table.insert (img_list, v)
|
||||
end
|
||||
|
||||
@@ -248,7 +245,7 @@ local function face_recognition ()
|
||||
dt.print_log ("Face recognition: Path to unknown images: " .. path)
|
||||
os.setlocale("C")
|
||||
local tolerance = dt.preferences.read(MODULE, "tolerance", "float")
|
||||
|
||||
|
||||
local command = bin_path .. " --cpus " .. nrCores .. " --tolerance " .. tolerance .. " " .. knownPath .. " " .. path .. " > " .. OUTPUT
|
||||
os.setlocale()
|
||||
dt.print_log("Face recognition: Running command: " .. command)
|
||||
@@ -258,61 +255,77 @@ local function face_recognition ()
|
||||
|
||||
-- Open output file
|
||||
local f = io.open(OUTPUT, "rb")
|
||||
|
||||
|
||||
if not f then
|
||||
dt.print(_("Face recognition failed"))
|
||||
else
|
||||
dt.print(_("Face recognition finished"))
|
||||
f:close ()
|
||||
end
|
||||
|
||||
|
||||
-- Read output
|
||||
dt.print(_("processing results..."))
|
||||
local result = {}
|
||||
for line in io.lines(OUTPUT) do
|
||||
if not string.match(line, "^WARNING:") then
|
||||
local tags_list = {}
|
||||
local tag_object = {}
|
||||
for line in io.lines(OUTPUT) do
|
||||
if not string.match(line, "^WARNING:") and line ~= "" and line ~= nil then
|
||||
local file, tag = string.match (line, "(.*),(.*)$")
|
||||
tag = string.gsub (tag, "%d*$", "")
|
||||
dt.print_log ("File:"..file .." Tag:".. tag)
|
||||
if result[file] ~= nil then
|
||||
table.insert (result[file], tag)
|
||||
tag_object = {}
|
||||
if result[file] == nil then
|
||||
tag_object[tag] = true
|
||||
result[file] = tag_object
|
||||
else
|
||||
result[file] = {tag}
|
||||
tag_object = result[file]
|
||||
tag_object[tag] = true
|
||||
result[file] = tag_object
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Attach tags
|
||||
local result_index = 0
|
||||
for file,tags in pairs(result) do
|
||||
result_index = result_index +1
|
||||
-- Find image in table
|
||||
for img,file2 in pairs(image_table) do
|
||||
if file == file2 then
|
||||
for _,t in ipairs (tags) do
|
||||
-- Check if image is ignored
|
||||
if ignoreByTag (img, ignoreTags) then
|
||||
dt.print_log("Face recognition: Ignoring image with ID " .. img.id)
|
||||
else
|
||||
-- Check of unrecognized unknown_person
|
||||
if t == "unknown_person" then
|
||||
t = unknownTag
|
||||
end
|
||||
-- Check of unrecognized no_persons_found
|
||||
if t == "no_persons_found" then
|
||||
t = nonpersonsfoundTag
|
||||
end
|
||||
if t ~= "" and t ~= nil then
|
||||
dt.print_log ("ImgId:" .. img.id .. " Tag:".. t)
|
||||
-- Create tag if it does not exists
|
||||
local tag = dt.tags.create (t)
|
||||
img:attach_tag (tag)
|
||||
img = image_table[file]
|
||||
if img == nil then
|
||||
dt.print_log("Face recognition: Ignoring face recognition entry: " .. file)
|
||||
else
|
||||
for t,_ in pairs (tags) do
|
||||
-- Check if image is ignored
|
||||
if ignoreByTag (img, ignoreTags) then
|
||||
dt.print_log("Face recognition: Ignoring image with ID " .. img.id)
|
||||
else
|
||||
-- Check of unrecognized unknown_person
|
||||
if t == "unknown_person" then
|
||||
t = unknownTag
|
||||
end
|
||||
-- Check of unrecognized no_persons_found
|
||||
if t == "no_persons_found" then
|
||||
t = nonpersonsfoundTag
|
||||
end
|
||||
if t ~= "" and t ~= nil then
|
||||
dt.print_log ("ImgId:" .. img.id .. " Tag:".. t)
|
||||
-- Create tag if it does not exist
|
||||
if tags_list[t] == nil then
|
||||
tag = dt.tags.create (t)
|
||||
tags_list[t] = tag
|
||||
else
|
||||
tag = tags_list[t]
|
||||
end
|
||||
img:attach_tag (tag)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
dt.print(_("face recognition complete"))
|
||||
cleanup(img_list)
|
||||
dt.print_log("img_list cleaned-up")
|
||||
dt.print_log("face recognition complete")
|
||||
dt.print(_("face recognition complete"))
|
||||
else
|
||||
dt.print(_("image export failed"))
|
||||
return
|
||||
@@ -321,8 +334,6 @@ local function face_recognition ()
|
||||
dt.print(_("no images selected"))
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
-- build the interface
|
||||
@@ -376,7 +387,7 @@ fc.known_image_path = dt.new_widget("file_chooser_button"){
|
||||
is_directory = true,
|
||||
changed_callback = function(this)
|
||||
dt.preferences.write(MODULE, "known_image_path", "directory", this.value)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
fc.export_format = dt.new_widget("combobox"){
|
||||
@@ -403,9 +414,9 @@ fc.height = dt.new_widget("entry"){
|
||||
|
||||
fc.execute = dt.new_widget("button"){
|
||||
label = "detect faces",
|
||||
clicked_callback = function(this)
|
||||
clicked_callback = function(this)
|
||||
face_recognition()
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
local widgets = {
|
||||
@@ -422,14 +433,14 @@ local widgets = {
|
||||
if dt.configuration.running_os == "windows" or dt.configuration.running_os == "macos" then
|
||||
table.insert(widgets, df.executable_path_widget({"face_recognition"}))
|
||||
end
|
||||
table.insert(widgets, dt.new_widget("section_label"){ label = _("processing options")})
|
||||
table.insert(widgets, fc.tolerance)
|
||||
table.insert(widgets, fc.num_cores)
|
||||
table.insert(widgets, fc.export_format)
|
||||
table.insert(widgets, dt.new_widget("box"){
|
||||
orientation = "horizontal",
|
||||
dt.new_widget("label"){ label = _("width ")},
|
||||
fc.width,
|
||||
table.insert(widgets, dt.new_widget("section_label"){ label = _("processing options")})
|
||||
table.insert(widgets, fc.tolerance)
|
||||
table.insert(widgets, fc.num_cores)
|
||||
table.insert(widgets, fc.export_format)
|
||||
table.insert(widgets, dt.new_widget("box"){
|
||||
orientation = "horizontal",
|
||||
dt.new_widget("label"){ label = _("width ")},
|
||||
fc.width,
|
||||
})
|
||||
table.insert(widgets, dt.new_widget("box"){
|
||||
orientation = "horizontal",
|
||||
@@ -439,18 +450,13 @@ table.insert(widgets, dt.new_widget("box"){
|
||||
table.insert(widgets, fc.execute)
|
||||
|
||||
fc.widget = dt.new_widget("box"){
|
||||
orientation = vertical,
|
||||
reset_callback = function(this)
|
||||
orientation = vertical,
|
||||
reset_callback = function(this)
|
||||
reset_preferences()
|
||||
end,
|
||||
table.unpack(widgets),
|
||||
end,
|
||||
table.unpack(widgets),
|
||||
}
|
||||
|
||||
--fc.tolerance.value = dt.preferences.read(MODULE, "tolerance", "float")
|
||||
|
||||
-- Register
|
||||
--dt.register_storage("module_face_recognition", _("Face recognition"), show_status, face_recognition)
|
||||
|
||||
dt.register_lib(
|
||||
"face_recognition", -- Module name
|
||||
_("face recognition"), -- Visible name
|
||||
|
||||
Reference in New Issue
Block a user