Skip to content

Commit 5e2a92f

Browse files
Speed-up bitmap operations on images. Fixes #5856 (#5857)
* Speed-up images bitmap operations. - Speed-up operations avoiding to use the very slow functions GetPixel and SetPixel. Implemented raw access to bitmap data. Signed-off-by: darth-vader-lg <[email protected]> * Just to restart CI checks. Signed-off-by: darth-vader-lg <[email protected]> * Fixed wrong parameter passed to the Image.LockBits. - It was passed ImageLockMode.ReadOnly where it was needed a write operation. Signed-off-by: darth-vader-lg <[email protected]>
1 parent bcbb847 commit 5e2a92f

File tree

2 files changed

+130
-96
lines changed

2 files changed

+130
-96
lines changed

src/Microsoft.ML.ImageAnalytics/ImagePixelExtractor.cs

Lines changed: 74 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -377,77 +377,92 @@ private ValueGetter<VBuffer<TValue>> GetGetterCore<TValue>(DataViewRow input, in
377377

378378
ImagePixelExtractingEstimator.GetOrder(ex.OrderOfExtraction, ex.ColorsToExtract, out int a, out int r, out int b, out int g);
379379

380-
int h = height;
381-
int w = width;
382-
383-
if (ex.InterleavePixelColors)
380+
BitmapData bmpData = null;
381+
try
384382
{
385-
int idst = 0;
386-
for (int y = 0; y < h; ++y)
387-
for (int x = 0; x < w; x++)
383+
bmpData = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadOnly, src.PixelFormat);
384+
int h = height;
385+
int w = width;
386+
byte[] row = new byte[bmpData.Stride];
387+
int pixelSize = System.Drawing.Image.GetPixelFormatSize(src.PixelFormat) / 8;
388+
Func<int, byte> alpha = pixelSize > 3 ? new Func<int, byte>(ix => row[ix + 3]) : new Func<int, byte>(ix => 255);
389+
390+
if (ex.InterleavePixelColors)
391+
{
392+
int idst = 0;
393+
for (int y = 0; y < h; ++y)
388394
{
389-
var pb = src.GetPixel(x, y);
390-
if (!vb.IsEmpty)
391-
{
392-
if (a != -1) { vb[idst + a] = pb.A; }
393-
if (r != -1) { vb[idst + r] = pb.R; }
394-
if (g != -1) { vb[idst + g] = pb.G; }
395-
if (b != -1) { vb[idst + b] = pb.B; }
396-
}
397-
else if (!needScale)
395+
Marshal.Copy(bmpData.Scan0 + bmpData.Stride * y, row, 0, bmpData.Stride);
396+
for (int x = 0; x < w; x++)
398397
{
399-
if (a != -1) { vf[idst + a] = pb.A; }
400-
if (r != -1) { vf[idst + r] = pb.R; }
401-
if (g != -1) { vf[idst + g] = pb.G; }
402-
if (b != -1) { vf[idst + b] = pb.B; }
398+
var ix = x * pixelSize;
399+
if (!vb.IsEmpty)
400+
{
401+
if (a != -1) { vb[idst + a] = alpha(ix); }
402+
if (r != -1) { vb[idst + r] = row[ix + 2]; }
403+
if (g != -1) { vb[idst + g] = row[ix + 1]; }
404+
if (b != -1) { vb[idst + b] = row[ix + 0]; }
405+
}
406+
else if (!needScale)
407+
{
408+
if (a != -1) { vf[idst + a] = alpha(ix); }
409+
if (r != -1) { vf[idst + r] = row[ix + 2]; }
410+
if (g != -1) { vf[idst + g] = row[ix + 1]; }
411+
if (b != -1) { vf[idst + b] = row[ix + 0]; }
412+
}
413+
else
414+
{
415+
416+
if (a != -1) { vf[idst + a] = (alpha(ix) - offset) * scale; }
417+
if (r != -1) { vf[idst + r] = (row[ix + 2] - offset) * scale; }
418+
if (g != -1) { vf[idst + g] = (row[ix + 1] - offset) * scale; }
419+
if (b != -1) { vf[idst + b] = (row[ix + 0] - offset) * scale; }
420+
}
421+
idst += ex.Planes;
403422
}
404-
else
405-
{
406-
407-
if (a != -1) { vf[idst + a] = (pb.A - offset) * scale; }
408-
if (r != -1) { vf[idst + r] = (pb.R - offset) * scale; }
409-
if (g != -1) { vf[idst + g] = (pb.G - offset) * scale; }
410-
if (b != -1) { vf[idst + b] = (pb.B - offset) * scale; }
411-
}
412-
idst += ex.Planes;
413423
}
414-
Contracts.Assert(idst == size);
415-
}
416-
else
417-
{
418-
int idstMin = 0;
419-
for (int y = 0; y < h; ++y)
424+
Contracts.Assert(idst == size);
425+
}
426+
else
420427
{
421-
int idst = idstMin + y * w;
422-
for (int x = 0; x < w; x++, idst++)
428+
int idstMin = 0;
429+
for (int y = 0; y < h; ++y)
423430
{
424-
if (!vb.IsEmpty)
425-
{
426-
var pb = src.GetPixel(x, y);
427-
if (a != -1) vb[idst + cpix * a] = pb.A;
428-
if (r != -1) vb[idst + cpix * r] = pb.R;
429-
if (g != -1) vb[idst + cpix * g] = pb.G;
430-
if (b != -1) vb[idst + cpix * b] = pb.B;
431-
}
432-
else if (!needScale)
431+
Marshal.Copy(bmpData.Scan0 + bmpData.Stride * y, row, 0, bmpData.Stride);
432+
int idst = idstMin + y * w;
433+
for (int x = 0; x < w; x++, idst++)
433434
{
434-
var pb = src.GetPixel(x, y);
435-
if (a != -1) vf[idst + cpix * a] = pb.A;
436-
if (r != -1) vf[idst + cpix * r] = pb.R;
437-
if (g != -1) vf[idst + cpix * g] = pb.G;
438-
if (b != -1) vf[idst + cpix * b] = pb.B;
439-
}
440-
else
441-
{
442-
var pb = src.GetPixel(x, y);
443-
if (a != -1) vf[idst + cpix * a] = (pb.A - offset) * scale;
444-
if (r != -1) vf[idst + cpix * r] = (pb.R - offset) * scale;
445-
if (g != -1) vf[idst + cpix * g] = (pb.G - offset) * scale;
446-
if (b != -1) vf[idst + cpix * b] = (pb.B - offset) * scale;
435+
var ix = x * pixelSize;
436+
if (!vb.IsEmpty)
437+
{
438+
if (a != -1) vb[idst + cpix * a] = alpha(ix);
439+
if (r != -1) vb[idst + cpix * r] = row[ix + 2];
440+
if (g != -1) vb[idst + cpix * g] = row[ix + 1];
441+
if (b != -1) vb[idst + cpix * b] = row[ix + 0];
442+
}
443+
else if (!needScale)
444+
{
445+
if (a != -1) vf[idst + cpix * a] = alpha(ix);
446+
if (r != -1) vf[idst + cpix * r] = row[ix + 2];
447+
if (g != -1) vf[idst + cpix * g] = row[ix + 1];
448+
if (b != -1) vf[idst + cpix * b] = row[ix + 0];
449+
}
450+
else
451+
{
452+
if (a != -1) vf[idst + cpix * a] = (alpha(ix) - offset) * scale;
453+
if (r != -1) vf[idst + cpix * r] = (row[ix + 2] - offset) * scale;
454+
if (g != -1) vf[idst + cpix * g] = (row[ix + 1] - offset) * scale;
455+
if (b != -1) vf[idst + cpix * b] = (row[ix + 0] - offset) * scale;
456+
}
447457
}
448458
}
449459
}
450460
}
461+
finally
462+
{
463+
if (bmpData != null)
464+
src.UnlockBits(bmpData);
465+
}
451466

