Lua Script Looping Forever - lua

I am using an opensource tool called NeuralTalk2 that essentially takes several pictures and outputs a caption it thinks describes the picture. There is a lua script in there that looks like this:
--[[
Same as DataLoader but only requires a folder of images.
Does not have an h5 dependency.
Only used at test time.
]]--
local utils = require 'misc.utils'
require 'lfs'
require 'image'
local DataLoaderRaw = torch.class('DataLoaderRaw')
function DataLoaderRaw:__init(opt)
local coco_json = utils.getopt(opt, 'coco_json', '')
-- load the json file which contains additional information about the dataset
print('DataLoaderRaw loading images from folder: ', opt.folder_path)
self.files = {}
self.ids = {}
if string.len(opt.coco_json) > 0 then
print('reading from ' .. opt.coco_json)
-- read in filenames from the coco-style json file
self.coco_annotation = utils.read_json(opt.coco_json)
for k,v in pairs(self.coco_annotation.images) do
local fullpath = path.join(opt.folder_path, v.file_name)
table.insert(self.files, fullpath)
table.insert(self.ids, v.id)
end
else
-- read in all the filenames from the folder
print('listing all images in directory ' .. opt.folder_path)
local function isImage(f)
local supportedExt = {'.jpg','.JPG','.jpeg','.JPEG','.png','.PNG','.ppm','.PPM'}
for _,ext in pairs(supportedExt) do
local _, end_idx = f:find(ext)
if end_idx and end_idx == f:len() then
return true
end
end
return false
end
local n = 1
for file in paths.files(opt.folder_path, isImage) do
local fullpath = path.join(opt.folder_path, file)
table.insert(self.files, fullpath)
table.insert(self.ids, tostring(n)) -- just order them sequentially
n=n+1
end
end
self.N = #self.files
print('DataLoaderRaw found ' .. self.N .. ' images')
self.iterator = 1
end
function DataLoaderRaw:resetIterator()
self.iterator = 1
end
--[[
Returns a batch of data:
- X (N,3,256,256) containing the images as uint8 ByteTensor
- info table of length N, containing additional information
The data is iterated linearly in order
--]]
function DataLoaderRaw:getBatch(opt)
local batch_size = utils.getopt(opt, 'batch_size', 5) -- how many images get returned at one time (to go through CNN)
-- pick an index of the datapoint to load next
local img_batch_raw = torch.ByteTensor(batch_size, 3, 256, 256)
local max_index = self.N
local wrapped = false
local infos = {}
for i=1,batch_size do
local ri = self.iterator
local ri_next = ri + 1 -- increment iterator
if ri_next > max_index then ri_next = 1; wrapped = true end -- wrap back around
self.iterator = ri_next
-- load the image
local img = image.load(self.files[ri], 3, 'byte')
img_batch_raw[i] = image.scale(img, 256, 256)
-- and record associated info as well
local info_struct = {}
info_struct.id = self.ids[ri]
info_struct.file_path = self.files[ri]
table.insert(infos, info_struct)
end
local data = {}
data.images = img_batch_raw
data.bounds = {it_pos_now = self.iterator, it_max = self.N, wrapped = wrapped}
data.infos = infos
return data
end
This code does many things, but please pay attention to the __init function that begins at line 13:
function DataLoaderRaw:__init(opt)
local coco_json = utils.getopt(opt, 'coco_json', '')
-- load the json file which contains additional information about the dataset
print('DataLoaderRaw loading images from folder: ', opt.folder_path)
self.files = {}
self.ids = {}
if string.len(opt.coco_json) > 0 then
print('reading from ' .. opt.coco_json)
-- read in filenames from the coco-style json file
self.coco_annotation = utils.read_json(opt.coco_json)
for k,v in pairs(self.coco_annotation.images) do
local fullpath = path.join(opt.folder_path, v.file_name)
table.insert(self.files, fullpath)
table.insert(self.ids, v.id)
end
else
-- read in all the filenames from the folder
print('listing all images in directory ' .. opt.folder_path)
local function isImage(f)
local supportedExt = {'.jpg','.JPG','.jpeg','.JPEG','.png','.PNG','.ppm','.PPM'}
for _,ext in pairs(supportedExt) do
local _, end_idx = f:find(ext)
if end_idx and end_idx == f:len() then
return true
end
end
return false
end
local n = 1
for file in paths.files(opt.folder_path, isImage) do
local fullpath = path.join(opt.folder_path, file)
table.insert(self.files, fullpath)
table.insert(self.ids, tostring(n)) -- just order them sequentially
n=n+1
end
end
self.N = #self.files
print('DataLoaderRaw found ' .. self.N .. ' images')
self.iterator = 1
end
The original code would output the the picture and the captions into an .hmtl file randomly. So, picture one would be adjacent to picture 10 would be adjacent to picture 4 would be adjacent to picture 38 (for example).
I can't have it do that, so I edited it:
function DataLoaderRaw:__init(opt)
local coco_json = utils.getopt(opt, 'coco_json', '')
-- load the json file which contains additional information about the dataset
print('DataLoaderRaw loading images from folder: ', opt.folder_path)
self.files = {}
self.ids = {}
if string.len(opt.coco_json) > 0 then
print('reading from ' .. opt.coco_json)
-- read in filenames from the coco-style json file
self.coco_annotation = utils.read_json(opt.coco_json)
for k,v in pairs(self.coco_annotation.images) do
local fullpath = path.join(opt.folder_path, v.file_name)
table.insert(self.files, fullpath)
table.insert(self.ids, v.id)
end
else
-- read in all the filenames from the folder
print('listing all images in directory ' .. opt.folder_path)
local function isImage(f)
local supportedExt = {'.jpg','.JPG','.jpeg','.JPEG','.png','.PNG','.ppm','.PPM'}
for _,ext in pairs(supportedExt) do
local _, end_idx = f:find(ext)
if end_idx and end_idx == f:len() then
return true
end
end
return false
end
local n = 1
local m = 0
local counter = 1
while counter <= #paths.dir(opt.folder_path) - 2 do -- two directories "." and ".." returned by dir that are not images
local file_added = false
for file in paths.files(opt.folder_path, isImage) do
if tonumber(string.sub(file, 7, 10)) == n then -- insert files into self.files in order
if tonumber(string.sub(file, 12, 12)) == m then
local fullpath = path.join(opt.folder_path, file)
table.insert(self.files, fullpath)
table.insert(self.ids, tostring(n)) -- just order them sequentially
m=m+1
counter = counter + 1
file_added = true
break
end
end
end
if file_added == false then
n = n + 1
m = 0
end
end
print(self.files)
end
self.N = #self.files
print('DataLoaderRaw found ' .. self.N .. ' images')
self.iterator = 1
end
The code I wrote is supposed to change that and make it so that picture 1,2,3,4,5...n are all adjacent to each other.
However, I am getting stuck in the while loop.. its looping forever. I really don't know why - this is my first time coding in lua. Hopefully.. a more trained expert would know?
Thank you.

