Skip to content

Commit bdb742d

Browse files
authored
Image support (#528)
Add images support based on System.Drawing
1 parent b727d10 commit bdb742d

23 files changed

+4004
-125
lines changed

Microsoft.ML.sln

+11
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.CodeAnalyzer",
9797
EndProject
9898
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.CodeAnalyzer.Tests", "test\Microsoft.ML.CodeAnalyzer.Tests\Microsoft.ML.CodeAnalyzer.Tests.csproj", "{3E4ABF07-7970-4BE6-B45B-A13D3C397545}"
9999
EndProject
100+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.ImageAnalytics", "src\Microsoft.ML.ImageAnalytics\Microsoft.ML.ImageAnalytics.csproj", "{00E38F77-1E61-4CDF-8F97-1417D4E85053}"
101+
EndProject
100102
Global
101103
GlobalSection(SolutionConfigurationPlatforms) = preSolution
102104
Debug|Any CPU = Debug|Any CPU
@@ -329,6 +331,14 @@ Global
329331
{3E4ABF07-7970-4BE6-B45B-A13D3C397545}.Release|Any CPU.Build.0 = Release|Any CPU
330332
{3E4ABF07-7970-4BE6-B45B-A13D3C397545}.Release-Intrinsics|Any CPU.ActiveCfg = Release|Any CPU
331333
{3E4ABF07-7970-4BE6-B45B-A13D3C397545}.Release-Intrinsics|Any CPU.Build.0 = Release|Any CPU
334+
{00E38F77-1E61-4CDF-8F97-1417D4E85053}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
335+
{00E38F77-1E61-4CDF-8F97-1417D4E85053}.Debug|Any CPU.Build.0 = Debug|Any CPU
336+
{00E38F77-1E61-4CDF-8F97-1417D4E85053}.Debug-Intrinsics|Any CPU.ActiveCfg = Debug|Any CPU
337+
{00E38F77-1E61-4CDF-8F97-1417D4E85053}.Debug-Intrinsics|Any CPU.Build.0 = Debug|Any CPU
338+
{00E38F77-1E61-4CDF-8F97-1417D4E85053}.Release|Any CPU.ActiveCfg = Release|Any CPU
339+
{00E38F77-1E61-4CDF-8F97-1417D4E85053}.Release|Any CPU.Build.0 = Release|Any CPU
340+
{00E38F77-1E61-4CDF-8F97-1417D4E85053}.Release-Intrinsics|Any CPU.ActiveCfg = Release|Any CPU
341+
{00E38F77-1E61-4CDF-8F97-1417D4E85053}.Release-Intrinsics|Any CPU.Build.0 = Release|Any CPU
332342
EndGlobalSection
333343
GlobalSection(SolutionProperties) = preSolution
334344
HideSolutionNode = FALSE
@@ -367,6 +377,7 @@ Global
367377
{BF66A305-DF10-47E4-8D81-42049B149D2B} = {D3D38B03-B557-484D-8348-8BADEE4DF592}
368378
{B4E55B2D-2A92-46E7-B72F-E76D6FD83440} = {7F13E156-3EBA-4021-84A5-CD56BA72F99E}
369379
{3E4ABF07-7970-4BE6-B45B-A13D3C397545} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
380+
{00E38F77-1E61-4CDF-8F97-1417D4E85053} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
370381
EndGlobalSection
371382
GlobalSection(ExtensibilityGlobals) = postSolution
372383
SolutionGuid = {41165AF1-35BB-4832-A189-73060F82B01D}

build/Dependencies.props

+1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@
99
<PublishSymbolsPackageVersion>1.0.0-beta-62824-02</PublishSymbolsPackageVersion>
1010
<LightGBMPackageVersion>2.1.2.2</LightGBMPackageVersion>
1111
<MlNetMklDepsPackageVersion>0.0.0.1</MlNetMklDepsPackageVersion>
12+
<SystemDrawingCommonPackageVersion>4.5.0</SystemDrawingCommonPackageVersion>
1213
</PropertyGroup>
1314
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk" DefaultTargets="Pack">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
<PackageDescription>ML.NET component for Image support</PackageDescription>
6+
</PropertyGroup>
7+
8+
<ItemGroup>
9+
<ProjectReference Include="../Microsoft.ML/Microsoft.ML.nupkgproj" />
10+
<PackageReference Include="System.Drawing.Common" Version="$(SystemDrawingCommonPackageVersion)" />
11+
</ItemGroup>
12+
13+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Project DefaultTargets="Pack">
2+
3+
<Import Project="Microsoft.ML.ImageAnalytics.nupkgproj" />
4+
5+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using Microsoft.ML.Runtime;
6+
using Microsoft.ML.Runtime.EntryPoints;
7+
using Microsoft.ML.Runtime.ImageAnalytics.EntryPoints;
8+
9+
[assembly: LoadableClass(typeof(void), typeof(ImageAnalytics), null, typeof(SignatureEntryPointModule), "ImageAnalytics")]
10+
namespace Microsoft.ML.Runtime.ImageAnalytics.EntryPoints
11+
{
12+
public static class ImageAnalytics
13+
{
14+
[TlcModule.EntryPoint(Name = "Transforms.ImageLoader", Desc = ImageLoaderTransform.Summary,
15+
UserName = ImageLoaderTransform.UserName, ShortName = ImageLoaderTransform.LoaderSignature)]
16+
public static CommonOutputs.TransformOutput ImageLoader(IHostEnvironment env, ImageLoaderTransform.Arguments input)
17+
{
18+
var h = EntryPointUtils.CheckArgsAndCreateHost(env, "ImageLoaderTransform", input);
19+
var xf = new ImageLoaderTransform(h, input, input.Data);
20+
return new CommonOutputs.TransformOutput()
21+
{
22+
Model = new TransformModel(h, xf, input.Data),
23+
OutputData = xf
24+
};
25+
}
26+
27+
[TlcModule.EntryPoint(Name = "Transforms.ImageResizer", Desc = ImageResizerTransform.Summary,
28+
UserName = ImageResizerTransform.UserName, ShortName = ImageResizerTransform.LoaderSignature)]
29+
public static CommonOutputs.TransformOutput ImageResizer(IHostEnvironment env, ImageResizerTransform.Arguments input)
30+
{
31+
var h = EntryPointUtils.CheckArgsAndCreateHost(env, "ImageResizerTransform", input);
32+
var xf = new ImageResizerTransform(h, input, input.Data);
33+
return new CommonOutputs.TransformOutput()
34+
{
35+
Model = new TransformModel(h, xf, input.Data),
36+
OutputData = xf
37+
};
38+
}
39+
40+
[TlcModule.EntryPoint(Name = "Transforms.ImagePixelExtractor", Desc = ImagePixelExtractorTransform.Summary,
41+
UserName = ImagePixelExtractorTransform.UserName, ShortName = ImagePixelExtractorTransform.LoaderSignature)]
42+
public static CommonOutputs.TransformOutput ImagePixelExtractor(IHostEnvironment env, ImagePixelExtractorTransform.Arguments input)
43+
{
44+
var h = EntryPointUtils.CheckArgsAndCreateHost(env, "ImagePixelExtractorTransform", input);
45+
var xf = new ImagePixelExtractorTransform(h, input, input.Data);
46+
return new CommonOutputs.TransformOutput()
47+
{
48+
Model = new TransformModel(h, xf, input.Data),
49+
OutputData = xf
50+
};
51+
}
52+
53+
[TlcModule.EntryPoint(Name = "Transforms.ImageGrayscale", Desc = ImageGrayscaleTransform.Summary,
54+
UserName = ImageGrayscaleTransform.UserName, ShortName = ImageGrayscaleTransform.LoaderSignature)]
55+
public static CommonOutputs.TransformOutput ImageGrayscale(IHostEnvironment env, ImageGrayscaleTransform.Arguments input)
56+
{
57+
var h = EntryPointUtils.CheckArgsAndCreateHost(env, "ImageGrayscaleTransform", input);
58+
var xf = new ImageGrayscaleTransform(h, input, input.Data);
59+
return new CommonOutputs.TransformOutput()
60+
{
61+
Model = new TransformModel(h, xf, input.Data),
62+
OutputData = xf
63+
};
64+
}
65+
66+
[TlcModule.EntryPoint(Name = "Transforms.VectorToImage", Desc = VectorToImageTransform.Summary,
67+
UserName = VectorToImageTransform.UserName, ShortName = VectorToImageTransform.LoaderSignature)]
68+
public static CommonOutputs.TransformOutput VectorToImage(IHostEnvironment env, VectorToImageTransform.Arguments input)
69+
{
70+
var h = EntryPointUtils.CheckArgsAndCreateHost(env, "VectorToImageTransform", input);
71+
var xf = new VectorToImageTransform(h, input, input.Data);
72+
return new CommonOutputs.TransformOutput()
73+
{
74+
Model = new TransformModel(h, xf, input.Data),
75+
OutputData = xf
76+
};
77+
}
78+
}
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Drawing;
7+
using System.Drawing.Imaging;
8+
using System.Text;
9+
using Microsoft.ML.Runtime;
10+
using Microsoft.ML.Runtime.CommandLine;
11+
using Microsoft.ML.Runtime.Data;
12+
using Microsoft.ML.Runtime.EntryPoints;
13+
using Microsoft.ML.Runtime.Internal.Utilities;
14+
using Microsoft.ML.Runtime.Model;
15+
using Microsoft.ML.Runtime.ImageAnalytics;
16+
17+
[assembly: LoadableClass(ImageGrayscaleTransform.Summary, typeof(ImageGrayscaleTransform), typeof(ImageGrayscaleTransform.Arguments), typeof(SignatureDataTransform),
18+
ImageGrayscaleTransform.UserName, "ImageGrayscaleTransform", "ImageGrayscale")]
19+
20+
[assembly: LoadableClass(ImageGrayscaleTransform.Summary, typeof(ImageGrayscaleTransform), null, typeof(SignatureLoadDataTransform),
21+
ImageGrayscaleTransform.UserName, ImageGrayscaleTransform.LoaderSignature)]
22+
23+
namespace Microsoft.ML.Runtime.ImageAnalytics
24+
{
25+
// REVIEW: Rewrite as LambdaTransform to simplify.
26+
// REVIEW: Should it be separate transform or part of ImageResizerTransform?
27+
/// <summary>
28+
/// Transform which takes one or many columns of <see cref="ImageType"/> type in IDataView and
29+
/// convert them to greyscale representation of the same image.
30+
/// </summary>
31+
public sealed class ImageGrayscaleTransform : OneToOneTransformBase
32+
{
33+
public sealed class Column : OneToOneColumn
34+
{
35+
public static Column Parse(string str)
36+
{
37+
var res = new Column();
38+
if (res.TryParse(str))
39+
return res;
40+
return null;
41+
}
42+
43+
public bool TryUnparse(StringBuilder sb)
44+
{
45+
Contracts.AssertValue(sb);
46+
return TryUnparseCore(sb);
47+
}
48+
}
49+
50+
public class Arguments : TransformInputBase
51+
{
52+
[Argument(ArgumentType.Multiple | ArgumentType.Required, HelpText = "New column definition(s) (optional form: name:src)", ShortName = "col", SortOrder = 1)]
53+
public Column[] Column;
54+
}
55+
56+
internal const string Summary = "Convert image into grayscale.";
57+
58+
internal const string UserName = "Image Greyscale Transform";
59+
public const string LoaderSignature = "ImageGrayscaleTransform";
60+
private static VersionInfo GetVersionInfo()
61+
{
62+
return new VersionInfo(
63+
modelSignature: "IMGGRAYT",
64+
verWrittenCur: 0x00010001, // Initial
65+
verReadableCur: 0x00010001,
66+
verWeCanReadBack: 0x00010001,
67+
loaderSignature: LoaderSignature);
68+
}
69+
70+
private const string RegistrationName = "ImageGrayscale";
71+
72+
// Public constructor corresponding to SignatureDataTransform.
73+
public ImageGrayscaleTransform(IHostEnvironment env, Arguments args, IDataView input)
74+
: base(env, RegistrationName, env.CheckRef(args, nameof(args)).Column, input, t => t is ImageType ? null : "Expected Image type")
75+
{
76+
Host.AssertNonEmpty(Infos);
77+
Host.Assert(Infos.Length == Utils.Size(args.Column));
78+
Metadata.Seal();
79+
}
80+
81+
private ImageGrayscaleTransform(IHost host, ModelLoadContext ctx, IDataView input)
82+
: base(host, ctx, input, t => t is ImageType ? null : "Expected Image type")
83+
{
84+
Host.AssertValue(ctx);
85+
// *** Binary format ***
86+
// <base>
87+
Host.AssertNonEmpty(Infos);
88+
Metadata.Seal();
89+
}
90+
91+
public static ImageGrayscaleTransform Create(IHostEnvironment env, ModelLoadContext ctx, IDataView input)
92+
{
93+
Contracts.CheckValue(env, nameof(env));
94+
var h = env.Register(RegistrationName);
95+
h.CheckValue(ctx, nameof(ctx));
96+
h.CheckValue(input, nameof(input));
97+
ctx.CheckAtModel(GetVersionInfo());
98+
return h.Apply("Loading Model", ch => new ImageGrayscaleTransform(h, ctx, input));
99+
}
100+
101+
public override void Save(ModelSaveContext ctx)
102+
{
103+
Host.CheckValue(ctx, nameof(ctx));
104+
ctx.CheckAtModel();
105+
ctx.SetVersionInfo(GetVersionInfo());
106+
107+
// *** Binary format ***
108+
// <base>
109+
SaveBase(ctx);
110+
}
111+
112+
protected override ColumnType GetColumnTypeCore(int iinfo)
113+
{
114+
Host.Assert(0 <= iinfo & iinfo < Infos.Length);
115+
return Infos[iinfo].TypeSrc;
116+
}
117+
118+
private static readonly ColorMatrix _grayscaleColorMatrix = new ColorMatrix(
119+
new float[][]
120+
{
121+
new float[] {.3f, .3f, .3f, 0, 0},
122+
new float[] {.59f, .59f, .59f, 0, 0},
123+
new float[] {.11f, .11f, .11f, 0, 0},
124+
new float[] {0, 0, 0, 1, 0},
125+
new float[] {0, 0, 0, 0, 1}
126+
});
127+
128+
protected override Delegate GetGetterCore(IChannel ch, IRow input, int iinfo, out Action disposer)
129+
{
130+
Host.AssertValueOrNull(ch);
131+
Host.AssertValue(input);
132+
Host.Assert(0 <= iinfo && iinfo < Infos.Length);
133+
134+
var src = default(Bitmap);
135+
var getSrc = GetSrcGetter<Bitmap>(input, iinfo);
136+
137+
disposer =
138+
() =>
139+
{
140+
if (src != null)
141+
{
142+
src.Dispose();
143+
src = null;
144+
}
145+
};
146+
147+
ValueGetter<Bitmap> del =
148+
(ref Bitmap dst) =>
149+
{
150+
if (dst != null)
151+
dst.Dispose();
152+
153+
getSrc(ref src);
154+
if (src == null || src.Height <= 0 || src.Width <= 0)
155+
return;
156+
157+
dst = new Bitmap(src.Width, src.Height);
158+
ImageAttributes attributes = new ImageAttributes();
159+
attributes.SetColorMatrix(_grayscaleColorMatrix);
160+
var srcRectangle = new Rectangle(0, 0, src.Width, src.Height);
161+
using (var g = Graphics.FromImage(dst))
162+
{
163+
g.DrawImage(src, srcRectangle, 0, 0, src.Width, src.Height, GraphicsUnit.Pixel, attributes);
164+
}
165+
Host.Assert(dst.Width == src.Width && dst.Height == src.Height);
166+
};
167+
168+
return del;
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)