Skip to content
This repository was archived by the owner on Nov 19, 2020. It is now read-only.

Commit 8b4a429

Browse files
authored
Merge pull request #825 from volgunin/development
Speed optimization: Replace multi-dimentional with jagged arrays in IntegralImage.cs
2 parents 2d3d097 + ac7e9a9 commit 8b4a429

File tree

4 files changed

+86
-43
lines changed

4 files changed

+86
-43
lines changed

Sources/Accord.Imaging/AForge.Imaging/IntegralImage.cs

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace Accord.Imaging
1111
using System;
1212
using System.Drawing;
1313
using System.Drawing.Imaging;
14+
using Accord.Math;
1415

1516
/// <summary>
1617
/// Integral image.
@@ -52,12 +53,20 @@ namespace Accord.Imaging
5253
public class IntegralImage : ICloneable
5354
{
5455
/// <summary>
55-
/// Intergral image's array.
56+
/// Integral image's array.
57+
/// </summary>
58+
///
59+
/// <remarks>See remarks to <see cref="Matrix"/> property.</remarks>
60+
///
61+
protected readonly uint[][] matrix = null;
62+
63+
/// <summary>
64+
/// Integral image's array.
5665
/// </summary>
5766
///
5867
/// <remarks>See remarks to <see cref="InternalData"/> property.</remarks>
5968
///
60-
protected readonly uint[,] integralImage = null;
69+
protected uint[,] integralImage = null;
6170

6271
// image's width and height
6372
private readonly int width;
@@ -79,6 +88,23 @@ public int Height
7988
get { return height; }
8089
}
8190

91+
/// <summary>
92+
/// Provides access to internal array keeping integral image data.
93+
/// </summary>
94+
///
95+
/// <remarks>
96+
/// <para><note>The array should be accessed by [y][x] indexing.</note></para>
97+
///
98+
/// <para><note>The array's size is [<see cref="Height"/>+1, <see cref="Width"/>+1]. The first
99+
/// row and column are filled with zeros, what is done for more efficient calculation of
100+
/// rectangles' sums.</note></para>
101+
/// </remarks>
102+
///
103+
public uint[][] Matrix
104+
{
105+
get { return matrix; }
106+
}
107+
82108
/// <summary>
83109
/// Provides access to internal array keeping integral image data.
84110
/// </summary>
@@ -91,9 +117,25 @@ public int Height
91117
/// rectangles' sums.</note></para>
92118
/// </remarks>
93119
///
120+
[Obsolete("Please use Matrix property instead.")]
94121
public uint[,] InternalData
95122
{
96-
get { return integralImage; }
123+
get
124+
{
125+
if (integralImage == null)
126+
{
127+
integralImage = new uint[height + 1, width + 1];
128+
for (int y = 0; y <= height; y++)
129+
{
130+
for (int x = 0; x <= width; x++)
131+
{
132+
integralImage[y, x] = matrix[y][x];
133+
}
134+
}
135+
}
136+
137+
return integralImage;
138+
}
97139
}
98140

99141
/// <summary>
@@ -103,15 +145,15 @@ public int Height
103145
/// <param name="width">Image width.</param>
104146
/// <param name="height">Image height.</param>
105147
///
106-
/// <remarks>The constractor is protected, what makes it imposible to instantiate this
148+
/// <remarks>The constructor is protected, what makes it impossible to instantiate this
107149
/// class directly. To create an instance of this class <see cref="FromBitmap(Bitmap)"/> or
108150
/// <see cref="FromBitmap(BitmapData)"/> method should be used.</remarks>
109151
///
110152
protected IntegralImage(int width, int height)
111153
{
112154
this.width = width;
113155
this.height = height;
114-
integralImage = new uint[height + 1, width + 1];
156+
this.matrix = Jagged.Zeros<uint>(height + 1, width + 1);
115157
}
116158

117159
/// <summary>
@@ -129,7 +171,7 @@ public static IntegralImage FromBitmap(Bitmap image)
129171
// check image format
130172
if (image.PixelFormat != PixelFormat.Format8bppIndexed)
131173
{
132-
throw new UnsupportedImageFormatException("Source image can be graysclae (8 bpp indexed) image only.");
174+
throw new UnsupportedImageFormatException("Source image can be grayscale (8 bpp indexed) image only.");
133175
}
134176

