Added README.md
[dyncnn.git] / img.lua
1
2 --[[
3
4    dyncnn is a deep-learning algorithm for the prediction of
5    interacting object dynamics
6
7    Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/
8    Written by Francois Fleuret <francois.fleuret@idiap.ch>
9
10    This file is part of dyncnn.
11
12    dyncnn is free software: you can redistribute it and/or modify it
13    under the terms of the GNU General Public License version 3 as
14    published by the Free Software Foundation.
15
16    dyncnn is distributed in the hope that it will be useful, but
17    WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    General Public License for more details.
20
21    You should have received a copy of the GNU General Public License
22    along with dyncnn.  If not, see <http://www.gnu.org/licenses/>.
23
24 ]]--
25
26 require 'torch'
27
28 --[[
29
30 The combineImage function takes as input a parameter c which is the
31 value to use for the background of the resulting image (padding and
32 such), and t which is either a 2d tensor, a 3d tensor, or a table.
33
34  * If t is a 3d tensor, it is returned unchanged.
35
36  * If t is a 2d tensor [r x c], it is reshaped to [1 x r x c] and
37    returned.
38
39  * If t is a table, combineImage first calls itself recursively on
40    t[1], t[2], etc.
41
42    It then creates a new tensor by concatenating the results
43    horizontally if t.vertical is nil, vertically otherwise.
44
45    It adds a padding of t.pad pixels if this field is set.
46
47  * Example
48
49    x = torch.Tensor(64, 64):fill(0.5)
50    y = torch.Tensor(100, 30):fill(0.85)
51
52    i = combineImages(1.0,
53       {
54          pad = 1,
55          vertical = true,
56          { pad = 1, x },
57          {
58             y,
59             { pad = 4, torch.Tensor(32, 16):fill(0.25) },
60             { pad = 1, torch.Tensor(45, 54):uniform(0.25, 0.9) },
61          }
62       }
63    )
64
65    image.save('example.png', i)
66
67 ]]--
68
69 function combineImages(c, t)
70
71    if torch.isTensor(t) then
72
73       if t:dim() == 3 then
74          return t
75       elseif t:dim() == 2 then
76          return torch.Tensor(1, t:size(1), t:size(2)):copy(t)
77       else
78          error('can only deal with [height x width] or [channel x height x width] tensors.')
79       end
80
81    else
82
83       local subImages = {} -- The subimages
84       local nc = 0 -- Nb of columns
85       local nr = 0 -- Nb of rows
86
87       for i, x in ipairs(t) do
88          subImages[i] = combineImages(c, x)
89          if t.vertical then
90             nr = nr + subImages[i]:size(2)
91             nc = math.max(nc, subImages[i]:size(3))
92          else
93             nr = math.max(nr, subImages[i]:size(2))
94             nc = nc + subImages[i]:size(3)
95          end
96       end
97
98       local pad = t.pad or 0
99       local result = torch.Tensor(subImages[1]:size(1), nr + 2 * pad, nc + 2 * pad):fill(c)
100       local co = 1 + pad -- Origin column
101       local ro = 1 + pad -- Origin row
102
103       for i in ipairs(t) do
104
105          result:sub(1, subImages[1]:size(1),
106                     ro, ro + subImages[i]:size(2) - 1,
107                     co, co + subImages[i]:size(3) - 1):copy(subImages[i])
108
109          if t.vertical then
110             ro = ro + subImages[i]:size(2)
111          else
112             co = co + subImages[i]:size(3)
113          end
114
115       end
116
117       return result
118
119    end
120
121 end
122
123 --[[
124
125 The imageFromTensors function gets as input a list of tensors of
126 arbitrary dimensions each, but whose two last dimensions stand for
127 height x width. It creates an image tensor (2d, one channel) with each
128 argument tensor unfolded per row.
129
130 ]]--
131
132 function imageFromTensors(bt, signed)
133    local gap = 1
134    local tgap = -1
135    local width = 0
136    local height = gap
137
138    for _, t in pairs(bt) do
139       local d = t:dim()
140       local h, w = t:size(d - 1), t:size(d)
141       local n = t:nElement() / (w * h)
142       width = math.max(width, gap + n * (gap + w))
143       height = height + gap + tgap + gap + h
144    end
145
146    local e = torch.Tensor(3, height, width):fill(1.0)
147    local y0 = 1 + gap
148
149    for _, t in pairs(bt) do
150       local d = t:dim()
151       local h, w = t:size(d - 1), t:size(d)
152       local n = t:nElement() / (w * h)
153       local z = t:norm() / math.sqrt(t:nElement())
154
155       local x0 = 1 + gap + math.floor( (width - n * (w + gap)) /2 )
156       local u = torch.Tensor(t:size()):copy(t):resize(n, h, w)
157       for m = 1, n do
158
159          for c = 1, 3 do
160             for y = 0, h+1 do
161                e[c][y0 + y - 1][x0     - 1] = 0.0
162                e[c][y0 + y - 1][x0 + w    ] = 0.0
163             end
164             for x = 0, w+1 do
165                e[c][y0     - 1][x0 + x - 1] = 0.0
166                e[c][y0 + h    ][x0 + x - 1] = 0.0
167             end
168          end
169
170          for y = 1, h do
171             for x = 1, w do
172                local v = u[m][y][x] / z
173                local r, g, b
174                if signed then
175                   if v < -1 then
176                      r, g, b = 0.0, 0.0, 1.0
177                   elseif v > 1 then
178                      r, g, b = 1.0, 0.0, 0.0
179                   elseif v >= 0 then
180                      r, g, b = 1.0, 1.0 - v, 1.0 - v
181                   else
182                      r, g, b = 1.0 + v, 1.0 + v, 1.0
183                   end
184                else
185                   if v <= 0 then
186                      r, g, b = 1.0, 1.0, 1.0
187                   elseif v > 1 then
188                      r, g, b = 0.0, 0.0, 0.0
189                   else
190                      r, g, b = 1.0 - v, 1.0 - v, 1.0 - v
191                   end
192                end
193                e[1][y0 + y - 1][x0 + x - 1] = r
194                e[2][y0 + y - 1][x0 + x - 1] = g
195                e[3][y0 + y - 1][x0 + x - 1] = b
196             end
197          end
198          x0 = x0 + w + gap
199       end
200       y0 = y0 + h + gap + tgap + gap
201    end
202
203    return e
204 end