OMR Scanner Software using C#.NET

Optical Mark Recognition (OMR) is an automated process of capturing and analyzing data marked on a special type of document form. This special type of document could be marked/ filled by people on survey forms, test sheets, and other paper documents. In this article, we will learn how to develop a GUI-based OMR Sheet Reader application using C#. Our solution will take the scanned OMR sheet image as input from a local disk, then recognize the marks and finally export the marked registration number and shaded answers in CSV format. After following the mentioned steps, we will have our C# Optical Mark Recognition (OMR) Software in .NET. So let’s begin.

The article shall cover the following topics:

  1. Features of C# Optical Mark Recognition (OMR) Software
  2. C# OMR .NET API and UI Control
  3. Steps to Develop C# OMR Software
  4. C# Optical Mark Recognition (OMR) Software Demo
  5. Download OMR Software Source Code

Features of C# Optical Mark Recognition (OMR) Software

Our Optical Mark Recognition (OMR) Software will have the following features:

  1. Interactively adjust recognition parameters and watch their effect in real-time. We can adjust the following:
    • Recognition threshold
    • Zoom
    • Show/hide bubbles
  2. Select and load the scanned image in the following formats:
  3. Recognize the optical marks on the image.
  4. Export results in CSV and save them to your local disk.

C# OMR .NET API and UI Control

Aspose.OMR for .NET API allows designing, creating, and recognizing answer sheets, tests, MCQ papers, quizzes, feedback forms, surveys, and ballots. Moreover, it provides a graphical user interface control that can be added to .NET UI applications. We will integrate Aspose.OMR for .NET UI control in the .NET UI application for developing an OMR scanner/reader application. Please either download the DLL of the API or install it using NuGet.

PM> Install-Package Aspose.OMR

Steps to Develop C# OMR Software

We can develop a GUI-based OMR scanner/reader application by following the steps given below:

  • Firstly, create a new project and select the WPF App (.NET Framework) project template.
Create a new project and select the project template.

Create a new project and select the project template.

  • Next, in Configure your new project dialog, enter the Project name, choose the Location, and set other parameters.
Configure your WPF App Project

Configure your WPF App Project

  • Then, open NuGet Package Manager and install Aspose.OMR for .NET package.
Install Aspose.OMR for .NET

Install Aspose.OMR for .NET

  • Next, add a new file DialogHelper.cs to the project.
Add DialogHelper class

Add DialogHelper.cs

  • Add the following code to the newly created DialogHelper.cs.
internal class DialogHelper
{
/// <summary>
/// The filter string for the dialog that opens template images.
/// </summary>
private static readonly string ImageFilesFilterPrompt = "Image files |*.jpg; *.jpeg; *.png; *.gif; *.tif; *.tiff;";
/// <summary>
/// The filter string for the dialog that saves recognition results
/// </summary>
private static readonly string DataExportFilesFilterPrompt = "Comma-Separated Values (*.csv)" + " | *.csv";
/// <summary>
/// Shows Open Image file dialog.
/// </summary>
/// <returns>Path to selected file, or <c>null</c> if no file was selected.</returns>
public static string ShowOpenImageDialog(string suggestedDir = null)
{
OpenFileDialog dialog = new OpenFileDialog();
return ShowDialog(dialog, ImageFilesFilterPrompt, suggestedDir);
}
/// <summary>
/// Shows Save Recognition Results file dialog.
/// </summary>
/// <returns>Path to selected file, or <c>null</c> if no file was selected.</returns>
public static string ShowSaveDataDialog(string suggestedName)
{
SaveFileDialog dialog = new SaveFileDialog();
return ShowDialog(dialog, DataExportFilesFilterPrompt, suggestedName);
}
/// <summary>
/// Displays given dialog and returns its result as a <c>string</c>.
/// </summary>
/// <param name="dialog">The dialog to show.</param>
/// <param name="filter">File type filter string.</param>
/// <param name="suggestedDir">Suggested dialog initial directory</param>
/// <param name="suggestedName">Suggested file name</param>
/// <returns>Path to selected file, or <c>null</c> if no file was selected.</returns>
private static string ShowDialog(FileDialog dialog, string filter, string suggestedDir = null, string suggestedName = null)
{
string fileName = null;
dialog.Filter = filter;
dialog.RestoreDirectory = true;
if (suggestedName != null)
{
dialog.FileName = suggestedName;
}
if (suggestedDir != null)
{
dialog.InitialDirectory = suggestedDir;
}
bool? result = dialog.ShowDialog();
if (result == true)
{
fileName = dialog.FileName;
}
return fileName;
}
}
  • Next, update MainWindow.xaml file with the following XAML content.
