Sorry to bother you, but I am very excited after discovering LuaImg:
It appears perfect for my needs (extracting metal implant coordinates from x-rays of hip replacements in my patients).
I am (just) able to use Lua, and find the syntax of your extensions very neat and appealing.
Not having to search for additional libraries (image load/save, vectors, matrix, convolve etc.) is ideal for a "non-compiling user".
Thank you for providing a compiled distribution for Windows (I have ver 0.9). I do not have, nor want, a compiler!
Using your examples, I can see how to use convolution kernels to process the images, and this works well with my 4MB pelvic x-ray exports (JPEG). I am gradually implementing a collection of processes, based upon an old text book and some googling - see below [1].
On my hardware [2] I am having memory problems; the script consumes RAM up to about 2GB (25% of my total) and then crashes!
Reading about Lua garbage collection [3], it states "objects accessible from the root set are preserved".
Are LuaImg vectors and images ever subject to GC?
Is there a better way for me to write my code to allow GC?
Would an image:clear() method be difficult to add?
I would really like to use LuaImg, it follows the Lua principle of providing the basic building blocks - so one can build anything.
Kind Regards Gavin Holt
Orthopaedic surgeon - "strong as an ox, and twice as intelligent"
[1] Code
-- LuaImg prototype module and test script
-- Gavin Holt 2024
-- Load some lua helpers
require("extensions") -- cls() and pause()
cls()
-- Convolution kernels -- globals
function identity()
return make(vec(3,3),1,{0,0,0, 0,1,0, 0,0,0})
end
function boxblur(n)
-- Allow no parameter
if not n then n=3 end
-- Integers only
n = floor(n)
-- Alter any even numbers
if (n % 2 == 0) then n = n + 1 end
local t = {}
for i=1,n*n,1 do
t[i] = 1
end
return make(vec(n,n),1,t)*(1/(n*n))
end
function sharpen()
return make(vec(3,3),1,{0,-1,0, -1,5,-1, 0,-1,0})
end
function sobelx()
return make(vec(3,3),1,{1,0,-1, 2,0,-2, 1,0,-1})
end
function sobely()
return make(vec(3,3),1,{1,2,1, 0,0,0, -1,-2,-1})
end
function prewittx()
return make(vec(3,3),1,{1,0,-1, 1,0,-1, 1,0,-1})
end
function prewitty()
return make(vec(3,3),1,{1,1,1, 0,0,0, -1,-1,-1})
end
function scharrx()
return make(vec(3,3),1,{47,0,-47, 162,0,-162, 47,0,-47})
end
function scharry()
return make(vec(3,3),1,{47,162,47, 0,0,0, -47,-162,-47})
end
function laplace()
return make(vec(3,3),1,{0,-1,0, -1,4,-1, 0,-1,0}):normalise()
end
-- Other global functions
function threashold(i,cutoff)
return make(vec(i.width,i.height),3,true,vec(cutoff,cutoff,cutoff))
end
-- Namespace for utility functions
img = {}
function img.hist(filename)
local i = open(filename)
local ret = {}
i:foreach(function(p)
intensity = unpack(p) -- takes the first returned value ?correct
intensity = math.floor(intensity*100)
if intensity>100 then intensity = 101 end
if not ret[intensity] then
ret[intensity] = 1
else
ret[intensity] = ret[intensity]+1
end
end)
return(ret)
end
function img.show(filename)
-- os.execute is blocking, so use a launcher
os.execute([[O:\MyProfile\cmd\shelexec.exe /Params:]].. filename ..[[ /EXE O:\MyProfile\cmd\Imagine.exe ]])
return
end
-- Tests
-- =====
-- Clear previous output - pass
os.execute([[del /q O:\MyProfile\tests\Lua_IMG\*.png]])
-- Load test image - pass
testfile = [[O:\MyProfile\tests\Lua_IMG\LeftHip001.jpg]]
testimg = open(testfile)
-- Too much activity seems to crash the thread @2GB memory usage
-- uncomment goto command to stop crashing
-- goto skip
-- testimg: type - pass
print(testfile..": type = "..type(testimg))
-- testimg: colourChannels - pass
print(testfile..": colourChannels = "..testimg.colourChannels)
-- testimg: hasAlpha - pass
print(testfile..": hasAlpha = "..tostring(testimg.hasAlpha))
-- testimg: height - pass
print(testfile..": height = "..testimg.height)
-- testimg: width - pass
print(testfile..": width = "..testimg.width)
-- testimg: numPixels - pass
print(testfile..": numPixels = "..testimg.numPixels)
-- testimg: size - pass
print(testfile..": size = "..testimg.size)
-- testimg: histogram - pass
t = img.hist(testfile)
print(testfile..": histogram\n")
print("|\tIntensity*100\t|\tFrequency\t|")
print("|\t-------------\t|\t---------\t|")
for i,v in ipairs(t) do print("|\t"..i.."\t\t|\t"..v.."\t\t|") end
-- testimg: identity save and view - pass
testimg:convolve(identity()):save([[O:\MyProfile\tests\Lua_IMG\01_identity.png]])
img.show([[O:\MyProfile\tests\Lua_IMG\01_identity.png]])
-- testimg: blur with boxblur - pass
testimg:convolve(boxblur(19)):save([[O:\MyProfile\tests\Lua_IMG\02_boxblur.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\02_boxblur.png]])
-- testimg: blur with a gaussian - pass
testimg:convolveSep(gaussian(33)):save([[O:\MyProfile\tests\Lua_IMG\03_gaussian.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\03_gaussian.png]])
-- tesimg: sobel - pass
testimg:convolve(sobelx()):convolve(sobely()):save([[O:\MyProfile\tests\Lua_IMG\04_sobel.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\04_sobel.png]])
-- tesimg: prewitt - pass
testimg:convolve(prewittx()):convolve(prewitty()):save([[O:\MyProfile\tests\Lua_IMG\05_prewitt.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\05_prewitt.png]])
-- tesimg: scharr - pass
testimg:convolve(scharrx()):convolve(scharry()):save([[O:\MyProfile\tests\Lua_IMG\06_scharr.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\06_scharr.png]])
-- tesimg: sharpen with "image + (image – blurred)" recipe - pass
s = testimg:convolveSep(gaussian(19))
S = (testimg + (testimg - s))
S:save([[O:\MyProfile\tests\Lua_IMG\07_sharp.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\07_sharp.png]])
-- testimg: sharpen with "laplace of the gaussian" recipe - pass with poor output
-- unpack should probably not be used here!
l = testimg:convolveSep(gaussian(9)):convolve(laplace())
M = l:reduce(0, function(a,b) return max(unpack(a),unpack(b)) end)
m = -(-l):reduce(0, function(a,b) return max(unpack(a),unpack(b)) end)
l = ((l - m)/(M-m))
l:save([[O:\MyProfile\tests\Lua_IMG\08_laplace.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\08_laplace.png]])
-- testimg: Combination testimg:max(laplace) - pass
testimg:max(l):save([[O:\MyProfile\tests\Lua_IMG\09_begona.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\09_begona.png]])
::skip::
-- testimg: thresholding1 - not quite right yet - I think want a binary output
testimg:max(threashold(testimg,0.5)):save([[O:\MyProfile\tests\Lua_IMG\10_upperhalf.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\10_upperhalf.png]])
-- testimg: thresholding2 - not quite right yet - I think want a binary output
testimg:min(threashold(testimg,0.5)):save([[O:\MyProfile\tests\Lua_IMG\11_lowerhalf.png]])
-- img.show([[O:\MyProfile\tests\Lua_IMG\11_lowerhalf.png]])
pause()
-- Future prospects
-- ================
-- testimg: grey scale - ? take Euclidian length of the color vector
-- testimg: median filter - May require hand rolled convolution
-- testimg: zero crossing edge detector
-- testimg: contour extraction
-- testimg: fast fourier curve fitting
-- testimg: classification into part: cup, stem, and head
-- testimg: orientation: of parts realtive to each other and a bone landmarks (e.g. pelvis, femoral shaft)
-- testing: registration: to allow comparison between images
-- testimg: migration: change over time indicating loosening of the implant from the bone
Dear Dave,
Sorry to bother you, but I am very excited after discovering LuaImg:
It appears perfect for my needs (extracting metal implant coordinates from x-rays of hip replacements in my patients).
I am (just) able to use Lua, and find the syntax of your extensions very neat and appealing.
Not having to search for additional libraries (image load/save, vectors, matrix, convolve etc.) is ideal for a "non-compiling user".
Thank you for providing a compiled distribution for Windows (I have ver 0.9). I do not have, nor want, a compiler!
Using your examples, I can see how to use convolution kernels to process the images, and this works well with my 4MB pelvic x-ray exports (JPEG). I am gradually implementing a collection of processes, based upon an old text book and some googling - see below [1].
On my hardware [2] I am having memory problems; the script consumes RAM up to about 2GB (25% of my total) and then crashes!
Reading about Lua garbage collection [3], it states "objects accessible from the root set are preserved".
Are LuaImg vectors and images ever subject to GC?
Is there a better way for me to write my code to allow GC?
Would an image:clear() method be difficult to add?
I would really like to use LuaImg, it follows the Lua principle of providing the basic building blocks - so one can build anything.
Kind Regards Gavin Holt Orthopaedic surgeon - "strong as an ox, and twice as intelligent"
[1] Code
[2] Hardware
[3] https://www.lua.org/wshop18/Ierusalimschy.pdf
[4] Sample x-ray