Skip to content

Commit

Permalink
Move WR and 1st clear template checks to Clear screen event, replaced…
Browse files Browse the repository at this point in the history
… worldrecord test frame with one generated from clear screen event.
  • Loading branch information
underscore-zi committed Jul 29, 2019
1 parent 1d9b741 commit f3a7203
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 16 deletions.
49 changes: 40 additions & 9 deletions MarioMaker2OCR/Form1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,22 @@ public partial class Form1 : Form
new Rectangle(new Point(400,275), new Size(230, 65)), //Pause Menu
new Rectangle(new Point(195,325), new Size(215, 60)), //Clear Screen
}),
new EventTemplate("./templates/480/worldrecord.png", "worldrecord", 0.8, new Rectangle[] {
new Rectangle(new Point(400,175), new Size(200, 55)),
}),
new EventTemplate("./templates/480/firstclear.png", "firstclear", 0.8, new Rectangle[] {
new Rectangle(new Point(400,175), new Size(200, 55)),
}),
new EventTemplate("./templates/480/death_big.png", "death", 0.8),
new EventTemplate("./templates/480/death_small.png", "death", 0.8),
new EventTemplate("./templates/480/death_partial.png", "death", 0.9),
new EventTemplate("./templates/480/gameover.png", "gameover", 0.8)
};

private readonly EventTemplate[] clearTemplates = new EventTemplate[]
{
new EventTemplate("./templates/480/worldrecord.png", "worldrecord", 0.3, new Rectangle[] {
new Rectangle(new Point(400,175), new Size(200, 55)),
}),
new EventTemplate("./templates/480/firstclear.png", "firstclear", 0.3, new Rectangle[] {
new Rectangle(new Point(400,175), new Size(200, 55)),
}),
};

public DsDevice SelectedDevice => (deviceComboBox.SelectedItem as dynamic)?.Value;
public Size SelectedResolution => (resolutionsCombobox.SelectedItem as dynamic)?.Value;

Expand Down Expand Up @@ -211,12 +215,39 @@ private void unlockForm()
/// </summary>
private void VideoProcessor_ClearScreen(object sender, VideoProcessor.VideoEventArgs e)
{
previewer.SetLastMatch(e.currentFrame);
log.Debug("Detected Level Clear");
SMMServer.BroadcastEvent("clear");

}
Image<Gray, byte> grayscaleFrame = e.currentFrame.Mat.ToImage<Gray, byte>().Resize(640, 480, Inter.Cubic);
//e.currentFrame.Save("clearmatch_" + DateTime.Now.ToString("yyyyMMddHHmmssffff") + ".png");


Dictionary<String, bool> events = new Dictionary<String, bool>
{
{ "worldrecord", false },
{ "firstclear", false },
};
List<Rectangle> boundaries = new List<Rectangle>();
foreach (EventTemplate tmpl in clearTemplates)
{
if (events[tmpl.eventType]) continue;
Point loc = tmpl.getLocation(grayscaleFrame);
if (!loc.IsEmpty)
{
events[tmpl.eventType] = true;
boundaries.Add(ImageLibrary.ChangeSize(new Rectangle(loc.X, loc.Y, tmpl.template.Width, tmpl.template.Height), grayscaleFrame.Size, e.currentFrame.Size));
previewer.SetLastMatch(e.currentFrame, boundaries.ToArray());
}
}

foreach (var evt in events)
{
if (evt.Value)
{
log.Info(String.Format("Detected {0}.", evt.Key));
SMMServer.BroadcastEvent(evt.Key);
}
}
}
/// <summary>
/// Event Callback for the Black Screen event generated by the VideoProcessor
/// </summary>
Expand Down
33 changes: 33 additions & 0 deletions MarioMaker2OCR/Objects/EventTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class EventTemplate : IDisposable
public double threshold { get; }
public string eventType { get; }
public string filename { get; }
public Rectangle[] regions { get; }

bool disposed = false;
public void Dispose()
Expand Down Expand Up @@ -42,8 +43,40 @@ public EventTemplate(string fn, string type, double thresh)
filename = fn;
}

public EventTemplate(string fn, string type, double thresh, Rectangle[] ROIs)
{

template = new Image<Gray, byte>(fn);
threshold = thresh;
eventType = type;
filename = fn;
regions = ROIs;
}

public Point getLocation(Image<Gray, byte> frame)
{
if(regions == null || regions.Length == 0)
{
return getLocation(frame, Rectangle.Empty);
} else
{
foreach(Rectangle roi in regions)
{
Point ret = getLocation(frame, roi);
if (!ret.IsEmpty)
{
ret.X += roi.X;
ret.Y += roi.Y;
return ret;
}
}
}
return Point.Empty;
}