Related

How to get webp image EXIF metadata in lua?

I can get this data with the following code. But it runs too slow:
local handle = io.popen("exiftool image.webp")
local result = handle:read("*a")
handle:close()
Is there a more elegant way to get the metadata?
UPD:
I use this software:
docker (20.10.7)
openresty/openresty:xenial (1.15.8.3)
luarocks (3.2.1)
LuaJIT (2.1.0-beta3)
Here is an example of a picture with a UserComment field: link
Exiftool sees this property:
$ exiftool -EXIF:UserComment Johnrogershousemay2020.webp
User Comment : {"foo":"bar"}
This method is less elegant, but it does not require to run external application :-)
function get_webp_user_comment(file_name)
local file = io.open(file_name, "rb")
local exif_offset, exif_found, is_big_endian, user_comment = 12
local function read_string(offset, size)
file:seek("set", offset)
return file:read(size)
end
local function read_uint(offset, size)
local n, s = 0, read_string(offset, size)
for j = 1, size do
n = n * 256 + s:byte(is_big_endian and j or size + 1 - j)
end
return n
end
local function read_uint32(disp)
return read_uint(exif_offset + disp, 4)
end
local function read_uint16(disp)
return read_uint(exif_offset + disp, 2)
end
local function search_for_tag(ifd_disp, tag)
if ifd_disp ~= 0 then
local entry_disp = ifd_disp + 2
for j = 1, read_uint16(ifd_disp) do
if read_uint16(entry_disp) == tag then
return read_uint32(entry_disp + 8), read_uint32(entry_disp + 4)
end
entry_disp = entry_disp + 12
end
return search_for_tag(read_uint32(entry_disp), tag)
end
end
if read_string(0, 4) == "RIFF" and read_string(8, 4) == "WEBP" then
local max_offset = read_uint32(-8)
while exif_offset < max_offset do
local section_name = read_string(exif_offset, 4)
exif_offset = exif_offset + 8
if section_name == "EXIF" then
local endianness = read_string(exif_offset, 2)
is_big_endian = endianness == "MM"
exif_found = is_big_endian or endianness == "II"
if exif_found then
break
end
end
exif_offset = exif_offset + read_uint32(-4)
exif_offset = exif_offset + exif_offset % 2
end
end
if exif_found then
local exif_ifd = search_for_tag(read_uint32(4), 0x8769)
if exif_ifd then
local disp, count = search_for_tag(exif_ifd, 0x9286)
user_comment = read_string(exif_offset + disp + 8, count - 8)
end
end
file:close()
return user_comment
end
Usage example:
local file_name = "path/to/Johnrogershousemay2020.webp"
print(get_webp_user_comment(file_name))
If all you need is 'UserComment', then pass that as a parameter during your popen call:
local handle = io.popen("exiftool -EXIF:UserComment image.webp")