<Window x:Class="OMR_APP.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OMR_APP"
mc:Ignorable="d"
Title="Aspose OMR Demo" Height="880" Width="1100">
<Grid Background="WhiteSmoke">
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ToolBar Grid.Row="0" Background="LightGray">
<TextBox Name="txtTemplatePath" Margin="5" Width="400" Height="30" Background="White"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
</TextBox>
<Button Margin="5" Width="100" Height="30" Background="White"
Content="Get control" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Click="GetButtonClicked"/>
<Separator/>
<Button Margin="5" Width="100" Height="30" Background="White"
Content="Select Image" Click="SelectImageClicked"/>
<Button Margin="5" Width="100" Height="30" Background="White"
Content="Recognize Image" Click="RecognizeImageClicked"/>
<Button Margin="5" Width="100" Height="30" Background="White"
Content="Export Results" Click="ExportResultsClicked"/>
</ToolBar>
<ContentControl Grid.Row="1" x:Name="CustomContentControl"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Window>
  • After that, replace the following content in MainWindow.xaml.cs file.
/// <summary>
/// Template for testing
/// </summary>
private static readonly string TemplateFilePath = @"C:\Files\OMR\Sheet.omr";
/// <summary>
/// Path to the license Aspose.OMR.NET.lic file
/// </summary>
private static readonly string LicensePath = @"";
private CorrectionControl control;
public MainWindow()
{
InitializeComponent();
// Set and show template file path
txtTemplatePath.Text = TemplateFilePath;
// Set license, provide License file Path and uncomment to test full results
//License lic = new License();
//lic.SetLicense(LicensePath);
}
public string UserImagePath { get; set; }
public string DataFolderPath { get; set; }
/// <summary>
/// Loads and displays CorrectionControl
/// </summary>
private void GetButtonClicked(object sender, RoutedEventArgs e)
{
string path = txtTemplatePath.Text;
try
{
OmrEngine engine = new OmrEngine();
TemplateProcessor processor = engine.GetTemplateProcessor(path);
control = engine.GetCorrectionControl(processor);
CustomContentControl.Content = control;
control.Initialize();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message,"Exception");
}
}
/// <summary>
/// Select and display image
/// </summary>
private void SelectImageClicked(object sender, RoutedEventArgs e)
{
if (control == null)
{
return;
}
string imagePath = DialogHelper.ShowOpenImageDialog(this.DataFolderPath);
if (string.IsNullOrEmpty(imagePath))
{
return;
}
this.UserImagePath = imagePath;
control.LoadAndDisplayImage(imagePath);
}
/// <summary>
/// Recognize loaded image
/// </summary>
private void RecognizeImageClicked(object sender, RoutedEventArgs e)
{
if (control == null)
{
return;
}
control.RecognizeImage();
}
/// <summary>
/// Export results to CSV
/// </summary>
private void ExportResultsClicked(object sender, RoutedEventArgs e)
{
if (control == null)
{
return;
}
string imageName = Path.GetFileNameWithoutExtension(this.UserImagePath);
string path = DialogHelper.ShowSaveDataDialog(imageName);
if (string.IsNullOrEmpty(path))
{
return;
}
control.ExportResults(path);
MessageBox.Show("The exported resultant CSV file can be found here : " + path, "Operation Successful");
}
  • Finally, run the application.

C# Optical Mark Recognition (OMR) Software Demo

The following is the demonstration of the OMR Scanner/Reader application we have just created.

C# OMR Software Demo

OMR Software Demo

Download C# .NET OMR Software Source Code

You can download the complete source code of the C# OMR Scanner application from GitHub.

Get a Free License

You can get a free temporary license to try the library without evaluation limitations.

Conclusion

In this article, we have learned how to

  • integrate Aspose.OMR for .NET UI control in the .NET application;
  • develop OMR sheet reader application in C#.

Besides, you can learn more about Aspose.OMR for .NET API using the documentation. In case of any ambiguity, please feel free to contact us on our forum.

See Also