135177
// lock source image
@@ -174,7 +216,7 @@ public static IntegralImage FromBitmap(UnmanagedImage image)
174216
// check image format
175217
if (image.PixelFormat != PixelFormat.Format8bppIndexed)
176218
{
177-
throw new ArgumentException("Source image can be graysclae (8 bpp indexed) image only.");
219+
throw new ArgumentException("Source image can be grayscale (8 bpp indexed) image only.");
178220
}
179221

180222
// get source image size
@@ -184,7 +226,7 @@ public static IntegralImage FromBitmap(UnmanagedImage image)
184226

185227
// create integral image
186228
var im = new IntegralImage(width, height);
187-
uint[,] integralImage = im.integralImage;
229+
uint[][] matrix = im.matrix;
188230

189231
// do the job
190232
unsafe
@@ -203,7 +245,7 @@ public static IntegralImage FromBitmap(UnmanagedImage image)
203245

204246
rowSum += *src;
205247

206-
integralImage[y, x] = rowSum + integralImage[y - 1, x];
248+
matrix[y][x] = rowSum + matrix[y - 1][x];
207249
}
208250
src += offset;
209251
}
@@ -240,7 +282,7 @@ public uint GetRectangleSum(int x1, int y1, int x2, int y2)
240282
if (x2 > width) x2 = width;
241283
if (y2 > height) y2 = height;
242284

243-
return integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2];
285+
return matrix[y2][x2] + matrix[y1][x1] - matrix[y2][x1] - matrix[y1][x2];
244286
}
245287

246288
/// <summary>
@@ -256,7 +298,7 @@ public uint GetRectangleSum(int x1, int y1, int x2, int y2)
256298
/// <remarks><para>The method calculates horizontal wavelet, which is a difference
257299
/// of two horizontally adjacent boxes' sums, i.e. <b>A-B</b>. A is the sum of rectangle with coordinates
258300
/// (x, y-radius, x+radius-1, y+radius-1). B is the sum of rectangle with coordinates
259-
/// (x-radius, y-radius, x-1, y+radiys-1).</para></remarks>
301+
/// (x-radius, y-radius, x-1, y+radius-1).</para></remarks>
260302
///
261303
public int GetHaarXWavelet(int x, int y, int radius)
262304
{
@@ -313,7 +355,7 @@ public uint GetRectangleSumUnsafe(int x1, int y1, int x2, int y2)
313355
x2++;
314356
y2++;
315357

316-
return integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2];
358+
return matrix[y2][x2] + matrix[y1][x1] - matrix[y2][x1] - matrix[y1][x2];
317359
}
318360

319361
/// <summary>
@@ -385,7 +427,7 @@ public float GetRectangleMean(int x1, int y1, int x2, int y2)
385427
if (y2 > height) y2 = height;
386428

387429
// return sum divided by actual rectangles size
388-
return (float)((double)(integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]) /
430+
return (float)((double)(matrix[y2][x2] + matrix[y1][x1] - matrix[y2][x1] - matrix[y1][x2]) /
389431
(double)((x2 - x1) * (y2 - y1)));
390432
}
391433

@@ -408,7 +450,7 @@ public float GetRectangleMeanUnsafe(int x1, int y1, int x2, int y2)
408450
y2++;
409451

410452
// return sum divided by actual rectangles size
411-
return (float)((double)(integralImage[y2, x2] + integralImage[y1, x1] - integralImage[y2, x1] - integralImage[y1, x2]) /
453+
return (float)((double)(matrix[y2][x2] + matrix[y1][x1] - matrix[y2][x1] - matrix[y1][x2]) /
412454
(double)((x2 - x1) * (y2 - y1)));
413455
}
414456

Sources/Accord.Imaging/Interest Points/FREAK/FastRetinaKeypointDescriptor.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -345,10 +345,10 @@ private unsafe int mean(double kx, double ky, int scale, int orientation, int po
345345
int x_right = (int)(xf + radius + 1.5); // integral image is 1px wider
346346
int y_bottom = (int)(yf + radius + 1.5); // integral image is 1px higher
347347

348-
ret_val = (int)Integral.InternalData[y_bottom, x_right]; // bottom right corner
349-
ret_val -= (int)Integral.InternalData[y_bottom, x_left];
350-
ret_val += (int)Integral.InternalData[y_top, x_left];
351-
ret_val -= (int)Integral.InternalData[y_top, x_right];
348+
ret_val = (int)Integral.Matrix[y_bottom][x_right]; // bottom right corner
349+
ret_val -= (int)Integral.Matrix[y_bottom][x_left];
350+
ret_val += (int)Integral.Matrix[y_top][x_left];
351+
ret_val -= (int)Integral.Matrix[y_top][x_right];
352352
ret_val = ret_val / ((x_right - x_left) * (y_bottom - y_top));
353353
return ret_val;
354354
}

