Skip to content

Commit 428e376

Browse files
committed
New sample, implicit conversions on TFTensors, some small touchups
1 parent 6de5952 commit 428e376

File tree

8 files changed

+693
-527
lines changed

8 files changed

+693
-527
lines changed

ExampleInceptionInference/ExampleInceptionInference.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
</Reference>
3434
<Reference Include="System.IO.Compression" />
3535
<Reference Include="System.IO.Compression.FileSystem" />
36+
<Reference Include="System.Numerics" />
3637
</ItemGroup>
3738
<ItemGroup>
3839
<Compile Include="Program.cs" />

ExampleInceptionInference/Program.cs

Lines changed: 136 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,35 @@
1-
using System;
1+
// An example for using the TensorFlow C# API for image recognition
2+
// using a pre-trained inception model (http://arxiv.org/abs/1512.00567).
3+
//
4+
// Sample usage: <program> -dir=/tmp/modeldir imagefile
5+
//
6+
// The pre-trained model takes input in the form of a 4-dimensional
7+
// tensor with shape [ BATCH_SIZE, IMAGE_HEIGHT, IMAGE_WIDTH, 3 ],
8+
// where:
9+
// - BATCH_SIZE allows for inference of multiple images in one pass through the graph
10+
// - IMAGE_HEIGHT is the height of the images on which the model was trained
11+
// - IMAGE_WIDTH is the width of the images on which the model was trained
12+
// - 3 is the (R, G, B) values of the pixel colors represented as a float.
13+
//
14+
// And produces as output a vector with shape [ NUM_LABELS ].
15+
// output[i] is the probability that the input image was recognized as
16+
// having the i-th label.
17+
//
18+
// A separate file contains a list of string labels corresponding to the
19+
// integer indices of the output.
20+
//
21+
// This example:
22+
// - Loads the serialized representation of the pre-trained model into a Graph
23+
// - Creates a Session to execute operations on the Graph
24+
// - Converts an image file to a Tensor to provide as input to a Session run
25+
// - Executes the Session and prints out the label with the highest probability
26+
//
27+
// To convert an image file to a Tensor suitable for input to the Inception model,
28+
// this example:
29+
// - Constructs another TensorFlow graph to normalize the image into a
30+
// form suitable for the model (for example, resizing the image)
31+
// - Creates an executes a Session to obtain a Tensor in this normalized form.
32+
using System;
233
using TensorFlow;
334
using Mono.Options;
435
using System.IO;
@@ -30,21 +61,118 @@ static void Help ()
3061
public static void Main (string [] args)
3162
{
3263
var files = options.Parse (args);
33-
if (dir == null)
34-
Error ("Must specify a directory with -m to store the training data");
35-
if (files == null)
36-
Error ("No files were specified");
64+
if (dir == null) {
65+
dir = "/tmp";
66+
//Error ("Must specify a directory with -m to store the training data");
67+
}
68+
string file;
69+
//if (files == null || files.Count == 0)
70+
// Error ("No files were specified");
71+
//file = files [0];
72+
file = "/tmp/demo.jpg";
3773

3874
ModelFiles (dir);
3975

76+
// Construct an in-memory graph from the serialized form.
77+
var graph = new TFGraph ();
78+
// Load the serialized GraphDef from a file.
4079
var model = File.ReadAllBytes (modelFile);
4180

42-
var g = new TFGraph ();
43-
g.Import (model, "");
44-
using (var s = new TFSession (g)) {
81+
graph.Import (model, "");
82+
using (var session = new TFSession (graph)) {
83+
// Run inference on the image files
84+
// For multiple images, session.Run() can be called in a loop (and
85+
// concurrently). Alternatively, images can be batched since the model
86+
// accepts batches of image data as input.
87+
var tensor = CreateTensorFromImageFile (file);
88+
89+
var output = session.Run (null,
90+
inputs: new [] { graph ["input"] [0] },
91+
inputValues: new [] { tensor },
92+
outputs: new [] { graph ["output"] [0] });
93+
// output[0].Value() is a vector containing probabilities of
94+
// labels for each image in the "batch". The batch size was 1.
95+
// Find the most probably label index.
96+
97+
var result = output [0];
98+
var rshape = result.Shape;
99+
if (result.NumDims != 2 || rshape [0] != 1) {
100+
var shape = "";
101+
foreach (var d in rshape) {
102+
shape += $"{d} ";
103+
}
104+
shape = shape.Trim ();
105+
Console.WriteLine ($"Error: expected to produce a [1 N] shaped tensor where N is the number of labels, instead it produced one with shape [{shape}]");
106+
Environment.Exit (1);
107+
}
108+
int nlabels = (int) rshape [1];
109+
110+
}
111+
}
112+
113+
// Convert the image in filename to a Tensor suitable as input to the Inception model.
114+
static TFTensor CreateTensorFromImageFile (string file)
115+
{
116+
var contents = File.ReadAllBytes (file);
117+
118+
// DecodeJpeg uses a scalar String-valued tensor as input.
119+
var tensor = (TFTensor) contents;
120+
121+
TFGraph graph;
122+
TFOutput input, output;
123+
124+
// Construct a graph to normalize the image
125+
ConstructGraphToNormalizeImage (out graph, out input, out output);
126+
127+
// Execute that graph to normalize this one image
128+
using (var session = new TFSession (graph)) {
129+
var normalized = session.Run (null,
130+
inputs: new [] { input },
131+
inputValues: new [] { tensor },
132+
outputs: new [] { output });
133+
134+
return normalized [0];
45135
}
46136
}
47137

138+
// The inception model takes as input the image described by a Tensor in a very
139+
// specific normalized format (a particular image size, shape of the input tensor,
140+
// normalized pixel values etc.).
141+
//
142+
// This function constructs a graph of TensorFlow operations which takes as
143+
// input a JPEG-encoded string and returns a tensor suitable as input to the
144+
// inception model.
145+
static void ConstructGraphToNormalizeImage (out TFGraph graph, out TFOutput input, out TFOutput output)
146+
{
147+
// Some constants specific to the pre-trained model at:
148+
// https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip
149+
//
150+
// - The model was trained after with images scaled to 224x224 pixels.
151+
// - The colors, represented as R, G, B in 1-byte each were converted to
152+
// float using (value - Mean)/Scale.
153+
154+
const int W = 224;
155+
const int H = 224;
156+
const float Mean = 117;
157+
const float Scale = 1;
158+
159+
graph = new TFGraph ();
160+
input = graph.Placeholder (TFDataType.String);
161+
output = graph.Div (
162+
x: graph.Sub (
163+
x: graph.ResizeBilinear (
164+
images: graph.ExpandDims (
165+
input: graph.Cast (
166+
graph.DecodeJpeg (contents: input, channels: 3), DstT: TFDataType.Float),
167+
dim: graph.Const (0, "make_batch")),
168+
size: graph.Const (new int [] { W, H })),
169+
y: graph.Const (Mean)),
170+
y: graph.Const (Scale));
171+
}
172+
173+
//
174+
// Downloads the inception graph and labels
175+
//
48176
static void ModelFiles (string dir)
49177
{
50178
string url = "https://storage.googleapis.com/download.tensorflow.org/models/inception5h.zip";

OpGenerator/OpGenerator.cs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
using System.Collections.Generic;
1818
using System.IO;
1919
using ProtoBuf;
20-
using TensorFlow;
21-
using tensorflow;
2220
using System.Linq;
2321
using System.Text;
22+
using System.Runtime.InteropServices;
23+
using tensorflow;
2424

2525
class OpGenerator
2626
{
@@ -143,19 +143,24 @@ void SetupArguments (OpDef def)
143143
string FillArguments (OpDef def)
144144
{
145145
var sb = new StringBuilder ();
146+
string comma = "";
146147
foreach (var inarg in def.input_arg) {
147148
string type = "TFOutput" + (IsListArg (inarg) ? "[]" : "");
148149

149-
sb.AppendFormat ($", {type} {ParamMap (inarg.name)}");
150+
sb.AppendFormat ($"{comma}{type} {ParamMap (inarg.name)}");
151+
comma = ", ";
152+
}
153+
foreach (var attr in required_attrs) {
154+
sb.AppendFormat ($"{comma}{CSharpType (attr.type)} {ParamMap (attr.name)}");
155+
comma = ", ";
150156
}
151-
foreach (var attr in required_attrs)
152-
sb.AppendFormat ($", {CSharpType (attr.type)} {ParamMap (attr.name)}");
153157

154158
if (!return_is_tfoutput) {
155159
foreach (var arg in def.output_arg) {
156160
string type = "TFOutput" + (IsListArg (arg) ? "[]" : "");
157161

158-
sb.AppendFormat ($", ref {type} {ParamMap (arg.name)}");
162+
sb.AppendFormat ($"{comma}ref {type} {ParamMap (arg.name)}");
163+
comma = ", ";
159164
}
160165
}
161166

@@ -165,8 +170,11 @@ string FillArguments (OpDef def)
165170
var cstype = CSharpType (attr.type);
166171
var cstypesuffix = reftype ? "" : "?";
167172

168-
sb.AppendFormat ($", {cstype}{cstypesuffix} {attr.name} = null");
173+
sb.AppendFormat ($"{comma}{cstype}{cstypesuffix} {attr.name} = null");
174+
comma = ", ";
169175
}
176+
if (sb.Length != 0)
177+
sb.Append (", ");
170178
return sb.ToString ();
171179
}
172180

@@ -289,7 +297,7 @@ void Generate (OpDef oper)
289297
retType = "TFOperation";
290298

291299

292-
p ($"public {retType} {name} (Scope scope{FillArguments(oper)}, string operName = null)");
300+
p ($"public {retType} {name} ({FillArguments(oper)}string operName = null)");
293301
pi ("{");
294302
bool needStatus = required_attrs.Concat (optional_attrs).Any (attr => attr.type.Contains ("TFTensor"));
295303
p ($"var desc = new TFOperationDesc (this, \"{oper.name}\", operName == null ? \"{oper.name}\" : operName);");
@@ -362,12 +370,33 @@ void Generate (OpDef oper)
362370
pd ("}\n");
363371
}
364372

373+
[StructLayout (LayoutKind.Sequential)]
374+
internal struct LLBuffer
375+
{
376+
internal IntPtr data;
377+
internal IntPtr length;
378+
internal IntPtr data_deallocator;
379+
}
380+
381+
[DllImport ("libtensorflow")]
382+
unsafe extern static LLBuffer *TF_GetAllOpList ();
383+
384+
MemoryStream GetOpsList ()
385+
{
386+
unsafe
387+
{
388+
LLBuffer* ptr = TF_GetAllOpList ();
389+
var ret = new byte [(int)ptr->length];
390+
Marshal.Copy (ptr->data, ret, 0, (int)ptr->length);
391+
return new MemoryStream (ret);
392+
}
393+
}
394+
365395
void Run ()
366396
{
367397

368398
output = File.CreateText ("../../../TensorFlowSharp/Operations.cs");
369-
370-
var operations = Serializer.Deserialize<List<OpDef>> (new MemoryStream (TFCore.GetAllOpList ().ToArray ()));
399+
var operations = Serializer.Deserialize<List<OpDef>> (GetOpsList ());
371400
p ("using System;\n");
372401

373402
pi ("namespace TensorFlow {");

OpGenerator/OpGenerator.csproj

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
<ErrorReport>prompt</ErrorReport>
1919
<WarningLevel>4</WarningLevel>
2020
<ExternalConsole>true</ExternalConsole>
21+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
2122
</PropertyGroup>
2223
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
2324
<Optimize>true</Optimize>
2425
<OutputPath>bin\Release</OutputPath>
2526
<ErrorReport>prompt</ErrorReport>
2627
<WarningLevel>4</WarningLevel>
2728
<ExternalConsole>true</ExternalConsole>
29+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
2830
</PropertyGroup>
2931
<ItemGroup>
3032
<Reference Include="System" />
@@ -44,12 +46,6 @@
4446
<ItemGroup>
4547
<None Include="packages.config" />
4648
</ItemGroup>
47-
<ItemGroup>
48-
<ProjectReference Include="..\TensorFlowSharp\TensorFlowSharp.csproj">
49-
<Project>{0264C321-34F4-46AF-819E-168D1E597232}</Project>
50-
<Name>TensorFlowSharp</Name>
51-
</ProjectReference>
52-
</ItemGroup>
5349
<ItemGroup>
5450
<Folder Include="New Folder\" />
5551
</ItemGroup>

SampleTest/SampleTest.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ TFOperation Placeholder (TFGraph graph, TFStatus s)
3737
TFOperation ScalarConst (int v, TFGraph graph, TFStatus status)
3838
{
3939
var desc = new TFOperationDesc (graph, "Const", "scalar");
40-
desc.SetAttr ("value", TFTensor.Constant (v), status);
40+
desc.SetAttr ("value", v, status);
4141
if (status.StatusCode != TFCode.Ok)
4242
return null;
4343
desc.SetAttrType ("dtype", TFDataType.Int32);
@@ -111,13 +111,13 @@ public void TestSession ()
111111
new TFOutput (feed, 0)
112112
};
113113
var input_values = new TFTensor [] {
114-
TFTensor.Constant (3)
114+
3
115115
};
116116
var outputs = new TFOutput [] {
117117
new TFOutput (add, 0)
118118
};
119119
var output_values = new TFTensor [] {
120-
TFTensor.Constant (3)
120+
3
121121
};
122122

123123
var results = session.Run ( runOptions: null,
@@ -142,10 +142,11 @@ public void TestSession ()
142142
public void TestOperationOutputListSize ()
143143
{
144144
using (var graph = new TFGraph ()) {
145-
var c1 = graph.Const (null, TFTensor.Constant (1L), "c1");
146-
var c2 = graph.Const (null, TFTensor.Constant (new long [,] { { 1, 2 }, { 3, 4 } }), "c2");
145+
var c1 = graph.Const (1L, "c1");
146+
var cl = graph.Const (new int []{ 1, 2 }, "cl");
147+
var c2 = graph.Const (new long [,] { { 1, 2 }, { 3, 4 } }, "c2");
147148

148-
var outputs = graph.ShapeN (null, new TFOutput [] { c1, c2 });
149+
var outputs = graph.ShapeN (new TFOutput [] { c1, c2 });
149150
var op = outputs [0].Operation;
150151

151152
Assert (op.OutputListLength ("output") == 2);
@@ -156,11 +157,11 @@ public void TestOperationOutputListSize ()
156157
public void TestOutputShape ()
157158
{
158159
using (var graph = new TFGraph ()) {
159-
var c1 = graph.Const (null, TFTensor.Constant (0L), "c1");
160+
var c1 = graph.Const (0L, "c1");
160161
var s1 = graph.GetShape (c1);
161-
var c2 = graph.Const (null, TFTensor.Constant (new long [] { 1, 2, 3 }), "c2");
162+
var c2 = graph.Const (new long [] { 1, 2, 3 }, "c2");
162163
var s2 = graph.GetShape (c2);
163-
var c3 = graph.Const (null, TFTensor.Constant (new long [,] { { 1, 2, 3 }, { 4, 5, 6 } }), "c3");
164+
var c3 = graph.Const (new long [,] { { 1, 2, 3 }, { 4, 5, 6 } }, "c3");
164165
var s3 = graph.GetShape (c3);
165166
}
166167
}

0 commit comments

Comments
 (0)