Skip to content

Commit

Permalink
fix bad results for bad binary scans
Browse files Browse the repository at this point in the history
  • Loading branch information
o-tho committed Dec 7, 2024
1 parent eccade9 commit 57e1124
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 18 deletions.
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Automatically grade MCQ exams using optical mark recognition

Autograder allows you to automatically grade MCQ exams. It is written in pure
`autograder` allows you to automatically grade MCQ exams. It is written in pure
rust and runs both in the command line and the modern web browsers using wasm.

## Installation
Expand Down Expand Up @@ -108,23 +108,27 @@ triggering an update in the view. You can always look into the developer
console, which has a rather verbose output to what is happening in the background.

Afterwards you can download a zip file containing a CSV file with all the
results and conveniently named reports like this ![example report](assets/sample_report.png).
results and conveniently named reports like this:

![example report](assets/sample_report.png)

These image reports were the main motivation to develop this software: using OMR
will always be a bit error-prone, especially when students use the bubble sheet
in ways that is not intended.

A green circle means that autograder thinks that the selected bubble is the
A green circle means that `autograder` thinks that the selected bubble is the
circled one, which according to the key is correct. A red circle shows the
correct answer, meaning that the selected bubble is elsewhere.
correct answer, meaning that the selected bubble is elsewhere. An orange box
indicates that `autograder` was not sure how to understand the choice and that
manual grading is indicated.

### Using autograder from a mobile device

If you only want to use autograder to grade a handful of bubble sheets, you can
If you only want to use `autograder` to grade a handful of bubble sheets, you can
do it like this:

First, on a device with a large display, navigate to *Create Magic Link*. Here
you can upload a key and a template and autograder generates a _very_ long link.
you can upload a key and a template and `autograder` generates a _very_ long link.
This link encodes all the template and key data and can be shared with anyone --
most importantly yourself for usage on a mobile device. Bookmark that
very long link with a descriptive name like "Stat101 Test 1" on your mobile device
Expand Down
Binary file modified assets/filled_out_example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/sample_report.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 7 additions & 11 deletions src/image_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ pub fn fax_to_grayimage(data: &[u8], width: u32, height: u32) -> GrayImage {
y += 1;
});

// we don't trust binary images and erode them first
imageproc::morphology::erode_mut(&mut result, imageproc::distance_transform::Norm::L1, 1);
result
}
pub fn binary_image_from_image(img: DynamicImage) -> GrayImage {
Expand Down Expand Up @@ -164,29 +166,23 @@ pub fn rgb_to_egui_color_image(image: &RgbImage) -> egui::ColorImage {
}

pub fn create_error_image(error_text: &str) -> GrayImage {
// Create a new 400x300 grayscale image
let mut image = GrayImage::new(800, 300);
let mut image = GrayImage::new(1200, 300);

// Fill with light gray background
for pixel in image.pixels_mut() {
*pixel = image::Luma([240u8]);
*pixel = image::Luma([255u8]);
}

// Load font from binary data embedded in the executable
let font_data = crate::typst_helpers::BIOLINUM_BOLD;
let font = ab_glyph::FontArc::try_from_slice(font_data).expect("Error loading font");

// Configure font scale (size)
let scale = ab_glyph::PxScale::from(30.0);

// Calculate text position
let x = 20; // Padding from left
let y = 150; // Vertically centered
let x = 20;
let y = 150;

// Draw the error text
imageproc::drawing::draw_text_mut(
&mut image,
image::Luma([50u8]), // Dark gray text
image::Luma([0u8]),
x,
y,
scale,
Expand Down
16 changes: 15 additions & 1 deletion src/template_scan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,21 @@ impl<'a> TemplateScan<'a> {

pub fn set_transformation(&mut self) {
let trafo = self.find_transformation();
self.transformation = trafo;

if trafo.is_some() {
self.transformation = trafo;
return;
}

// if this did not work, then in all cases known to us the image had too
// much white noise, so we erode it
imageproc::morphology::erode_mut(
&mut self.scan.image,
imageproc::distance_transform::Norm::L1,
1,
);

self.transformation = self.find_transformation();
}

pub fn find_transformation(&self) -> Option<Transformation> {
Expand Down

0 comments on commit 57e1124

Please sign in to comment.