Sources/Accord.Imaging/Interest Points/SURF/ResponseLayer.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace Accord.Imaging
2929
using System.Collections;
3030
using System.Collections.Generic;
3131
using Accord.Imaging;
32+
using Accord.Math;
3233

3334
/// <summary>
3435
/// Response filter.
@@ -53,7 +54,7 @@ internal class ResponseLayerCollection : IEnumerable<ResponseLayer[]>
5354
private int step;
5455
private int octaves;
5556

56-
private static readonly int[,] map =
57+
private static readonly int[,] map =
5758
{
5859
{ 0, 1, 2, 3 },
5960
{ 1, 3, 4, 5 },
@@ -268,13 +269,13 @@ internal class ResponseLayer
268269
/// Gets the responses computed from the filter.
269270
/// </summary>
270271
///
271-
public float[,] Responses { get; private set; }
272+
public float[][] Responses { get; private set; }
272273

273274
/// <summary>
274275
/// Gets the Laplacian computed from the filter.
275276
/// </summary>
276277
///
277-
public int[,] Laplacian { get; private set; }
278+
public int[][] Laplacian { get; private set; }
278279

279280

280281
/// <summary>
@@ -288,8 +289,8 @@ public ResponseLayer(int width, int height, int step, int filter)
288289
this.Step = step;
289290
this.Size = filter;
290291

291-
this.Responses = new float[height, width];
292-
this.Laplacian = new int[height, width];
292+
this.Responses = Jagged.Zeros<float>(height, width);
293+
this.Laplacian = Jagged.Zeros<int>(height, width);
293294
}
294295

295296
/// <summary>
@@ -301,8 +302,8 @@ public void Update(int width, int height, int step, int filter)
301302
{
302303
if (height > Height || width > Width)
303304
{
304-
this.Responses = new float[height, width];
305-
this.Laplacian = new int[height, width];
305+
this.Responses = Jagged.Zeros<float>(height, width);
306+
this.Laplacian = Jagged.Zeros<int>(height, width);
306307
}
307308

308309
this.Width = width;
@@ -351,8 +352,8 @@ public void Compute(IntegralImage image)
351352
Dxy *= inv / 255f;
352353

353354
// Get the determinant of Hessian response & Laplacian sign
354-
Responses[y, x] = (Dxx * Dyy) - (0.9f * 0.9f * Dxy * Dxy);
355-
Laplacian[y, x] = (Dxx + Dyy) >= 0 ? 1 : 0;
355+
Responses[y][x] = (Dxx * Dyy) - (0.9f * 0.9f * Dxy * Dxy);
356+
Laplacian[y][x] = (Dxx + Dyy) >= 0 ? 1 : 0;
356357
}
357358
}
358359
}

Sources/Accord.Imaging/Interest Points/SURF/SpeededUpRobustFeaturesDetector.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ private List<SpeededUpRobustFeaturePoint> processImage(UnmanagedImage image)
342342
int mscale = mid.Width / top.Width;
343343
int bscale = bot.Width / top.Width;
344344

345-
double currentValue = mid.Responses[y * mscale, x * mscale];
345+
double currentValue = mid.Responses[y * mscale][x * mscale];
346346

347347
// for each windows' row
348348
for (int i = -r; (currentValue >= threshold) && (i <= r); i++)
@@ -354,9 +354,9 @@ private List<SpeededUpRobustFeaturePoint> processImage(UnmanagedImage image)
354354
int xj = x + j;
355355

