MyCaffe  1.12.2.41
Deep learning software for Windows C# programmers.
MyCaffePythonGym.cs
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Drawing;
5using System.Linq;
6using System.Runtime.InteropServices;
7using System.ServiceModel;
8using System.Text;
9using System.Threading;
10using System.Threading.Tasks;
11using System.Windows.Forms;
12using MyCaffe.basecode;
14
16{
45 public class MyCaffePythonGym : IXMyCaffeGymUiCallback, IDisposable
46 {
47 int m_nUiId = -1;
48 IXMyCaffeGym m_igym = null;
49 MyCaffeGymUiProxy m_gymui = null;
50 Log m_log = new Log("MyCaffePythonGym");
51 Tuple<SimpleDatum, double, bool> m_state = null;
52 List<SimpleDatum> m_rgData = new List<SimpleDatum>();
53 double[][] m_rgrgActionDistributions = null;
54 bool m_bNormalizeOverlay = true;
55 Font m_font = null;
56 Dictionary<Color, Tuple<Brush, Brush, Pen, Brush>> m_rgStyle = new Dictionary<Color, Tuple<Brush, Brush, Pen, Brush>>();
57
62 {
63 }
64
68 public void Dispose()
69 {
70 if (m_font != null)
71 {
72 m_font.Dispose();
73 m_font = null;
74 }
75
76 foreach (KeyValuePair<Color, Tuple<Brush, Brush, Pen, Brush>> kv in m_rgStyle)
77 {
78 kv.Value.Item1.Dispose();
79 kv.Value.Item2.Dispose();
80 kv.Value.Item3.Dispose();
81 kv.Value.Item4.Dispose();
82 }
83
84 m_rgStyle.Clear();
85 }
86
96 public int Initialize(string strGym, string strParam)
97 {
98 m_log.EnableTrace = true;
99
100 GymCollection col = new GymCollection();
101 col.Load();
102
103 m_igym = col.Find(strGym);
104 if (m_igym == null)
105 throw new Exception("Could not find the gym '" + strGym + "'!");
106
107 m_igym.Initialize(m_log, new PropertySet(strParam));
108
109 return 0;
110 }
111
115 public string Name
116 {
117 get
118 {
119 if (m_igym == null)
120 throw new Exception("You must call 'Initialize' first!");
121
122 return "MyCaffe " + m_igym.Name;
123 }
124 }
125
130 public void SetActionDistributions(double[][] rgrg)
131 {
132 m_rgrgActionDistributions = rgrg;
133 }
134
138 public int[] Actions
139 {
140 get
141 {
142 if (m_igym == null)
143 throw new Exception("You must call 'Initialize' first!");
144
145 List<int> rg = new List<int>();
146 Dictionary<string, int> rgActions = m_igym.GetActionSpace();
147 int nActionIdx = 0;
148
149 foreach (KeyValuePair<string, int> kv in rgActions)
150 {
151 rg.Add(nActionIdx);
152 nActionIdx++;
153 }
154
155 return rg.ToArray();
156 }
157 }
158
162 public string[] ActionNames
163 {
164 get
165 {
166 if (m_igym == null)
167 throw new Exception("You must call 'Initialize' first!");
168
169 List<string> rg = new List<string>();
170 Dictionary<string, int> rgActions = m_igym.GetActionSpace();
171
172 foreach (KeyValuePair<string, int> kv in rgActions)
173 {
174 rg.Add(kv.Key);
175 }
176
177 return rg.ToArray();
178 }
179 }
180
184 public bool IsTerminal
185 {
186 get { return (m_state == null) ? true : m_state.Item3; }
187 }
188
192 public double Reward
193 {
194 get { return (m_state == null) ? 0 : m_state.Item2; }
195 }
196
200 public List<double> Data
201 {
202 get { return (m_state == null) ? null : m_state.Item1.GetData<double>().ToList(); }
203 }
204
212 public List<List<List<double>>> GetDataAs3D(bool bGrayscale = false, double dfScale = 1, SimpleDatum sd = null)
213 {
214 double[] rgData1 = (sd == null) ? Data.ToArray() : sd.GetData<double>();
215 int nChannels = (sd == null) ? m_state.Item1.Channels : sd.Channels;
216 int nHeight = (sd == null) ? m_state.Item1.Height : sd.Height;
217 int nWidth = (sd == null) ? m_state.Item1.Width : sd.Width;
218 List<List<List<double>>> rgrgrgData = new List<List<List<double>>>();
219
220 for (int h = 0; h < nHeight; h++)
221 {
222 List<List<double>> rgrgData = new List<List<double>>();
223
224 for (int w = 0; w < nWidth; w++)
225 {
226 List<double> rgData = new List<double>();
227
228 if (bGrayscale)
229 {
230 double dfSum = 0;
231
232 for (int c = 0; c < nChannels; c++)
233 {
234 int nIdx = (c * nHeight * nWidth) + (h * nWidth) + w;
235 dfSum += rgData1[nIdx];
236 }
237
238 rgData.Add((dfSum / nChannels) * dfScale);
239 }
240 else
241 {
242 for (int c = 0; c < nChannels; c++)
243 {
244 int nIdx = (c * nHeight * nWidth) + (h * nWidth) + w;
245 double dfVal = rgData1[nIdx];
246
247 rgData.Add(dfVal * dfScale);
248 }
249 }
250
251 rgrgData.Add(rgData);
252 }
253
254 rgrgrgData.Add(rgrgData);
255 }
256
257 return rgrgrgData;
258 }
259
260 private SimpleDatum preprocess(SimpleDatum sd, bool bGrayscale, double dfScale)
261 {
262 if (!bGrayscale && dfScale == 1)
263 return sd;
264
265 double[] rgData = sd.GetData<double>();
266 int nCount = sd.Height * sd.Width;
267 int nChannels = sd.Channels;
268 bool bIsReal = sd.IsRealData;
269 byte[] rgByteData = null;
270 double[] rgRealData = null;
271
272 if (bIsReal && !bGrayscale)
273 {
274 nCount *= sd.Channels;
275 rgRealData = new double[nCount];
276 }
277 else
278 {
279 bIsReal = false;
280 nChannels = 1;
281 rgByteData = new byte[nCount];
282 }
283
284 for (int h = 0; h < sd.Height; h++)
285 {
286 for (int w = 0; w < sd.Width; w++)
287 {
288 int nIdx = (h * sd.Width) + w;
289 int nIdxSrc = nIdx * sd.Channels;
290
291 if (rgRealData != null)
292 {
293 for (int c = 0; c < sd.Channels; c++)
294 {
295 double dfVal = rgData[nIdxSrc + c] * dfScale;
296 rgRealData[nIdxSrc + c] = dfVal;
297 }
298 }
299 else
300 {
301 double dfSum = 0;
302
303 for (int c = 0; c < sd.Channels; c++)
304 {
305 dfSum += rgData[nIdxSrc + c];
306 }
307
308 double dfVal = ((dfSum / sd.Channels) * dfScale);
309 if (dfVal > 255)
310 dfVal = 255;
311
312 if (dfVal < 0)
313 dfVal = 0;
314
315 rgByteData[nIdx] = (byte)dfVal;
316 }
317 }
318 }
319
320 SimpleDatum sdResult;
321
322 if (rgRealData != null)
323 sdResult = new SimpleDatum(bIsReal, nChannels, sd.Width, sd.Height, sd.Label, sd.TimeStamp, rgRealData, sd.Boost, sd.AutoLabeled, sd.Index);
324 else
325 sdResult = new SimpleDatum(bIsReal, nChannels, sd.Width, sd.Height, sd.Label, sd.TimeStamp, rgByteData, sd.Boost, sd.AutoLabeled, sd.Index);
326
327 sdResult.Tag = sd.Tag;
328
329 return sdResult;
330 }
331
341 public List<List<List<double>>> GetDataAsStacked3D(bool bReset, int nFrames = 4, int nStacks = 4, bool bGrayscale = true, double dfScale = 1)
342 {
343 SimpleDatum sd = preprocess(m_state.Item1, bGrayscale, dfScale);
344
345 if (bReset)
346 {
347 m_rgData.Clear();
348
349 for (int i = 0; i < nFrames * nStacks; i++)
350 {
351 m_rgData.Add(sd);
352 }
353 }
354 else
355 {
356 m_rgData.Add(sd);
357 m_rgData.RemoveAt(0);
358 }
359
360 SimpleDatum[] rgSd = new SimpleDatum[nStacks];
361
362 for (int i = 0; i < nStacks; i++)
363 {
364 int nIdx = ((nStacks - i) * nFrames) - 1;
365 rgSd[i] = m_rgData[nIdx];
366 }
367
368 SimpleDatum sd1 = new SimpleDatum(rgSd.ToList(), true);
369
370 return GetDataAs3D(false, 1, sd1);
371 }
372
378 public CurrentState Reset(PropertySet props = null)
379 {
380 if (m_igym == null)
381 throw new Exception("You must call 'Initialize' first!");
382
383 Tuple<State, double, bool> state = m_igym.Reset(false, props);
384
385 bool bIsOpen = (m_nUiId >= 0) ? true : false;
386 Tuple<Bitmap, SimpleDatum> data = m_igym.Render(bIsOpen, 512, 512, true);
387 int nDataLen = 0;
388 SimpleDatum sd = state.Item1.GetData(false, out nDataLen);
389
390 if (data.Item1 != null || data.Item2 != null)
391 {
392 Observation obs = new Observation(data.Item1, ImageData.GetImage(data.Item2), m_igym.RequiresDisplayImage, sd.GetData<double>(), state.Item2, state.Item3);
393
394 if (bIsOpen)
395 {
396 if (m_rgrgActionDistributions != null)
397 overlay(obs.ImageDisplay, m_rgrgActionDistributions);
398
399 m_gymui.Render(m_nUiId, obs);
400 Thread.Sleep(m_igym.UiDelay);
401 }
402
403 if (m_igym.SelectedDataType == DATA_TYPE.BLOB)
404 sd = data.Item2;
405 else
406 sd.Clip(nDataLen, null, nDataLen, null);
407 }
408
409 m_state = new Tuple<SimpleDatum, double, bool>(sd, state.Item2, state.Item3);
410
411 return new CurrentState(m_state.Item1.GetData<double>(), m_state.Item2, m_state.Item3);
412 }
413
421 public CurrentState Step(int nAction, int nSteps = 1, PropertySet props = null)
422 {
423 if (m_igym == null)
424 throw new Exception("You must call 'Initialize' first!");
425
426 for (int i = 0; i < nSteps-1; i++)
427 {
428 m_igym.Step(nAction);
429 }
430
431 Tuple<State, double, bool> state = m_igym.Step(nAction, false, props);
432
433 bool bIsOpen = (m_nUiId >= 0) ? true : false;
434 Tuple<Bitmap, SimpleDatum> data = m_igym.Render(bIsOpen, 512, 512, true);
435 int nDataLen = 0;
436 SimpleDatum sd = state.Item1.GetData(false, out nDataLen);
437
438 if (data.Item1 != null || data.Item2 != null)
439 {
440 Observation obs = new Observation(data.Item1, ImageData.GetImage(data.Item2), m_igym.RequiresDisplayImage, sd.GetData<double>(), state.Item2, state.Item3);
441
442 if (bIsOpen)
443 {
444 if (m_rgrgActionDistributions != null)
445 overlay(obs.ImageDisplay, m_rgrgActionDistributions);
446
447 m_gymui.Render(m_nUiId, obs);
448 Thread.Sleep(m_igym.UiDelay);
449 }
450
451 if (m_igym.SelectedDataType == DATA_TYPE.BLOB)
452 sd = data.Item2;
453 else
454 sd.Clip(nDataLen, null, nDataLen, null);
455 }
456
457 m_state = new Tuple<SimpleDatum, double, bool>(sd, state.Item2, state.Item3);
458
459 return new CurrentState(m_state.Item1.GetData<double>(), m_state.Item2, m_state.Item3, state.Item1);
460 }
461
462 private void overlay(Bitmap bmp, double[][] rgData)
463 {
464 if (bmp == null)
465 return;
466
467 using (Graphics g = Graphics.FromImage(bmp))
468 {
469 int nBorder = 30;
470 int nWid = bmp.Width - (nBorder * 2);
471 int nWid1 = nWid / rgData.Length;
472 int nHt1 = (int)(bmp.Height * 0.3);
473 int nX = nBorder;
474 int nY = bmp.Height - nHt1;
475 ColorMapper clrMap = new ColorMapper(0, rgData.Length + 1, Color.Black, Color.Red);
476 float[] rgfMin = new float[rgData.Length];
477 float[] rgfMax = new float[rgData.Length];
478 float fMax = -float.MaxValue;
479 float fMaxMax = -float.MaxValue;
480 int nMaxIdx = 0;
481
482 for (int i = 0; i < rgData.Length; i++)
483 {
484 rgfMin[i] = (float)rgData[i].Min(p => p);
485 rgfMax[i] = (float)rgData[i].Max(p => p);
486
487 if (rgfMax[i] > fMax)
488 {
489 fMax = rgfMax[i];
490 nMaxIdx = i;
491 }
492
493 fMaxMax = Math.Max(fMax, fMaxMax);
494 }
495
496 if (fMaxMax > 0.2f)
497 m_bNormalizeOverlay = false;
498
499 for (int i = 0; i < rgData.Length; i++)
500 {
501 drawProbabilities(g, nX, nY, nWid1, nHt1, i, rgData[i], clrMap.GetColor(i + 1), rgfMin.Min(p => p), rgfMax.Max(p => p), (i == nMaxIdx) ? true : false, m_bNormalizeOverlay);
502 nX += nWid1;
503 }
504 }
505 }
506
507 private void drawProbabilities(Graphics g, int nX, int nY, int nWid, int nHt, int nAction, double[] rgProb, Color clr, float fMin, float fMax, bool bMax, bool bNormalize)
508 {
509 string str = "";
510
511 if (m_font == null)
512 m_font = new Font("Century Gothic", 9.0f);
513
514 if (!m_rgStyle.ContainsKey(clr))
515 {
516 Color clr1 = Color.FromArgb(128, clr);
517 Brush br1 = new SolidBrush(clr1);
518 Color clr2 = Color.FromArgb(64, clr);
519 Pen pen = new Pen(clr2, 1.0f);
520 Brush br2 = new SolidBrush(clr2);
521 Brush brBright = new SolidBrush(clr);
522 m_rgStyle.Add(clr, new Tuple<Brush, Brush, Pen, Brush>(br1, br2, pen, brBright));
523 }
524
525 Brush brBack = m_rgStyle[clr].Item1;
526 Brush brFront = m_rgStyle[clr].Item2;
527 Brush brTop = m_rgStyle[clr].Item4;
528 Pen penLine = m_rgStyle[clr].Item3;
529
530 if (fMin != 0 || fMax != 0)
531 {
532 str = "Action " + nAction.ToString() + " (" + (fMax - fMin).ToString("N7") + ")";
533 }
534 else
535 {
536 str = "Action " + nAction.ToString() + " - No Probabilities";
537 }
538
539 SizeF sz = g.MeasureString(str, m_font);
540
541 int nY1 = (int)(nY + (nHt - sz.Height));
542 int nX1 = (int)(nX + (nWid / 2) - (sz.Width / 2));
543 g.DrawString(str, m_font, (bMax) ? brTop : brFront, new Point(nX1, nY1));
544
545 if (fMin != 0 || fMax != 0)
546 {
547 float fX = nX;
548 float fWid = nWid / (float)rgProb.Length;
549 nHt -= (int)sz.Height;
550
551 for (int i = 0; i < rgProb.Length; i++)
552 {
553 float fProb = (float)rgProb[i];
554
555 if (bNormalize)
556 fProb = (fProb - fMin) / (fMax - fMin);
557
558 float fHt = nHt * fProb;
559 float fHt1 = nHt - fHt;
560 RectangleF rc1 = new RectangleF(fX, nY + fHt1, fWid, fHt);
561 g.FillRectangle(brBack, rc1);
562 g.DrawRectangle(penLine, rc1.X, rc1.Y, rc1.Width, rc1.Height);
563 fX += fWid;
564 }
565 }
566 }
567
574 {
575 if (m_igym == null)
576 throw new Exception("You must first Initialize the MyCaffePythonGym!");
577
578 return m_igym.GetDataset(type);
579 }
580
585 public void OpenUi(bool bStartRecording = false)
586 {
587 if (m_gymui != null)
588 return;
589
590 try
591 {
592 m_gymui = new MyCaffeGymUiProxy(new InstanceContext(this));
593 m_gymui.Open();
594 m_nUiId = m_gymui.OpenUi(Name, m_nUiId, bStartRecording);
595 }
596 catch (Exception excpt)
597 {
598 throw new Exception("You need to run the MyCaffe Test Application which supports the gym user interface host.", excpt);
599 }
600 }
601
605 public void CloseUi()
606 {
607 if (m_gymui == null)
608 return;
609
610 m_gymui.CloseUi(0);
611 m_gymui.Close();
612 m_gymui = null;
613 m_nUiId = -1;
614 }
615
619 public void Closing()
620 {
621 }
622 }
623
627 public class CurrentState
628 {
629 State m_gymState = null;
630 SimpleDatum m_rgData;
631 double m_dfReward;
632 bool m_bTerminal;
633
641 public CurrentState(double[] rgData, double dfReward, bool bTerminal, State gymState = null)
642 {
643 m_rgData = new SimpleDatum(rgData.Length, 1, 1, rgData.Select(p => (float)p).ToArray(), 0, rgData.Length);
644 m_dfReward = dfReward;
645 m_bTerminal = bTerminal;
646 m_gymState = gymState;
647 }
648
653 {
654 get { return m_rgData; }
655 }
656
660 public double Reward
661 {
662 get { return m_dfReward; }
663 }
664
668 public bool Terminal
669 {
670 get { return m_bTerminal; }
671 }
672
677 {
678 get { return m_gymState; }
679 }
680 }
681}
The ColorMapper maps a value within a number range, to a Color within a color scheme.
Definition: ColorMapper.cs:14
Color GetColor(double dfVal)
Find the color using a binary search algorithm.
Definition: ColorMapper.cs:350
The ImageData class is a helper class used to convert between Datum, other raw data,...
Definition: ImageData.cs:14
static Bitmap GetImage(SimpleDatum d, ColorMapper clrMap=null, List< int > rgClrOrder=null)
Converts a SimplDatum (or Datum) into an image, optionally using a ColorMapper.
Definition: ImageData.cs:506
The Log class provides general output in text form.
Definition: Log.cs:13
bool EnableTrace
Enables/disables the Trace. When enabled, the .Net Trace.WriteLine is called in addition to the norma...
Definition: Log.cs:67
Specifies a key-value pair of properties.
Definition: PropertySet.cs:16
The SimpleDatum class holds a data input within host memory.
Definition: SimpleDatum.cs:161
DateTime TimeStamp
Get/set the Timestamp.
object Tag
Specifies user data associated with the SimpleDatum.
Definition: SimpleDatum.cs:901
int Channels
Return the number of channels of the data.
bool IsRealData
Returns whether or not the data contains real numbers or byte data.
int Boost
Get/set the boost for this data.
void Clip(int nDataLen, int? nNewChannel, int? nNewHeight, int? nNewWidth)
Clip the data length down to a smaller size and copies the clipped data.
Definition: SimpleDatum.cs:974
int Width
Return the width of the data.
bool AutoLabeled
Get/set whether or not the label was auto generated.
int Index
Returns the index of the SimpleDatum.
int Height
Return the height of the data.
int Label
Return the known label of the data.
The DatasetDescriptor class describes a dataset which contains both a training data source and testin...
The GymCollection contains the available Gyms.
List< Exception > Load()
Loads the default and dynamic gyms.
IXMyCaffeGym Find(string strName)
Search for a given Gym by its name.
The MyCaffeGymUiProxy is used to interact with the MyCaffeGymUiService.
int OpenUi(string strName, int nId, bool bStartRecording=false)
Open the Gym user interface.
void Render(int nId, Observation obs)
Render the observation of the Gym.
void CloseUi(int nId)
Closes the Gym user interface.
The Observation contains data describing the Gym as it runs.
Bitmap ImageDisplay
Get/set the image to display.
The State class defines an abstract base class for the state information and gym data.
Definition: Interfaces.cs:337
The State contains the current state of the gym.
CurrentState(double[] rgData, double dfReward, bool bTerminal, State gymState=null)
The constructor.
State GymState
Returns the GymState if provided.
SimpleDatum Data
Returns the data.
double Reward
Returns the reward.
bool Terminal
Returns whether or not the state is terminal or not.
The MyCaffePythonGym class provides a simple interface that can easily be used from within Python.
bool? IsTerminal
Returns the terminal state from the last state.
double? Reward
Returns the reward from the last state.
void CloseUi()
The CloseUi method closes the user interface if it is open.
void Dispose()
Release resources used.
void OpenUi(bool bStartRecording=false)
The OpenUi method opens the user interface to visualize the gym as it progresses.
List< List< List< double > > > GetDataAs3D(bool bGrayscale=false, double dfScale=1, SimpleDatum sd=null)
Returns the data int a 3D form compatible with CV2.
int Initialize(string strGym, string strParam)
The Initialize method loads the gym specified.
int[] Actions
Returns the action values
void SetActionDistributions(double[][] rgrg)
Set the action distribution for display.
CurrentState Reset(PropertySet props=null)
Resets the gym to its initial state.
string[] ActionNames
Returns the action names
DatasetDescriptor GetDataset(DATA_TYPE type)
Returns the dataset descriptor of the Gym.
List< List< List< double > > > GetDataAsStacked3D(bool bReset, int nFrames=4, int nStacks=4, bool bGrayscale=true, double dfScale=1)
Returns stacked data in a 3D form compatible with CV2.
string Name
Returns the name of the gym.
List< double >? Data
Returns the data from the last state.
CurrentState Step(int nAction, int nSteps=1, PropertySet props=null)
Steps the gym one or more steps with a given action.
void Closing()
The Closing method is a call-back method called when the gym closes.
The IXMyCaffeGym interface is used to interact with each Gym.
Definition: Interfaces.cs:99
DatasetDescriptor GetDataset(DATA_TYPE dt, Log log=null)
Returns the dataset of the gym.
string Name
Returns the name of the gym.
Definition: Interfaces.cs:119
Tuple< Bitmap, SimpleDatum > Render(bool bShowUi, int nWidth, int nHeight, bool bGetAction)
Render the gym on a bitmap.
Tuple< State, double, bool > Step(int nAction, bool bGetLabel=false, PropertySet extraProp=null)
Run an action on the gym.
int UiDelay
Returns the user-interface delay to use (if any).
Definition: Interfaces.cs:169
Tuple< State, double, bool > Reset(bool bGetLabel=false, PropertySet props=null)
Resets the state of they gym.
DATA_TYPE SelectedDataType
Returns the selected data-type.
Definition: Interfaces.cs:173
Dictionary< string, int > GetActionSpace()
Returns a dictionary containing the action space where each entry contains the action name and action...
void Initialize(Log log, PropertySet properties)
Initialize the gym using the properties in the PropertySet.
bool RequiresDisplayImage
Returns whether or not the gym requires the display image.
Definition: Interfaces.cs:181
The IXMyCaffeGymUiCallback is used to interact with the user of the IXMyCaffeGymUiService interface.
The descriptors namespace contains all descriptor used to describe various items stored within the da...
The MyCaffe.basecode contains all generic types used throughout MyCaffe.
Definition: Annotation.cs:12
DATA_TYPE
Defines the gym data type.
Definition: Interfaces.cs:135
The MyCaffe namespace contains the main body of MyCaffe code that closesly tracks the C++ Caffe open-...
Definition: Annotation.cs:12