-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathImageTiles.cs
More file actions
276 lines (254 loc) · 11.1 KB
/
ImageTiles.cs
File metadata and controls
276 lines (254 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
// Copyright (c) TensorStack. All rights reserved.
// Licensed under the Apache 2.0 License.
using System;
using System.Threading.Tasks;
using TensorStack.Common.Tensor;
namespace TensorStack.Common.Image
{
/// <summary>
/// ImageTile Class to handle splitting and joining a larrgr image tensor into quarters with overlap.
/// </summary>
public record ImageTiles
{
/// <summary>
/// Initializes a new instance of the <see cref="ImageTiles"/> class.
/// </summary>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
/// <param name="tileMode">The tile mode</param>
/// <param name="overlap">The overlap.</param>
/// <param name="tile1">The tile1.</param>
/// <param name="tile2">The tile2.</param>
/// <param name="tile3">The tile3.</param>
/// <param name="tile4">The tile4.</param>
public ImageTiles(int width, int height, TileMode tileMode, int overlap, ImageTensor tile1, ImageTensor tile2, ImageTensor tile3, ImageTensor tile4)
{
Width = width;
Height = height;
Overlap = overlap;
TileMode = tileMode;
Tile1 = tile1;
Tile2 = tile2;
Tile3 = tile3;
Tile4 = tile4;
}
/// <summary>
/// Initializes a new instance of the <see cref="ImageTiles"/> class.
/// </summary>
/// <param name="sourceTensor">The source tensor.</param>
/// <param name="overlap">The overlap.</param>
public ImageTiles(ImageTensor sourceTensor, TileMode tileMode, int overlap = 16)
{
Overlap = overlap;
TileMode = tileMode;
Width = sourceTensor.Dimensions[3] / 2;
Height = sourceTensor.Dimensions[2] / 2;
Tile1 = SplitImageTile(sourceTensor, 0, 0, Height + overlap, Width + overlap);
Tile2 = SplitImageTile(sourceTensor, 0, Width - overlap, Height + overlap, Width * 2);
Tile3 = SplitImageTile(sourceTensor, Height - overlap, 0, Height * 2, Width + overlap);
Tile4 = SplitImageTile(sourceTensor, Height - overlap, Width - overlap, Height * 2, Width * 2);
}
public int Width { get; init; }
public int Height { get; init; }
public int Overlap { get; init; }
public TileMode TileMode { get; init; }
public ImageTensor Tile1 { get; init; }
public ImageTensor Tile2 { get; init; }
public ImageTensor Tile3 { get; init; }
public ImageTensor Tile4 { get; init; }
/// <summary>
/// Joins the tiles into a single ImageTensor.
/// </summary>
/// <returns>ImageTensor.</returns>
public ImageTensor JoinTiles()
{
var totalWidth = Width * 2;
var totalHeight = Height * 2;
var channels = Tile1.Dimensions[1];
var destination = new ImageTensor(totalHeight, totalWidth);
JoinTile(destination, Tile1, 0, 0, Height + Overlap, Width + Overlap);
JoinTile(destination, Tile2, 0, Width - Overlap, Height + Overlap, totalWidth);
JoinTile(destination, Tile3, Height - Overlap, 0, totalHeight, Width + Overlap);
JoinTile(destination, Tile4, Height - Overlap, Width - Overlap, totalHeight, totalWidth);
return destination;
}
public void JoinTile(ImageTensor destination, ImageTensor tile, int startRow, int startCol, int endRow, int endCol)
{
switch (TileMode)
{
case TileMode.Blend:
JoinTileBlend(destination, tile, startRow, startCol, endRow, endCol);
break;
case TileMode.Clip:
JoinTileClip(destination, tile, startRow, startCol, endRow, endCol);
break;
case TileMode.ClipBlend:
JoinTileClipBlend(destination, tile, startRow, startCol, endRow, endCol);
break;
case TileMode.Overlap:
JoinTileOverlap(destination, tile, startRow, startCol, endRow, endCol);
break;
case TileMode.None:
default:
throw new ArgumentException("TileMode None is invalid");
}
}
/// <summary>
/// Joins the tiles overlapping edges.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="tile">The tile.</param>
/// <param name="startRow">The start row.</param>
/// <param name="startCol">The start col.</param>
/// <param name="endRow">The end row.</param>
/// <param name="endCol">The end col.</param>
private static void JoinTileOverlap(ImageTensor destination, ImageTensor tile, int startRow, int startCol, int endRow, int endCol)
{
int height = endRow - startRow;
int width = endCol - startCol;
int channels = tile.Dimensions[1];
Parallel.For(0, channels, (c) =>
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
destination[0, c, startRow + i, startCol + j] = tile[0, c, i, j];
}
}
});
}
/// <summary>
/// Joins the tiles blending the overlapped edges.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="tile">The tile.</param>
/// <param name="startRow">The start row.</param>
/// <param name="startCol">The start col.</param>
/// <param name="endRow">The end row.</param>
/// <param name="endCol">The end col.</param>
private void JoinTileBlend(ImageTensor destination, ImageTensor tile, int startRow, int startCol, int endRow, int endCol)
{
int height = endRow - startRow;
int width = endCol - startCol;
int channels = tile.Dimensions[1];
Parallel.For(0, channels, (c) =>
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
var value = tile[0, c, i, j];
var existing = destination[0, c, startRow + i, startCol + j];
if (existing > 0)
{
value = (existing + value) / 2f;
}
destination[0, c, startRow + i, startCol + j] = value;
}
}
});
}
/// <summary>
/// Joins the tiles clipping the overlapped edges.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="tile">The tile.</param>
/// <param name="startRow">The start row.</param>
/// <param name="startCol">The start col.</param>
/// <param name="endRow">The end row.</param>
/// <param name="endCol">The end col.</param>
private void JoinTileClip(ImageTensor destination, ImageTensor tile, int startRow, int startCol, int endRow, int endCol)
{
int height = endRow - startRow;
int width = endCol - startCol;
int channels = tile.Dimensions[1];
Parallel.For(0, channels, (c) =>
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (startRow > 0 && i < Overlap)
continue;
if (startCol > 0 && j < Overlap)
continue;
if (startRow == 0 && i > (endRow - Overlap))
continue;
if (startCol == 0 && j > (endCol - Overlap))
continue;
var existing = destination[0, c, startRow + i, startCol + j];
if (existing > 0)
continue;
destination[0, c, startRow + i, startCol + j] = tile[0, c, i, j];
}
}
});
}
/// <summary>
/// Joins the tiles clipping and blending the overlapped edges.
/// </summary>
/// <param name="destination">The destination.</param>
/// <param name="tile">The tile.</param>
/// <param name="startRow">The start row.</param>
/// <param name="startCol">The start col.</param>
/// <param name="endRow">The end row.</param>
/// <param name="endCol">The end col.</param>
private void JoinTileClipBlend(ImageTensor destination, ImageTensor tile, int startRow, int startCol, int endRow, int endCol)
{
int clip = Overlap / 2;
int height = endRow - startRow;
int width = endCol - startCol;
int channels = tile.Dimensions[1];
Parallel.For(0, channels, (c) =>
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
if (startRow > 0 && i < clip)
continue;
if (startCol > 0 && j < clip)
continue;
if (startRow == 0 && i > (endRow - clip))
continue;
if (startCol == 0 && j > (endCol - clip))
continue;
var value = tile[0, c, i, j];
var existing = destination[0, c, startRow + i, startCol + j];
if (existing > 0)
value = (existing + value) / 2f;
destination[0, c, startRow + i, startCol + j] = value;
}
}
});
}
/// <summary>
/// Splits the image tile.
/// </summary>
/// <param name="source">The source.</param>
/// <param name="startRow">The start row.</param>
/// <param name="startCol">The start col.</param>
/// <param name="endRow">The end row.</param>
/// <param name="endCol">The end col.</param>
/// <returns>ImageTensor.</returns>
private static ImageTensor SplitImageTile(ImageTensor source, int startRow, int startCol, int endRow, int endCol)
{
int height = endRow - startRow;
int width = endCol - startCol;
int channels = source.Dimensions[1];
var splitTensor = new ImageTensor(height, width);
Parallel.For(0, channels, (c) =>
{
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
splitTensor[0, c, i, j] = source[0, c, startRow + i, startCol + j];
}
}
});
return splitTensor;
}
}
}