From e87a02ed1e4152fbf377cb8a7c1cf074db0982fd Mon Sep 17 00:00:00 2001 From: Ivan Matantsev Date: Fri, 28 Sep 2018 12:14:16 -0700 Subject: [PATCH 1/3] CustomPipeline --- .../StaticPipe/PipelineColumn.cs | 15 +++++++++++++++ .../ImageGrayscaleTransform.cs | 10 +++++----- .../ImageLoaderTransform.cs | 2 +- .../ImagePixelExtractorTransform.cs | 10 +++++----- .../ImageResizerTransform.cs | 6 +++--- .../ImageStaticPipe.cs | 14 +++++++------- 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs b/src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs index bf097b9571..820ade7d9d 100644 --- a/src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs +++ b/src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs @@ -142,4 +142,19 @@ protected VarKey(Reconciler reconciler, params PipelineColumn[] dependencies) public override string ToString() => $"{nameof(VarKey)}<{typeof(T).Name}>"; } + + /// + /// For representing a custom . + /// + /// + public abstract class Custom: PipelineColumn + { + protected Custom(Reconciler reconciler, params PipelineColumn[] dependencies) + : base(reconciler, dependencies) + { + } + + public override string ToString() => $"{nameof(Custom)}<{typeof(T).Name}>"; + } + } diff --git a/src/Microsoft.ML.ImageAnalytics/ImageGrayscaleTransform.cs b/src/Microsoft.ML.ImageAnalytics/ImageGrayscaleTransform.cs index f7dd526505..7a2fd6ba43 100644 --- a/src/Microsoft.ML.ImageAnalytics/ImageGrayscaleTransform.cs +++ b/src/Microsoft.ML.ImageAnalytics/ImageGrayscaleTransform.cs @@ -240,12 +240,12 @@ private interface IColInput PipelineColumn Input { get; } } - internal sealed class OutPipelineColumn : Scalar, IColInput + internal sealed class OutPipelineColumn : Custom, IColInput { public PipelineColumn Input { get; } - public OutPipelineColumn(Scalar input) - : base(Reconciler.Inst, input) + public OutPipelineColumn(Custom input) + : base(Reconciler.Inst, input) { Contracts.AssertValue(input); Contracts.Assert(typeof(T) == typeof(Bitmap) || typeof(T) == typeof(UnknownSizeBitmap)); @@ -257,8 +257,8 @@ public OutPipelineColumn(Scalar input) /// Reconciler to an for the . /// /// Because we want to use the same reconciler for - /// - /// + /// + /// private sealed class Reconciler : EstimatorReconciler { public static Reconciler Inst = new Reconciler(); diff --git a/src/Microsoft.ML.ImageAnalytics/ImageLoaderTransform.cs b/src/Microsoft.ML.ImageAnalytics/ImageLoaderTransform.cs index e0ad15bbd5..253bd523d2 100644 --- a/src/Microsoft.ML.ImageAnalytics/ImageLoaderTransform.cs +++ b/src/Microsoft.ML.ImageAnalytics/ImageLoaderTransform.cs @@ -242,7 +242,7 @@ public override SchemaShape GetOutputSchema(SchemaShape inputSchema) return new SchemaShape(result.Values); } - internal sealed class OutPipelineColumn : Scalar + internal sealed class OutPipelineColumn : Custom { private readonly Scalar _input; diff --git a/src/Microsoft.ML.ImageAnalytics/ImagePixelExtractorTransform.cs b/src/Microsoft.ML.ImageAnalytics/ImagePixelExtractorTransform.cs index 0a70dee243..c526222501 100644 --- a/src/Microsoft.ML.ImageAnalytics/ImagePixelExtractorTransform.cs +++ b/src/Microsoft.ML.ImageAnalytics/ImagePixelExtractorTransform.cs @@ -669,18 +669,18 @@ public override SchemaShape GetOutputSchema(SchemaShape inputSchema) private interface IColInput { - Scalar Input { get; } + Custom Input { get; } ImagePixelExtractorTransform.ColumnInfo MakeColumnInfo(string input, string output); } internal sealed class OutPipelineColumn : Vector, IColInput { - public Scalar Input { get; } + public Custom Input { get; } private static readonly ImagePixelExtractorTransform.Arguments _defaultArgs = new ImagePixelExtractorTransform.Arguments(); private readonly ImagePixelExtractorTransform.Column _colParam; - public OutPipelineColumn(Scalar input, ImagePixelExtractorTransform.Column col) + public OutPipelineColumn(Custom input, ImagePixelExtractorTransform.Column col) : base(Reconciler.Inst, input) { Contracts.AssertValue(input); @@ -705,8 +705,8 @@ public ImagePixelExtractorTransform.ColumnInfo MakeColumnInfo(string input, stri /// Reconciler to an for the . /// /// Because we want to use the same reconciler for - /// - /// + /// + /// private sealed class Reconciler : EstimatorReconciler { /// diff --git a/src/Microsoft.ML.ImageAnalytics/ImageResizerTransform.cs b/src/Microsoft.ML.ImageAnalytics/ImageResizerTransform.cs index 4db5d9ac01..83d5c97736 100644 --- a/src/Microsoft.ML.ImageAnalytics/ImageResizerTransform.cs +++ b/src/Microsoft.ML.ImageAnalytics/ImageResizerTransform.cs @@ -461,7 +461,7 @@ public override SchemaShape GetOutputSchema(SchemaShape inputSchema) return new SchemaShape(result.Values); } - internal sealed class OutPipelineColumn : Scalar + internal sealed class OutPipelineColumn : Custom { private readonly PipelineColumn _input; private readonly int _width; @@ -487,8 +487,8 @@ private ImageResizerTransform.ColumnInfo MakeColumnInfo(string input, string out /// /// Reconciler to an for the . /// - /// - /// + /// + /// private sealed class Reconciler : EstimatorReconciler { public static Reconciler Inst = new Reconciler(); diff --git a/src/Microsoft.ML.ImageAnalytics/ImageStaticPipe.cs b/src/Microsoft.ML.ImageAnalytics/ImageStaticPipe.cs index 829aca3179..daf94f2bc0 100644 --- a/src/Microsoft.ML.ImageAnalytics/ImageStaticPipe.cs +++ b/src/Microsoft.ML.ImageAnalytics/ImageStaticPipe.cs @@ -29,7 +29,7 @@ public static class ImageStaticPipe /// safe for users to simply always make their input paths absolute. /// The loaded images /// - public static Scalar LoadAsImage(this Scalar path, string relativeTo = null) + public static Custom LoadAsImage(this Scalar path, string relativeTo = null) { Contracts.CheckValue(path, nameof(path)); Contracts.CheckValueOrNull(relativeTo); @@ -42,7 +42,7 @@ public static Scalar LoadAsImage(this Scalar path, st /// The image to convert /// The grayscale images /// - public static Scalar AsGrayscale(this Scalar input) + public static Custom AsGrayscale(this Custom input) { Contracts.CheckValue(input, nameof(input)); return new ImageGrayscaleEstimator.OutPipelineColumn(input); @@ -54,7 +54,7 @@ public static Scalar AsGrayscale(this ScalarThe image to convert /// The grayscale images /// - public static Scalar AsGrayscale(this Scalar input) + public static Custom AsGrayscale(this Custom input) { Contracts.CheckValue(input, nameof(input)); return new ImageGrayscaleEstimator.OutPipelineColumn(input); @@ -70,7 +70,7 @@ public static Scalar AsGrayscale(this Scalar input) /// If cropping is necessary, at what position will the image be fixed? /// The now uniformly sized images /// - public static Scalar Resize(this Scalar input, int width, int height, + public static Custom Resize(this Custom input, int width, int height, ImageResizerTransform.ResizingKind resizing = ImageResizerTransform.ResizingKind.IsoCrop, ImageResizerTransform.Anchor cropAnchor = ImageResizerTransform.Anchor.Center) { @@ -93,7 +93,7 @@ public static Scalar Resize(this Scalar input, int wi /// If cropping is necessary, at what /// The resized images /// - public static Scalar Resize(this Scalar input, int width, int height, + public static Custom Resize(this Custom input, int width, int height, ImageResizerTransform.ResizingKind resizing = ImageResizerTransform.ResizingKind.IsoCrop, ImageResizerTransform.Anchor cropAnchor = ImageResizerTransform.Anchor.Center) { @@ -121,7 +121,7 @@ public static Scalar Resize(this Scalar input, int width, int he /// Add this amount to the pixel values, before scaling /// The vectorized image /// - public static Vector ExtractPixels(this Scalar input, bool useAlpha = false, bool useRed = true, + public static Vector ExtractPixels(this Custom input, bool useAlpha = false, bool useRed = true, bool useGreen = true, bool useBlue = true, bool interleaveArgb = false, float scale = 1.0f, float offset = 0.0f) { var colParams = new ImagePixelExtractorTransform.Column @@ -151,7 +151,7 @@ public static Vector ExtractPixels(this Scalar input, bool useAlp /// Whether the pixel values should be interleaved, as opposed to being separated by channel /// The vectorized image /// - public static Vector ExtractPixelsAsBytes(this Scalar input, bool useAlpha = false, bool useRed = true, + public static Vector ExtractPixelsAsBytes(this Custom input, bool useAlpha = false, bool useRed = true, bool useGreen = true, bool useBlue = true, bool interleaveArgb = false) { var colParams = new ImagePixelExtractorTransform.Column From 6ff2e59fbd38a93bf912063b9caadfb7b84398eb Mon Sep 17 00:00:00 2001 From: Ivan Matantsev Date: Fri, 28 Sep 2018 13:42:25 -0700 Subject: [PATCH 2/3] comments --- src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs b/src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs index 820ade7d9d..91e68c4c30 100644 --- a/src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs +++ b/src/Microsoft.ML.Data/StaticPipe/PipelineColumn.cs @@ -34,7 +34,7 @@ private protected PipelineColumn(Reconciler reconciler, PipelineColumn[] depende /// /// For representing a non-key, non-vector . /// - /// + /// The scalar item type. public abstract class Scalar : PipelineColumn { protected Scalar(Reconciler reconciler, params PipelineColumn[] dependencies) @@ -146,7 +146,7 @@ protected VarKey(Reconciler reconciler, params PipelineColumn[] dependencies) /// /// For representing a custom . /// - /// + /// The custom item type. public abstract class Custom: PipelineColumn { protected Custom(Reconciler reconciler, params PipelineColumn[] dependencies) From f8a88b3ee75ffb6d8fd739cba5d96688a1bb1017 Mon Sep 17 00:00:00 2001 From: Ivan Matantsev Date: Fri, 28 Sep 2018 16:53:29 -0700 Subject: [PATCH 3/3] Let's make Tom happy! --- src/Microsoft.ML.Data/StaticPipe/StaticPipeInternalUtils.cs | 4 ++++ src/Microsoft.ML.Data/StaticPipe/StaticSchemaShape.cs | 2 +- .../ImageAnalyticsTests.cs | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.ML.Data/StaticPipe/StaticPipeInternalUtils.cs b/src/Microsoft.ML.Data/StaticPipe/StaticPipeInternalUtils.cs index 9b0040f3b3..4b61e90957 100644 --- a/src/Microsoft.ML.Data/StaticPipe/StaticPipeInternalUtils.cs +++ b/src/Microsoft.ML.Data/StaticPipe/StaticPipeInternalUtils.cs @@ -59,6 +59,7 @@ private sealed class AVarVector : VarVector { public AVarVector(Rec rec) : private sealed class AKey : Key { public AKey(Rec rec) : base(rec, null) { } } private sealed class AKey : Key { public AKey(Rec rec) : base(rec, null) { } } private sealed class AVarKey : VarKey { public AVarKey(Rec rec) : base(rec, null) { } } + private sealed class ACustom : Custom { public ACustom(Rec rec) : base(rec, null) { } } private static PipelineColumn MakeScalar(Rec rec) => new AScalar(rec); private static PipelineColumn MakeVector(Rec rec) => new AVector(rec); @@ -67,6 +68,7 @@ private sealed class AVarKey : VarKey { public AVarKey(Rec rec) : base(rec private static PipelineColumn MakeKey(Rec rec) => new AKey(rec); private static Key MakeKey(Rec rec) => new AKey(rec); private static PipelineColumn MakeVarKey(Rec rec) => new AVarKey(rec); + private static PipelineColumn MakeCustom(Rec rec) => new ACustom(rec); private static MethodInfo[] _valueTupleCreateMethod = InitValueTupleCreateMethods(); @@ -121,6 +123,8 @@ public static object MakeAnalysisInstanceCore(Rec rec) } if (genT == typeof(VarKey<>)) return Utils.MarshalInvoke(MakeVector, genP[0], rec); + if (genT== typeof(Custom<>)) + return Utils.MarshalInvoke(MakeCustom, genP[0], rec); } throw Contracts.Except($"Type {t} is a {nameof(PipelineColumn)} yet does not appear to be directly one of " + $"the official types. This is commonly due to a mistake by the component author and can be addressed by " + diff --git a/src/Microsoft.ML.Data/StaticPipe/StaticSchemaShape.cs b/src/Microsoft.ML.Data/StaticPipe/StaticSchemaShape.cs index df981d25bb..82ae82141f 100644 --- a/src/Microsoft.ML.Data/StaticPipe/StaticSchemaShape.cs +++ b/src/Microsoft.ML.Data/StaticPipe/StaticSchemaShape.cs @@ -195,7 +195,7 @@ private static bool IsStandard(IExceptionContext ectx, Type t) } var gt = t.IsGenericType ? t.GetGenericTypeDefinition() : t; if (gt != typeof(Scalar<>) && gt != typeof(Key<>) && gt != typeof(Key<,>) && gt != typeof(VarKey<>) && - gt != typeof(Vector<>) && gt != typeof(VarVector<>) && gt != typeof(NormVector<>)) + gt != typeof(Vector<>) && gt != typeof(VarVector<>) && gt != typeof(NormVector<>) && gt != typeof(Custom<>)) { throw ectx.ExceptParam(nameof(t), $"Type {t} was not one of the standard subclasses of {nameof(PipelineColumn)}"); diff --git a/test/Microsoft.ML.StaticPipelineTesting/ImageAnalyticsTests.cs b/test/Microsoft.ML.StaticPipelineTesting/ImageAnalyticsTests.cs index ad29dbfde5..ea6fee04d3 100644 --- a/test/Microsoft.ML.StaticPipelineTesting/ImageAnalyticsTests.cs +++ b/test/Microsoft.ML.StaticPipelineTesting/ImageAnalyticsTests.cs @@ -35,6 +35,11 @@ public void SimpleImageSmokeTest() Assert.Equal(3, vecType.GetDim(0)); Assert.Equal(8, vecType.GetDim(1)); Assert.Equal(10, vecType.GetDim(2)); + + var readAsImage = TextLoader.CreateReader(env, + ctx => ctx.LoadText(0).LoadAsImage()); + var est = readAsImage.MakeNewEstimator().Append(r => r.AsGrayscale().Resize(10, 8).ExtractPixels()); + var pipe= readAsImage.Append(est); } } }