452467
dst = editor.Commit();
453468
};

src/Microsoft.ML.ImageAnalytics/VectorToImageTransform.cs

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Drawing;
8+
using System.Drawing.Imaging;
89
using System.Linq;
10+
using System.Runtime.InteropServices;
911
using System.Text;
1012
using Microsoft.ML;
1113
using Microsoft.ML.CommandLine;
@@ -370,47 +372,64 @@ private ValueGetter<Bitmap> GetterFromType<TValue>(PrimitiveDataViewType srcType
370372
int position = 0;
371373
ImagePixelExtractingEstimator.GetOrder(ex.Order, ex.Colors, out int a, out int r, out int b, out int g);
372374

373-
for (int y = 0; y < height; ++y)
374-
for (int x = 0; x < width; x++)
375+
BitmapData bmpData = null;
376+
try
377+
{
378+
bmpData = dst.LockBits(new Rectangle(0, 0, dst.Width, dst.Height), ImageLockMode.WriteOnly, dst.PixelFormat);
379+
for (int y = 0; y < height; ++y)
375380
{
376-
float red = ex.DefaultRed;
377-
float green = ex.DefaultGreen;
378-
float blue = ex.DefaultBlue;
379-
float alpha = ex.DefaultAlpha;
380-
if (ex.InterleavedColors)
381-
{
382-
if (ex.Alpha)
383-
alpha = Convert.ToSingle(values[position + a]);
384-
if (ex.Red)
385-
red = Convert.ToSingle(values[position + r]);
386-
if (ex.Green)
387-
green = Convert.ToSingle(values[position + g]);
388-
if (ex.Blue)
389-
blue = Convert.ToSingle(values[position + b]);
390-
position += ex.Planes;
391-
}
392-
else
393-
{
394-
position = y * width + x;
395-
if (ex.Alpha) alpha = Convert.ToSingle(values[position + cpix * a]);
396-
if (ex.Red) red = Convert.ToSingle(values[position + cpix * r]);
397-
if (ex.Green) green = Convert.ToSingle(values[position + cpix * g]);
398-
if (ex.Blue) blue = Convert.ToSingle(values[position + cpix * b]);
399-
}
400-
Color pixel;
401-
if (!needScale)
402-
pixel = Color.FromArgb((int)alpha, (int)red, (int)green, (int)blue);
403-
else
381+
byte[] row = new byte[bmpData.Stride];
382+
int ix = 0;
383+
for (int x = 0; x < width; x++)
404384
{
405-
pixel = Color.FromArgb(
406-
ex.Alpha ? (int)Math.Round(alpha * scale - offset) : 0,
407-
(int)Math.Round(red * scale - offset),
408-
(int)Math.Round(green * scale - offset),
409-
(int)Math.Round(blue * scale - offset));
385+
float red = ex.DefaultRed;
386+
float green = ex.DefaultGreen;
387+
float blue = ex.DefaultBlue;
388+
float alpha = ex.DefaultAlpha;
389+
if (ex.InterleavedColors)
390+
{
391+
if (ex.Alpha)
392+
alpha = Convert.ToSingle(values[position + a]);
393+
if (ex.Red)
394+
red = Convert.ToSingle(values[position + r]);
395+
if (ex.Green)
396+
green = Convert.ToSingle(values[position + g]);
397+
if (ex.Blue)
398+
blue = Convert.ToSingle(values[position + b]);
399+
position += ex.Planes;
400+
}
401+
else
402+
{
403+
position = y * width + x;
404+
if (ex.Alpha) alpha = Convert.ToSingle(values[position + cpix * a]);
405+
if (ex.Red) red = Convert.ToSingle(values[position + cpix * r]);
406+
if (ex.Green) green = Convert.ToSingle(values[position + cpix * g]);
407+
if (ex.Blue) blue = Convert.ToSingle(values[position + cpix * b]);
408+
}
409+
if (!needScale)
410+
{
411+
row[ix++] = (byte)blue;
412+
row[ix++] = (byte)green;
413+
row[ix++] = (byte)red;
414+
row[ix++] = (byte)alpha;
415+
}
416+
else
417+
{
418+
row[ix++] = (byte)Math.Round(blue * scale - offset);
419+
row[ix++] = (byte)Math.Round(green * scale - offset);
420+
row[ix++] = (byte)Math.Round(red * scale - offset);
421+
row[ix++] = (byte)(ex.Alpha ? Math.Round(alpha * scale - offset) : 0);
422+
}
410423
}
411-
dst.SetPixel(x, y, pixel);
412-
dst.Tag = nameof(VectorToImageConvertingTransformer);
424+
Marshal.Copy(row, 0, bmpData.Scan0 + y * bmpData.Stride, bmpData.Stride);
413425
}
426+
dst.Tag = nameof(VectorToImageConvertingTransformer);
427+
}
428+
finally
429+
{
430+
if (bmpData != null)
431+
dst.UnlockBits(bmpData);
432+
}
414433
};
415434
}
416435

0 commit comments

Comments
 (0)