356356
// for each response layer
357-
if (top.Responses[yi, xj] >= currentValue ||
358-
bot.Responses[yi * bscale, xj * bscale] >= currentValue || ((i != 0 || j != 0) &&
359-
mid.Responses[yi * mscale, xj * mscale] >= currentValue))
357+
if (top.Responses[yi][xj] >= currentValue ||
358+
bot.Responses[yi * bscale][xj * bscale] >= currentValue || ((i != 0 || j != 0) &&
359+
mid.Responses[yi * mscale][xj * mscale] >= currentValue))
360360
{
361361
currentValue = 0;
362362
break;
@@ -378,7 +378,7 @@ private List<SpeededUpRobustFeaturePoint> processImage(UnmanagedImage image)
378378
(x + offset[0]) * tstep,
379379
(y + offset[1]) * tstep,
380380
0.133333333 * (mid.Size + offset[2] * mstep),
381-
mid.Laplacian[y * mscale, x * mscale]));
381+
mid.Laplacian[y * mscale][x * mscale]));
382382
}
383383
}
384384

@@ -490,9 +490,9 @@ private static double[] interpolate(int y, int x, ResponseLayer top, ResponseLay
490490
int xm1 = x - 1, ym1 = y - 1;
491491

492492
// Compute first order scale-space derivatives
493-
double dx = (mid.Responses[y * ms, xp1 * ms] - mid.Responses[y * ms, xm1 * ms]) / 2f;
494-
double dy = (mid.Responses[yp1 * ms, x * ms] - mid.Responses[ym1 * ms, x * ms]) / 2f;
495-
double ds = (top.Responses[y, x] - bot.Responses[y * bs, x * bs]) / 2f;
493+
double dx = (mid.Responses[y * ms][xp1 * ms] - mid.Responses[y * ms][xm1 * ms]) / 2f;
494+
double dy = (mid.Responses[yp1 * ms][x * ms] - mid.Responses[ym1 * ms][x * ms]) / 2f;
495+
double ds = (top.Responses[y][x] - bot.Responses[y * bs][x * bs]) / 2f;
496496

497497
double[] d =
498498
{
@@ -502,14 +502,14 @@ private static double[] interpolate(int y, int x, ResponseLayer top, ResponseLay
502502
};
503503

504504
// Compute Hessian
505-
double v = mid.Responses[y * ms, x * ms] * 2.0;
506-
double dxx = (mid.Responses[y * ms, xp1 * ms] + mid.Responses[y * ms, xm1 * ms] - v);
507-
double dyy = (mid.Responses[yp1 * ms, x * ms] + mid.Responses[ym1 * ms, x * ms] - v);
508-
double dxs = (top.Responses[y, xp1] - top.Responses[y, x - 1] - bot.Responses[y * bs, xp1 * bs] + bot.Responses[y * bs, xm1 * bs]) / 4f;
509-
double dys = (top.Responses[yp1, x] - top.Responses[y - 1, x] - bot.Responses[yp1 * bs, x * bs] + bot.Responses[ym1 * bs, x * bs]) / 4f;
510-
double dss = (top.Responses[y, x] + bot.Responses[y * ms, x * ms] - v);
511-
double dxy = (mid.Responses[yp1 * ms, xp1 * ms] - mid.Responses[yp1 * ms, xm1 * ms]
512-
- mid.Responses[ym1 * ms, xp1 * ms] + mid.Responses[ym1 * ms, xm1 * ms]) / 4f;
505+
double v = mid.Responses[y * ms][x * ms] * 2.0;
506+
double dxx = (mid.Responses[y * ms][xp1 * ms] + mid.Responses[y * ms][xm1 * ms] - v);
507+
double dyy = (mid.Responses[yp1 * ms][x * ms] + mid.Responses[ym1 * ms][x * ms] - v);
508+
double dxs = (top.Responses[y][xp1] - top.Responses[y][x - 1] - bot.Responses[y * bs][xp1 * bs] + bot.Responses[y * bs][xm1 * bs]) / 4f;
509+
double dys = (top.Responses[yp1][x] - top.Responses[y - 1][x] - bot.Responses[yp1 * bs][x * bs] + bot.Responses[ym1 * bs][x * bs]) / 4f;
510+
double dss = (top.Responses[y][x] + bot.Responses[y * ms][x * ms] - v);
511+
double dxy = (mid.Responses[yp1 * ms][xp1 * ms] - mid.Responses[yp1 * ms][xm1 * ms]
512+
- mid.Responses[ym1 * ms][xp1 * ms] + mid.Responses[ym1 * ms][xm1 * ms]) / 4f;
513513

514514
double[,] H =
515515
{

0 commit comments

Comments
 (0)