Lua error attempt to index nil value while checking if player has the correct amount of a item

This script allows people to produce meth, starting production will succeed when you have the right amount of objects in your inventory.
Only when you don't have the right amount you should get a notification and you don't get that notification..
Following error pops up: SCRIPT ERROR: #ns-meth/server.lua:18: Attempt to index a nil value.
Code:
RSCore = nil
Citizen.CreateThread(function()
while RSCore == nil do
TriggerEvent('RSCore:GetObject', function(obj) RSCore = obj end)
Citizen.Wait(0)
end
end)
RegisterServerEvent('RSCore_methcar:start')
AddEventHandler('RSCore_methcar:start', function()
local src = source
local Player = RSCore.Functions.GetPlayer(src)
local amount = 0
if Player.Functions.GetItemByName('acetone').amount >= 5 and Player.Functions.GetItemByName('lithium').amount >= 2 and Player.Functions.GetItemByName('methlab').amount >= 1 then
TriggerClientEvent('RSCore_methcar:startprod', src)
Player.Functions.RemoveItem('acetone', 5)
Player.Functions.RemoveItem('lithium', 2)
else
if Player.Functions.GetItemByName('acetone').amount <= 4 and Player.Functions.GetItemByName('lithium').amount <= 1 and Player.Functions.GetItemByName('methlab').amount <= 0 then
RSCore.Functions.Notify("Je hebt niet de juiste benodigdheden!", "error")
end
end
end)
RegisterServerEvent('RSCore_methcar:stopf')
AddEventHandler('RSCore_methcar:stopf', function(id)
local src = source
local Players = RSCore.GetPlayers()
local Player = RSCore.Functions.GetPlayer(src)
for i=1, #Players, 1 do
TriggerClientEvent('RSCore_methcar:stopfreeze', Players[i], id)
end
end)
RegisterServerEvent('RSCore_methcar:make')
AddEventHandler('RSCore_methcar:make', function(posx,posy,posz)
local src = source
local Player = RSCore.Functions.GetPlayer(src)
if Player.Functions.GetItemByName('methlab').amount >= 1 then
local Players = RSCore.Functions.GetPlayer(src)
for i=1, #Players, 1 do
TriggerClientEvent('RSCore_methcar:smoke',Players[i],posx,posy,posz, 'a')
end
else
TriggerClientEvent('RSCore_methcar:stop', src)
end
end)
RegisterServerEvent('RSCore_methcar:finish')
AddEventHandler('RSCore_methcar:finish', function(qualtiy)
local src = source
local Player = RSCore.Functions.GetPlayer(src)
print(qualtiy)
local rnd = math.random(-5, 5)
TriggerEvent('KLevels:addXP', src, 20)
Player.Functions.AddItem('meth', math.floor(qualtiy / 2) + rnd)
end)
RegisterServerEvent('RSCore_methcar:blow')
AddEventHandler('RSCore_methcar:blow', function(posx, posy, posz)
local src = source
local Players = RSCore.GetPlayers()
local Player = RSCore.Functions.GetPlayer(src)
for i=1, #Players, 1 do
TriggerClientEvent('RSCore_methcar:blowup', Players[i],posx, posy, posz)
end
Player.removeInventoryItem('methlab', 1)
end)
RegisterServerEvent('ns-meth:server:callCops')
AddEventHandler('ns-meth:server:callCops', function(streetLabel, coords)
local msg = "Er is een verdachte situatie op "..streetLabel..", mogelijks drugs productie."
local alertData = {
title = "Verdachte situatie",
coords = {x = coords.x, y = coords.y, z = coords.z},
description = msg
}
for k, v in pairs(RSCore.Functions.GetPlayers()) do
local Player = RSCore.Functions.GetPlayer(v)
if Player ~= nil then
if (Player.PlayerData.job.name == "police" and Player.PlayerData.job.onduty) then
TriggerClientEvent("ns-meth:client:robberyCall", Player.PlayerData.source, msg, streetLabel, coords)
TriggerClientEvent("rs-phone:client:addPoliceAlert", Player.PlayerData.source, alertData)
end
end
end
end)