public Point getLocation(Image<Gray, byte> frame, Rectangle roi)
{
frame.ROI = roi;
Image<Gray, float> match = frame.MatchTemplate(template, Emgu.CV.CvEnum.TemplateMatchingType.CcoeffNormed);
match.MinMax(out _, out double[] max, out _, out Point[] maxLoc);
if (max[0] < threshold) return Point.Empty;
Expand Down
68 changes: 61 additions & 7 deletions MarioMaker2OCR/Test/TemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,38 @@
using Emgu.CV.Structure;
using MarioMaker2OCR.Objects;
using System.IO;
using System.Drawing;

namespace MarioMaker2OCR.Test
{
[TestClass]
public class TemplateTests
{
public static readonly string frameDir = "./Test/testdata/frames";
public static readonly EventTemplate[] templates = new EventTemplate[] {
private readonly EventTemplate[] templates = new EventTemplate[] {
new EventTemplate("./templates/480/exit.png", "exit", 0.8, new Rectangle[] {
new Rectangle(new Point(400,330), new Size(230, 65)), //Pause Menu
new Rectangle(new Point(410,325), new Size(215, 60)), //Clear Screen
}),
new EventTemplate("./templates/480/quit.png", "exit", 0.9),
new EventTemplate("./templates/480/startover.png", "restart", 0.8, new Rectangle[] {
new Rectangle(new Point(400,275), new Size(230, 65)), //Pause Menu
new Rectangle(new Point(195,325), new Size(215, 60)), //Clear Screen
}),
new EventTemplate("./templates/480/death_big.png", "death", 0.8),
new EventTemplate("./templates/480/death_small.png", "death", 0.8),
new EventTemplate("./templates/480/death_partial.png", "death", 0.9),
new EventTemplate("./templates/480/exit.png", "exit", 0.8),
new EventTemplate("./templates/480/quit.png", "exit", 0.9),
new EventTemplate("./templates/480/startover.png", "restart", 0.8),
new EventTemplate("./templates/480/gameover.png", "gameover", 0.8)
};

private readonly EventTemplate[] clearTemplates = new EventTemplate[]
{
new EventTemplate("./templates/480/worldrecord.png", "worldrecord", 0.3, new Rectangle[] {
new Rectangle(new Point(400,175), new Size(200, 55)),
}),
new EventTemplate("./templates/480/firstclear.png", "firstclear", 0.3, new Rectangle[] {
new Rectangle(new Point(400,175), new Size(200, 55)),
}),
};

[TestMethod]
Expand Down Expand Up @@ -163,6 +181,45 @@ public void DetectsExit()
}
}
}
[TestMethod]
public void DetectWorldRecord()
{
string[] files = new string[]
{
"/1080/worldrecord.png",
};
foreach (var t in clearTemplates)
{
if (!t.filename.EndsWith("worldrecord.png")) continue;
foreach (var fn in files)
{
var frame = new Image<Gray, byte>(frameDir + fn).Resize(640, 480, Emgu.CV.CvEnum.Inter.Cubic);
Assert.IsFalse(t.getLocation(frame).IsEmpty);
var result = t.getLocation(frame);
Assert.IsFalse(result.IsEmpty, String.Format("Template {0} did not match {1}", t.filename, fn));
}
}
}

[TestMethod]
public void DetectFirstClear()
{
string[] files = new string[]
{
"/1080/firstclear.png",
};
foreach (var t in clearTemplates)
{
if (!t.filename.EndsWith("firstclear.png")) continue;
foreach (var fn in files)
{
var frame = new Image<Gray, byte>(frameDir + fn).Resize(640, 480, Emgu.CV.CvEnum.Inter.Cubic);
Assert.IsFalse(t.getLocation(frame).IsEmpty);
var result = t.getLocation(frame);
Assert.IsFalse(result.IsEmpty, String.Format("Template {0} did not match {1}", t.filename, fn));
}
}
}

[TestMethod]
public void ReturnsEmptyOnGameplay()
Expand All @@ -178,8 +235,5 @@ public void ReturnsEmptyOnGameplay()
}
}
}

// TODO: Detects Quit (Endless-mode)
// TODO: False-positive testcases
}
}
Binary file modified MarioMaker2OCR/Test/testdata/frames/1080/worldrecord.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f3a7203

Please sign in to comment.