2015-05-16 17:48:05 +12:00
|
|
|
local gm = require 'graphicsmagick'
|
2015-06-16 23:41:48 +12:00
|
|
|
local ffi = require 'ffi'
|
2015-11-13 19:53:41 +13:00
|
|
|
local iproc = require 'iproc'
|
2017-02-12 21:04:29 +13:00
|
|
|
local sRGB2014 = require 'sRGB2014'
|
2015-05-16 17:48:05 +12:00
|
|
|
require 'pl'
|
|
|
|
|
|
|
|
local image_loader = {}
|
2015-11-09 08:35:11 +13:00
|
|
|
local clip_eps8 = (1.0 / 255.0) * 0.5 - (1.0e-7 * (1.0 / 255.0) * 0.5)
|
|
|
|
local clip_eps16 = (1.0 / 65535.0) * 0.5 - (1.0e-7 * (1.0 / 65535.0) * 0.5)
|
2015-11-08 10:31:29 +13:00
|
|
|
local background_color = 0.5
|
2015-11-08 09:44:14 +13:00
|
|
|
|
2016-04-15 12:13:37 +12:00
|
|
|
function image_loader.encode_png(rgb, options)
|
|
|
|
options = options or {}
|
|
|
|
options.depth = options.depth or 8
|
|
|
|
if options.inplace == nil then
|
|
|
|
options.inplace = false
|
2016-03-21 07:42:47 +13:00
|
|
|
end
|
2015-11-13 19:53:41 +13:00
|
|
|
rgb = iproc.byte2float(rgb)
|
2016-04-15 12:13:37 +12:00
|
|
|
if options.depth < 16 then
|
|
|
|
if options.inplace then
|
2016-03-21 07:42:47 +13:00
|
|
|
rgb:add(clip_eps8)
|
|
|
|
else
|
|
|
|
rgb = rgb:clone():add(clip_eps8)
|
|
|
|
end
|
2016-09-24 11:17:37 +12:00
|
|
|
rgb:clamp(0.0, 1.0)
|
2016-03-21 07:42:47 +13:00
|
|
|
rgb = rgb:mul(255):floor():div(255)
|
2015-06-16 23:41:48 +12:00
|
|
|
else
|
2016-04-15 12:13:37 +12:00
|
|
|
if options.inplace then
|
2016-03-21 07:42:47 +13:00
|
|
|
rgb:add(clip_eps16)
|
|
|
|
else
|
|
|
|
rgb = rgb:clone():add(clip_eps16)
|
|
|
|
end
|
2016-09-24 11:17:37 +12:00
|
|
|
rgb:clamp(0.0, 1.0)
|
2016-03-21 07:42:47 +13:00
|
|
|
rgb = rgb:mul(65535):floor():div(65535)
|
2015-12-01 21:26:45 +13:00
|
|
|
end
|
|
|
|
local im
|
|
|
|
if rgb:size(1) == 4 then -- RGBA
|
|
|
|
im = gm.Image(rgb, "RGBA", "DHW")
|
2016-04-15 16:29:50 +12:00
|
|
|
if options.grayscale then
|
|
|
|
im:type("GrayscaleMatte")
|
|
|
|
end
|
2015-12-01 21:26:45 +13:00
|
|
|
elseif rgb:size(1) == 3 then -- RGB
|
|
|
|
im = gm.Image(rgb, "RGB", "DHW")
|
2016-04-15 16:29:50 +12:00
|
|
|
if options.grayscale then
|
|
|
|
im:type("Grayscale")
|
|
|
|
end
|
2015-12-01 21:26:45 +13:00
|
|
|
elseif rgb:size(1) == 1 then -- Y
|
|
|
|
im = gm.Image(rgb, "I", "DHW")
|
2016-04-15 16:29:50 +12:00
|
|
|
im:type("Grayscale")
|
2015-06-16 23:41:48 +12:00
|
|
|
end
|
2016-04-15 12:13:37 +12:00
|
|
|
if options.gamma then
|
|
|
|
im:gamma(options.gamma)
|
|
|
|
end
|
2017-02-12 21:04:29 +13:00
|
|
|
if options.icm and im.profile then
|
|
|
|
im:profile("icm", sRGB2014)
|
|
|
|
im:profile("icm", options.icm)
|
|
|
|
end
|
2016-04-23 15:48:24 +12:00
|
|
|
return im:depth(options.depth):format("PNG"):toString()
|
2015-06-16 23:41:48 +12:00
|
|
|
end
|
2016-04-15 12:13:37 +12:00
|
|
|
function image_loader.save_png(filename, rgb, options)
|
|
|
|
local blob = image_loader.encode_png(rgb, options)
|
2015-06-16 23:41:48 +12:00
|
|
|
local fp = io.open(filename, "wb")
|
2015-11-05 18:59:51 +13:00
|
|
|
if not fp then
|
|
|
|
error("IO error: " .. filename)
|
|
|
|
end
|
2015-11-13 20:11:12 +13:00
|
|
|
fp:write(blob)
|
2015-06-16 23:41:48 +12:00
|
|
|
fp:close()
|
|
|
|
return true
|
2015-05-16 17:48:05 +12:00
|
|
|
end
|
2015-11-13 19:53:41 +13:00
|
|
|
function image_loader.decode_float(blob)
|
2015-05-16 17:48:05 +12:00
|
|
|
local load_image = function()
|
2016-04-15 12:13:37 +12:00
|
|
|
local meta = {}
|
2015-05-16 17:48:05 +12:00
|
|
|
local im = gm.Image()
|
2015-11-05 18:59:51 +13:00
|
|
|
local gamma_lcd = 0.454545
|
2015-06-16 23:41:48 +12:00
|
|
|
|
2015-05-16 17:48:05 +12:00
|
|
|
im:fromBlob(blob, #blob)
|
2017-02-12 21:04:29 +13:00
|
|
|
if im.profile then
|
|
|
|
meta.icm = im:profile("icm")
|
|
|
|
if meta.icm then
|
|
|
|
im:profile("icm", sRGB2014)
|
|
|
|
im:removeProfile()
|
|
|
|
end
|
|
|
|
end
|
2015-11-04 03:20:21 +13:00
|
|
|
if im:colorspace() == "CMYK" then
|
|
|
|
im:colorspace("RGB")
|
|
|
|
end
|
2016-04-15 12:13:37 +12:00
|
|
|
if gamma ~= 0 and math.floor(im:gamma() * 1000000) / 1000000 ~= gamma_lcd then
|
|
|
|
meta.gamma = im:gamma()
|
2015-11-05 18:59:51 +13:00
|
|
|
end
|
2016-04-15 16:14:02 +12:00
|
|
|
local image_type = im:type()
|
2016-04-15 16:29:50 +12:00
|
|
|
if image_type == "Grayscale" or image_type == "GrayscaleMatte" then
|
|
|
|
meta.grayscale = true
|
|
|
|
end
|
2016-04-15 16:14:02 +12:00
|
|
|
if image_type == "TrueColorMatte" or image_type == "GrayscaleMatte" then
|
2015-06-16 23:41:48 +12:00
|
|
|
-- split alpha channel
|
2015-05-16 17:48:05 +12:00
|
|
|
im = im:toTensor('float', 'RGBA', 'DHW')
|
2016-04-15 16:14:02 +12:00
|
|
|
meta.alpha = im[4]:reshape(1, im:size(2), im:size(3))
|
|
|
|
-- drop full transparent background
|
|
|
|
local mask = torch.le(meta.alpha, 0.0)
|
|
|
|
im[1][mask] = background_color
|
|
|
|
im[2][mask] = background_color
|
|
|
|
im[3][mask] = background_color
|
2015-05-16 17:48:05 +12:00
|
|
|
local new_im = torch.FloatTensor(3, im:size(2), im:size(3))
|
2015-06-16 23:41:48 +12:00
|
|
|
new_im[1]:copy(im[1])
|
|
|
|
new_im[2]:copy(im[2])
|
|
|
|
new_im[3]:copy(im[3])
|
2015-11-13 19:53:41 +13:00
|
|
|
im = new_im
|
2015-05-16 17:48:05 +12:00
|
|
|
else
|
2015-11-13 19:53:41 +13:00
|
|
|
im = im:toTensor('float', 'RGB', 'DHW')
|
2015-05-16 17:48:05 +12:00
|
|
|
end
|
2016-04-15 12:13:37 +12:00
|
|
|
meta.blob = blob
|
|
|
|
return {im, meta}
|
2015-05-16 17:48:05 +12:00
|
|
|
end
|
|
|
|
local state, ret = pcall(load_image)
|
|
|
|
if state then
|
2016-04-15 12:13:37 +12:00
|
|
|
return ret[1], ret[2]
|
2015-05-16 17:48:05 +12:00
|
|
|
else
|
2016-04-15 12:13:37 +12:00
|
|
|
return nil, nil
|
2015-05-16 17:48:05 +12:00
|
|
|
end
|
|
|
|
end
|
2015-11-13 19:53:41 +13:00
|
|
|
function image_loader.decode_byte(blob)
|
2016-04-15 12:13:37 +12:00
|
|
|
local im, meta
|
|
|
|
im, meta = image_loader.decode_float(blob)
|
2015-11-13 19:53:41 +13:00
|
|
|
|
|
|
|
if im then
|
|
|
|
im = iproc.float2byte(im)
|
|
|
|
-- hmm, alpha does not convert here
|
2016-04-15 12:13:37 +12:00
|
|
|
return im, meta
|
2015-11-13 19:53:41 +13:00
|
|
|
else
|
2016-04-15 12:13:37 +12:00
|
|
|
return nil, nil
|
2015-11-13 19:53:41 +13:00
|
|
|
end
|
|
|
|
end
|
2015-05-16 17:48:05 +12:00
|
|
|
function image_loader.load_float(file)
|
|
|
|
local fp = io.open(file, "rb")
|
2015-06-23 02:01:56 +12:00
|
|
|
if not fp then
|
|
|
|
error(file .. ": failed to load image")
|
|
|
|
end
|
2015-05-16 17:48:05 +12:00
|
|
|
local buff = fp:read("*a")
|
|
|
|
fp:close()
|
|
|
|
return image_loader.decode_float(buff)
|
|
|
|
end
|
|
|
|
function image_loader.load_byte(file)
|
|
|
|
local fp = io.open(file, "rb")
|
2015-06-23 02:01:56 +12:00
|
|
|
if not fp then
|
|
|
|
error(file .. ": failed to load image")
|
|
|
|
end
|
2015-05-16 17:48:05 +12:00
|
|
|
local buff = fp:read("*a")
|
|
|
|
fp:close()
|
|
|
|
return image_loader.decode_byte(buff)
|
|
|
|
end
|
|
|
|
local function test()
|
2015-11-13 19:53:41 +13:00
|
|
|
torch.setdefaulttensortype("torch.FloatTensor")
|
|
|
|
local a = image_loader.load_float("../images/lena.png")
|
2015-11-13 20:11:12 +13:00
|
|
|
local blob = image_loader.encode_png(a)
|
|
|
|
local b = image_loader.decode_float(blob)
|
2015-11-13 19:53:41 +13:00
|
|
|
assert((b - a):abs():sum() == 0)
|
|
|
|
|
|
|
|
a = image_loader.load_byte("../images/lena.png")
|
2015-11-13 20:11:12 +13:00
|
|
|
blob = image_loader.encode_png(a)
|
|
|
|
b = image_loader.decode_byte(blob)
|
2015-11-13 19:53:41 +13:00
|
|
|
assert((b:float() - a:float()):abs():sum() == 0)
|
2015-05-16 17:48:05 +12:00
|
|
|
end
|
|
|
|
--test()
|
|
|
|
return image_loader
|