Parsing file in Lua for specific value in table

I am very new to lua programming and am trying to parse a local file on my pc and then save certain elements of the table/array into strings.
I have been able to get all the data and print each line of the file, except I am having problems when trying to get specific value and save them into strings or just print a certain line. Any help would be greatly appreciated.
Here is my code sample:
function file_exists(file)
local f = io.open(file, "rb")
if f then f:close() end
return f ~= nil
end
function lines_from(file)
if not file_exists(file) then return {} end
lines = {}
for line in io.lines(file) do
lines[#lines + 1] = line
end
return lines
end
local file = 'Stats.txt'
local lines = lines_from(file)
for k,v in pairs(lines) do
print('line[' .. k .. ']', v)
end
Your code works as expected. I copied the code to a file named temp.lua. Here is the output:
line[1] function file_exists(file)
line[2] local f = io.open(file, "rb")
line[3] if f then f:close() end
line[4] return f ~= nil
line[5] end
line[6]
line[7]
line[8] function lines_from(file)
line[9] if not file_exists(file) then return {} end
line[10] lines = {}
line[11] for line in io.lines(file) do
line[12] lines[#lines + 1] = line
line[13] end
line[14] return lines
line[15]
line[16] end
line[17]
line[18] local file = 'temp.lua'
line[19] local lines = lines_from(file)
line[20]
line[21] for k,v in pairs(lines) do
line[22] print('line[' .. k .. ']', v)
line[23] end

Memory is not released after a dofile in lua

I am new to lua and was working on NodeMCU. I was trying to extract data from an xml file.
Here is my xml file:
<?xml version="1.0" encoding="UTF-8"?>
<netconfig>
<mode>0</mode>
<stamac>18-FE-34-A4-4B-05</stamac>
<staip>XXX.XXX.XXX.XXX</staip>
<stanetmask>XXX.XXX.XXX.XXX</stanetmask>
<stagateway>XXX.XXX.XXX.XXX</stagateway>
<apmac>1A-FE-34-A4-4B-05</apmac>
<apip>192.168.4.1</apip>
<apnetmask>255.255.255.0</apnetmask>
<apgateway>192.168.4.1</apgateway>
<port>80</port>
<dns>XXX.XXX.XXX.XXX</dns>
<dhcp>1</dhcp>
<stacustomconfig></stacustomconfig>
<timezone>10</timezone>
<serial>0x00000001</serial>
<connssid>ESP-10767109</connssid>
<ssid></ssid>
<passwd></passwd>
<hostname>ESP-10767109</hostname>
<reboot></reboot>
<message></message>
</netconfig>
Here is my xmlparser:
return function (xmlfile, xmlword)
file.open(xmlfile,"r")
local eofflag = 0
local i, j, k, l, xmloutput
while(eofflag < 1) do
local m = file.readline()
if(m == nil) then
eofflag = eofflag + 1
elseif (string.find(m, xmlword) ~= nil) then
i, j = string.find(m, xmlword, 1)
i = i - 1
j = j + 2
k, l = string.find(m, xmlword, j)
k = k - 3
l = l + 1
xmloutput = string.sub(m, j, k)
eoffile = 1
end
end
file.close()
return xmloutput
end
I call this file by using:
local port = dofile("xmlparser.lc")("netconfig.xml", "port")
But I always endup with memory less than I started with even though I call the garbagecollector. Moreover The heap seems to decrease further if the word I am searching for is near the end of file. I also observed that if the word I am looking for is not present, the heap has the less difference I started with.
Am I missing something?
and thanks for taking a look.
Alternatively, is the XML strictly for settings/only used by your lua code? If so I found it much easier and less memory and compute intensive to create the settings file in lua syntax, and then simply execute it. Declare a global table and exec the file:
cfg = {}
dofile("settings.lua")
and in the settings.lua file assign members:
cfg.port = "80"
cfg.dhcp = "1"
cfg.mode = "0"
You can write the table to file easily:
local buf = ""
for mykey,myval in pairs(cfg) do
buf = "cfg." .. mykey .. " = \"" .. myval .. "\""
file.writeline(buf)
end
For what it's worth...
Slight changes to your module:
local module =...
return function (xmlfile, xmlword)
file.open(xmlfile,"r")
local eofflag = 0
local i, j, k, l, xmloutput
while(eofflag < 1) do
local m = file.readline()
if(m == nil) then
eofflag = eofflag + 1
elseif (string.find(m, xmlword) ~= nil) then
i, j = string.find(m, xmlword, 1)
i = i - 1
j = j + 2
k, l = string.find(m, xmlword, j)
k = k - 3
l = l + 1
xmloutput = string.sub(m, j, k)
eoffile = 1
end
end
file.close()
package.loaded[module] = nil
module = nil
return xmloutput
end
That makes the whole thing local and dereferences the module in the packages.loaded table, allowing it to be garbage collected.
And call it using...
xmplarser = require("xmplarser")
v = xmplarser("netconfig.xml", "port")
Hope it helps.

Get a certain value from a concatenated table

Trying to allow a concatenated table to be referenced as such:
local group = table.concat(arguments, ",", 1)
where arguments = {"1,1,1"}
Currently, doing group[2] gives me the comma. How do I avoid that while still allowing for two-digit numbers?
(snippet of what I'm trying to use it for)
for i = 1, #group do
target:SetGroup(i, tonumber(group[i]))
end
Maybe you want something like
local i = 1
for v in string.gmatch(s, "(%w+),*") do
group[i] = v
i = i + 1
end
Revised version in response to comment, avoiding the table altogether:
local i = 1
for v in string.gmatch(s, "(%w+),*") do
target:SetGroup(i, tonumber(v))
i = i + 1
end
split function (you have to add it to code)
split = function(str, delim)
if not delim then
delim = " "
end
-- Eliminate bad cases...
if string.find(str, delim) == nil then
return { str }
end
local result = {}
local pat = "(.-)" .. delim .. "()"
local nb = 0
local lastPos
for part, pos in string.gfind(str, pat) do
nb = nb + 1
result[nb] = part
lastPos = pos
end
-- Handle the last field
result[nb + 1] = string.sub(str, lastPos)
return result
end
so
local arguments = {"1,1,1"};
local group = split(arguments[1], ",");
for i = 1, #group do
target:SetGroup(i, tonumber(group[i]))
end
also note that
local arguments = {"1,1,1"};
local group = split(arguments[1], ",");
local group_count = #group;
for i = 1, group_count do
target:SetGroup(i, tonumber(group[i]))
end
is faster code ;)

Resources