├── .gitignore
├── .gitmodules
├── LICENSE
├── MapRender.Invoker
├── MapRender.Invoker.csproj
├── MapRender.cs
├── MapRenderInvoker.cs
├── ObjectClass.cs
├── ScreenShotData.cs
└── TargetItem.cs
├── MapleStory.Common
├── Exceptions
│ └── WzImgNotFoundException.cs
├── MapleStory.Common.csproj
├── MapleStoryPathHelper.cs
└── WzTreeSearcher.cs
├── MapleStory.MachineLearningSampleGenerator
├── MapleStory.MachineLearningSampleGenerator.csproj
├── OutputFormat.cs
└── Program.cs
├── MapleStory.Sampler
├── CocoWriter.cs
├── DarknetWriter.cs
├── IDatasetWriter.cs
├── MapleStory.Sampler.csproj
├── PostProcessor
│ ├── IPostProcessor.cs
│ └── PlayerProcessor.cs
├── Sample.cs
├── Sampler.cs
├── TfExample.cs
├── TfRecordWriter.cs
└── protobuf
│ ├── Example.cs
│ ├── Feature.cs
│ ├── README.md
│ ├── example.proto
│ └── feature.proto
├── MapleStoryDetectionSampleGenerator.sln
├── README.md
└── pictures
├── chart_yolov4-custom.png
└── result.png
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 | */**/launchSettings.json
13 |
14 | # User-specific files (MonoDevelop/Xamarin Studio)
15 | *.userprefs
16 |
17 | # Mono auto generated files
18 | mono_crash.*
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Ll]og/
33 | [Ll]ogs/
34 |
35 | # Visual Studio 2015/2017 cache/options directory
36 | .vs/
37 | # Uncomment if you have tasks that create the project's static files in wwwroot
38 | #wwwroot/
39 |
40 | # Visual Studio 2017 auto generated files
41 | Generated\ Files/
42 |
43 | # MSTest test Results
44 | [Tt]est[Rr]esult*/
45 | [Bb]uild[Ll]og.*
46 |
47 | # NUnit
48 | *.VisualState.xml
49 | TestResult.xml
50 | nunit-*.xml
51 |
52 | # Build Results of an ATL Project
53 | [Dd]ebugPS/
54 | [Rr]eleasePS/
55 | dlldata.c
56 |
57 | # Benchmark Results
58 | BenchmarkDotNet.Artifacts/
59 |
60 | # .NET Core
61 | project.lock.json
62 | project.fragment.lock.json
63 | artifacts/
64 |
65 | # StyleCop
66 | StyleCopReport.xml
67 |
68 | # Files built by Visual Studio
69 | *_i.c
70 | *_p.c
71 | *_h.h
72 | *.ilk
73 | *.meta
74 | *.obj
75 | *.iobj
76 | *.pch
77 | *.pdb
78 | *.ipdb
79 | *.pgc
80 | *.pgd
81 | *.rsp
82 | *.sbr
83 | *.tlb
84 | *.tli
85 | *.tlh
86 | *.tmp
87 | *.tmp_proj
88 | *_wpftmp.csproj
89 | *.log
90 | *.vspscc
91 | *.vssscc
92 | .builds
93 | *.pidb
94 | *.svclog
95 | *.scc
96 |
97 | # Chutzpah Test files
98 | _Chutzpah*
99 |
100 | # Visual C++ cache files
101 | ipch/
102 | *.aps
103 | *.ncb
104 | *.opendb
105 | *.opensdf
106 | *.sdf
107 | *.cachefile
108 | *.VC.db
109 | *.VC.VC.opendb
110 |
111 | # Visual Studio profiler
112 | *.psess
113 | *.vsp
114 | *.vspx
115 | *.sap
116 |
117 | # Visual Studio Trace Files
118 | *.e2e
119 |
120 | # TFS 2012 Local Workspace
121 | $tf/
122 |
123 | # Guidance Automation Toolkit
124 | *.gpState
125 |
126 | # ReSharper is a .NET coding add-in
127 | _ReSharper*/
128 | *.[Rr]e[Ss]harper
129 | *.DotSettings.user
130 |
131 | # TeamCity is a build add-in
132 | _TeamCity*
133 |
134 | # DotCover is a Code Coverage Tool
135 | *.dotCover
136 |
137 | # AxoCover is a Code Coverage Tool
138 | .axoCover/*
139 | !.axoCover/settings.json
140 |
141 | # Visual Studio code coverage results
142 | *.coverage
143 | *.coveragexml
144 |
145 | # NCrunch
146 | _NCrunch_*
147 | .*crunch*.local.xml
148 | nCrunchTemp_*
149 |
150 | # MightyMoose
151 | *.mm.*
152 | AutoTest.Net/
153 |
154 | # Web workbench (sass)
155 | .sass-cache/
156 |
157 | # Installshield output folder
158 | [Ee]xpress/
159 |
160 | # DocProject is a documentation generator add-in
161 | DocProject/buildhelp/
162 | DocProject/Help/*.HxT
163 | DocProject/Help/*.HxC
164 | DocProject/Help/*.hhc
165 | DocProject/Help/*.hhk
166 | DocProject/Help/*.hhp
167 | DocProject/Help/Html2
168 | DocProject/Help/html
169 |
170 | # Click-Once directory
171 | publish/
172 |
173 | # Publish Web Output
174 | *.[Pp]ublish.xml
175 | *.azurePubxml
176 | # Note: Comment the next line if you want to checkin your web deploy settings,
177 | # but database connection strings (with potential passwords) will be unencrypted
178 | *.pubxml
179 | *.publishproj
180 |
181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
182 | # checkin your Azure Web App publish settings, but sensitive information contained
183 | # in these scripts will be unencrypted
184 | PublishScripts/
185 |
186 | # NuGet Packages
187 | *.nupkg
188 | # NuGet Symbol Packages
189 | *.snupkg
190 | # The packages folder can be ignored because of Package Restore
191 | **/[Pp]ackages/*
192 | # except build/, which is used as an MSBuild target.
193 | !**/[Pp]ackages/build/
194 | # Uncomment if necessary however generally it will be regenerated when needed
195 | #!**/[Pp]ackages/repositories.config
196 | # NuGet v3's project.json files produces more ignorable files
197 | *.nuget.props
198 | *.nuget.targets
199 |
200 | # Microsoft Azure Build Output
201 | csx/
202 | *.build.csdef
203 |
204 | # Microsoft Azure Emulator
205 | ecf/
206 | rcf/
207 |
208 | # Windows Store app package directories and files
209 | AppPackages/
210 | BundleArtifacts/
211 | Package.StoreAssociation.xml
212 | _pkginfo.txt
213 | *.appx
214 | *.appxbundle
215 | *.appxupload
216 |
217 | # Visual Studio cache files
218 | # files ending in .cache can be ignored
219 | *.[Cc]ache
220 | # but keep track of directories ending in .cache
221 | !?*.[Cc]ache/
222 |
223 | # Others
224 | ClientBin/
225 | ~$*
226 | *~
227 | *.dbmdl
228 | *.dbproj.schemaview
229 | *.jfm
230 | *.pfx
231 | *.publishsettings
232 | orleans.codegen.cs
233 |
234 | # Including strong name files can present a security risk
235 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
236 | #*.snk
237 |
238 | # Since there are multiple workflows, uncomment next line to ignore bower_components
239 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
240 | #bower_components/
241 |
242 | # RIA/Silverlight projects
243 | Generated_Code/
244 |
245 | # Backup & report files from converting an old project file
246 | # to a newer Visual Studio version. Backup files are not needed,
247 | # because we have git ;-)
248 | _UpgradeReport_Files/
249 | Backup*/
250 | UpgradeLog*.XML
251 | UpgradeLog*.htm
252 | ServiceFabricBackup/
253 | *.rptproj.bak
254 |
255 | # SQL Server files
256 | *.mdf
257 | *.ldf
258 | *.ndf
259 |
260 | # Business Intelligence projects
261 | *.rdl.data
262 | *.bim.layout
263 | *.bim_*.settings
264 | *.rptproj.rsuser
265 | *- [Bb]ackup.rdl
266 | *- [Bb]ackup ([0-9]).rdl
267 | *- [Bb]ackup ([0-9][0-9]).rdl
268 |
269 | # Microsoft Fakes
270 | FakesAssemblies/
271 |
272 | # GhostDoc plugin setting file
273 | *.GhostDoc.xml
274 |
275 | # Node.js Tools for Visual Studio
276 | .ntvs_analysis.dat
277 | node_modules/
278 |
279 | # Visual Studio 6 build log
280 | *.plg
281 |
282 | # Visual Studio 6 workspace options file
283 | *.opt
284 |
285 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
286 | *.vbw
287 |
288 | # Visual Studio LightSwitch build output
289 | **/*.HTMLClient/GeneratedArtifacts
290 | **/*.DesktopClient/GeneratedArtifacts
291 | **/*.DesktopClient/ModelManifest.xml
292 | **/*.Server/GeneratedArtifacts
293 | **/*.Server/ModelManifest.xml
294 | _Pvt_Extensions
295 |
296 | # Paket dependency manager
297 | .paket/paket.exe
298 | paket-files/
299 |
300 | # FAKE - F# Make
301 | .fake/
302 |
303 | # CodeRush personal settings
304 | .cr/personal
305 |
306 | # Python Tools for Visual Studio (PTVS)
307 | __pycache__/
308 | *.pyc
309 |
310 | # Cake - Uncomment if you are using it
311 | # tools/**
312 | # !tools/packages.config
313 |
314 | # Tabs Studio
315 | *.tss
316 |
317 | # Telerik's JustMock configuration file
318 | *.jmconfig
319 |
320 | # BizTalk build output
321 | *.btp.cs
322 | *.btm.cs
323 | *.odx.cs
324 | *.xsd.cs
325 |
326 | # OpenCover UI analysis results
327 | OpenCover/
328 |
329 | # Azure Stream Analytics local run output
330 | ASALocalRun/
331 |
332 | # MSBuild Binary and Structured Log
333 | *.binlog
334 |
335 | # NVidia Nsight GPU debugger configuration file
336 | *.nvuser
337 |
338 | # MFractors (Xamarin productivity tool) working folder
339 | .mfractor/
340 |
341 | # Local History for Visual Studio
342 | .localhistory/
343 |
344 | # BeatPulse healthcheck temp database
345 | healthchecksdb
346 |
347 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
348 | MigrationBackup/
349 |
350 | # Ionide (cross platform F# VS Code tools) working folder
351 | .ionide/
352 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "WzComparerR2"]
2 | path = WzComparerR2
3 | url = git@github.com:charlescao460/WzComparerR2.git
4 | branch = .
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Charles Cao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MapRender.Invoker/MapRender.Invoker.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows7.0
4 | x64
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ..\WzComparerR2\References\DevComponents.DotNetBar2.dll
18 |
19 |
20 | ..\WzComparerR2\References\EmptyKeys.UserInterface.dll
21 |
22 |
23 | ..\WzComparerR2\References\EmptyKeys.UserInterface.Core.dll
24 |
25 |
26 | ..\WzComparerR2\References\MonoGame.Framework.dll
27 |
28 |
29 |
--------------------------------------------------------------------------------
/MapRender.Invoker/MapRender.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading;
7 | using System.Threading.Tasks;
8 | using Microsoft.Xna.Framework;
9 | using Microsoft.Xna.Framework.Graphics;
10 | using WzComparerR2.Animation;
11 | using WzComparerR2.MapRender;
12 | using WzComparerR2.MapRender.Patches;
13 | using WzComparerR2.MapRender.Patches2;
14 | using WzComparerR2.WzLib;
15 |
16 | namespace MapRender.Invoker
17 | {
18 | internal class MapRender : FrmMapRender2
19 | {
20 | private volatile Stream _screenShotStream;
21 | private volatile ScreenShotData _screenShotData;
22 |
23 | public MapRender(Wz_Image img) : base()
24 | {
25 | LoadMap(img);
26 | }
27 |
28 | public void ChangeResolution(int width, int height)
29 | {
30 | GraphicsDeviceManager deviceManager = this.GraphicsManager;
31 | deviceManager.PreferredBackBufferWidth = width;
32 | deviceManager.PreferredBackBufferHeight = height;
33 | WzComparerR2.Rendering.D2DFactory.Instance.ReleaseContext(deviceManager.GraphicsDevice);
34 | deviceManager.ApplyChanges();
35 | this.ui.Width = width;
36 | this.ui.Height = height;
37 | engine.Renderer.ResetNativeSize();
38 | }
39 |
40 | ///
41 | /// Take Screen, save image to stream, and return items info on the entire map.
42 | ///
43 | /// Stream to save image
44 | public ScreenShotData TakeScreenShot(Stream stream)
45 | {
46 | _screenShotStream = stream;
47 | while (_screenShotStream != null) ; //Wait next Draw(), yield to GetScreenShotMapData()
48 | var ret = _screenShotData;
49 | _screenShotData = null;
50 | return ret;
51 | }
52 |
53 | ///
54 | /// Switch to a new map
55 | ///
56 | /// Wz img id
57 | public void SwitchToNewMap(int imgId)
58 | {
59 | MoveToPortal(imgId, null);
60 | WaitSceneLoading();
61 | }
62 |
63 | public void WaitSceneLoading()
64 | {
65 | SpinWait spinWait = new SpinWait();
66 | // Wait until new map loaded
67 | while (!SceneRunning)
68 | {
69 | spinWait.SpinOnce();
70 | }
71 | }
72 |
73 | protected override void Draw(GameTime gameTime)
74 | {
75 | base.Draw(gameTime);
76 | if (_screenShotStream != null)
77 | {
78 | ScreenShotHelper(_screenShotStream, gameTime);
79 | _screenShotData = new ScreenShotData(new List(), this.renderEnv.Camera.ClipRect);
80 | GetScreenShotMapData(mapData.Scene, ref _screenShotData);
81 | _screenShotStream = null;
82 | }
83 | }
84 |
85 | private void ScreenShotHelper(Stream destination, GameTime gameTime)
86 | {
87 | int width = GraphicsDevice.PresentationParameters.BackBufferWidth;
88 | int height = GraphicsDevice.PresentationParameters.BackBufferHeight;
89 | using RenderTarget2D target = new RenderTarget2D(GraphicsDevice, width, height, false,
90 | SurfaceFormat.Rgba64, DepthFormat.None);
91 | var oldTarget = GraphicsDevice.GetRenderTargets();
92 | GraphicsDevice.SetRenderTarget(target);
93 | GraphicsDevice.Clear(Color.Black);
94 | DrawScene(gameTime);
95 | DrawTooltipItems(gameTime);
96 | this.ui.Draw(gameTime.ElapsedGameTime.TotalMilliseconds);
97 | this.tooltip.Draw(gameTime, renderEnv);
98 | GraphicsDevice.SetRenderTargets(oldTarget);
99 | target.SaveAsPng(destination, width, height);
100 |
101 | }
102 |
103 | private void GetScreenShotMapData(SceneNode node, ref ScreenShotData screenShotData)
104 | {
105 | var itemsOnMap = screenShotData.Items;
106 | if (node is ContainerNode container)
107 | {
108 | foreach (var item in container.Slots)
109 | {
110 | if (item is LifeItem life)
111 | {
112 | var rectangle = this.GetLifeBoundingBox(life);
113 | if (rectangle.HasValue)
114 | {
115 | itemsOnMap.Add(new TargetItem(life, rectangle.Value, RenderObjectType.Mob) { Id = life.ID });
116 | }
117 | }
118 | }
119 | }
120 | else
121 | {
122 | for (int i = 0, total = node.Nodes.Count; i < total; ++i)
123 | {
124 | GetScreenShotMapData(node.Nodes[i], ref screenShotData);
125 | }
126 | }
127 | }
128 |
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/MapRender.Invoker/MapRenderInvoker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 | using MapleStory.Common;
11 | using Microsoft.Xna.Framework;
12 | using WzComparerR2.Common;
13 | using WzComparerR2.MapRender;
14 | using WzComparerR2.PluginBase;
15 | using WzComparerR2.WzLib;
16 |
17 | namespace MapRender.Invoker
18 | {
19 | public class MapRenderInvoker : MapRenderInvokerBase
20 | {
21 | private Wz_Image _currentMapImage;
22 | private StringLinker _stringLinker;
23 | private Thread _renderThread;
24 | private MapRender _mapRender;
25 | private Camera _camera;
26 | private volatile bool _isRunning;
27 |
28 | public bool IsRunning => _isRunning;
29 |
30 | public int ScreenWidth { private set; get; }
31 |
32 | public int ScreenHeight { private set; get; }
33 |
34 | public int WorldWidth => _camera.WorldRect.Width;
35 |
36 | public int WorldHeight => _camera.WorldRect.Height;
37 |
38 | public int WorldOriginX => _camera.WorldRect.X;
39 |
40 | public int WorldOriginY => _camera.WorldRect.Y;
41 |
42 | public int CurrentCameraX => (int)_camera.Center.X;
43 |
44 | public int CurrentCameraY => (int)_camera.Center.Y;
45 |
46 | public int CurrentMap { get; private set; }
47 |
48 | public MapRenderInvoker(string mapleStoryPath, Encoding encoding, bool disableImgCheck = false)
49 | : base(mapleStoryPath, encoding, disableImgCheck)
50 | {
51 | _isRunning = false;
52 | AddFindWzEventHandler();
53 | }
54 |
55 | ///
56 | /// Attach event handler to PlugManager, let it throw if reflection fail so we know there are changes in WzComparerR2
57 | ///
58 | ///
59 | private void AddFindWzEventHandler()
60 | {
61 | EventInfo findWzEvent = typeof(PluginManager)
62 | .GetEvent("WzFileFinding", BindingFlags.Static | BindingFlags.NonPublic);
63 | MethodInfo findWzHandler =
64 | typeof(MapRenderInvoker).GetMethod("CharaSimLoader_WzFileFinding", BindingFlags.NonPublic | BindingFlags.Instance);
65 | Delegate findWzDelegate = Delegate.CreateDelegate(findWzEvent.EventHandlerType, this, findWzHandler);
66 | findWzEvent.AddMethod.Invoke(this, new[] { findWzDelegate });
67 | }
68 |
69 | ~MapRenderInvoker()
70 | {
71 | _renderThread?.Abort();
72 | }
73 |
74 | ///
75 | public override void LoadMap(string imgText)
76 | {
77 | CurrentMap = int.Parse(imgText);
78 | imgText = imgText.EndsWith(".img") ? imgText : (imgText + ".img");
79 | _currentMapImage = WzTreeSearcher.SearchForMap(_wzStructure.WzNode, imgText);
80 | Exception ex;
81 | _currentMapImage.TryExtract(out ex);
82 | if (ex != null)
83 | {
84 | throw ex;
85 | }
86 | _stringLinker = new StringLinker();
87 | _stringLinker.Load(PluginManager.FindWz(Wz_Type.String).GetValueEx(null));
88 | }
89 |
90 | ///
91 | public override void Launch(int width, int height)
92 | {
93 | if (_currentMapImage == null)
94 | {
95 | throw new InvalidOperationException("MapRenderInvoker.LoadMap() must be called before Launch().");
96 | }
97 |
98 | _isRunning = false;
99 | _renderThread = new Thread(() =>
100 | {
101 | _mapRender = new MapRender(_currentMapImage) { StringLinker = _stringLinker };
102 | _mapRender.Window.Title = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileName;
103 | try
104 | {
105 | using (_mapRender)
106 | {
107 | _mapRender.RunOneFrame(); // Initialize
108 | _mapRender.ChangeResolution(width, height);
109 | _camera = _mapRender.renderEnv.Camera;
110 | _mapRender.Run();
111 | }
112 | }
113 | finally
114 | {
115 | _mapRender = null;
116 | }
117 | });
118 | ScreenHeight = height;
119 | ScreenWidth = width;
120 | _renderThread.SetApartmentState(ApartmentState.STA);
121 | _renderThread.IsBackground = true;
122 | _renderThread.Start();
123 | SpinWait spinWait = new SpinWait();
124 | while (_mapRender == null)
125 | {
126 | spinWait.SpinOnce();
127 | }
128 | _mapRender.WaitSceneLoading();
129 | _isRunning = true;
130 | }
131 |
132 | public override void SwitchMap(string imgText)
133 | {
134 | CurrentMap = int.Parse(imgText);
135 | _mapRender.SwitchToNewMap(CurrentMap);
136 | }
137 |
138 | public void MoveCamera(int centerX, int centerY)
139 | {
140 | _camera.Center = new Vector2(centerX, centerY);
141 | _camera.AdjustToWorldRect();
142 | }
143 |
144 | public ScreenShotData TakeScreenShot(Stream stream)
145 | {
146 | return _mapRender.TakeScreenShot(stream);
147 | }
148 |
149 | #region COPIED_CODE
150 |
151 | ///
152 | /// !!!!!!!!!!!!COPIED CODE!!!!!!!!!!!!!!
153 | /// Version: git@github.com:Kagamia/WzComparerR2.git:f6ecfb18cae661f125a189e527feea1964f5bda8
154 | ///
155 | ///
156 | private void CharaSimLoader_WzFileFinding(object sender, WzComparerR2.FindWzEventArgs e)
157 | {
158 | string[] fullPath = null;
159 | if (!string.IsNullOrEmpty(e.FullPath)) //用fullpath作为输入参数
160 | {
161 | fullPath = e.FullPath.Split('/', '\\');
162 | try
163 | {
164 | e.WzType = (Wz_Type)Enum.Parse(typeof(Wz_Type), fullPath[0], true);
165 | }
166 | catch
167 | {
168 | e.WzType = Wz_Type.Unknown;
169 | }
170 | }
171 |
172 | List preSearch = new List();
173 | if (e.WzType != Wz_Type.Unknown) //用wztype作为输入参数
174 | {
175 | IEnumerable preSearchWz = e.WzFile?.WzStructure != null ?
176 | Enumerable.Repeat(e.WzFile.WzStructure, 1) : new List() { _wzStructure };
177 | foreach (var wzs in preSearchWz)
178 | {
179 | Wz_File baseWz = null;
180 | bool find = false;
181 | foreach (Wz_File wz_f in wzs.wz_files)
182 | {
183 | if (wz_f.Type == e.WzType)
184 | {
185 | preSearch.Add(wz_f.Node);
186 | find = true;
187 | //e.WzFile = wz_f;
188 | }
189 | if (wz_f.Type == Wz_Type.Base)
190 | {
191 | baseWz = wz_f;
192 | }
193 | }
194 |
195 | // detect data.wz
196 | if (baseWz != null && !find)
197 | {
198 | string key = e.WzType.ToString();
199 | foreach (Wz_Node node in baseWz.Node.Nodes)
200 | {
201 | if (node.Text == key && node.Nodes.Count > 0)
202 | {
203 | preSearch.Add(node);
204 | }
205 | }
206 | }
207 | }
208 | }
209 |
210 | if (fullPath == null || fullPath.Length <= 1)
211 | {
212 | if (e.WzType != Wz_Type.Unknown && preSearch.Count > 0) //返回wzFile
213 | {
214 | e.WzNode = preSearch[0];
215 | e.WzFile = preSearch[0].Value as Wz_File;
216 | }
217 | return;
218 | }
219 |
220 | if (preSearch.Count <= 0)
221 | {
222 | return;
223 | }
224 |
225 | foreach (var wzFileNode in preSearch)
226 | {
227 | var searchNode = wzFileNode;
228 | for (int i = 1; i < fullPath.Length && searchNode != null; i++)
229 | {
230 | searchNode = searchNode.Nodes[fullPath[i]];
231 | var img = searchNode.GetValueEx(null);
232 | if (img != null)
233 | {
234 | searchNode = img.TryExtract() ? img.Node : null;
235 | }
236 | }
237 |
238 | if (searchNode != null)
239 | {
240 | e.WzNode = searchNode;
241 | e.WzFile = wzFileNode.Value as Wz_File;
242 | return;
243 | }
244 | }
245 | //寻找失败
246 | e.WzNode = null;
247 | }
248 | #endregion
249 | }
250 |
251 | public abstract class MapRenderInvokerBase
252 | {
253 |
254 | protected readonly Wz_Structure _wzStructure;
255 |
256 | ///
257 | /// Provide base methods to open MapleStory data directory.
258 | /// For maintenance with upstream WzR2, check
259 | ///
260 | ///
261 | ///
262 | ///
263 | ///
264 | protected MapRenderInvokerBase(string mapleStoryPath, Encoding encoding, bool disableImgCheck = false)
265 | {
266 | // Static settings for Wz_Structure :(
267 | Wz_Structure.DefaultAutoDetectExtFiles = true;
268 | Wz_Structure.DefaultEncoding = encoding;
269 | Wz_Structure.DefaultImgCheckDisabled = disableImgCheck;
270 | Wz_Structure.DefaultWzVersionVerifyMode = WzVersionVerifyMode.Fast;
271 | // Then our constructor
272 | string baseWzPath = Path.Combine(mapleStoryPath, MapleStoryPathHelper.MapleStoryBaseWzName);
273 | if (!File.Exists(baseWzPath))
274 | {
275 | throw new ArgumentException($"Cannot find {MapleStoryPathHelper.MapleStoryBaseWzName} in given directory {mapleStoryPath}.");
276 | }
277 | // See WzComparerR2.MainForm.openWz()
278 | _wzStructure = new Wz_Structure();
279 | if (string.Equals(Path.GetExtension(baseWzPath), ".ms", StringComparison.OrdinalIgnoreCase))
280 | {
281 | _wzStructure.LoadMsFile(baseWzPath);
282 | }
283 | else if (_wzStructure.IsKMST1125WzFormat(baseWzPath))
284 | {
285 | _wzStructure.LoadKMST1125DataWz(baseWzPath);
286 | if (string.Equals(Path.GetFileName(baseWzPath), "Base.wz", StringComparison.OrdinalIgnoreCase))
287 | {
288 | string packsDir = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(baseWzPath)), "Packs");
289 | if (Directory.Exists(packsDir))
290 | {
291 | foreach (var msFile in Directory.GetFiles(packsDir, "*.ms"))
292 | {
293 | _wzStructure.LoadMsFile(msFile);
294 | }
295 | }
296 | }
297 | }
298 | else
299 | {
300 | _wzStructure.Load(baseWzPath, true);
301 | }
302 | }
303 |
304 | ///
305 | /// Load specified Wz map img to this invoker.
306 | ///
307 | /// The map node search. E.g. "450007010"
308 | /// If supplied img cannot be found.
309 | public abstract void LoadMap(string imgText);
310 |
311 | ///
312 | /// Lunch map render. Make sure we have loaded img.
313 | ///
314 | /// Width of resolution
315 | /// Height of resolution
316 | public abstract void Launch(int width, int height);
317 |
318 | ///
319 | /// Switch to a new map after
320 | ///
321 | /// The map node search. E.g. "450007010"
322 | public abstract void SwitchMap(string imgText);
323 |
324 | }
325 | }
326 |
--------------------------------------------------------------------------------
/MapRender.Invoker/ObjectClass.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MapRender.Invoker
8 | {
9 | public enum ObjectClass
10 | {
11 | Mob = 1,
12 | Player,
13 | Npc,
14 | InMapPortal,
15 | CrossMapPortal,
16 | Foothold,
17 | LadderRope,
18 | Unknown
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/MapRender.Invoker/ScreenShotData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace MapRender.Invoker
9 | {
10 | public class ScreenShotData
11 | {
12 | public List Items { get; private set; }
13 |
14 | public Rectangle CameraRectangle { get; private set; }
15 |
16 | public ScreenShotData(List items, Microsoft.Xna.Framework.Rectangle camRectangle)
17 | {
18 | Items = items;
19 | CameraRectangle = new Rectangle(camRectangle.X, camRectangle.Y, camRectangle.Width, camRectangle.Height);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/MapRender.Invoker/TargetItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.Xna.Framework;
7 | using WzComparerR2.MapRender.Patches;
8 | using WzComparerR2.MapRender.Patches2;
9 |
10 | namespace MapRender.Invoker
11 | {
12 | //Object detection interests item
13 | public class TargetItem : SceneItem
14 | {
15 | public int Id { get; set; }
16 |
17 | public int X { get; set; }
18 |
19 | public int Y { get; set; }
20 |
21 | public ObjectClass Type { get; set; }
22 |
23 | public int Width { get; set; }
24 |
25 | public int Height { get; set; }
26 |
27 | public TargetItem() { }
28 |
29 | public TargetItem(SceneItem item)
30 | {
31 | Name = item.Name;
32 | Index = item.Index;
33 | Tags = item.Tags;
34 | }
35 |
36 | public TargetItem(SceneItem item, Rectangle rectangle) : this(item)
37 | {
38 | X = rectangle.X;
39 | Y = rectangle.Y;
40 | Width = rectangle.Width;
41 | Height = rectangle.Height;
42 | }
43 |
44 | public TargetItem(SceneItem item, Rectangle rectangle, RenderObjectType type) : this(item, rectangle)
45 | {
46 | Type = RenderTypeToObjectClass(type);
47 | }
48 |
49 | public static ObjectClass RenderTypeToObjectClass(RenderObjectType type)
50 | {
51 | switch (type)
52 | {
53 | case RenderObjectType.Mob:
54 | return ObjectClass.Mob;
55 | case RenderObjectType.Foothold:
56 | return ObjectClass.Foothold;
57 | case RenderObjectType.Npc:
58 | return ObjectClass.Npc;
59 | case RenderObjectType.LadderRope:
60 | return ObjectClass.LadderRope;
61 | case RenderObjectType.Portal:
62 | return ObjectClass.CrossMapPortal; //TODO: Classify portal
63 | }
64 | return ObjectClass.Unknown;
65 | }
66 |
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/MapleStory.Common/Exceptions/WzImgNotFoundException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.Serialization;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace MapleStory.Common.Exceptions
9 | {
10 | ///
11 | /// Simple exception class indicating specified Wz Img cannot be found.
12 | ///
13 | public class WzImgNotFoundException : Exception
14 | {
15 | public WzImgNotFoundException()
16 | {
17 | }
18 |
19 | public WzImgNotFoundException(string message) : base(message)
20 | {
21 | }
22 |
23 | public WzImgNotFoundException(string message, Exception innerException) : base(message, innerException)
24 | {
25 | }
26 |
27 | protected WzImgNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
28 | {
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/MapleStory.Common/MapleStory.Common.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows7.0
4 | x64
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/MapleStory.Common/MapleStoryPathHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Microsoft.Win32;
8 |
9 | namespace MapleStory.Common
10 | {
11 | ///
12 | /// Helper class for finding MapleStory's path.
13 | ///
14 | public static class MapleStoryPathHelper
15 | {
16 | private static readonly string REG_KEY_PATH_CMS =
17 | @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\MapleStory"; // Tested for CMS
18 | private static readonly string REG_VALUE_NAME_CMS = @"UninstallString";
19 |
20 | private static readonly string REG_KEY_PATH_GMS = @"SOFTWARE\WOW6432Node\Wizet\MapleStory";
21 | private static readonly string REG_VALUE_NAME_GMS = @"ExecPath";
22 |
23 | public static readonly string MapleStoryExecutableName = @"MapleStory.exe";
24 |
25 | public static readonly string MapleStoryBaseWzName = @"Data\Base\Base.wz";
26 |
27 | private static readonly Lazy _mapleStoryInstallDirectory = new Lazy(() =>
28 | {
29 | // For CMS
30 | try
31 | {
32 | RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_KEY_PATH_CMS); // Possible null
33 | string uninstallPath = key.GetValue(REG_VALUE_NAME_CMS) as string; // Possible invalid cast
34 | return Path.GetDirectoryName(uninstallPath); // Possible corrupted path
35 | }
36 | catch (Exception ex) when (ex is NullReferenceException || ex is InvalidCastException || ex is ArgumentException)
37 | {
38 | // ignored, not found
39 | }
40 |
41 | // For GMS
42 | try
43 | {
44 | RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_KEY_PATH_GMS); // Possible null
45 | string execPath = key.GetValue(REG_VALUE_NAME_GMS) as string; // Possible invalid cast
46 | return execPath;
47 | }
48 | catch (Exception ex) when (ex is NullReferenceException || ex is InvalidCastException)
49 | {
50 | // ignored, not found
51 | }
52 | return string.Empty;
53 | });
54 |
55 | ///
56 | /// Indicate if MapleStory installed location was found.
57 | ///
58 | public static bool FoundMapleStoryInstalled => Directory.Exists(_mapleStoryInstallDirectory.Value);
59 |
60 | ///
61 | /// Get MapleStory installed folder based on registry, might fail if no elevate privilege, in which case it will return empty string.
62 | ///
63 | public static string MapleStoryInstallDirectory => _mapleStoryInstallDirectory.Value;
64 |
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/MapleStory.Common/WzTreeSearcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using WzComparerR2.WzLib;
7 | using MapleStory.Common.Exceptions;
8 |
9 | namespace MapleStory.Common
10 | {
11 | public static class WzTreeSearcher
12 | {
13 | ///
14 | /// Generic searching based on BFS traversal. This is tree-search (only search for children).
15 | ///
16 | /// Root of target tree. The origin of search.
17 | /// Return true if the input node meet search criteria.
18 | /// Null if not found, otherwise the target node.
19 | public static Wz_Node GenericBfsSearcher(Wz_Node root, Func determinator)
20 | {
21 | Queue queue = new Queue();
22 | queue.Enqueue(root);
23 | while (queue.Count != 0)
24 | {
25 | Wz_Node currNode = queue.Dequeue();
26 | if (determinator(currNode))
27 | {
28 | return currNode;
29 | }
30 | foreach (var child in currNode.Nodes)
31 | {
32 | queue.Enqueue(child);
33 | }
34 | }
35 | return null;
36 | }
37 |
38 | ///
39 | /// Search for specified map Img starting from Base.wz root.
40 | ///
41 | /// The root of Base.wz.
42 | /// The node text to search. E.g. "450007010.img"
43 | /// The Wz img containing desired map.
44 | /// If not found.
45 | public static Wz_Image SearchForMap(Wz_Node root, string imgText)
46 | {
47 | if (!imgText.Contains(".img"))
48 | {
49 | throw new ArgumentException("Supplied imgText is not legal.", nameof(imgText));
50 | }
51 | // Filter map nodes, and find string wz file.
52 | IEnumerable mapNodes = root.Nodes
53 | .Where(n => n.GetNodeWzFile().Type == Wz_Type.Map);
54 | // Do search on each map node
55 | foreach (var mapRoot in mapNodes)
56 | {
57 | Wz_Node result = GenericBfsSearcher(mapRoot, (node) => node.Text == imgText);
58 | if (result != null)
59 | {
60 | return result.GetNodeWzImage();
61 | }
62 | }
63 | // Throw if not found.
64 | throw new WzImgNotFoundException(string.Format("Target Img {0} cannot be found.", imgText));
65 | }
66 |
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/MapleStory.MachineLearningSampleGenerator/MapleStory.MachineLearningSampleGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows7.0
4 | true
5 | x64
6 | Exe
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/MapleStory.MachineLearningSampleGenerator/OutputFormat.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MapleStory.MachineLearningSampleGenerator
8 | {
9 | internal enum OutputFormat
10 | {
11 | TfRecord,
12 | Darknet,
13 | Coco
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/MapleStory.MachineLearningSampleGenerator/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Runtime.InteropServices;
8 | using System.Text;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 | using CommandLine;
12 | using CommandLine.Text;
13 | using MapleStory.Common;
14 | using MapleStory.Sampler;
15 | using MapleStory.Sampler.PostProcessor;
16 | using MapRender.Invoker;
17 |
18 | namespace MapleStory.MachineLearningSampleGenerator
19 | {
20 | internal static class Program
21 | {
22 | [DllImport("kernel32.dll")]
23 | static extern bool SetDllDirectory(string path);
24 |
25 | [DllImport("kernel32")]
26 | static extern bool AllocConsole();
27 |
28 | [DllImport("kernel32.dll")]
29 | static extern bool AttachConsole(int dwProcessId);
30 |
31 | [DllImport("kernel32.dll")]
32 | static extern bool FreeConsole();
33 |
34 | [DllImport("kernel32.dll")]
35 | static extern IntPtr GetConsoleWindow();
36 |
37 | static Program()
38 | {
39 | AttachConsole(-1); // Try to attach to parent process's console
40 | if (GetConsoleWindow() == IntPtr.Zero)
41 | {
42 | AllocConsole();
43 | }
44 |
45 | Console.OutputEncoding = Encoding.UTF8; // Correctly show non-English characters
46 | string libPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Lib",
47 | Environment.Is64BitProcess ? "x64" : "x86");
48 | SetDllDirectory(libPath); // Add dll search path for WzComparerR2
49 | }
50 |
51 | private class Options
52 | {
53 | [Option('m', "map", Required = true, HelpText = "Space-separated Wz image ID of map(s) used for generating TFRecord.")]
54 | public IEnumerable Maps { get; set; }
55 |
56 | [Option('x', "xStep", Required = true, HelpText = "Step in X.")]
57 | public int StepX { get; set; }
58 |
59 | [Option('y', "yStep", Required = true, HelpText = "Step in Y.")]
60 | public int StepY { get; set; }
61 |
62 | [Option('f', "format", Required = true, HelpText = "Output format, must be one of: [tfrecord, darknet, coco]")]
63 | public string Format { get; set; }
64 |
65 | [Option('o', "output", Required = false, Default = ".", HelpText = "Data set output location")]
66 | public string OutputPath { get; set; }
67 |
68 | [Option('w', "width", Required = false, Default = 1366, HelpText = "Width of sample image.")]
69 | public int RenderWidth { get; set; }
70 |
71 | [Option('h', "height", Required = false, Default = 768, HelpText = "Height of sample image.")]
72 | public int RenderHeight { get; set; }
73 |
74 | [Option('i', "interval", Required = false, Default = 0, HelpText = "Time interval in ms between each sample")]
75 | public int SampleInterval { get; set; }
76 |
77 | [Option('p', "path", Required = false, Default = "", HelpText = "MapleStory Installed Path")]
78 | public string MapleStoryPath { get; set; }
79 |
80 | [Option("post", Required = false, Default = false, HelpText = "Indicate whether to enable post-processing.")]
81 | public bool PostProcessingEnable { get; set; }
82 |
83 | [Option("players", Required = false, Default = "", HelpText = "Directory where the post-processing player images stored.")]
84 | public string PlayerImageDirectory { get; set; }
85 |
86 | [Option('e', "encoding", Required = false, HelpText = "Encoding used to decode Wz strings. Using system default if not specified.")]
87 | public string Encoding { get; set; } = "";
88 | }
89 |
90 | [STAThread]
91 | private static int Main(string[] args)
92 | {
93 | int ret = CommandLine.Parser.Default.ParseArguments(args).MapResult(RunAndReturn, OnParseError);
94 | Console.WriteLine("MapleStoryDetectionSampleGenerator exited with code= {0}", ret);
95 | FreeConsole();
96 | return ret;
97 | }
98 |
99 | private static int OnParseError(IEnumerable errors)
100 | {
101 | foreach (var error in errors)
102 | {
103 | Console.Error.WriteLine(Enum.GetName(typeof(ErrorType), error.Tag));
104 | }
105 | return -1;
106 | }
107 |
108 | ///
109 | /// Main logic here
110 | ///
111 | private static int RunAndReturn(Options options)
112 | {
113 | // Print program info
114 | Console.WriteLine(HeadingInfo.Default);
115 | Console.WriteLine(FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).LegalCopyright);
116 | // Check arguments
117 | PreRunTest(options);
118 | Console.WriteLine("MapleStory Location: {0}", options.MapleStoryPath);
119 | // Initialize render
120 | MapRenderInvoker renderInvoker = new MapRenderInvoker(options.MapleStoryPath,
121 | options.Encoding == string.Empty ? Encoding.Default : Encoding.GetEncoding(options.Encoding),
122 | false);
123 | Queue maps = new Queue(options.Maps);
124 | var first = maps.Dequeue();
125 | renderInvoker.LoadMap(first);
126 | renderInvoker.Launch(options.RenderWidth, options.RenderHeight);
127 | // Initialize sampler
128 | IDatasetWriter writer = GetDatasetWriter(options, first);
129 | Sampler.Sampler sampler = new Sampler.Sampler(renderInvoker);
130 | if (options.PostProcessingEnable && options.PlayerImageDirectory != "")
131 | {
132 | IPostProcessor postProcessor = new PlayerProcessor(options.PlayerImageDirectory);
133 | sampler.OnSampleCaptured += (s, e) => postProcessor.Process(s);
134 | }
135 |
136 | while (true)
137 | {
138 | sampler.SampleAll(options.StepX, options.StepY, writer, options.SampleInterval);
139 | if (maps.Count == 0)
140 | {
141 | break;
142 | }
143 | renderInvoker.SwitchMap(maps.Dequeue());
144 | }
145 | writer.Finish();
146 | return 0;
147 | }
148 |
149 |
150 | ///
151 | /// Throw if condition not meet.
152 | ///
153 | private static void PreRunTest(Options options)
154 | {
155 | // Check resolution
156 | if (options.RenderHeight <= 0)
157 | {
158 | throw new ArgumentException("Render size cannot exceed screen size. Height illegal.",
159 | nameof(options.RenderHeight));
160 | }
161 | if (options.RenderWidth <= 0)
162 | {
163 | throw new ArgumentException("Render size cannot exceed screen size. Width illegal.",
164 | nameof(options.RenderWidth));
165 | }
166 |
167 | // Check format
168 | Enum.Parse(typeof(OutputFormat), options.Format, true);
169 |
170 | // Check file path
171 | if (options.MapleStoryPath == string.Empty)
172 | {
173 | if (MapleStoryPathHelper.FoundMapleStoryInstalled)
174 | {
175 | options.MapleStoryPath = MapleStoryPathHelper.MapleStoryInstallDirectory;
176 | }
177 | else
178 | {
179 | throw new ArgumentException("Cannot find MapleStory installed location. Please specify it in commandline or retry as Administrator.");
180 | }
181 | }
182 | else if (!Directory.Exists(options.MapleStoryPath))
183 | {
184 | throw new ArgumentException("Supplied MapleStory directory does not exist.");
185 | }
186 |
187 | // Check map id format
188 | foreach (var map in options.Maps)
189 | {
190 | string id = map.Replace(".img", string.Empty);
191 | if (!id.All(char.IsDigit))
192 | {
193 | throw new ArgumentException("Supplied Map Id is not in correct format." +
194 | " --map parameter should be space-separated list of IDs. " +
195 | "E.g. --map 450007010 450007060");
196 | }
197 | }
198 |
199 | // Check encoding
200 | if ((options.Encoding != string.Empty) &&
201 | Encoding.GetEncodings()
202 | .Any(e => e.Name.Equals(options.Encoding, StringComparison.OrdinalIgnoreCase)))
203 | {
204 | throw new ArgumentException($"{options.Encoding} is not an available Encoding in your system.");
205 | }
206 |
207 | // Check output path
208 | if (!Directory.Exists(options.OutputPath))
209 | {
210 | throw new ArgumentException($"OutputPath {options.OutputPath} does not exist.");
211 | }
212 |
213 | // Check interval
214 | if (options.SampleInterval < 0)
215 | {
216 | throw new ArgumentException("SampleInterval cannot be negative!");
217 | }
218 |
219 | // Check for post-processing
220 | if (options.PostProcessingEnable)
221 | {
222 | if (options.PlayerImageDirectory != "")
223 | {
224 | if (!Directory.Exists(options.PlayerImageDirectory))
225 | {
226 | throw new ArgumentException($"PlayerImageDirectory {options.PlayerImageDirectory} cannot be found!");
227 | }
228 | }
229 | }
230 | }
231 |
232 | private static IDatasetWriter GetDatasetWriter(Options options, string map)
233 | {
234 | switch (Enum.Parse(typeof(OutputFormat), options.Format, true))
235 | {
236 | case OutputFormat.TfRecord:
237 | return new TfRecordWriter(Path.Combine(options.OutputPath, map));
238 | case OutputFormat.Darknet:
239 | return new DarknetWriter(options.OutputPath);
240 | case OutputFormat.Coco:
241 | return new CocoWriter(options.OutputPath, $"MapleStory {map}.img Object Detection Samples");
242 | default:
243 | throw new ArgumentOutOfRangeException(nameof(options), options, null);
244 | }
245 | }
246 |
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/CocoWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Text.Json;
7 | using System.Text.Json.Serialization;
8 | using System.Threading.Tasks;
9 | using MapRender.Invoker;
10 |
11 | namespace MapleStory.Sampler
12 | {
13 | public class CocoWriter : IDatasetWriter
14 | {
15 | private const double DefaultTestingPortion = 0.05;
16 | private const string DefaultRootDirectory = "coco";
17 | private const string TrainingDirectory = "train2017";
18 | private const string ValidationDirectory = "val2017";
19 | private const string AnnotationDirectory = "annotations";
20 | private const string TrainingJson = "instances_train2017.json";
21 | private const string ValidationJson = "instances_val2017.json";
22 |
23 | public string RootPath { get; }
24 |
25 | public string AnnotationsPath { get; }
26 |
27 | private readonly string _trainingImagesPath;
28 | private readonly string _validationImagesPath;
29 | private readonly Random _random;
30 | private readonly List _occurrenceClasses;
31 | private readonly string _datasetName;
32 | private readonly List _trainingImages;
33 | private readonly List _validationImages;
34 | private readonly List _categories;
35 | private readonly List _trainingAnnotations;
36 | private readonly List _validationAnnotations;
37 |
38 | public CocoWriter(string path, string name)
39 | {
40 | if (!Directory.Exists(path))
41 | {
42 | Console.WriteLine($"Target path {path} does not exist. Creating one...");
43 | Directory.CreateDirectory(path);
44 | }
45 | RootPath = Path.Combine(path, DefaultRootDirectory);
46 | if (!Directory.Exists(RootPath))
47 | {
48 | Directory.CreateDirectory(RootPath);
49 | }
50 | CleanDirectory(RootPath);
51 | _trainingImagesPath = Path.Combine(RootPath, TrainingDirectory);
52 | Directory.CreateDirectory(_trainingImagesPath);
53 | _validationImagesPath = Path.Combine(RootPath, ValidationDirectory);
54 | Directory.CreateDirectory(_validationImagesPath);
55 | AnnotationsPath = Path.Combine(RootPath, AnnotationDirectory);
56 | Directory.CreateDirectory(AnnotationsPath);
57 |
58 | _random = new Random();
59 | _occurrenceClasses = new List();
60 | _datasetName = name;
61 | _trainingImages = new List();
62 | _validationImages = new List();
63 | _validationAnnotations = new List();
64 | _categories = new List();
65 | _trainingAnnotations = new List();
66 | _validationAnnotations = new List();
67 | }
68 |
69 | public void Write(Sample sample)
70 | {
71 | double rand = _random.NextDouble();
72 | List annotationsToAdd;
73 | List imagesToAdd;
74 | string pathToWrite;
75 | if (rand < DefaultTestingPortion)
76 | {
77 | annotationsToAdd = _validationAnnotations;
78 | imagesToAdd = _validationImages;
79 | pathToWrite = _validationImagesPath;
80 | }
81 | else
82 | {
83 | annotationsToAdd = _trainingAnnotations;
84 | imagesToAdd = _trainingImages;
85 | pathToWrite = _trainingImagesPath;
86 | }
87 | // Write image
88 | string jpgFileName = sample.Guid + ".jpg";
89 | using FileStream imageStream = new FileStream(Path.Combine(pathToWrite, jpgFileName), FileMode.CreateNew);
90 | sample.ImageStream.WriteTo(imageStream);
91 | imageStream.Flush();
92 | CocoImage cocoImage = new CocoImage(jpgFileName, sample.Width, sample.Height);
93 | imagesToAdd.Add(cocoImage);
94 |
95 | // Write annotations
96 | GetAnnotationFromSample(sample, cocoImage.Id, ref annotationsToAdd);
97 | }
98 |
99 | public void Finish()
100 | {
101 | CocoLicense license = new CocoLicense();
102 | // Write training set
103 | CocoJson trainingJson = new CocoJson(new CocoInfo($"{_datasetName} - Training"),
104 | license,
105 | _trainingImages,
106 | _categories,
107 | _trainingAnnotations);
108 | using FileStream trainingStream =
109 | new FileStream(Path.Combine(AnnotationsPath, TrainingJson), FileMode.CreateNew);
110 | using Utf8JsonWriter trainingWriter = new Utf8JsonWriter(trainingStream);
111 | JsonSerializer.Serialize(trainingWriter, trainingJson);
112 |
113 | // Write validation set
114 | CocoJson validationJson = new CocoJson(new CocoInfo($"{_datasetName} - Training"),
115 | license,
116 | _validationImages,
117 | _categories,
118 | _validationAnnotations);
119 | using FileStream validationStream =
120 | new FileStream(Path.Combine(AnnotationsPath, ValidationJson), FileMode.CreateNew);
121 | using Utf8JsonWriter validationWriter = new Utf8JsonWriter(validationStream);
122 | JsonSerializer.Serialize(validationWriter, validationJson);
123 |
124 | // Flush buffer (if any)
125 | trainingWriter.Flush();
126 | trainingStream.Flush();
127 | validationWriter.Flush();
128 | validationStream.Flush();
129 | }
130 |
131 | private void GetAnnotationFromSample(Sample sample, int imageId, ref List dstList)
132 | {
133 | foreach (var sampleItem in sample.Items)
134 | {
135 | ObjectClass type = sampleItem.Type;
136 | if (!_occurrenceClasses.Contains(type))
137 | {
138 | _occurrenceClasses.Add(type);
139 | _categories.Add(new CocoCategory("element", type.ToString()));
140 | }
141 | int categoryId = _occurrenceClasses.IndexOf(type) + 1;
142 | CocoAnnotation toAdd = new CocoAnnotation(imageId, sampleItem.X, sampleItem.Y, sampleItem.Width,
143 | sampleItem.Height, categoryId);
144 | dstList.Add(toAdd);
145 | }
146 | }
147 |
148 | private static void CleanDirectory(string path)
149 | {
150 | if (Directory.EnumerateFileSystemEntries(path).Any())
151 | {
152 | Console.WriteLine("Warning: Target RootPath Is Not Empty. Cleaning target path...");
153 | DirectoryInfo directory = new DirectoryInfo(path);
154 | foreach (var fileInfo in directory.GetFiles())
155 | {
156 | fileInfo.Delete();
157 | }
158 | foreach (var directoryInfo in directory.GetDirectories())
159 | {
160 | directoryInfo.Delete(true);
161 | }
162 | }
163 | }
164 |
165 | #region JsonDefinitions
166 | private class CocoInfo
167 | {
168 | [JsonPropertyName("description")]
169 | public string Description { get; }
170 |
171 | [JsonPropertyName("url")]
172 | public string Url { get; } = @"https://github.com/charlescao460/MapleStoryDetectionSampleGenerator";
173 |
174 | [JsonPropertyName("version")]
175 | public string Version { get; } = "1.0";
176 |
177 | [JsonPropertyName("year")]
178 | public int Year { get; } = DateTime.Today.Year;
179 |
180 | [JsonPropertyName("contributor")]
181 | public string Contributor { get; } = "CSR";
182 |
183 | [JsonPropertyName("date_created")]
184 | public string DateCreated = DateTime.Today.ToLongDateString();
185 |
186 | public CocoInfo(string description)
187 | {
188 | Description = description;
189 | }
190 | }
191 |
192 | private class CocoLicense
193 | {
194 | [JsonPropertyName("url")]
195 | public string Url { get; } = @"https://github.com/charlescao460/MapleStoryDetectionSampleGenerator/blob/main/LICENSE";
196 |
197 | [JsonPropertyName("id")]
198 | public int Id { get; } = 1;
199 |
200 | [JsonPropertyName("name")]
201 | public string Name { get; } = "MIT License";
202 | }
203 |
204 | private class CocoImage
205 | {
206 | [JsonPropertyName("license")]
207 | public int License { get; } = 1;
208 |
209 | [JsonPropertyName("file_name")]
210 | public string FileName { get; }
211 |
212 | [JsonPropertyName("coco_url")]
213 | public string CocoUrl { get; } = string.Empty;
214 |
215 | [JsonPropertyName("height")]
216 | public int Height { get; }
217 |
218 | [JsonPropertyName("width")]
219 | public int Width { get; }
220 |
221 | [JsonPropertyName("date_captured")]
222 | public string DateCaptured = $"{DateTime.Now:G}";
223 |
224 | [JsonPropertyName("flickr_url")]
225 | public string FlickrUrl { get; } = string.Empty;
226 |
227 | [JsonPropertyName("id")]
228 | public int Id { get; }
229 |
230 | private static int _count = 1;
231 |
232 | public CocoImage(string fileName, int width, int height)
233 | {
234 | FileName = fileName;
235 | Width = width;
236 | Height = height;
237 | Id = _count++;
238 | }
239 | }
240 |
241 | private class CocoCategory
242 | {
243 | [JsonPropertyName("supercategory")]
244 | public string SuperCategory { get; }
245 |
246 | [JsonPropertyName("id")]
247 | public int Id { get; }
248 |
249 | [JsonPropertyName("name")]
250 | public string Name { get; }
251 |
252 | private static int _count = 1;
253 |
254 | public CocoCategory(string superCategory, string name)
255 | {
256 | SuperCategory = superCategory;
257 | Name = name;
258 | Id = _count++;
259 | }
260 | }
261 |
262 | private class CocoAnnotation
263 | {
264 | [JsonPropertyName("segmentation")]
265 | public float[][] Segmentation { get; }
266 |
267 | [JsonPropertyName("area")]
268 | public float Area { get; }
269 |
270 | [JsonPropertyName("iscrowd")]
271 | public int IsCrowd { get; } = 0;
272 |
273 | [JsonPropertyName("image_id")]
274 | public int ImageId { get; }
275 |
276 | [JsonPropertyName("bbox")]
277 | public float[] Bbox { get; }
278 |
279 | [JsonPropertyName("category_id")]
280 | public int CategoryId { get; }
281 |
282 | [JsonPropertyName("id")]
283 | public int Id { get; }
284 |
285 | private static int _count = 1;
286 |
287 | public CocoAnnotation(int imageId, float x, float y, float width, float height, int categoryId)
288 | {
289 | Segmentation = new float[][]
290 | {
291 | new float[] { x, y, x + width, y, x + width, y + height, x, y + height }
292 | };
293 | Area = width * height;
294 | ImageId = imageId;
295 | Bbox = new float[] { x, y, width, height };
296 | CategoryId = categoryId;
297 | Id = _count++;
298 | }
299 | }
300 |
301 | private class CocoJson
302 | {
303 | [JsonPropertyName("info")]
304 | public CocoInfo Info { get; }
305 |
306 | [JsonPropertyName("licenses")]
307 | public CocoLicense[] Licenses { get; }
308 |
309 | [JsonPropertyName("images")]
310 | public IList Images { get; }
311 |
312 | [JsonPropertyName("categories")]
313 | public IList Categories { get; }
314 |
315 | [JsonPropertyName("annotations")]
316 | public IList Annotations { get; }
317 |
318 | public CocoJson(CocoInfo info, CocoLicense license, IList images, IList categories,
319 | IList annotations)
320 | {
321 | Info = info;
322 | Licenses = new[] { license };
323 | Images = images;
324 | Categories = categories;
325 | Annotations = annotations;
326 | }
327 | }
328 |
329 | #endregion
330 | }
331 | }
332 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/DarknetWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using MapRender.Invoker;
8 |
9 | namespace MapleStory.Sampler
10 | {
11 | public class DarknetWriter : IDatasetWriter, IDisposable
12 | {
13 | private const double DefaultTestingPortion = 0.05;
14 |
15 | private const string DefaultRootDirectory = "data";
16 | private const string ClassNamesFile = "obj.names";
17 | private const string ObjectDataFile = "obj.data";
18 | private const string TrainingDataFile = "train.txt";
19 | private const string TestingDataFile = "test.txt";
20 | private const string ObjDirectory = "obj";
21 |
22 | private readonly List _occurrenceClasses;
23 | private bool _isFinished;
24 | private bool _disposed;
25 | private readonly StreamWriter _trainingDataListWriter;
26 | private readonly StreamWriter _testingDataListWriter;
27 | private readonly Random _random;
28 |
29 | public string RootPath { get; }
30 |
31 | public string ObjPath { get; }
32 |
33 | public DarknetWriter(string path)
34 | {
35 | if (!Directory.Exists(path))
36 | {
37 | Console.WriteLine($"Target path {path} does not exist. Creating one...");
38 | Directory.CreateDirectory(path);
39 | }
40 | RootPath = Path.Combine(path, DefaultRootDirectory);
41 | if (!Directory.Exists(RootPath))
42 | {
43 | Directory.CreateDirectory(RootPath);
44 | }
45 | CleanDirectory(RootPath);
46 | ObjPath = Path.Combine(RootPath, ObjDirectory);
47 | Directory.CreateDirectory(ObjPath);
48 | _occurrenceClasses = new List();
49 | _isFinished = false;
50 | _random = new Random();
51 | _trainingDataListWriter = new StreamWriter(new FileStream(Path.Combine(RootPath, TrainingDataFile), FileMode.CreateNew));
52 | _trainingDataListWriter.AutoFlush = true;
53 | _testingDataListWriter = new StreamWriter(new FileStream(Path.Combine(RootPath, TestingDataFile), FileMode.CreateNew));
54 | _testingDataListWriter.AutoFlush = true;
55 | }
56 |
57 |
58 | public void Write(Sample sample)
59 | {
60 | // Write training list
61 | string dataLine = $"{DefaultRootDirectory}/{ObjDirectory}/{sample.Guid + ".jpg"}";
62 | StreamWriter destination = _random.NextDouble() < DefaultTestingPortion
63 | ? _testingDataListWriter
64 | : _trainingDataListWriter;
65 | destination.WriteLine(dataLine);
66 | destination.Flush();
67 |
68 | // Write images
69 | using (FileStream imageStream =
70 | new FileStream(Path.Combine(ObjPath, sample.Guid + ".jpg"), FileMode.CreateNew))
71 | {
72 | sample.ImageStream.WriteTo(imageStream);
73 | }
74 | // Write labels
75 | using (FileStream lableStream =
76 | new FileStream(Path.Combine(ObjPath, sample.Guid + ".txt"), FileMode.CreateNew))
77 | {
78 | using (StreamWriter lableStreamWriter = new StreamWriter(lableStream))
79 | {
80 | WriteItems(sample, lableStreamWriter);
81 | }
82 | }
83 | }
84 |
85 | public void Finish()
86 | {
87 | WriteObjData();
88 | WriteClassNames();
89 | _trainingDataListWriter.Flush();
90 | _testingDataListWriter.Flush();
91 | _isFinished = true;
92 | }
93 |
94 | ///
95 | /// Write to imageName.txt
96 | ///
97 | ///
98 | private void WriteItems(Sample sample, StreamWriter writer)
99 | {
100 | double width = sample.Width;
101 | double height = sample.Height;
102 | foreach (var sampleItem in sample.Items)
103 | {
104 | ObjectClass type = sampleItem.Type;
105 | if (!_occurrenceClasses.Contains(type))
106 | {
107 | _occurrenceClasses.Add(type);
108 | }
109 |
110 | double xCenter = ((double)sampleItem.X + sampleItem.Width / 2.0) / width;
111 | double yCenter = ((double)sampleItem.Y + sampleItem.Height / 2.0) / height;
112 | double sampleWidth = sampleItem.Width / width;
113 | double sampleHeight = sampleItem.Height / height;
114 | double[] numbers = { xCenter, yCenter, sampleWidth, sampleHeight };
115 | if (numbers.Any(n => n < 0 || n > 1))
116 | {
117 | throw new InvalidDataException("Size and coordinates must be positive number smaller than 1");
118 | }
119 |
120 | writer.Write(_occurrenceClasses.IndexOf(type)); //
121 | writer.Write(' ');
122 | writer.Write(xCenter); //
123 | writer.Write(' ');
124 | writer.Write(yCenter); //
125 | writer.Write(' ');
126 | writer.Write(sampleWidth); //
127 | writer.Write(' ');
128 | writer.Write(sampleHeight); //
129 | writer.WriteLine();
130 | }
131 | }
132 |
133 | private static void CleanDirectory(string path)
134 | {
135 | if (Directory.EnumerateFileSystemEntries(path).Any())
136 | {
137 | Console.WriteLine("Warning: Target RootPath Is Not Empty. Cleaning target path...");
138 | DirectoryInfo directory = new DirectoryInfo(path);
139 | foreach (var fileInfo in directory.GetFiles())
140 | {
141 | fileInfo.Delete();
142 | }
143 | foreach (var directoryInfo in directory.GetDirectories())
144 | {
145 | directoryInfo.Delete(true);
146 | }
147 | }
148 | }
149 |
150 | private void WriteObjData()
151 | {
152 | using (FileStream file = new FileStream(Path.Combine(RootPath, ObjectDataFile), FileMode.CreateNew))
153 | {
154 | using (StreamWriter writer = new StreamWriter(file))
155 | {
156 | writer.WriteLine($"classes={_occurrenceClasses.Count}");
157 | writer.WriteLine($"train={DefaultRootDirectory}/{TrainingDataFile}");
158 | writer.WriteLine($"valid={DefaultRootDirectory}/{TestingDataFile}");
159 | writer.WriteLine($"names={DefaultRootDirectory}/{ClassNamesFile}");
160 | writer.WriteLine(@"backup = backup/");
161 | }
162 | }
163 | }
164 |
165 | private void WriteClassNames()
166 | {
167 | using (FileStream file = new FileStream(Path.Combine(RootPath, ClassNamesFile), FileMode.CreateNew))
168 | {
169 | using (StreamWriter writer = new StreamWriter(file))
170 | {
171 | foreach (var occurenceClass in _occurrenceClasses)
172 | {
173 | writer.WriteLine(Enum.GetName(typeof(ObjectClass), occurenceClass));
174 | }
175 | }
176 | }
177 | }
178 |
179 | public void Dispose()
180 | {
181 | if (!_isFinished)
182 | {
183 | Finish();
184 | }
185 | _trainingDataListWriter.Dispose();
186 | _testingDataListWriter.Dispose();
187 | _disposed = true;
188 | }
189 |
190 | ~DarknetWriter()
191 | {
192 | if (!_disposed)
193 | {
194 | Dispose();
195 | }
196 | }
197 |
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/IDatasetWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MapleStory.Sampler
8 | {
9 | public interface IDatasetWriter
10 | {
11 | void Write(Sample sample);
12 |
13 | void Finish();
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/MapleStory.Sampler.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0-windows7.0
4 | x64
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/PostProcessor/IPostProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MapleStory.Sampler.PostProcessor
8 | {
9 | ///
10 | /// Interface for post-processor of samples
11 | ///
12 | public interface IPostProcessor
13 | {
14 | ///
15 | /// Post-process the sample. Post-processing could be adding items, resizing, etc..
16 | ///
17 | /// Sample before processing.
18 | /// Sample after processing.
19 | Sample Process(Sample sample);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/PostProcessor/PlayerProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Drawing.Imaging;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using MapRender.Invoker;
10 | using SharpDX.MediaFoundation;
11 |
12 | namespace MapleStory.Sampler.PostProcessor
13 | {
14 | ///
15 | /// Post-processor by adding player images and coordinates.
16 | ///
17 | public class PlayerProcessor : IPostProcessor
18 | {
19 | private const double VerticalRange = 0.85;
20 | private const double HorizontalRange = 0.85;
21 | private const int NumPlayers = 3;
22 |
23 | private IEnumerable _playerImages;
24 | private int _numImages;
25 | private Random _random;
26 |
27 | public PlayerProcessor(string directory)
28 | {
29 | if (!Directory.Exists(directory))
30 | {
31 | throw new FileNotFoundException($"{directory} is not a valid directory!");
32 | }
33 | _random = new Random();
34 | _playerImages = Directory.GetFiles(directory, "*.*").Where(f => f.EndsWith(".png") || f.EndsWith(".bmp")).Select(Image.FromFile);
35 | _numImages = _playerImages.Count();
36 | if (_numImages == 0)
37 | {
38 | throw new FileNotFoundException($"{directory} does not contain any .bmp images!");
39 | }
40 | }
41 |
42 | public Sample Process(Sample sample)
43 | {
44 | // Draw player & replace stream
45 | sample.ImageStream = DrawPlayer(sample.ImageStream, sample);
46 | return sample;
47 | }
48 |
49 | private Image GetNextPlayer()
50 | {
51 | return _playerImages.ElementAt(_random.Next(0, _numImages));
52 | }
53 |
54 | private MemoryStream DrawPlayer(MemoryStream source, Sample sample)
55 | {
56 | Bitmap result = new Bitmap(source);
57 | using (Graphics graphics = Graphics.FromImage(result))
58 | {
59 | for (int i = 0; i < NumPlayers; ++i)
60 | {
61 | Image player = GetNextPlayer();
62 | int drawX = _random.Next((int)(sample.Width * (1 - HorizontalRange)), (int)(sample.Width * HorizontalRange));
63 | int drawY = _random.Next((int)(sample.Height * (1 - VerticalRange)), (int)(sample.Height * VerticalRange));
64 | if (_random.NextDouble() > 0.5)
65 | {
66 | player.RotateFlip(RotateFlipType.RotateNoneFlipX);
67 | }
68 | Rectangle drawRegion = new Rectangle(drawX, drawY, player.Width, player.Height);
69 | graphics.DrawImageUnscaled(player, drawRegion);
70 | // Add coordinate information
71 | sample.Items.Add(new TargetItem() { Height = player.Height, Width = player.Width, X = drawX, Y = drawY, Type = ObjectClass.Player });
72 | }
73 | }
74 | MemoryStream ret = new MemoryStream();
75 | result.Save(ret, ImageFormat.Png);
76 | return ret;
77 | }
78 |
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/Sample.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using MapRender.Invoker;
8 |
9 | namespace MapleStory.Sampler
10 | {
11 | public class Sample
12 | {
13 | public MemoryStream ImageStream { get; internal set; }
14 |
15 | public IList Items { get; internal set; }
16 |
17 | public int Width { get; internal set; }
18 |
19 | public int Height { get; internal set; }
20 |
21 | public Guid Guid { get; private set; }
22 |
23 | public Sample(MemoryStream imageStream, IEnumerable items, int width, int height)
24 | {
25 | ImageStream = imageStream;
26 | Items = new List(items);
27 | Width = width;
28 | Height = height;
29 | Guid = Guid.NewGuid();
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/Sampler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Drawing.Imaging;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Runtime.InteropServices;
8 | using System.Text;
9 | using System.Threading;
10 | using System.Threading.Tasks;
11 | using MapRender.Invoker;
12 | using SharpDX.MediaFoundation;
13 | using Encoder = System.Drawing.Imaging.Encoder;
14 |
15 | namespace MapleStory.Sampler
16 | {
17 | public class Sampler
18 | {
19 | private const long JPEG_RATIO = 90L;
20 | private const double ITEM_PARTIAL_AREA_THRESHOLD = 0.70;
21 | private readonly MapRenderInvoker _renderInvoker;
22 |
23 | public delegate void SamplePostProcessor(Sample sample, EventArgs e);
24 |
25 | public event SamplePostProcessor OnSampleCaptured;
26 |
27 | public Sampler(MapRenderInvoker renderInvoker)
28 | {
29 | _renderInvoker = renderInvoker;
30 | if (!renderInvoker.IsRunning)
31 | {
32 | throw new ArgumentException("Sampler must have a lunched render!");
33 | }
34 | }
35 |
36 | public Task SampleSingleAsync()
37 | {
38 | MemoryStream stream = new MemoryStream();
39 | var screenShotData = _renderInvoker.TakeScreenShot(stream);
40 | var items = FilterTargetsInCamera(screenShotData);
41 | int width = screenShotData.CameraRectangle.Width;
42 | int height = screenShotData.CameraRectangle.Height;
43 | return Task.Run(() =>
44 | {
45 | Sample ret = new Sample(stream, items, width, height);
46 | OnSampleCaptured?.Invoke(ret, null);
47 | ret.ImageStream = EncodeScreenShot(ret.ImageStream);
48 | return ret;
49 | });
50 | }
51 |
52 | ///
53 | /// Sample all based on provided step
54 | ///
55 | /// step in X to sample
56 | /// step in Y to sample
57 | /// Writer to save result
58 | /// Sampling time interval, in ms.
59 | public void SampleAll(int xStep, int yStep, IDatasetWriter writer, int interval = 0)
60 | {
61 | xStep = Math.Abs(xStep);
62 | yStep = Math.Abs(yStep);
63 | int initX = _renderInvoker.WorldOriginX + _renderInvoker.ScreenWidth / 2;
64 | int initY = _renderInvoker.WorldOriginY + _renderInvoker.ScreenHeight / 2;
65 | int endX = _renderInvoker.WorldOriginX + _renderInvoker.WorldWidth - _renderInvoker.ScreenWidth / 2;
66 | int endY = _renderInvoker.WorldOriginY + _renderInvoker.WorldHeight - _renderInvoker.ScreenHeight / 2;
67 |
68 | int count = 0;
69 | int total = (int)(Math.Round((double)(endX - initX) / xStep, MidpointRounding.ToPositiveInfinity) *
70 | Math.Round((double)(endY - initY) / yStep, MidpointRounding.ToPositiveInfinity));
71 | HashSet writingTasks = new HashSet();
72 | Queue completedTasks = new Queue();
73 |
74 | for (int x = initX; x < endX; x += xStep)
75 | {
76 | for (int y = initY; y < endY; y += yStep)
77 | {
78 | Console.WriteLine($"Sampling at center x={x},y={y}....");
79 | // Move Camera
80 | _renderInvoker.MoveCamera(x, y);
81 | // Do sample
82 | writingTasks.Add(SampleSingleAsync().ContinueWith(s =>
83 | {
84 | lock (writer)
85 | {
86 | writer.Write(s.Result);
87 | }
88 | }));
89 | // Check pending tasks
90 | foreach (var task in writingTasks)
91 | {
92 | if (task.Exception != null)
93 | {
94 | throw task.Exception;
95 | }
96 | if (task.IsCompleted)
97 | {
98 | count++;
99 | completedTasks.Enqueue(task);
100 | Console.WriteLine($"Progress: {count}/{total}, {(double)count / total * 100}%\n");
101 | }
102 | }
103 | foreach (var task in completedTasks)
104 | {
105 | writingTasks.Remove(task);
106 | }
107 | Thread.Sleep(interval);
108 | }
109 | }
110 | foreach (var task in writingTasks)
111 | {
112 | task.GetAwaiter().GetResult();
113 | }
114 | Console.WriteLine("############## All Samples Captured ##############");
115 | return;
116 | }
117 |
118 | private MemoryStream EncodeScreenShot(Stream screenShotStream)
119 | {
120 | using Bitmap source = new Bitmap(screenShotStream);
121 | using Bitmap result = new Bitmap(_renderInvoker.ScreenWidth, _renderInvoker.ScreenHeight);
122 | Rectangle rectangle = new Rectangle(Point.Empty, source.Size);
123 | using (Graphics graphics = Graphics.FromImage(result))
124 | {
125 | // Dark background, to back alpha channel.
126 | graphics.Clear(Color.Black);
127 | graphics.DrawImageUnscaledAndClipped(source, rectangle);
128 | }
129 | MemoryStream ret = new MemoryStream();
130 | ImageCodecInfo jpegCodecInfo = ImageCodecInfo.GetImageEncoders().First(i => i.MimeType == "image/jpeg");
131 | using (EncoderParameters parameters = new EncoderParameters(1))
132 | {
133 | EncoderParameter parameter = new EncoderParameter(Encoder.Quality, JPEG_RATIO);
134 | parameters.Param[0] = parameter;
135 | result.Save(ret, jpegCodecInfo, parameters);
136 | }
137 |
138 | return ret;
139 | }
140 |
141 | private static List FilterTargetsInCamera(ScreenShotData data)
142 | {
143 | List ret = new List();
144 | List source = data.Items;
145 | Rectangle camRectangle = data.CameraRectangle;
146 | int imgWidth = camRectangle.Width;
147 | int imgHeight = camRectangle.Height;
148 | source.ForEach(i =>
149 | {
150 | i.X -= camRectangle.X;
151 | i.Y -= camRectangle.Y;
152 |
153 | // Validation
154 | if (i.Height < 0 || i.Width < 0)
155 | {
156 | throw new InvalidDataException("Items Height or Width is negative!!");
157 | }
158 |
159 | // Not show in screenshots at all
160 | if (i.X > imgWidth || i.Y > imgHeight)
161 | {
162 | return;
163 | }
164 | int inCameraWidth = i.Width;
165 | int inCameraHeight = i.Height;
166 | double itemArea = i.Width * i.Height;
167 |
168 | // Partial in X - left
169 | if (i.X < 0)
170 | {
171 | inCameraWidth = i.X + i.Width;
172 | if (inCameraWidth < 0)
173 | {
174 | return;
175 | }
176 | i.X = 0;
177 | }
178 |
179 | // Partial in X - right
180 | if (i.X + i.Width > imgWidth)
181 | {
182 | inCameraWidth = imgWidth - i.X;
183 | }
184 |
185 | // Partial in Y - up
186 | if (i.Y < 0)
187 | {
188 | inCameraHeight = i.Y + i.Height;
189 | if (inCameraHeight < 0)
190 | {
191 | return;
192 | }
193 | i.Y = 0;
194 | }
195 |
196 | // Partial in Y - bottom
197 | if (i.Y + i.Height > imgHeight)
198 | {
199 | inCameraHeight = imgHeight - i.Y;
200 | }
201 |
202 | double inCameraArea = inCameraHeight * imgWidth;
203 | if (inCameraArea / itemArea >= ITEM_PARTIAL_AREA_THRESHOLD)
204 | {
205 | i.Width = inCameraWidth;
206 | i.Height = inCameraHeight;
207 | ret.Add(i);
208 | }
209 | });
210 | return ret;
211 | }
212 |
213 |
214 | }
215 | }
216 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/TfExample.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using MapRender.Invoker;
8 | using ProtoBuf;
9 | using Tensorflow;
10 |
11 | namespace MapleStory.Sampler
12 | {
13 | public class TfExample
14 | {
15 | #region KEYS
16 | private const string KeyHeight = @"image/height";
17 | private const string KeyWidth = @"image/width";
18 | private const string KeyFileName = @"image/filename";
19 | private const string KeySourceId = @"image/source_id";
20 | private const string KeyEncoded = @"image/encoded";
21 | private const string KeyFormat = @"image/format";
22 | private const string KeyMinX = @"image/object/bbox/xmin";
23 | private const string KeyMaxX = @"image/object/bbox/xmax";
24 | private const string KeyMinY = @"image/object/bbox/ymin";
25 | private const string KeyMaxY = @"image/object/bbox/ymax";
26 | private const string KeyClassText = @"image/object/class/text";
27 | private const string KeyLabel = @"image/object/class/label";
28 | #endregion
29 |
30 | private const string JpegFormatString = @"jpeg";
31 |
32 | private Example _underlyingExample;
33 | private Features _features;
34 | private Dictionary _featureMap;
35 | private List _minXList;
36 | private List _minYList;
37 | private List _maxXList;
38 | private List _maxYList;
39 | private List _classTextList;
40 | private List _labelList;
41 | private float _sampleWidth;
42 | private float _sampleHeight;
43 |
44 | public Guid Guid { get; private set; }
45 |
46 | private TfExample(MemoryStream imageStream, int width, int height, int itemCount)
47 | {
48 | _underlyingExample = new Example();
49 | _underlyingExample.Features = new Features();
50 | _features = _underlyingExample.Features;
51 | _featureMap = _features.feature;
52 | _sampleHeight = height;
53 | _sampleWidth = width;
54 | Guid = Guid.NewGuid();
55 |
56 | _minXList = new List(itemCount);
57 | _minYList = new List(itemCount);
58 | _maxXList = new List(itemCount);
59 | _maxYList = new List(itemCount);
60 | _classTextList = new List(itemCount);
61 | _labelList = new List(itemCount);
62 |
63 | _featureMap[KeyFormat] = NewBytesFeature(JpegFormatString);
64 | _featureMap[KeyHeight] = NewInt64Feature(height);
65 | _featureMap[KeyWidth] = NewInt64Feature(width);
66 | _featureMap[KeyEncoded] = NewBytesFeature(imageStream.ToArray());
67 | var guidString = Guid.ToString();
68 | _featureMap[KeyFileName] = NewBytesFeature(guidString);
69 | _featureMap[KeySourceId] = NewBytesFeature(guidString);
70 | }
71 |
72 | public static TfExample From(Sample sample)
73 | {
74 | TfExample ret = new TfExample(sample.ImageStream, sample.Width, sample.Height, sample.Items.Count());
75 | foreach (var item in sample.Items)
76 | {
77 | ret.AddItem(item);
78 | }
79 | return ret;
80 | }
81 |
82 | public MemoryStream SerializeToStream()
83 | {
84 | _featureMap[KeyMinX] = NewFloatListFeature(_minXList.ToArray());
85 | _featureMap[KeyMaxX] = NewFloatListFeature(_maxXList.ToArray());
86 | _featureMap[KeyMinY] = NewFloatListFeature(_minYList.ToArray());
87 | _featureMap[KeyMaxY] = NewFloatListFeature(_maxYList.ToArray());
88 | _featureMap[KeyClassText] = NewBytesListFeature(_classTextList);
89 | _featureMap[KeyLabel] = NewInt64ListFeature(_labelList.ToArray());
90 |
91 | MemoryStream ret = new MemoryStream();
92 | Serializer.Serialize(ret, _underlyingExample);
93 | return ret;
94 | }
95 |
96 | private void AddItem(TargetItem item)
97 | {
98 | float x = item.X;
99 | float y = item.Y;
100 | float x2 = x + item.Width;
101 | float y2 = y + item.Height;
102 | _minXList.Add(x / _sampleWidth);
103 | _maxXList.Add(x2 / _sampleWidth);
104 |
105 | _minYList.Add(y / _sampleHeight);
106 | _maxYList.Add(y2 / _sampleHeight);
107 |
108 | if (item.Type == MapRender.Invoker.ObjectClass.Unknown || !Enum.IsDefined(typeof(ObjectClass), item.Type))
109 | {
110 | throw new ArgumentException("TargetItem contains unknown type item!");
111 | }
112 | _classTextList.Add(Encoding.UTF8.GetBytes(Enum.GetName(typeof(ObjectClass), item.Type)));
113 | _labelList.Add((long)item.Type);
114 | }
115 |
116 | private static Feature NewBytesListFeature(IEnumerable value)
117 | {
118 | var bytesList = new BytesList();
119 | bytesList.Values.AddRange(value);
120 | return new Feature() { BytesList = bytesList };
121 | }
122 |
123 | private static Feature NewFloatListFeature(float[] value)
124 | {
125 | return new Feature() { FloatList = new FloatList() { Values = value } };
126 | }
127 |
128 | private static Feature NewInt64ListFeature(long[] value)
129 | {
130 | return new Feature() { Int64List = new Int64List() { Values = value } };
131 | }
132 |
133 | private static Feature NewInt64Feature(long value)
134 | {
135 | return new Feature() { Int64List = new Int64List() { Values = new[] { value } } };
136 | }
137 |
138 | private static Feature NewBytesFeature(byte[] value)
139 | {
140 | return new Feature() { BytesList = new BytesList() { Values = { value } } };
141 | }
142 |
143 | private static Feature NewBytesFeature(string text)
144 | {
145 | return NewBytesFeature(Encoding.UTF8.GetBytes(text));
146 | }
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/TfRecordWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Force.Crc32;
8 |
9 | namespace MapleStory.Sampler
10 | {
11 | public class TfRecordWriter : IDisposable, IDatasetWriter
12 | {
13 | public const string TfRecordNameExtension = ".tfrecord";
14 | private const int BufferSize = 5120;
15 | private FileStream _fileStream;
16 |
17 | ///
18 | /// Create a TfRecordWriter, by creating an underlying
19 | ///
20 | /// If path does not end with ".tfrecord", it will be added.
21 | public TfRecordWriter(string path)
22 | {
23 | if (!path.EndsWith(TfRecordNameExtension, StringComparison.InvariantCultureIgnoreCase))
24 | {
25 | path += TfRecordNameExtension;
26 | }
27 | _fileStream = new FileStream(path, FileMode.CreateNew);
28 | }
29 |
30 | ~TfRecordWriter()
31 | {
32 | this.Dispose();
33 | }
34 |
35 | void IDatasetWriter.Write(Sample sample)
36 | {
37 | Write(TfExample.From(sample));
38 | }
39 |
40 | public void Write(TfExample example)
41 | {
42 | Write(example.SerializeToStream());
43 | }
44 |
45 | public void Write(byte[] bytes)
46 | {
47 | Write(new MemoryStream(bytes));
48 | }
49 |
50 | ///
51 | /// Write a single record.
52 | ///
53 | /// Stream containing record in binary form
54 | public void Write(Stream stream)
55 | {
56 | ulong length = (ulong)stream.Length;
57 | uint crcLength = Crc32CAlgorithm.Compute(BitConverter.GetBytes(length));
58 | uint maskLength = MaskCrc32(crcLength);
59 | _fileStream.Write(BitConverter.GetBytes(length), 0, 8); // uint64 length
60 | _fileStream.Write(BitConverter.GetBytes(maskLength), 0, 4); // uint32 masked_crc32_of_length
61 | stream.Seek(0, SeekOrigin.Begin); // Read from head
62 |
63 | byte[] buffer = new byte[BufferSize];
64 | int count = 0;
65 | int readSize = stream.Read(buffer, 0, buffer.Length);
66 | uint crcData = Crc32CAlgorithm.Compute(buffer, 0, readSize);
67 | for (bool firstRun = true; readSize > 0;
68 | count += readSize, readSize = stream.Read(buffer, 0, buffer.Length), firstRun = false)
69 | {
70 | if (!firstRun)
71 | {
72 | crcData = Crc32CAlgorithm.Append(crcData, buffer, 0, readSize);
73 | }
74 | _fileStream.Write(buffer, 0, readSize); // byte data[length]
75 | }
76 |
77 | if (count != (int)length)
78 | {
79 | throw new Exception("Stream length does not equal to read length.");
80 | }
81 |
82 | uint maskCrcData = MaskCrc32(crcData);
83 | _fileStream.Write(BitConverter.GetBytes(maskCrcData), 0, 4); // uint32 masked_crc32_of_data
84 | }
85 |
86 | ///
87 | /// See
88 | ///
89 | private uint MaskCrc32(uint crc)
90 | {
91 | return (uint)(((crc >> 15) | (crc << 17)) + 0xa282ead8UL);
92 | }
93 |
94 | public void Finish()
95 | {
96 | // Not needed for TfRecord.
97 | return;
98 | }
99 |
100 | public void Dispose()
101 | {
102 | _fileStream?.Dispose();
103 | }
104 |
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/protobuf/Example.cs:
--------------------------------------------------------------------------------
1 | //
2 | // This file was generated by a tool; you should avoid making direct changes.
3 | // Consider using 'partial classes' to extend these types
4 | // Input: example.proto
5 | //
6 |
7 | #region Designer generated code
8 | #pragma warning disable CS0612, CS0618, CS1591, CS3021, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
9 | namespace Tensorflow
10 | {
11 |
12 | [global::ProtoBuf.ProtoContract()]
13 | public partial class Example : global::ProtoBuf.IExtensible
14 | {
15 | private global::ProtoBuf.IExtension __pbn__extensionData;
16 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
17 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
18 |
19 | [global::ProtoBuf.ProtoMember(1, Name = @"features")]
20 | public Features Features { get; set; }
21 |
22 | }
23 |
24 | [global::ProtoBuf.ProtoContract()]
25 | public partial class SequenceExample : global::ProtoBuf.IExtensible
26 | {
27 | private global::ProtoBuf.IExtension __pbn__extensionData;
28 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
29 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
30 |
31 | [global::ProtoBuf.ProtoMember(1, Name = @"context")]
32 | public Features Context { get; set; }
33 |
34 | [global::ProtoBuf.ProtoMember(2, Name = @"feature_lists")]
35 | public FeatureLists FeatureLists { get; set; }
36 |
37 | }
38 |
39 | }
40 |
41 | #pragma warning restore CS0612, CS0618, CS1591, CS3021, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
42 | #endregion
43 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/protobuf/Feature.cs:
--------------------------------------------------------------------------------
1 | //
2 | // This file was generated by a tool; you should avoid making direct changes.
3 | // Consider using 'partial classes' to extend these types
4 | // Input: feature.proto
5 | //
6 |
7 | #region Designer generated code
8 | #pragma warning disable CS0612, CS0618, CS1591, CS3021, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
9 | namespace Tensorflow
10 | {
11 |
12 | [global::ProtoBuf.ProtoContract()]
13 | public partial class BytesList : global::ProtoBuf.IExtensible
14 | {
15 | private global::ProtoBuf.IExtension __pbn__extensionData;
16 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
17 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
18 |
19 | [global::ProtoBuf.ProtoMember(1, Name = @"value")]
20 | public global::System.Collections.Generic.List Values { get; } = new global::System.Collections.Generic.List();
21 |
22 | }
23 |
24 | [global::ProtoBuf.ProtoContract()]
25 | public partial class FloatList : global::ProtoBuf.IExtensible
26 | {
27 | private global::ProtoBuf.IExtension __pbn__extensionData;
28 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
29 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
30 |
31 | [global::ProtoBuf.ProtoMember(1, Name = @"value", IsPacked = true)]
32 | public float[] Values { get; set; }
33 |
34 | }
35 |
36 | [global::ProtoBuf.ProtoContract()]
37 | public partial class Int64List : global::ProtoBuf.IExtensible
38 | {
39 | private global::ProtoBuf.IExtension __pbn__extensionData;
40 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
41 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
42 |
43 | [global::ProtoBuf.ProtoMember(1, Name = @"value", IsPacked = true)]
44 | public long[] Values { get; set; }
45 |
46 | }
47 |
48 | [global::ProtoBuf.ProtoContract()]
49 | public partial class Feature : global::ProtoBuf.IExtensible
50 | {
51 | private global::ProtoBuf.IExtension __pbn__extensionData;
52 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
53 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
54 |
55 | [global::ProtoBuf.ProtoMember(1, Name = @"bytes_list")]
56 | public BytesList BytesList
57 | {
58 | get => __pbn__kind.Is(1) ? ((BytesList)__pbn__kind.Object) : default;
59 | set => __pbn__kind = new global::ProtoBuf.DiscriminatedUnionObject(1, value);
60 | }
61 | public bool ShouldSerializeBytesList() => __pbn__kind.Is(1);
62 | public void ResetBytesList() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__kind, 1);
63 |
64 | private global::ProtoBuf.DiscriminatedUnionObject __pbn__kind;
65 |
66 | [global::ProtoBuf.ProtoMember(2, Name = @"float_list")]
67 | public FloatList FloatList
68 | {
69 | get => __pbn__kind.Is(2) ? ((FloatList)__pbn__kind.Object) : default;
70 | set => __pbn__kind = new global::ProtoBuf.DiscriminatedUnionObject(2, value);
71 | }
72 | public bool ShouldSerializeFloatList() => __pbn__kind.Is(2);
73 | public void ResetFloatList() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__kind, 2);
74 |
75 | [global::ProtoBuf.ProtoMember(3, Name = @"int64_list")]
76 | public Int64List Int64List
77 | {
78 | get => __pbn__kind.Is(3) ? ((Int64List)__pbn__kind.Object) : default;
79 | set => __pbn__kind = new global::ProtoBuf.DiscriminatedUnionObject(3, value);
80 | }
81 | public bool ShouldSerializeInt64List() => __pbn__kind.Is(3);
82 | public void ResetInt64List() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__kind, 3);
83 |
84 | }
85 |
86 | [global::ProtoBuf.ProtoContract()]
87 | public partial class Features : global::ProtoBuf.IExtensible
88 | {
89 | private global::ProtoBuf.IExtension __pbn__extensionData;
90 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
91 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
92 |
93 | [global::ProtoBuf.ProtoMember(1)]
94 | [global::ProtoBuf.ProtoMap]
95 | public global::System.Collections.Generic.Dictionary feature { get; } = new global::System.Collections.Generic.Dictionary();
96 |
97 | }
98 |
99 | [global::ProtoBuf.ProtoContract()]
100 | public partial class FeatureList : global::ProtoBuf.IExtensible
101 | {
102 | private global::ProtoBuf.IExtension __pbn__extensionData;
103 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
104 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
105 |
106 | [global::ProtoBuf.ProtoMember(1, Name = @"feature")]
107 | public global::System.Collections.Generic.List Features { get; } = new global::System.Collections.Generic.List();
108 |
109 | }
110 |
111 | [global::ProtoBuf.ProtoContract()]
112 | public partial class FeatureLists : global::ProtoBuf.IExtensible
113 | {
114 | private global::ProtoBuf.IExtension __pbn__extensionData;
115 | global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
116 | => global::ProtoBuf.Extensible.GetExtensionObject(ref __pbn__extensionData, createIfMissing);
117 |
118 | [global::ProtoBuf.ProtoMember(1)]
119 | [global::ProtoBuf.ProtoMap]
120 | public global::System.Collections.Generic.Dictionary feature_list { get; } = new global::System.Collections.Generic.Dictionary();
121 |
122 | }
123 |
124 | }
125 |
126 | #pragma warning restore CS0612, CS0618, CS1591, CS3021, IDE1006, RCS1036, RCS1057, RCS1085, RCS1192
127 | #endregion
128 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/protobuf/README.md:
--------------------------------------------------------------------------------
1 | # NOTE
2 | Files in this folder are copied from https://github.com/tensorflow/tensorflow/tree/master/tensorflow/core/example
--------------------------------------------------------------------------------
/MapleStory.Sampler/protobuf/example.proto:
--------------------------------------------------------------------------------
1 | // Protocol messages for describing input data Examples for machine learning
2 | // model training or inference.
3 | syntax = "proto3";
4 |
5 | package tensorflow;
6 |
7 | import "feature.proto";
8 |
9 | option cc_enable_arenas = true;
10 | option java_outer_classname = "ExampleProtos";
11 | option java_multiple_files = true;
12 | option java_package = "org.tensorflow.example";
13 | option go_package = "github.com/tensorflow/tensorflow/tensorflow/go/core/example/example_protos_go_proto";
14 |
15 | // LINT.IfChange
16 | // An Example is a mostly-normalized data format for storing data for
17 | // training and inference. It contains a key-value store (features); where
18 | // each key (string) maps to a Feature message (which is oneof packed BytesList,
19 | // FloatList, or Int64List). This flexible and compact format allows the
20 | // storage of large amounts of typed data, but requires that the data shape
21 | // and use be determined by the configuration files and parsers that are used to
22 | // read and write this format. That is, the Example is mostly *not* a
23 | // self-describing format. In TensorFlow, Examples are read in row-major
24 | // format, so any configuration that describes data with rank-2 or above
25 | // should keep this in mind. For example, to store an M x N matrix of Bytes,
26 | // the BytesList must contain M*N bytes, with M rows of N contiguous values
27 | // each. That is, the BytesList value must store the matrix as:
28 | // .... row 0 .... .... row 1 .... // ........... // ... row M-1 ....
29 | //
30 | // An Example for a movie recommendation application:
31 | // features {
32 | // feature {
33 | // key: "age"
34 | // value { float_list {
35 | // value: 29.0
36 | // }}
37 | // }
38 | // feature {
39 | // key: "movie"
40 | // value { bytes_list {
41 | // value: "The Shawshank Redemption"
42 | // value: "Fight Club"
43 | // }}
44 | // }
45 | // feature {
46 | // key: "movie_ratings"
47 | // value { float_list {
48 | // value: 9.0
49 | // value: 9.7
50 | // }}
51 | // }
52 | // feature {
53 | // key: "suggestion"
54 | // value { bytes_list {
55 | // value: "Inception"
56 | // }}
57 | // }
58 | // # Note that this feature exists to be used as a label in training.
59 | // # E.g., if training a logistic regression model to predict purchase
60 | // # probability in our learning tool we would set the label feature to
61 | // # "suggestion_purchased".
62 | // feature {
63 | // key: "suggestion_purchased"
64 | // value { float_list {
65 | // value: 1.0
66 | // }}
67 | // }
68 | // # Similar to "suggestion_purchased" above this feature exists to be used
69 | // # as a label in training.
70 | // # E.g., if training a linear regression model to predict purchase
71 | // # price in our learning tool we would set the label feature to
72 | // # "purchase_price".
73 | // feature {
74 | // key: "purchase_price"
75 | // value { float_list {
76 | // value: 9.99
77 | // }}
78 | // }
79 | // }
80 | //
81 | // A conformant Example data set obeys the following conventions:
82 | // - If a Feature K exists in one example with data type T, it must be of
83 | // type T in all other examples when present. It may be omitted.
84 | // - The number of instances of Feature K list data may vary across examples,
85 | // depending on the requirements of the model.
86 | // - If a Feature K doesn't exist in an example, a K-specific default will be
87 | // used, if configured.
88 | // - If a Feature K exists in an example but contains no items, the intent
89 | // is considered to be an empty tensor and no default will be used.
90 |
91 | message Example {
92 | Features features = 1;
93 | }
94 |
95 | // A SequenceExample is an Example representing one or more sequences, and
96 | // some context. The context contains features which apply to the entire
97 | // example. The feature_lists contain a key, value map where each key is
98 | // associated with a repeated set of Features (a FeatureList).
99 | // A FeatureList thus represents the values of a feature identified by its key
100 | // over time / frames.
101 | //
102 | // Below is a SequenceExample for a movie recommendation application recording a
103 | // sequence of ratings by a user. The time-independent features ("locale",
104 | // "age", "favorites") describing the user are part of the context. The sequence
105 | // of movies the user rated are part of the feature_lists. For each movie in the
106 | // sequence we have information on its name and actors and the user's rating.
107 | // This information is recorded in three separate feature_list(s).
108 | // In the example below there are only two movies. All three feature_list(s),
109 | // namely "movie_ratings", "movie_names", and "actors" have a feature value for
110 | // both movies. Note, that "actors" is itself a bytes_list with multiple
111 | // strings per movie.
112 | //
113 | // context: {
114 | // feature: {
115 | // key : "locale"
116 | // value: {
117 | // bytes_list: {
118 | // value: [ "pt_BR" ]
119 | // }
120 | // }
121 | // }
122 | // feature: {
123 | // key : "age"
124 | // value: {
125 | // float_list: {
126 | // value: [ 19.0 ]
127 | // }
128 | // }
129 | // }
130 | // feature: {
131 | // key : "favorites"
132 | // value: {
133 | // bytes_list: {
134 | // value: [ "Majesty Rose", "Savannah Outen", "One Direction" ]
135 | // }
136 | // }
137 | // }
138 | // }
139 | // feature_lists: {
140 | // feature_list: {
141 | // key : "movie_ratings"
142 | // value: {
143 | // feature: {
144 | // float_list: {
145 | // value: [ 4.5 ]
146 | // }
147 | // }
148 | // feature: {
149 | // float_list: {
150 | // value: [ 5.0 ]
151 | // }
152 | // }
153 | // }
154 | // }
155 | // feature_list: {
156 | // key : "movie_names"
157 | // value: {
158 | // feature: {
159 | // bytes_list: {
160 | // value: [ "The Shawshank Redemption" ]
161 | // }
162 | // }
163 | // feature: {
164 | // bytes_list: {
165 | // value: [ "Fight Club" ]
166 | // }
167 | // }
168 | // }
169 | // }
170 | // feature_list: {
171 | // key : "actors"
172 | // value: {
173 | // feature: {
174 | // bytes_list: {
175 | // value: [ "Tim Robbins", "Morgan Freeman" ]
176 | // }
177 | // }
178 | // feature: {
179 | // bytes_list: {
180 | // value: [ "Brad Pitt", "Edward Norton", "Helena Bonham Carter" ]
181 | // }
182 | // }
183 | // }
184 | // }
185 | // }
186 | //
187 | // A conformant SequenceExample data set obeys the following conventions:
188 | //
189 | // Context:
190 | // - All conformant context features K must obey the same conventions as
191 | // a conformant Example's features (see above).
192 | // Feature lists:
193 | // - A FeatureList L may be missing in an example; it is up to the
194 | // parser configuration to determine if this is allowed or considered
195 | // an empty list (zero length).
196 | // - If a FeatureList L exists, it may be empty (zero length).
197 | // - If a FeatureList L is non-empty, all features within the FeatureList
198 | // must have the same data type T. Even across SequenceExamples, the type T
199 | // of the FeatureList identified by the same key must be the same. An entry
200 | // without any values may serve as an empty feature.
201 | // - If a FeatureList L is non-empty, it is up to the parser configuration
202 | // to determine if all features within the FeatureList must
203 | // have the same size. The same holds for this FeatureList across multiple
204 | // examples.
205 | // - For sequence modeling, e.g.:
206 | // http://colah.github.io/posts/2015-08-Understanding-LSTMs/
207 | // https://github.com/tensorflow/nmt
208 | // the feature lists represent a sequence of frames.
209 | // In this scenario, all FeatureLists in a SequenceExample have the same
210 | // number of Feature messages, so that the ith element in each FeatureList
211 | // is part of the ith frame (or time step).
212 | // Examples of conformant and non-conformant examples' FeatureLists:
213 | //
214 | // Conformant FeatureLists:
215 | // feature_lists: { feature_list: {
216 | // key: "movie_ratings"
217 | // value: { feature: { float_list: { value: [ 4.5 ] } }
218 | // feature: { float_list: { value: [ 5.0 ] } } }
219 | // } }
220 | //
221 | // Non-conformant FeatureLists (mismatched types):
222 | // feature_lists: { feature_list: {
223 | // key: "movie_ratings"
224 | // value: { feature: { float_list: { value: [ 4.5 ] } }
225 | // feature: { int64_list: { value: [ 5 ] } } }
226 | // } }
227 | //
228 | // Conditionally conformant FeatureLists, the parser configuration determines
229 | // if the feature sizes must match:
230 | // feature_lists: { feature_list: {
231 | // key: "movie_ratings"
232 | // value: { feature: { float_list: { value: [ 4.5 ] } }
233 | // feature: { float_list: { value: [ 5.0, 6.0 ] } } }
234 | // } }
235 | //
236 | // Conformant pair of SequenceExample
237 | // feature_lists: { feature_list: {
238 | // key: "movie_ratings"
239 | // value: { feature: { float_list: { value: [ 4.5 ] } }
240 | // feature: { float_list: { value: [ 5.0 ] } } }
241 | // } }
242 | // and:
243 | // feature_lists: { feature_list: {
244 | // key: "movie_ratings"
245 | // value: { feature: { float_list: { value: [ 4.5 ] } }
246 | // feature: { float_list: { value: [ 5.0 ] } }
247 | // feature: { float_list: { value: [ 2.0 ] } } }
248 | // } }
249 | //
250 | // Conformant pair of SequenceExample
251 | // feature_lists: { feature_list: {
252 | // key: "movie_ratings"
253 | // value: { feature: { float_list: { value: [ 4.5 ] } }
254 | // feature: { float_list: { value: [ 5.0 ] } } }
255 | // } }
256 | // and:
257 | // feature_lists: { feature_list: {
258 | // key: "movie_ratings"
259 | // value: { }
260 | // } }
261 | //
262 | // Conditionally conformant pair of SequenceExample, the parser configuration
263 | // determines if the second feature_lists is consistent (zero-length) or
264 | // invalid (missing "movie_ratings"):
265 | // feature_lists: { feature_list: {
266 | // key: "movie_ratings"
267 | // value: { feature: { float_list: { value: [ 4.5 ] } }
268 | // feature: { float_list: { value: [ 5.0 ] } } }
269 | // } }
270 | // and:
271 | // feature_lists: { }
272 | //
273 | // Non-conformant pair of SequenceExample (mismatched types)
274 | // feature_lists: { feature_list: {
275 | // key: "movie_ratings"
276 | // value: { feature: { float_list: { value: [ 4.5 ] } }
277 | // feature: { float_list: { value: [ 5.0 ] } } }
278 | // } }
279 | // and:
280 | // feature_lists: { feature_list: {
281 | // key: "movie_ratings"
282 | // value: { feature: { int64_list: { value: [ 4 ] } }
283 | // feature: { int64_list: { value: [ 5 ] } }
284 | // feature: { int64_list: { value: [ 2 ] } } }
285 | // } }
286 | //
287 | // Conditionally conformant pair of SequenceExample; the parser configuration
288 | // determines if the feature sizes must match:
289 | // feature_lists: { feature_list: {
290 | // key: "movie_ratings"
291 | // value: { feature: { float_list: { value: [ 4.5 ] } }
292 | // feature: { float_list: { value: [ 5.0 ] } } }
293 | // } }
294 | // and:
295 | // feature_lists: { feature_list: {
296 | // key: "movie_ratings"
297 | // value: { feature: { float_list: { value: [ 4.0 ] } }
298 | // feature: { float_list: { value: [ 5.0, 3.0 ] } }
299 | // } }
300 |
301 | message SequenceExample {
302 | Features context = 1;
303 | FeatureLists feature_lists = 2;
304 | }
305 | // LINT.ThenChange(
306 | // https://www.tensorflow.org/code/tensorflow/python/training/training.py)
307 |
--------------------------------------------------------------------------------
/MapleStory.Sampler/protobuf/feature.proto:
--------------------------------------------------------------------------------
1 | // Protocol messages for describing features for machine learning model
2 | // training or inference.
3 | //
4 | // There are three base Feature types:
5 | // - bytes
6 | // - float
7 | // - int64
8 | //
9 | // A Feature contains Lists which may hold zero or more values. These
10 | // lists are the base values BytesList, FloatList, Int64List.
11 | //
12 | // Features are organized into categories by name. The Features message
13 | // contains the mapping from name to Feature.
14 | //
15 | // Example Features for a movie recommendation application:
16 | // feature {
17 | // key: "age"
18 | // value { float_list {
19 | // value: 29.0
20 | // }}
21 | // }
22 | // feature {
23 | // key: "movie"
24 | // value { bytes_list {
25 | // value: "The Shawshank Redemption"
26 | // value: "Fight Club"
27 | // }}
28 | // }
29 | // feature {
30 | // key: "movie_ratings"
31 | // value { float_list {
32 | // value: 9.0
33 | // value: 9.7
34 | // }}
35 | // }
36 | // feature {
37 | // key: "suggestion"
38 | // value { bytes_list {
39 | // value: "Inception"
40 | // }}
41 | // }
42 | // feature {
43 | // key: "suggestion_purchased"
44 | // value { int64_list {
45 | // value: 1
46 | // }}
47 | // }
48 | // feature {
49 | // key: "purchase_price"
50 | // value { float_list {
51 | // value: 9.99
52 | // }}
53 | // }
54 | //
55 |
56 | syntax = "proto3";
57 |
58 | package tensorflow;
59 |
60 | option cc_enable_arenas = true;
61 | option java_outer_classname = "FeatureProtos";
62 | option java_multiple_files = true;
63 | option java_package = "org.tensorflow.example";
64 | option go_package = "github.com/tensorflow/tensorflow/tensorflow/go/core/example/example_protos_go_proto";
65 |
66 | // LINT.IfChange
67 | // Containers to hold repeated fundamental values.
68 | message BytesList {
69 | repeated bytes value = 1;
70 | }
71 | message FloatList {
72 | repeated float value = 1 [packed = true];
73 | }
74 | message Int64List {
75 | repeated int64 value = 1 [packed = true];
76 | }
77 |
78 | // Containers for non-sequential data.
79 | message Feature {
80 | // Each feature can be exactly one kind.
81 | oneof kind {
82 | BytesList bytes_list = 1;
83 | FloatList float_list = 2;
84 | Int64List int64_list = 3;
85 | }
86 | }
87 |
88 | message Features {
89 | // Map from feature name to feature.
90 | map feature = 1;
91 | }
92 |
93 | // Containers for sequential data.
94 | //
95 | // A FeatureList contains lists of Features. These may hold zero or more
96 | // Feature values.
97 | //
98 | // FeatureLists are organized into categories by name. The FeatureLists message
99 | // contains the mapping from name to FeatureList.
100 | //
101 | message FeatureList {
102 | repeated Feature feature = 1;
103 | }
104 |
105 | message FeatureLists {
106 | // Map from feature name to feature list.
107 | map feature_list = 1;
108 | }
109 | // LINT.ThenChange(
110 | // https://www.tensorflow.org/code/tensorflow/python/training/training.py)
111 |
--------------------------------------------------------------------------------
/MapleStoryDetectionSampleGenerator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31112.23
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapleStory.Common", "MapleStory.Common\MapleStory.Common.csproj", "{43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CharaSimResource", "WzComparerR2\CharaSimResource\CharaSimResource.csproj", "{54797F38-A12C-4202-92A4-1A3DDCE914B7}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WzComparerR2", "WzComparerR2\WzComparerR2\WzComparerR2.csproj", "{5E883BE2-2009-4517-8026-4B90DEB83884}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WzComparerR2.Avatar", "WzComparerR2\WzComparerR2.Avatar\WzComparerR2.Avatar.csproj", "{A0753218-2C58-4E4A-9017-A435D2E5F639}"
13 | EndProject
14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WzComparerR2.Common", "WzComparerR2\WzComparerR2.Common\WzComparerR2.Common.csproj", "{818060BC-404C-470A-94B3-5160716C5247}"
15 | EndProject
16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WzComparerR2.PluginBase", "WzComparerR2\WzComparerR2.PluginBase\WzComparerR2.PluginBase.csproj", "{FA74A2FD-0250-4182-845D-DD98D829B525}"
17 | EndProject
18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WzComparerR2.WzLib", "WzComparerR2\WzComparerR2.WzLib\WzComparerR2.WzLib.csproj", "{0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}"
19 | EndProject
20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WzComparerR2.MapRender", "WzComparerR2\WzComparerR2.MapRender\WzComparerR2.MapRender.csproj", "{11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapRender.Invoker", "MapRender.Invoker\MapRender.Invoker.csproj", "{C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}"
23 | EndProject
24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapleStory.Sampler", "MapleStory.Sampler\MapleStory.Sampler.csproj", "{6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}"
25 | EndProject
26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MapleStory.MachineLearningSampleGenerator", "MapleStory.MachineLearningSampleGenerator\MapleStory.MachineLearningSampleGenerator.csproj", "{5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}"
27 | EndProject
28 | Global
29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
30 | Debug|Any CPU = Debug|Any CPU
31 | Debug|x64 = Debug|x64
32 | Debug|x86 = Debug|x86
33 | Release|Any CPU = Release|Any CPU
34 | Release|x64 = Release|x64
35 | Release|x86 = Release|x86
36 | EndGlobalSection
37 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
38 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
40 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Debug|x64.ActiveCfg = Debug|Any CPU
41 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Debug|x64.Build.0 = Debug|Any CPU
42 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Debug|x86.ActiveCfg = Debug|Any CPU
43 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Debug|x86.Build.0 = Debug|Any CPU
44 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
45 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Release|Any CPU.Build.0 = Release|Any CPU
46 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Release|x64.ActiveCfg = Release|Any CPU
47 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Release|x64.Build.0 = Release|Any CPU
48 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Release|x86.ActiveCfg = Release|Any CPU
49 | {43F1F84E-0B40-4AAD-9BC9-B6100CAB1D4C}.Release|x86.Build.0 = Release|Any CPU
50 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
52 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Debug|x64.ActiveCfg = Debug|Any CPU
53 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Debug|x64.Build.0 = Debug|Any CPU
54 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Debug|x86.ActiveCfg = Debug|Any CPU
55 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Debug|x86.Build.0 = Debug|Any CPU
56 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
57 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Release|Any CPU.Build.0 = Release|Any CPU
58 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Release|x64.ActiveCfg = Release|Any CPU
59 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Release|x64.Build.0 = Release|Any CPU
60 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Release|x86.ActiveCfg = Release|Any CPU
61 | {54797F38-A12C-4202-92A4-1A3DDCE914B7}.Release|x86.Build.0 = Release|Any CPU
62 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Debug|Any CPU.Build.0 = Debug|Any CPU
64 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Debug|x64.ActiveCfg = Debug|x64
65 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Debug|x64.Build.0 = Debug|x64
66 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Debug|x86.ActiveCfg = Debug|x86
67 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Debug|x86.Build.0 = Debug|x86
68 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Release|Any CPU.ActiveCfg = Release|Any CPU
69 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Release|Any CPU.Build.0 = Release|Any CPU
70 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Release|x64.ActiveCfg = Release|x64
71 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Release|x64.Build.0 = Release|x64
72 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Release|x86.ActiveCfg = Release|x86
73 | {5E883BE2-2009-4517-8026-4B90DEB83884}.Release|x86.Build.0 = Release|x86
74 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
75 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Debug|Any CPU.Build.0 = Debug|Any CPU
76 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Debug|x64.ActiveCfg = Debug|Any CPU
77 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Debug|x64.Build.0 = Debug|Any CPU
78 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Debug|x86.ActiveCfg = Debug|Any CPU
79 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Debug|x86.Build.0 = Debug|Any CPU
80 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Release|Any CPU.ActiveCfg = Release|Any CPU
81 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Release|Any CPU.Build.0 = Release|Any CPU
82 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Release|x64.ActiveCfg = Release|Any CPU
83 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Release|x64.Build.0 = Release|Any CPU
84 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Release|x86.ActiveCfg = Release|Any CPU
85 | {A0753218-2C58-4E4A-9017-A435D2E5F639}.Release|x86.Build.0 = Release|Any CPU
86 | {818060BC-404C-470A-94B3-5160716C5247}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
87 | {818060BC-404C-470A-94B3-5160716C5247}.Debug|Any CPU.Build.0 = Debug|Any CPU
88 | {818060BC-404C-470A-94B3-5160716C5247}.Debug|x64.ActiveCfg = Debug|Any CPU
89 | {818060BC-404C-470A-94B3-5160716C5247}.Debug|x64.Build.0 = Debug|Any CPU
90 | {818060BC-404C-470A-94B3-5160716C5247}.Debug|x86.ActiveCfg = Debug|Any CPU
91 | {818060BC-404C-470A-94B3-5160716C5247}.Debug|x86.Build.0 = Debug|Any CPU
92 | {818060BC-404C-470A-94B3-5160716C5247}.Release|Any CPU.ActiveCfg = Release|Any CPU
93 | {818060BC-404C-470A-94B3-5160716C5247}.Release|Any CPU.Build.0 = Release|Any CPU
94 | {818060BC-404C-470A-94B3-5160716C5247}.Release|x64.ActiveCfg = Release|Any CPU
95 | {818060BC-404C-470A-94B3-5160716C5247}.Release|x64.Build.0 = Release|Any CPU
96 | {818060BC-404C-470A-94B3-5160716C5247}.Release|x86.ActiveCfg = Release|Any CPU
97 | {818060BC-404C-470A-94B3-5160716C5247}.Release|x86.Build.0 = Release|Any CPU
98 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
99 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Debug|Any CPU.Build.0 = Debug|Any CPU
100 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Debug|x64.ActiveCfg = Debug|Any CPU
101 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Debug|x64.Build.0 = Debug|Any CPU
102 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Debug|x86.ActiveCfg = Debug|Any CPU
103 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Debug|x86.Build.0 = Debug|Any CPU
104 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Release|Any CPU.ActiveCfg = Release|Any CPU
105 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Release|Any CPU.Build.0 = Release|Any CPU
106 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Release|x64.ActiveCfg = Release|Any CPU
107 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Release|x64.Build.0 = Release|Any CPU
108 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Release|x86.ActiveCfg = Release|Any CPU
109 | {FA74A2FD-0250-4182-845D-DD98D829B525}.Release|x86.Build.0 = Release|Any CPU
110 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
111 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
112 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Debug|x64.ActiveCfg = Debug|Any CPU
113 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Debug|x64.Build.0 = Debug|Any CPU
114 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Debug|x86.ActiveCfg = Debug|Any CPU
115 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Debug|x86.Build.0 = Debug|Any CPU
116 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
117 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Release|Any CPU.Build.0 = Release|Any CPU
118 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Release|x64.ActiveCfg = Release|Any CPU
119 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Release|x64.Build.0 = Release|Any CPU
120 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Release|x86.ActiveCfg = Release|Any CPU
121 | {0E9801FD-44A2-4AF8-AE91-D6E74BAD56B2}.Release|x86.Build.0 = Release|Any CPU
122 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
123 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Debug|Any CPU.Build.0 = Debug|Any CPU
124 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Debug|x64.ActiveCfg = Debug|Any CPU
125 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Debug|x64.Build.0 = Debug|Any CPU
126 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Debug|x86.ActiveCfg = Debug|Any CPU
127 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Debug|x86.Build.0 = Debug|Any CPU
128 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Release|Any CPU.ActiveCfg = Release|Any CPU
129 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Release|Any CPU.Build.0 = Release|Any CPU
130 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Release|x64.ActiveCfg = Release|Any CPU
131 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Release|x64.Build.0 = Release|Any CPU
132 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Release|x86.ActiveCfg = Release|Any CPU
133 | {11E362E4-B8FB-4BD4-B0D3-BA078D5FB002}.Release|x86.Build.0 = Release|Any CPU
134 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
135 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
136 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Debug|x64.ActiveCfg = Debug|Any CPU
137 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Debug|x64.Build.0 = Debug|Any CPU
138 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Debug|x86.ActiveCfg = Debug|Any CPU
139 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Debug|x86.Build.0 = Debug|Any CPU
140 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
141 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Release|Any CPU.Build.0 = Release|Any CPU
142 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Release|x64.ActiveCfg = Release|Any CPU
143 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Release|x64.Build.0 = Release|Any CPU
144 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Release|x86.ActiveCfg = Release|Any CPU
145 | {C28EA64C-2C4C-40CE-8FF9-EF74E50CEA5B}.Release|x86.Build.0 = Release|Any CPU
146 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
147 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
148 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Debug|x64.ActiveCfg = Debug|Any CPU
149 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Debug|x64.Build.0 = Debug|Any CPU
150 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Debug|x86.ActiveCfg = Debug|Any CPU
151 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Debug|x86.Build.0 = Debug|Any CPU
152 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
153 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Release|Any CPU.Build.0 = Release|Any CPU
154 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Release|x64.ActiveCfg = Release|Any CPU
155 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Release|x64.Build.0 = Release|Any CPU
156 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Release|x86.ActiveCfg = Release|Any CPU
157 | {6B6E65DC-B238-4925-A4FC-6C1DEC2920F5}.Release|x86.Build.0 = Release|Any CPU
158 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
159 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Debug|Any CPU.Build.0 = Debug|Any CPU
160 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Debug|x64.ActiveCfg = Debug|Any CPU
161 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Debug|x64.Build.0 = Debug|Any CPU
162 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Debug|x86.ActiveCfg = Debug|Any CPU
163 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Debug|x86.Build.0 = Debug|Any CPU
164 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Release|Any CPU.ActiveCfg = Release|Any CPU
165 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Release|Any CPU.Build.0 = Release|Any CPU
166 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Release|x64.ActiveCfg = Release|Any CPU
167 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Release|x64.Build.0 = Release|Any CPU
168 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Release|x86.ActiveCfg = Release|Any CPU
169 | {5DD2E446-DCD0-4104-A3F2-EDB6941A0B34}.Release|x86.Build.0 = Release|Any CPU
170 | EndGlobalSection
171 | GlobalSection(SolutionProperties) = preSolution
172 | HideSolutionNode = FALSE
173 | EndGlobalSection
174 | GlobalSection(ExtensibilityGlobals) = postSolution
175 | SolutionGuid = {58DAE377-DCA8-48A9-88E3-A8C9AE2197DB}
176 | EndGlobalSection
177 | EndGlobal
178 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MapleStoryDetectionSampleGenerator
2 | Generate Machine Learning Samples Object Detection In MapleStory
3 | 
4 |
5 |
6 |
7 | # Performance
8 | This generator can generate arbitrarily many annotated samples. All bounding boxes are precisely annotated based on rendering coordinates.
9 |
10 | With [YOLOv4](https://github.com/AlexeyAB/darknet/blob/master/cfg/yolov4-custom.cfg) and ~5000 samples, it can achieve 99.8%mAP in test set.
11 |
12 |
13 | 
14 |
15 | # Requirement
16 | * Visual Studio 2022 v17.8 or above, with .NET workload installed
17 | * .NET 8.0 SDK (8.0.0 or above)
18 |
19 | # Build
20 | 1. Clone this repository with submodules by `git clone --recursive git@github.com:charlescao460/MapleStoryDetectionSampleGenerator.git`. Note that `--recursive` is necessary.
21 | 2. Build `WzComparerR2/WzComparerR2.sln` (submodule MUST be built first)
22 | 3. Build `MapleStoryDetectionSampleGenerator.sln`
23 | 4. Run `MapleStory.MachineLearningSampleGenerator\bin\Release\net8.0-windows7.0\WzComparerR2.exe`. Running `WzComparerR2.exe` will generate `Setting.config`, which is required for our MapRender.
24 |
25 | # Run
26 | (Assuming assemblies are built with `Release` configuration. `Debug` configuration is similar)
27 | 1. Cd into executable directory: `cd MapleStory.MachineLearningSampleGenerator\bin\Release\net8.0-windows7.0`
28 | 2. Use `WzComparerR2.exe` to find the desired map you want to sample. Assuming `993134200.img` is the map you want in Limina.
29 | 3. Prepare your player PNGs in a directory. Since WzComparerR2 does not have Avatar supported inside MapRender, we have to draw player images in our post-processing steps. Player images should be transparent PNGs with only the player's appearance. You can get these PNGs by Photoshop or save from WzComparerR2's Avatar plugin. Assuming `.\players` is the directory containing all images
30 | 4. Run ```.\MapleStory.MachineLearningSampleGenerator.exe -m 993134200 -x 5 -y 5 -f coco -o ".\output" --post --players ".\players"```
31 | This means run the sampler in map 993134200.img with every 5 pixels in X and every 5 pixels in Y, outputing COCO format, and drawing players in post processor.
32 | You can run `.\MapleStory.MachineLearningSampleGenerator.exe --help` for usage hint. Also you can take a look of the entrypoint [Program.cs](https://github.com/charlescao460/MapleStoryDetectionSampleGenerator/blob/main/MapleStory.MachineLearningSampleGenerator/Program.cs)
33 |
34 | # Note
35 | * Since NPCs look like players, including them without annotation could result a negative effect on our model. If you want to hide all NPCs from generated samples, simply change [WzComparerR2.MapRender/MapData.cs](https://github.com/Kagamia/WzComparerR2/blob/main/WzComparerR2.MapRender/MapData.cs) to prevent any NPC data loaded into map render.
36 |
37 | # Output Formats
38 | ## Tensorflow TFRecord
39 | According to Tensorflow [official document](https://www.tensorflow.org/tutorials/load_data/tfrecord#tfrecords_format_details), the output .tfrecord contains multiple [tf.train.Example](https://www.tensorflow.org/api_docs/python/tf/train/Example) in single file. With each example store in the following formats:
40 |
41 | ```
42 | uint64 length
43 | uint32 masked_crc32_of_length
44 | byte data[length]
45 | uint32 masked_crc32_of_data
46 | ```
47 | And
48 | ```
49 | masked_crc = ((crc >> 15) | (crc << 17)) + 0xa282ead8ul
50 | ```
51 | Each `tf.train.Example` is generated by [protobuf-net](https://github.com/protobuf-net/protobuf-net) according to Tensorflow [example.proto](https://github.com/tensorflow/tensorflow/blob/master/tensorflow/core/example/example.proto)
52 |
53 | ## Darknet
54 | Output directory structure:
55 | ```
56 | data/
57 | |---obj/
58 | | |---1.jpg
59 | | |---1.txt
60 | | |---......
61 | |---obj.data
62 | |---obj.names
63 | |---test.txt
64 | |---train.txt
65 | ```
66 | `obj.data` contains
67 | ```
68 | classes=2
69 | train=data/train.txt
70 | valid=data/test.txt
71 | names=data/obj.names
72 | backup = backup/
73 | ```
74 | And `obj.names` contains the class name for object. `test.txt` and `train.txt` contains samples for testing/training with ratio of 5:95 (5% of images in `obj/` are used for testing).
75 |
76 | ## COCO
77 | Output directory structure:
78 | ```
79 | coco/
80 | |---train2017/
81 | | |---1.jpg
82 | | |---2.jpg
83 | | |---......
84 | |---val2017/
85 | | |---1000.jpg
86 | | |---1001.jpg
87 | | |---......
88 | |---annotations/
89 | | |---instances_train2017.json
90 | | |---instances_val2017.json
91 | ```
92 | The COCO json is defined as following:
93 | ```json
94 | {
95 | "info": {
96 | "description": "MapleStory 993134100.img Object Detection Samples - Training",
97 | "url": "https://github.com/charlescao460/MapleStoryDetectionSampleGenerator",
98 | "version": "1.0",
99 | "year": 2021,
100 | "contributor": "CSR"
101 | },
102 | "licenses": [
103 | {
104 | "url": "https://github.com/charlescao460/MapleStoryDetectionSampleGenerator/blob/main/LICENSE",
105 | "id": 1,
106 | "name": "MIT License"
107 | }
108 | ],
109 | "images": [
110 | {
111 | "license": 1,
112 | "file_name": "30a892e1-7f3d-4c65-bdd1-9d28f1ae5187.jpg",
113 | "coco_url": "",
114 | "height": 768,
115 | "width": 1366,
116 | "flickr_url": "",
117 | "id": 1
118 | },
119 | ...],
120 | "categories": [
121 | {
122 | "supercategory": "element",
123 | "id": 1,
124 | "name": "Mob"
125 | },
126 | {
127 | "supercategory": "element",
128 | "id": 2,
129 | "name": "Player"
130 | }
131 | ],
132 | "annotations": [
133 | {
134 | "segmentation": [
135 | [
136 | 524,
137 | 429,
138 | 664,
139 | 429,
140 | 664,
141 | 578,
142 | 524,
143 | 578
144 | ]
145 | ],
146 | "area": 20860,
147 | "iscrowd": 0,
148 | "image_id": 1,
149 | "bbox": [
150 | 524,
151 | 429,
152 | 140,
153 | 149
154 | ],
155 | "category_id": 1,
156 | "id": 1
157 | },
158 | ...]
159 | ```
160 | Note that `segmentation` covers the area as the same as `bbox` does. No segmentation or masked implemented .
161 |
--------------------------------------------------------------------------------
/pictures/chart_yolov4-custom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlescao460/MapleStoryDetectionSampleGenerator/bbbe97ba5fe52733ab0813d018f984fbb77d1838/pictures/chart_yolov4-custom.png
--------------------------------------------------------------------------------
/pictures/result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/charlescao460/MapleStoryDetectionSampleGenerator/bbbe97ba5fe52733ab0813d018f984fbb77d1838/pictures/result.png
--------------------------------------------------------------------------------