diff --git a/docs/For Contributors.md b/docs/For Contributors.md index 885c3e57..244250bc 100644 --- a/docs/For Contributors.md +++ b/docs/For Contributors.md @@ -1,34 +1,34 @@ -### Links - -- [Contributor Guide](https://github.com/Udayraj123/OMRChecker/blob/master/CONTRIBUTING.md) -- [Project Ideas List](https://github.com/users/Udayraj123/projects/2/views/1) -- [Legacy TODOs List](./TODOs) - -### ๐ŸŽฏ What can contributors get from this project? -In order of beginner to expert level: - -๐Ÿ”ฐ Great learning experience to up your game in computer vision. - -๐Ÿ”ฐ Intro to Opensource, Working with github - -๐Ÿ”ฐ Visually rich outputs - for learning easily what is happening under the hood. - -๐Ÿ”ฐ Working on large code base with well documented code - -๐Ÿ”ฐ Dedicated Discord channel for doubts as well as open discussions - -๐Ÿ”ฐ Get access to the Android OMR helper application - -๐Ÿ”ฐ Learn Basics like developing an app like camscanner - -๐Ÿ”ฐ Structured way of presenting the working via images. - -๐Ÿ”ฐ Edge detection using OpenCV (used in self-driving vehicles/robot vision) - -๐Ÿ”ฐ Working with contours (OCR/self-driving vehicles) - -๐Ÿ”ฐ If your PR is accepted, this is a good project to add to your CV. - -๐Ÿ”ฐ Out of the box use cases of popular techniques : morphology, adaptive threshold, outlier detection, reverse perspective transform, etc. - -๐Ÿ”ฐ Example of when AI/ML cannot easily bring solution to your table, CV never fails to impress! +### Links + +- [Contributor Guide](https://github.com/Udayraj123/OMRChecker/blob/master/CONTRIBUTING.md) +- [Project Ideas List](https://github.com/users/Udayraj123/projects/2/views/1) +- [Legacy TODOs List](./TODOs) + +### ๐ŸŽฏ What can contributors get from this project? +In order of beginner to expert level: + +๐Ÿ”ฐ Great learning experience to up your game in computer vision. + +๐Ÿ”ฐ Intro to Opensource, Working with github + +๐Ÿ”ฐ Visually rich outputs - for learning easily what is happening under the hood. + +๐Ÿ”ฐ Working on large code base with well documented code + +๐Ÿ”ฐ Dedicated Discord channel for doubts as well as open discussions + +๐Ÿ”ฐ Get access to the Android OMR helper application + +๐Ÿ”ฐ Learn Basics like developing an app like camscanner + +๐Ÿ”ฐ Structured way of presenting the working via images. + +๐Ÿ”ฐ Edge detection using OpenCV (used in self-driving vehicles/robot vision) + +๐Ÿ”ฐ Working with contours (OCR/self-driving vehicles) + +๐Ÿ”ฐ If your PR is accepted, this is a good project to add to your CV. + +๐Ÿ”ฐ Out of the box use cases of popular techniques : morphology, adaptive threshold, outlier detection, reverse perspective transform, etc. + +๐Ÿ”ฐ Example of when AI/ML cannot easily bring solution to your table, CV never fails to impress! diff --git a/docs/Home.md b/docs/Home.md index d9e2c030..cfbdb143 100644 --- a/docs/Home.md +++ b/docs/Home.md @@ -1,34 +1,34 @@ -Welcome to the OMRChecker wiki! - - - -Click on one of the pages to get started. - -## v2 documentation (latest) - -
    -
  1. User Guide
  2. -
  3. About Templates
  4. - - -
- -## v1 documentation - -
    -
  1. User Guide
  2. -
  3. About Templates
  4. -
  5. Rich Visuals
  6. -
  7. Diagrams
  8. -
- -## Others - -
    -
  1. For Contributors
  2. -
  3. TODOs
  4. -
- - - +Welcome to the OMRChecker wiki! + + + +Click on one of the pages to get started. + +## v2 documentation (latest) + +
    +
  1. User Guide
  2. +
  3. About Templates
  4. + + +
+ +## v1 documentation + +
    +
  1. User Guide
  2. +
  3. About Templates
  4. +
  5. Rich Visuals
  6. +
  7. Diagrams
  8. +
+ +## Others + +
    +
  1. For Contributors
  2. +
  3. TODOs
  4. +
+ + + diff --git a/docs/TODOs.md b/docs/TODOs.md index 1d66c172..85a94348 100644 --- a/docs/TODOs.md +++ b/docs/TODOs.md @@ -1,123 +1,123 @@ -Interesting tasks available in the whole range from Beginner to Advanced level. -Looking for computer vision enthusiasts to take this project to the next level! - -## Project Progress -#### Current Goals Progress : -### ๐Ÿ”ฒ๐Ÿ”ฒ๐Ÿ”ฒ๐Ÿ”ฒ๐Ÿ”ฒโนโนโนโนโน - - - -## Legend: -๐Ÿ“ Beginner Quests - -๐Ÿ† Intermediate Challenges - -๐ŸŽ“ Advanced Experiments - -The tasks are ordered in increasing levels of difficulty below. - -## ToDos: - -๐Ÿ“ Run code on your own images and add it to [samples](#) - - -๐Ÿ“ Generate your own code-in-action gifs on those images - - -๐Ÿ“ Use cv2.putText to add appropriate labels to all output images - - -๐Ÿ“ Add validation checks for configuration - - -๐Ÿ“ Implement Accuracy evaluation -> There are 5-6 popular methods of evaluation available. We will be implementing all of them. See [this link](https://www.wikiwand.com/en/Multi-label_classification#/Statistics_and_evaluation_metrics). -> From above link, "Exact match" method is implemented in main.py (look for 'TEST_FILE'). -> Need help in implementing any of the remaining methods. -> The ultimate plan is to create a reliable benchmark score for evaluating future algorithms. -> For any discussion/doubts, ask on [discord](https://discord.gg/HKw6juP). - - - -๐Ÿ“ Contribute to Mobile images dataset using the [Android App](https://github.com/Udayraj123/AndroidOMRHelper)(Contact on [discord](https://discord.gg/HKw6juP)) - - -๐Ÿ“ Add coloroma to output texts - - - -๐Ÿ“ Product comparision articles: - Review and Compare with Existing OMR Softwares with this free software. For a start, see [Auto Multiple Choices](https://www.auto-multiple-choice.net/) - - - - - - -๐Ÿ† Generate Template Layout directly from blank OMR image. -> Use methods like [morphology](./extras/Progress/2019-04-26/images/align_correct.PNG) and then blob detection to find presence of bubbles in a good quality image of a blank OMR Sheet like [this one](./extras/Original%20OMRs/OMR_JUNIORS/OMR_JUNIORS_front.jpg). -> Illustrative image coming soon. - - - - -๐Ÿ† Auto Rotating the OMR sheet - -๐Ÿ† Calculate confidence using data -> Count times when only localTHR was used vs globalTHR over localTHR. -> Image contrast levels score. - -๐Ÿ† Extract code snippets which may be re-usable in other projects and add them to gist - - - -๐Ÿ† Put explanatory text on output images -> Show QBlock Labels and Column Orientations for template in the setLayout mode - - - -๐Ÿ† Improve page boundary detection : Defeat the [bossbg.jpg](./extras/Test/Backgrounds/bossbg.jpg) - -๐ŸŽ“ Refactor code to a software [design pattern](https://refactoring.guru/design-patterns/python) - - -๐ŸŽ“ Faster Speeds : parallelization, pyrDowns, etc - - - -๐ŸŽ“ Making more visualizations from available data. See [Visualization Ideas](#) for ideas on Flow diagrams, Animations and 3D outputs. - - - - - - - - - -๐ŸŽ“ Explore methods to find global threshold more accurately(perhaps using ML) - - - - - -๐ŸŽ“ Introduce git submodules - +Interesting tasks available in the whole range from Beginner to Advanced level. +Looking for computer vision enthusiasts to take this project to the next level! + +## Project Progress +#### Current Goals Progress : +### ๐Ÿ”ฒ๐Ÿ”ฒ๐Ÿ”ฒ๐Ÿ”ฒ๐Ÿ”ฒโนโนโนโนโน + + + +## Legend: +๐Ÿ“ Beginner Quests + +๐Ÿ† Intermediate Challenges + +๐ŸŽ“ Advanced Experiments + +The tasks are ordered in increasing levels of difficulty below. + +## ToDos: + +๐Ÿ“ Run code on your own images and add it to [samples](#) + + +๐Ÿ“ Generate your own code-in-action gifs on those images + + +๐Ÿ“ Use cv2.putText to add appropriate labels to all output images + + +๐Ÿ“ Add validation checks for configuration + + +๐Ÿ“ Implement Accuracy evaluation +> There are 5-6 popular methods of evaluation available. We will be implementing all of them. See [this link](https://www.wikiwand.com/en/Multi-label_classification#/Statistics_and_evaluation_metrics). +> From above link, "Exact match" method is implemented in main.py (look for 'TEST_FILE'). +> Need help in implementing any of the remaining methods. +> The ultimate plan is to create a reliable benchmark score for evaluating future algorithms. +> For any discussion/doubts, ask on [discord](https://discord.gg/HKw6juP). + + + +๐Ÿ“ Contribute to Mobile images dataset using the [Android App](https://github.com/Udayraj123/AndroidOMRHelper)(Contact on [discord](https://discord.gg/HKw6juP)) + + +๐Ÿ“ Add coloroma to output texts + + + +๐Ÿ“ Product comparision articles: + Review and Compare with Existing OMR Softwares with this free software. For a start, see [Auto Multiple Choices](https://www.auto-multiple-choice.net/) + + + + + + +๐Ÿ† Generate Template Layout directly from blank OMR image. +> Use methods like [morphology](./extras/Progress/2019-04-26/images/align_correct.PNG) and then blob detection to find presence of bubbles in a good quality image of a blank OMR Sheet like [this one](./extras/Original%20OMRs/OMR_JUNIORS/OMR_JUNIORS_front.jpg). +> Illustrative image coming soon. + + + + +๐Ÿ† Auto Rotating the OMR sheet + +๐Ÿ† Calculate confidence using data +> Count times when only localTHR was used vs globalTHR over localTHR. +> Image contrast levels score. + +๐Ÿ† Extract code snippets which may be re-usable in other projects and add them to gist + + + +๐Ÿ† Put explanatory text on output images +> Show QBlock Labels and Column Orientations for template in the setLayout mode + + + +๐Ÿ† Improve page boundary detection : Defeat the [bossbg.jpg](./extras/Test/Backgrounds/bossbg.jpg) + +๐ŸŽ“ Refactor code to a software [design pattern](https://refactoring.guru/design-patterns/python) + + +๐ŸŽ“ Faster Speeds : parallelization, pyrDowns, etc + + + +๐ŸŽ“ Making more visualizations from available data. See [Visualization Ideas](#) for ideas on Flow diagrams, Animations and 3D outputs. + + + + + + + + + +๐ŸŽ“ Explore methods to find global threshold more accurately(perhaps using ML) + + + + + +๐ŸŽ“ Introduce git submodules + \ No newline at end of file diff --git a/docs/[v1]-About-Templates.md b/docs/[v1]-About-Templates.md index 46a04d12..a921976c 100644 --- a/docs/[v1]-About-Templates.md +++ b/docs/[v1]-About-Templates.md @@ -1,144 +1,144 @@ -## Meaning of parameters used in the template.json file - -Let's take an example of sample1 from the [samples](https://github.com/Udayraj123/OMRChecker/tree/master/samples). - -You may use below commands from project root to load the sample template. -```bash -python3 main.py -i samples/sample1 --setLayout -``` -Which shows the template overlay like below - -

- Initial Layout -

- - -Let's examine the template.json used in this sample. -```js -{ - // The dimensions(width, height) to which the page will be resized to before applying template - "pageDimensions": [ - 1846, - 1500 - ], - // The dimensions of the overlay bubble area - "bubbleDimensions": [ - 40, - 40 - ], - // Custom configuration values to use in the template's directory - "preProcessors": [ - { - // Page boundary based cropping plugin - "name": "CropPage", - "options": { - // size of the kernel to use for cropping (default for most cases) - "morphKernel": [ - 10, - 10 - ] - } - }, - { - // Marker based cropping plugin - "name": "CropOnMarkers", - "options": { - // path to marker file - "relativePath": "omr_marker.jpg", - // a factor by which marker shall be resized relative to input image - "sheetToMarkerWidthRatio": 17 - } - } - ], - -// The customLabels contain fields that need to be joined together before generating the results sheet - "customLabels": { - // Here the "Roll" key corresponds to a single column in the results - "Roll": [ - "Medium", - "roll1", - "roll2", - "roll3", - "roll4", - "roll5", - "roll6", - "roll7", - "roll8", - "roll9" - ], - // customLabels can also be used for two-digit integer type questions. - "q5": [ - "q5_1", - "q5_2" - ], - // ... - }, - // Each rectangular box you see in the template overlay image is an item in fieldBlocks - "fieldBlocks": { - // ... - - // Create a block of MCQ type questions - - "MCQ_Block_Q1": { - // Here, QTYPE_MCQ4 is a built-in field type with four values ["A", "B", "C", "D"] - "fieldType": "QTYPE_MCQ4", - // The ordered questions in this block (length of this array is the number of questions in this block) - // A custom parser also recognises ["q1..4"] to implement the same list below - "fieldLabels": ["q1", "q2", "q3", "q4"], - // The gaps between the bubbles - "bubblesGap": 59, - // The gaps between the labels i.e. the mcq questions - "labelsGap": 50, - // The starting point of the block (top left) - "origin": [121, 860] - }, - - // This shows how integer type questions are created. - "Int_Block_Q5": { - // Here, QTYPE_INT is a built-in field type with vertical arrangement of 0-9 integers - "fieldType": "QTYPE_INT", - // These field labels will concatenated together as declared in customLabels earlier. - "fieldLabels": ["q5_1", "q5_2"], - // The gaps between the bubbles - "bubblesGap": 46, - // The gaps between the labels i.e. the two integer columns - "labelsGap": 60, - // The starting point of the block (top left) - "origin": [903, 282] - }, - - // A custom field - "Medium": { - // The starting point of the block (top left) - "origin": [ - 170, - 282 - ], - // The direction of the bubbles - "direction": "vertical", - // The gaps between the bubbles - "bubblesGap": 41, - // Custom values for the bubbles (length of this array is the number of bubbles per field) - "bubbleValues": ["E", "H"], - // The labels for fields in the block - "fieldLabels": ["Medium"], - // Since we have only one label in this block, we set labelsGap as 0 - "labelsGap": 0, - }, - - // ... rest of the fieldBlocks - }, - // All the columns in order(custom and non-custom) to be outputted in the results sheet - "outputColumns": [ - "Roll", - "q1", - "q2", - // ... - ], - // The value to be used in case of empty bubble detected - "emptyValue": "" -} -``` - -## More details - +## Meaning of parameters used in the template.json file + +Let's take an example of sample1 from the [samples](https://github.com/Udayraj123/OMRChecker/tree/master/samples). + +You may use below commands from project root to load the sample template. +```bash +python3 main.py -i samples/sample1 --setLayout +``` +Which shows the template overlay like below - +

+ Initial Layout +

+ + +Let's examine the template.json used in this sample. +```js +{ + // The dimensions(width, height) to which the page will be resized to before applying template + "pageDimensions": [ + 1846, + 1500 + ], + // The dimensions of the overlay bubble area + "bubbleDimensions": [ + 40, + 40 + ], + // Custom configuration values to use in the template's directory + "preProcessors": [ + { + // Page boundary based cropping plugin + "name": "CropPage", + "options": { + // size of the kernel to use for cropping (default for most cases) + "morphKernel": [ + 10, + 10 + ] + } + }, + { + // Marker based cropping plugin + "name": "CropOnMarkers", + "options": { + // path to marker file + "relativePath": "omr_marker.jpg", + // a factor by which marker shall be resized relative to input image + "sheetToMarkerWidthRatio": 17 + } + } + ], + +// The customLabels contain fields that need to be joined together before generating the results sheet + "customLabels": { + // Here the "Roll" key corresponds to a single column in the results + "Roll": [ + "Medium", + "roll1", + "roll2", + "roll3", + "roll4", + "roll5", + "roll6", + "roll7", + "roll8", + "roll9" + ], + // customLabels can also be used for two-digit integer type questions. + "q5": [ + "q5_1", + "q5_2" + ], + // ... + }, + // Each rectangular box you see in the template overlay image is an item in fieldBlocks + "fieldBlocks": { + // ... + + // Create a block of MCQ type questions + + "MCQ_Block_Q1": { + // Here, QTYPE_MCQ4 is a built-in field type with four values ["A", "B", "C", "D"] + "fieldType": "QTYPE_MCQ4", + // The ordered questions in this block (length of this array is the number of questions in this block) + // A custom parser also recognises ["q1..4"] to implement the same list below + "fieldLabels": ["q1", "q2", "q3", "q4"], + // The gaps between the bubbles + "bubblesGap": 59, + // The gaps between the labels i.e. the mcq questions + "labelsGap": 50, + // The starting point of the block (top left) + "origin": [121, 860] + }, + + // This shows how integer type questions are created. + "Int_Block_Q5": { + // Here, QTYPE_INT is a built-in field type with vertical arrangement of 0-9 integers + "fieldType": "QTYPE_INT", + // These field labels will concatenated together as declared in customLabels earlier. + "fieldLabels": ["q5_1", "q5_2"], + // The gaps between the bubbles + "bubblesGap": 46, + // The gaps between the labels i.e. the two integer columns + "labelsGap": 60, + // The starting point of the block (top left) + "origin": [903, 282] + }, + + // A custom field + "Medium": { + // The starting point of the block (top left) + "origin": [ + 170, + 282 + ], + // The direction of the bubbles + "direction": "vertical", + // The gaps between the bubbles + "bubblesGap": 41, + // Custom values for the bubbles (length of this array is the number of bubbles per field) + "bubbleValues": ["E", "H"], + // The labels for fields in the block + "fieldLabels": ["Medium"], + // Since we have only one label in this block, we set labelsGap as 0 + "labelsGap": 0, + }, + + // ... rest of the fieldBlocks + }, + // All the columns in order(custom and non-custom) to be outputted in the results sheet + "outputColumns": [ + "Roll", + "q1", + "q2", + // ... + ], + // The value to be used in case of empty bubble detected + "emptyValue": "" +} +``` + +## More details + You can find the full template schema in the [source code](https://github.com/Udayraj123/OMRChecker/blob/master/src/schemas/template_schema.py). \ No newline at end of file diff --git a/docs/[v1]-Diagrams.md b/docs/[v1]-Diagrams.md index 05082923..2ed9d915 100644 --- a/docs/[v1]-Diagrams.md +++ b/docs/[v1]-Diagrams.md @@ -1,6 +1,6 @@ -## Block Diagrams -### Block Diagram of the program -![block_diagram](./extras/Progress/2019-04-26/images/block_diagram.PNG) - -### Block Diagram of findPage algorithm -![find_page](./extras/Progress/2019-04-26/images/find_page.PNG) +## Block Diagrams +### Block Diagram of the program +![block_diagram](./extras/Progress/2019-04-26/images/block_diagram.PNG) + +### Block Diagram of findPage algorithm +![find_page](./extras/Progress/2019-04-26/images/find_page.PNG) diff --git a/docs/[v1]-Rich Visuals.md b/docs/[v1]-Rich Visuals.md index b7edc2bd..5db4024a 100644 --- a/docs/[v1]-Rich Visuals.md +++ b/docs/[v1]-Rich Visuals.md @@ -1,147 +1,147 @@ -## All Detailed Visuals possible using this software: - - -### Inputs: -Sample Input - -![sample_input](./extras/Progress/2019-04-26/images/sample_input.PNG) - -Inputs Scanner - -![inputs_scanner](./extras/mini_scripts/outputs/gif/inputs_scanner.gif) - -Inputs Xeroxed - -![inputs_xeroxed](./extras/mini_scripts/outputs/gif/inputs_xeroxed.gif) - -Inputs Mobile - -![inputs_mobile](./extras/mini_scripts/outputs/gif/inputs_mobile.gif) - -Inputs Mobile Xxeroxed - -![inputs_mobile_xeroxed](./extras/mini_scripts/outputs/gif/inputs_mobile_xeroxed.gif) - -## Gifs: -### Code in Action: -Document Scanner - -![document_scanner](./extras/mini_scripts/outputs/gif/document_scanner.gif) - -Checking Inputs Mobile - -![checking_inputs_mobile](./extras/mini_scripts/outputs/gif/checking_inputs_mobile.gif) - -Checking Xeroxed Mobile - -![checking_xeroxed_mobile](./extras/mini_scripts/outputs/gif/checking_xeroxed_mobile.gif) - -Checking Xeroxed Scanned - -![checking_xeroxed_scanned](./extras/mini_scripts/outputs/gif/checking_xeroxed_scanned.gif) - -### Histograms -Colored Print: - -![commoncolor](./extras/Progress/2019-04-26/images/commoncolor.PNG) - -Xeroxed OMR: - -![commonxerox](./extras/Progress/2019-04-26/images/commonxerox.PNG) - -Histogram for Dark image: - -![dark_hist](./extras/Progress/2019-04-26/images/dark_hist.PNG) - -Histogram on High bubbling: - -![largebubbles](./extras/Progress/2019-04-26/images/largebubbles.PNG) - -Histogram on Images with Shadow: - -![shadowxerox](./extras/Progress/2019-04-26/images/shadowxerox.PNG) - -![wideshadow](./extras/Progress/2019-04-26/images/wideshadow.PNG) - -Various Histograms - -![histograms](./extras/Progress/2019-04-26/images/histograms.PNG) - -![histograms_1](./extras/Progress/2019-04-26/images/histograms_1.PNG) - -![histograms_2](./extras/Progress/2019-04-26/images/histograms_2.PNG) - -### Boxplots -Boxplot for Dark image: - -![dark_boxplots](./extras/Progress/2019-04-26/images/dark_boxplots.PNG) - -### Intermediate Steps: - -#### Finding the page - -![find_page_images](./extras/Progress/2019-04-26/images/find_page_images.PNG) - -#### Finding Markers - -![template_circle](./extras/Progress/2019-04-26/images/template_circle.PNG) - -![template_matching](./extras/Progress/2019-04-26/images/template_matching.PNG) - -#### Template Layout Alignment - -![align_correct](./extras/Progress/2019-04-26/images/align_correct.PNG) - -#### Finding Threshold -###### Global - -![calc_tglobal](./extras/Progress/2019-04-26/images/calc_tglobal.PNG) -###### Local - -![colwise_hist](./extras/Progress/2019-04-26/images/colwise_hist.PNG) - -![read_response](./extras/Progress/2019-04-26/images/read_response.PNG) - - -### All Output Steps (Stacks) - -Bubbling: - -![bubbling](./extras/Progress/2019-04-26/images/bubbling.PNG) - -Midfolding: - -![midfolding](./extras/Progress/2019-04-26/images/midfolding.PNG) - -Perspective: - -![perspective](./extras/Progress/2019-04-26/images/perspective.PNG) - -Rotation: - -![rotation](./extras/Progress/2019-04-26/images/rotation.PNG) - -Shadow: - -![shadow](./extras/Progress/2019-04-26/images/shadow.PNG) - -Temperament: - -![temperament](./extras/Progress/2019-04-26/images/temperament.PNG) - -Sidefolding: - -![sidefolding](./extras/Progress/2019-04-26/images/sidefolding.PNG) - -### The ultimate output -CSV File: - -![csv_output](./extras/Progress/2019-04-26/images/csv_output.PNG) - - -### Template Layout Setting -![final_layout](./images/final_layout.png) - -### Android App Sneak Peek -![app_flow](./extras/Progress/2019-04-26/images/app_flow.PNG) - +## All Detailed Visuals possible using this software: + + +### Inputs: +Sample Input + +![sample_input](./extras/Progress/2019-04-26/images/sample_input.PNG) + +Inputs Scanner + +![inputs_scanner](./extras/mini_scripts/outputs/gif/inputs_scanner.gif) + +Inputs Xeroxed + +![inputs_xeroxed](./extras/mini_scripts/outputs/gif/inputs_xeroxed.gif) + +Inputs Mobile + +![inputs_mobile](./extras/mini_scripts/outputs/gif/inputs_mobile.gif) + +Inputs Mobile Xxeroxed + +![inputs_mobile_xeroxed](./extras/mini_scripts/outputs/gif/inputs_mobile_xeroxed.gif) + +## Gifs: +### Code in Action: +Document Scanner + +![document_scanner](./extras/mini_scripts/outputs/gif/document_scanner.gif) + +Checking Inputs Mobile + +![checking_inputs_mobile](./extras/mini_scripts/outputs/gif/checking_inputs_mobile.gif) + +Checking Xeroxed Mobile + +![checking_xeroxed_mobile](./extras/mini_scripts/outputs/gif/checking_xeroxed_mobile.gif) + +Checking Xeroxed Scanned + +![checking_xeroxed_scanned](./extras/mini_scripts/outputs/gif/checking_xeroxed_scanned.gif) + +### Histograms +Colored Print: + +![commoncolor](./extras/Progress/2019-04-26/images/commoncolor.PNG) + +Xeroxed OMR: + +![commonxerox](./extras/Progress/2019-04-26/images/commonxerox.PNG) + +Histogram for Dark image: + +![dark_hist](./extras/Progress/2019-04-26/images/dark_hist.PNG) + +Histogram on High bubbling: + +![largebubbles](./extras/Progress/2019-04-26/images/largebubbles.PNG) + +Histogram on Images with Shadow: + +![shadowxerox](./extras/Progress/2019-04-26/images/shadowxerox.PNG) + +![wideshadow](./extras/Progress/2019-04-26/images/wideshadow.PNG) + +Various Histograms + +![histograms](./extras/Progress/2019-04-26/images/histograms.PNG) + +![histograms_1](./extras/Progress/2019-04-26/images/histograms_1.PNG) + +![histograms_2](./extras/Progress/2019-04-26/images/histograms_2.PNG) + +### Boxplots +Boxplot for Dark image: + +![dark_boxplots](./extras/Progress/2019-04-26/images/dark_boxplots.PNG) + +### Intermediate Steps: + +#### Finding the page + +![find_page_images](./extras/Progress/2019-04-26/images/find_page_images.PNG) + +#### Finding Markers + +![template_circle](./extras/Progress/2019-04-26/images/template_circle.PNG) + +![template_matching](./extras/Progress/2019-04-26/images/template_matching.PNG) + +#### Template Layout Alignment + +![align_correct](./extras/Progress/2019-04-26/images/align_correct.PNG) + +#### Finding Threshold +###### Global + +![calc_tglobal](./extras/Progress/2019-04-26/images/calc_tglobal.PNG) +###### Local + +![colwise_hist](./extras/Progress/2019-04-26/images/colwise_hist.PNG) + +![read_response](./extras/Progress/2019-04-26/images/read_response.PNG) + + +### All Output Steps (Stacks) + +Bubbling: + +![bubbling](./extras/Progress/2019-04-26/images/bubbling.PNG) + +Midfolding: + +![midfolding](./extras/Progress/2019-04-26/images/midfolding.PNG) + +Perspective: + +![perspective](./extras/Progress/2019-04-26/images/perspective.PNG) + +Rotation: + +![rotation](./extras/Progress/2019-04-26/images/rotation.PNG) + +Shadow: + +![shadow](./extras/Progress/2019-04-26/images/shadow.PNG) + +Temperament: + +![temperament](./extras/Progress/2019-04-26/images/temperament.PNG) + +Sidefolding: + +![sidefolding](./extras/Progress/2019-04-26/images/sidefolding.PNG) + +### The ultimate output +CSV File: + +![csv_output](./extras/Progress/2019-04-26/images/csv_output.PNG) + + +### Template Layout Setting +![final_layout](./images/final_layout.png) + +### Android App Sneak Peek +![app_flow](./extras/Progress/2019-04-26/images/app_flow.PNG) + diff --git a/docs/[v1]-User-Guide.md b/docs/[v1]-User-Guide.md index 3a3c9500..7fcdb5fa 100644 --- a/docs/[v1]-User-Guide.md +++ b/docs/[v1]-User-Guide.md @@ -1,110 +1,110 @@ -## Step by step walkthrough for creating a basic template - - -This tutorial will show you how to create template layout files using a simple example. - - - -First let's make a layout for a sample OMR from [Adrian's blog](https://pyimagesearch.com/2016/10/03/bubble-sheet-multiple-choice-scanner-and-test-grader-using-omr-python-and-opencv/). - -

- Adrian OMR -

- -1. Create a directory for your files, say `inputs/AdrianSamples`. Note that all directories in `inputs/` directory will be processed by default. - -2. Download above OMR image and put it into `inputs/AdrianSamples/`. - -3. Create a file `inputs/template.json`. Putting the following starter json in it. - -``` -{ - "pageDimensions": [ 300, 400 ], - "bubbleDimensions": [ 20, 20 ], - "customLabels": {}, - "fieldBlocks": { - "MCQBlock1": { - "fieldType": "QTYPE_MCQ5", - "origin": [ 0, 0 ], - "fieldLabels": ["q1", "q2", "q3", "q4", "q5"], - "bubblesGap": 30, - "labelsGap": 30 - } - }, - "preProcessors": [ - { - "name": "CropPage", - "options": { - "morphKernel": [ 10, 10 ] - } - } - ] -} -``` - -Now run `python3 main.py --setLayout`. The page should get cropped automatically and show a basic overlay of the template. -Note that we have put `"origin": [0, 0],` which means the overlay will start from the top left corner. - -

- Initial Layout -

-Now let's adjust the top left corner(origin). Change origin from [0,0] to a better coordinate, say [50, 50] and run above command again. After multiple trials, you should find that origin is best fit at [65, 60]. Update the origin in json file : - -``` - "origin": [65, 60], -``` -Run the command again. - -

- Origin Step -

- -Now let's tweak over the two gaps `bubblesGap` and `labelsGap`. -Clearly we need to update the gaps to be bigger. Also, horizontal gaps are smaller than vertical ones. Tweaked gaps come out to be- -``` - "bubblesGap" : 41, - "labelsGap" : 52, -``` -The bubbles also should be made slightly bigger -``` - "bubbleDimensions": [25, 25 ], -``` -Run the command again to get the arranged layout. - -

- Final Layout -

- -Note the "preProcessors" array, there are various plugins to use. Each plugin is described with a `name` and an `options` object that contains the configuration of the plugin. In our case, we use the 'CropPage' plugin with a (default) option of using morph kernel of size [10, 10]. - -Above is the simplest version of what the template.json can do. - -For more templates see [sample folders](https://github.com/Udayraj123/OMRChecker/tree/master/samples). - -To understand how rest of the parameters work in template.json, checkout [About Templates](./About-Templates) - -### Note for capturing using mobile phones - -Please check the `sample1/` folder to understand the use of `omr_marker.jpg`. If you can modify your OMR sheets with these markers, it will give you much higher accuracy when scanning using mobile camera. We enable the markers plugin using the following snippet. - -```js -{ - // ... - "preProcessors": [ - // ... - { - "name": "CropOnMarkers", - "options": { - "relativePath": "omr_marker.jpg", - } - } - ] -} -``` - - -## Running OMRChecker - -Run `python3 main.py` to generate outputs for input file. - +## Step by step walkthrough for creating a basic template + + +This tutorial will show you how to create template layout files using a simple example. + + + +First let's make a layout for a sample OMR from [Adrian's blog](https://pyimagesearch.com/2016/10/03/bubble-sheet-multiple-choice-scanner-and-test-grader-using-omr-python-and-opencv/). + +

+ Adrian OMR +

+ +1. Create a directory for your files, say `inputs/AdrianSamples`. Note that all directories in `inputs/` directory will be processed by default. + +2. Download above OMR image and put it into `inputs/AdrianSamples/`. + +3. Create a file `inputs/template.json`. Putting the following starter json in it. + +``` +{ + "pageDimensions": [ 300, 400 ], + "bubbleDimensions": [ 20, 20 ], + "customLabels": {}, + "fieldBlocks": { + "MCQBlock1": { + "fieldType": "QTYPE_MCQ5", + "origin": [ 0, 0 ], + "fieldLabels": ["q1", "q2", "q3", "q4", "q5"], + "bubblesGap": 30, + "labelsGap": 30 + } + }, + "preProcessors": [ + { + "name": "CropPage", + "options": { + "morphKernel": [ 10, 10 ] + } + } + ] +} +``` + +Now run `python3 main.py --setLayout`. The page should get cropped automatically and show a basic overlay of the template. +Note that we have put `"origin": [0, 0],` which means the overlay will start from the top left corner. + +

+ Initial Layout +

+Now let's adjust the top left corner(origin). Change origin from [0,0] to a better coordinate, say [50, 50] and run above command again. After multiple trials, you should find that origin is best fit at [65, 60]. Update the origin in json file : + +``` + "origin": [65, 60], +``` +Run the command again. + +

+ Origin Step +

+ +Now let's tweak over the two gaps `bubblesGap` and `labelsGap`. +Clearly we need to update the gaps to be bigger. Also, horizontal gaps are smaller than vertical ones. Tweaked gaps come out to be- +``` + "bubblesGap" : 41, + "labelsGap" : 52, +``` +The bubbles also should be made slightly bigger +``` + "bubbleDimensions": [25, 25 ], +``` +Run the command again to get the arranged layout. + +

+ Final Layout +

+ +Note the "preProcessors" array, there are various plugins to use. Each plugin is described with a `name` and an `options` object that contains the configuration of the plugin. In our case, we use the 'CropPage' plugin with a (default) option of using morph kernel of size [10, 10]. + +Above is the simplest version of what the template.json can do. + +For more templates see [sample folders](https://github.com/Udayraj123/OMRChecker/tree/master/samples). + +To understand how rest of the parameters work in template.json, checkout [About Templates](./About-Templates) + +### Note for capturing using mobile phones + +Please check the `sample1/` folder to understand the use of `omr_marker.jpg`. If you can modify your OMR sheets with these markers, it will give you much higher accuracy when scanning using mobile camera. We enable the markers plugin using the following snippet. + +```js +{ + // ... + "preProcessors": [ + // ... + { + "name": "CropOnMarkers", + "options": { + "relativePath": "omr_marker.jpg", + } + } + ] +} +``` + + +## Running OMRChecker + +Run `python3 main.py` to generate outputs for input file. + Note: For full usage refer the [Project Readme](https://github.com/Udayraj123/OMRChecker#full-usage) \ No newline at end of file diff --git a/docs/[v2]-About-Config.md b/docs/[v2]-About-Config.md index b304f919..6fd99203 100644 --- a/docs/[v2]-About-Config.md +++ b/docs/[v2]-About-Config.md @@ -1,92 +1,92 @@ -### Config schema -OMRChecker config schema for custom tuning - -```js -{ - "title": "Config Schema", - "description": "OMRChecker config schema for custom tuning", - "thresholding": { - "description": "The values used in the core algorithm of OMRChecker", - "MIN_GAP_TWO_BUBBLES": { - "description": "Minimum difference between all mean values of the bubbles. Used for local thresholding of 2 or 1 bubbles", - }, - "MIN_JUMP": { - "description": "Minimum difference between consecutive elements to be consider as a jump in a sorted array of mean values of the bubbles", - }, - "MIN_JUMP_STD": { - "description": "The MIN_JUMP for the standard deviation plot", - }, - "MIN_JUMP_SURPLUS_FOR_GLOBAL_FALLBACK": { - "description": "This value is added to jump value, underconfident bubbles fallback to global_threshold_for_template", - }, - "GLOBAL_THRESHOLD_MARGIN": { - "description": 'This value determines if the calculated global threshold is "too close" to lower bubbles in confidence metrics', - }, - "JUMP_DELTA": { - "description": "Note: JUMP_DELTA is deprecated, used only in plots currently to determine a stricter threshold", - }, - "JUMP_DELTA_STD": { - "description": "JUMP_DELTA_STD is the minimum delta to be considered as a jump in the std plot", - }, - "CONFIDENT_JUMP_SURPLUS_FOR_DISPARITY": { - "description": "This value is added to jump value to distinguish safe detections vs underconfident detections", - }, - "GLOBAL_PAGE_THRESHOLD": { - "description": "This option decides the starting value to use before applying local outlier threshold", - }, - "GLOBAL_PAGE_THRESHOLD_STD": { - "description": "This option decides the starting value to use for standard deviation threshold which determines outliers", - }, - "GAMMA_LOW": { - "description": "Used in the CropOnDotLines processor to create a darker image for enhanced line detection (darker boxes)", - }, - }, - "outputs": { - "description": "The configuration related to the outputs generated by OMRChecker", - - "display_image_dimensions": { - "description": "The dimensions (width, height) for images displayed during the execution", - }, - "show_logs_by_type": { - "description": "The toggles for enabling logs per level", - "properties": { - "critical": true, - "error": true, - "warning": true, - "info": true, - "debug": true, - }, - }, - "show_image_level": { - "description": "The toggle level for showing debug images (higher means more debug images)", - }, - "save_image_level": { - "description": "The toggle level for saving debug images (higher means more debug images)", - }, - "colored_outputs_enabled": { - "description": "This option shows colored outputs while taking a small toll on the processing speeds. Disable this option to slightly improve the speed", - }, - "save_detections": { - "description": "This option saves the detection outputs while taking a small toll on the processing speeds", - }, - "save_image_metrics": { - "description": "This option exports the confidence metrics etc related to the images. These can be later used for deeper analysis/visualizations", - }, - "filter_out_multimarked_files": { - "description": "This option moves files having multi-marked responses into a separate folder for manual checking, skipping evaluation", - }, - "show_preprocessors_diff": { - "description": "This option shows a preview of the processed image for every preprocessor. Also granular at preprocessor level using a map", - "properties": { - "CropOnMarkers": true, - "CropPage": true, - "FeatureBasedAlignment": true, - "GaussianBlur": true, - "Levels": true, - "MedianBlur": true, - "AutoAlign": true, - }, - }, - }, -} -``` +### Config schema +OMRChecker config schema for custom tuning + +```js +{ + "title": "Config Schema", + "description": "OMRChecker config schema for custom tuning", + "thresholding": { + "description": "The values used in the core algorithm of OMRChecker", + "MIN_GAP_TWO_BUBBLES": { + "description": "Minimum difference between all mean values of the bubbles. Used for local thresholding of 2 or 1 bubbles", + }, + "MIN_JUMP": { + "description": "Minimum difference between consecutive elements to be consider as a jump in a sorted array of mean values of the bubbles", + }, + "MIN_JUMP_STD": { + "description": "The MIN_JUMP for the standard deviation plot", + }, + "MIN_JUMP_SURPLUS_FOR_GLOBAL_FALLBACK": { + "description": "This value is added to jump value, underconfident bubbles fallback to global_threshold_for_template", + }, + "GLOBAL_THRESHOLD_MARGIN": { + "description": 'This value determines if the calculated global threshold is "too close" to lower bubbles in confidence metrics', + }, + "JUMP_DELTA": { + "description": "Note: JUMP_DELTA is deprecated, used only in plots currently to determine a stricter threshold", + }, + "JUMP_DELTA_STD": { + "description": "JUMP_DELTA_STD is the minimum delta to be considered as a jump in the std plot", + }, + "CONFIDENT_JUMP_SURPLUS_FOR_DISPARITY": { + "description": "This value is added to jump value to distinguish safe detections vs underconfident detections", + }, + "GLOBAL_PAGE_THRESHOLD": { + "description": "This option decides the starting value to use before applying local outlier threshold", + }, + "GLOBAL_PAGE_THRESHOLD_STD": { + "description": "This option decides the starting value to use for standard deviation threshold which determines outliers", + }, + "GAMMA_LOW": { + "description": "Used in the CropOnDotLines processor to create a darker image for enhanced line detection (darker boxes)", + }, + }, + "outputs": { + "description": "The configuration related to the outputs generated by OMRChecker", + + "display_image_dimensions": { + "description": "The dimensions (width, height) for images displayed during the execution", + }, + "show_logs_by_type": { + "description": "The toggles for enabling logs per level", + "properties": { + "critical": true, + "error": true, + "warning": true, + "info": true, + "debug": true, + }, + }, + "show_image_level": { + "description": "The toggle level for showing debug images (higher means more debug images)", + }, + "save_image_level": { + "description": "The toggle level for saving debug images (higher means more debug images)", + }, + "colored_outputs_enabled": { + "description": "This option shows colored outputs while taking a small toll on the processing speeds. Disable this option to slightly improve the speed", + }, + "save_detections": { + "description": "This option saves the detection outputs while taking a small toll on the processing speeds", + }, + "save_image_metrics": { + "description": "This option exports the confidence metrics etc related to the images. These can be later used for deeper analysis/visualizations", + }, + "filter_out_multimarked_files": { + "description": "This option moves files having multi-marked responses into a separate folder for manual checking, skipping evaluation", + }, + "show_preprocessors_diff": { + "description": "This option shows a preview of the processed image for every preprocessor. Also granular at preprocessor level using a map", + "properties": { + "CropOnMarkers": true, + "CropPage": true, + "FeatureBasedAlignment": true, + "GaussianBlur": true, + "Levels": true, + "MedianBlur": true, + "AutoAlign": true, + }, + }, + }, +} +``` diff --git a/docs/[v2]-About-Evaluation.md b/docs/[v2]-About-Evaluation.md index ca00bc82..5bd5257d 100644 --- a/docs/[v2]-About-Evaluation.md +++ b/docs/[v2]-About-Evaluation.md @@ -1,175 +1,175 @@ -# The evaluation.json file - -This documentation provides a clear overview of the `evaluation.json` structure and its components, enabling efficient customization and understanding of the evaluation process within OMRChecker. Adjustments and expansions to meet specific evaluation needs can be made based on these defined parameters and configurations. - -## Meaning of Parameters Used in the `evaluation.json` File - -To understand the structure and usage of the `evaluation.json` file for custom evaluation processes, let's delve into its parameters and configurations. Below is an annotated explanation of each section: - -**Note: Add evaluation.json at the same folder level as your template.json** - -### List of capabilities - -- multiple marking schemes including negative/fractional -- colored outputs with printing score, -- set-mapping of answer keys - -

- - colored_output - -

- -### Source Types and Answer Key Options - -- **source_type**: Specifies the type of source data being evaluated, in this case, it's "local". We also support `csv` and `image_and_csv` sources. Please refer to the [samples](https://github.com/Udayraj123/OMRChecker/tree/master/samples/3-answer-key/) folder for more examples. -- **questions_in_order**: Defines the questions in a serial order in the evaluation. -- **answers_in_order**: Defines the answers expected in a serial order for each question in the evaluation. - -##### Types of answer keys supported: - -1. Standard answer type: allows single correct answers. They can have multiple characters(multi-marked) as well. - Useful for any standard response e.g. 'A', '01', '99', 'AB', etc -2. Multiple correct answer type: covers multiple correct answers - Useful for ambiguous/bonus questions e.g. `['A', 'B'], ['1', '01'], ['A', 'B', 'AB']`, etc -3. Multiple correct weighted answer: covers multiple answers with custom scores - Useful for partial marking e.g. `[['A', 2], ['B', 0.5], ['AB', 2.5]], [['1', 0.5], ['01', 1]]`, etc - -```js -{ - "source_type": "local", - "options": { - // The field names mentioned in 'questions_in_order' are picked up from the OMR response. - "questions_in_order": [ - "q1..10", // A field string indicating questions 1 to 11 (for default section) - "s2_q1..5", // Another field string indicating questions 1 to 5 from another section - ], - // Various answer formats for each question - "answers_in_order": [ - "A", // q1: Single correct answer 'A' - "B", // q2: Single correct answer 'B' - "AB", // q3: Multicharacter answer 'AB' (both A & B should be marked) - ["A", "B"], // q4: Multiple correct answers (either A or B is correct but not both) - ["A", "B", "AB"], // q5: Multiple correct answers (either A or B or both AB is correct) - ["A", "B", "C", "D"], // q6: Effective bonus marks using custom score (without using bonus section) - [["A", 2], ["D", 1], ["AD", 3]], // q7: Multiple answers with custom score (Marks for AD = 2 + 1 = 3) - [["C", 10], ["D", -1]], // q8: Multiple answers with custom negative score - "D", // q9: Original answer 'D' (But score overridden by bonus marking scheme below) - "C", // q10: Original answer 'C' (But score overridden by bonus marking scheme below) - ] - "marking_schemes":{ - // Default marking scheme (fallback) - "DEFAULT": { - "correct": "3", // Default marking for correct answers - "incorrect": "0", // Default marking for incorrect answers - "unmarked": "0" // Default marking for unmarked answers - }, - // Custom section with a different marking scheme - "SECTION_2":{ - // List of questions to apply the marking scheme on - "questions":["s2_q1..5"], - // Custom marking on the section questions - "marking": { - "correct": "4", - "incorrect": "-1", - "unmarked": "0" - } - }, - // Another section mentioning bonus questions(on attempt) - "BONUS_MARKS_ON_ATTEMPT": { - "questions": [ - "q9" - ], - // Custom marking on the section questions - "marking": { - "correct": "3", - "incorrect": "3", - "unmarked": "0" - } - }, - // Another section mentioning bonus questions(with/without attempt) - "BONUS_MARKS_FOR_ALL": { - "questions": [ - "q10" - ], - "marking": { - "correct": "3", - "incorrect": "3", - "unmarked": "3" - } - }, - } - }, -``` - -### Symbols and Color Notations -When `draw_question_verdicts` is enabled, the output images will contain the answer key and corresponding question verdicts. Both the gray and colored images follow a set of notations to show the different types of answers supported. The diagram below(initial draft) explains the possible answer key cases covered in a single picture: -![evaluation-outputs-export](https://github.com/Udayraj123/OMRChecker/assets/16881051/844895f4-c3ce-47dc-9688-60cd9bc6a3e3) - - -Note: As of now the output notations for keys using `customLabels` are yet to be supported and will not be shown visually, but used in the score calculation directly. - - -### Outputs Configuration - -- **should_explain_scoring**: Indicates whether to print a table explaining question-wise verdicts. -- **draw_score**: Configuration for drawing the final score, including its position and size. -- **draw_answers_summary**: Configuration for drawing the answers summary, including its position and size. -- **draw_question_verdicts**: Configuration for drawing question verdicts, specifying colors and symbols for different verdict types. -- **draw_detected_bubble_texts**: Configuration for drawing detected bubble texts, which is disabled in this example. - - -```js -{ - "outputs_configuration": { - "should_explain_scoring": true, // Whether to explain question-wise verdicts - "draw_score": { - "enabled": true, // Enable drawing the score - "position": [600, 650], // Position of the score box - "size": 1.5 // Font size of the score box - }, - "draw_answers_summary": { - "enabled": true, // Enable drawing answers summary - "position": [300, 550], // Position of the answers summary box - "size": 1.0 // Font size of the answers summary box - }, - "draw_question_verdicts": { - "enabled": true, // Enable drawing question verdicts - - // Colors for different verdicts - "verdict_colors": { - "correct": "lightgreen", // Color for correct answers - "neutral": "#000000", // Color for neutral verdicts (delta == 0) - "incorrect": "#ff0000", // Color for incorrect answers - "bonus": "#00DDDD" // Color for bonus questions - }, - - // Colors for different verdict symbols - "verdict_symbol_colors": { - "positive": "#000000", // Color for '+' symbol (delta > 0) - "neutral": "#000000", // Color for 'o' symbol (delta == 0) - "negative": "#000000", // Color for '-' symbol (delta < 0) - "bonus": "#000000" // Color for '*' symbol (bonus question) - }, - - // Configuration for drawing answer groups - "draw_answer_groups": { - "enabled": true // Enable drawing answer groups - } - }, - "draw_detected_bubble_texts": { - "enabled": false // Disable drawing detected bubble texts - } - }, -} -``` - -### Example Usage - -To load and utilize this schema, navigate to the project root and execute the following command: - -```bash -python3 main.py -i samples/3-answer-key/bonus-marking-grouping -``` - -For further details and examples, refer to the [samples](https://github.com/Udayraj123/OMRChecker/tree/master/samples/3-answer-key/) directory. +# The evaluation.json file + +This documentation provides a clear overview of the `evaluation.json` structure and its components, enabling efficient customization and understanding of the evaluation process within OMRChecker. Adjustments and expansions to meet specific evaluation needs can be made based on these defined parameters and configurations. + +## Meaning of Parameters Used in the `evaluation.json` File + +To understand the structure and usage of the `evaluation.json` file for custom evaluation processes, let's delve into its parameters and configurations. Below is an annotated explanation of each section: + +**Note: Add evaluation.json at the same folder level as your template.json** + +### List of capabilities + +- multiple marking schemes including negative/fractional +- colored outputs with printing score, +- set-mapping of answer keys + +

+ + colored_output + +

+ +### Source Types and Answer Key Options + +- **source_type**: Specifies the type of source data being evaluated, in this case, it's "local". We also support `csv` and `image_and_csv` sources. Please refer to the [samples](https://github.com/Udayraj123/OMRChecker/tree/master/samples/3-answer-key/) folder for more examples. +- **questions_in_order**: Defines the questions in a serial order in the evaluation. +- **answers_in_order**: Defines the answers expected in a serial order for each question in the evaluation. + +##### Types of answer keys supported: + +1. Standard answer type: allows single correct answers. They can have multiple characters(multi-marked) as well. + Useful for any standard response e.g. 'A', '01', '99', 'AB', etc +2. Multiple correct answer type: covers multiple correct answers + Useful for ambiguous/bonus questions e.g. `['A', 'B'], ['1', '01'], ['A', 'B', 'AB']`, etc +3. Multiple correct weighted answer: covers multiple answers with custom scores + Useful for partial marking e.g. `[['A', 2], ['B', 0.5], ['AB', 2.5]], [['1', 0.5], ['01', 1]]`, etc + +```js +{ + "source_type": "local", + "options": { + // The field names mentioned in 'questions_in_order' are picked up from the OMR response. + "questions_in_order": [ + "q1..10", // A field string indicating questions 1 to 11 (for default section) + "s2_q1..5", // Another field string indicating questions 1 to 5 from another section + ], + // Various answer formats for each question + "answers_in_order": [ + "A", // q1: Single correct answer 'A' + "B", // q2: Single correct answer 'B' + "AB", // q3: Multicharacter answer 'AB' (both A & B should be marked) + ["A", "B"], // q4: Multiple correct answers (either A or B is correct but not both) + ["A", "B", "AB"], // q5: Multiple correct answers (either A or B or both AB is correct) + ["A", "B", "C", "D"], // q6: Effective bonus marks using custom score (without using bonus section) + [["A", 2], ["D", 1], ["AD", 3]], // q7: Multiple answers with custom score (Marks for AD = 2 + 1 = 3) + [["C", 10], ["D", -1]], // q8: Multiple answers with custom negative score + "D", // q9: Original answer 'D' (But score overridden by bonus marking scheme below) + "C", // q10: Original answer 'C' (But score overridden by bonus marking scheme below) + ] + "marking_schemes":{ + // Default marking scheme (fallback) + "DEFAULT": { + "correct": "3", // Default marking for correct answers + "incorrect": "0", // Default marking for incorrect answers + "unmarked": "0" // Default marking for unmarked answers + }, + // Custom section with a different marking scheme + "SECTION_2":{ + // List of questions to apply the marking scheme on + "questions":["s2_q1..5"], + // Custom marking on the section questions + "marking": { + "correct": "4", + "incorrect": "-1", + "unmarked": "0" + } + }, + // Another section mentioning bonus questions(on attempt) + "BONUS_MARKS_ON_ATTEMPT": { + "questions": [ + "q9" + ], + // Custom marking on the section questions + "marking": { + "correct": "3", + "incorrect": "3", + "unmarked": "0" + } + }, + // Another section mentioning bonus questions(with/without attempt) + "BONUS_MARKS_FOR_ALL": { + "questions": [ + "q10" + ], + "marking": { + "correct": "3", + "incorrect": "3", + "unmarked": "3" + } + }, + } + }, +``` + +### Symbols and Color Notations +When `draw_question_verdicts` is enabled, the output images will contain the answer key and corresponding question verdicts. Both the gray and colored images follow a set of notations to show the different types of answers supported. The diagram below(initial draft) explains the possible answer key cases covered in a single picture: +![evaluation-outputs-export](https://github.com/Udayraj123/OMRChecker/assets/16881051/844895f4-c3ce-47dc-9688-60cd9bc6a3e3) + + +Note: As of now the output notations for keys using `customLabels` are yet to be supported and will not be shown visually, but used in the score calculation directly. + + +### Outputs Configuration + +- **should_explain_scoring**: Indicates whether to print a table explaining question-wise verdicts. +- **draw_score**: Configuration for drawing the final score, including its position and size. +- **draw_answers_summary**: Configuration for drawing the answers summary, including its position and size. +- **draw_question_verdicts**: Configuration for drawing question verdicts, specifying colors and symbols for different verdict types. +- **draw_detected_bubble_texts**: Configuration for drawing detected bubble texts, which is disabled in this example. + + +```js +{ + "outputs_configuration": { + "should_explain_scoring": true, // Whether to explain question-wise verdicts + "draw_score": { + "enabled": true, // Enable drawing the score + "position": [600, 650], // Position of the score box + "size": 1.5 // Font size of the score box + }, + "draw_answers_summary": { + "enabled": true, // Enable drawing answers summary + "position": [300, 550], // Position of the answers summary box + "size": 1.0 // Font size of the answers summary box + }, + "draw_question_verdicts": { + "enabled": true, // Enable drawing question verdicts + + // Colors for different verdicts + "verdict_colors": { + "correct": "lightgreen", // Color for correct answers + "neutral": "#000000", // Color for neutral verdicts (delta == 0) + "incorrect": "#ff0000", // Color for incorrect answers + "bonus": "#00DDDD" // Color for bonus questions + }, + + // Colors for different verdict symbols + "verdict_symbol_colors": { + "positive": "#000000", // Color for '+' symbol (delta > 0) + "neutral": "#000000", // Color for 'o' symbol (delta == 0) + "negative": "#000000", // Color for '-' symbol (delta < 0) + "bonus": "#000000" // Color for '*' symbol (bonus question) + }, + + // Configuration for drawing answer groups + "draw_answer_groups": { + "enabled": true // Enable drawing answer groups + } + }, + "draw_detected_bubble_texts": { + "enabled": false // Disable drawing detected bubble texts + } + }, +} +``` + +### Example Usage + +To load and utilize this schema, navigate to the project root and execute the following command: + +```bash +python3 main.py -i samples/3-answer-key/bonus-marking-grouping +``` + +For further details and examples, refer to the [samples](https://github.com/Udayraj123/OMRChecker/tree/master/samples/3-answer-key/) directory. diff --git a/docs/[v2]-About-Templates.md b/docs/[v2]-About-Templates.md index cd8d4613..29bf7426 100644 --- a/docs/[v2]-About-Templates.md +++ b/docs/[v2]-About-Templates.md @@ -1,9 +1,9 @@ -wip - -Template schema changes: - - Renamed pageDimensions -> templateDimensions - - Support for a top level processingImageShape as well as pre-processor level processingImageShape - - Added conditionalSets in the schema. - - Support for outputImageShape - +wip + +Template schema changes: + - Renamed pageDimensions -> templateDimensions + - Support for a top level processingImageShape as well as pre-processor level processingImageShape + - Added conditionalSets in the schema. + - Support for outputImageShape + - TODO: add Links to Readmes inside individual samples \ No newline at end of file diff --git a/docs/[v2]-User-Guide.md b/docs/[v2]-User-Guide.md index 6f57b561..e7123041 100644 --- a/docs/[v2]-User-Guide.md +++ b/docs/[v2]-User-Guide.md @@ -1 +1 @@ -wip +wip diff --git a/docs/images/final_layout.png b/docs/images/final_layout.png old mode 100755 new mode 100644 diff --git a/samples/3-answer-key/bonus-marking-grouping/template.json b/samples/3-answer-key/bonus-marking-grouping/template.json index cf3034b7..32f380ef 100644 --- a/samples/3-answer-key/bonus-marking-grouping/template.json +++ b/samples/3-answer-key/bonus-marking-grouping/template.json @@ -1,46 +1,46 @@ -{ - "templateDimensions": [ - 1200, - 1500 - ], - "bubbleDimensions": [ - 30, - 30 - ], - "preProcessors": [ - { - "name": "GaussianBlur", - "options": { - "kSize": [ - 3, - 3 - ], - "sigmaX": 0 - } - }, - { - "name": "CropPage", - "options": { - "morphKernel": [ - 10, - 10 - ] - } - } - ], - "fieldBlocks": { - "MCQBlock1": { - "fieldDetectionType": "BUBBLES_THRESHOLD", - "bubbleFieldType": "QTYPE_MCQ4", - "origin": [ - 138, - 605 - ], - "fieldLabels": [ - "q1..11" - ], - "bubblesGap": 79, - "labelsGap": 55 - } - } -} +{ + "templateDimensions": [ + 1200, + 1500 + ], + "bubbleDimensions": [ + 30, + 30 + ], + "preProcessors": [ + { + "name": "GaussianBlur", + "options": { + "kSize": [ + 3, + 3 + ], + "sigmaX": 0 + } + }, + { + "name": "CropPage", + "options": { + "morphKernel": [ + 10, + 10 + ] + } + } + ], + "fieldBlocks": { + "MCQBlock1": { + "fieldDetectionType": "BUBBLES_THRESHOLD", + "bubbleFieldType": "QTYPE_MCQ4", + "origin": [ + 138, + 605 + ], + "fieldLabels": [ + "q1..11" + ], + "bubblesGap": 79, + "labelsGap": 55 + } + } +} diff --git a/samples/Autorotating-samples/1-mobile-camera/config.json b/samples/Autorotating-samples/1-mobile-camera/config.json index 5c2ae5d8..df674766 100644 --- a/samples/Autorotating-samples/1-mobile-camera/config.json +++ b/samples/Autorotating-samples/1-mobile-camera/config.json @@ -1,6 +1,6 @@ -{ - "outputs": { - "show_image_level": 0, - "show_colored_outputs": true - } -} +{ + "outputs": { + "show_image_level": 0, + "show_colored_outputs": true + } +} diff --git a/scripts/hooks/convert_images_hook.py b/scripts/hooks/convert_images_hook.py old mode 100755 new mode 100644 diff --git a/scripts/hooks/resize_images_hook.py b/scripts/hooks/resize_images_hook.py old mode 100755 new mode 100644 diff --git a/scripts/run_all_tests_and_coverage.sh b/scripts/run_all_tests_and_coverage.sh old mode 100755 new mode 100644 diff --git a/scripts/run_python_hook.sh b/scripts/run_python_hook.sh old mode 100755 new mode 100644 diff --git a/scripts/run_single_test.sh b/scripts/run_single_test.sh old mode 100755 new mode 100644 diff --git a/src/algorithm/evaluation/config.py b/src/algorithm/evaluation/config.py index 127b140f..0df8c9fe 100644 --- a/src/algorithm/evaluation/config.py +++ b/src/algorithm/evaluation/config.py @@ -458,6 +458,7 @@ def __init__( self.draw_question_verdicts, self.draw_score, self.should_explain_scoring, + self.should_export_csv, ) = map( outputs_configuration.get, [ @@ -466,6 +467,7 @@ def __init__( "draw_question_verdicts", "draw_score", "should_explain_scoring", + "should_export_csv", ], ) if self.draw_question_verdicts["enabled"]: @@ -995,6 +997,7 @@ def conditionally_add_explanation( ] self.explanation_table.add_row(*row) + @staticmethod def get_schema_verdict(answer_type, question_verdict, delta=None): # Note: Negative custom weights should be considered as incorrect schema verdict(special case) @@ -1014,6 +1017,12 @@ def conditionally_print_explanation(self): def get_should_explain_scoring(self): return self.should_explain_scoring + def get_should_export_csv(self): + return self.should_export_csv + + def get_explanation_table(self): + return self.explanation_table + def get_formatted_answers_summary(self, answers_summary_format_string=None): if answers_summary_format_string is None: answers_summary_format_string = self.draw_answers_summary[ diff --git a/src/algorithm/template/template.py b/src/algorithm/template/template.py index fd92b16f..fb7d70a1 100644 --- a/src/algorithm/template/template.py +++ b/src/algorithm/template/template.py @@ -122,6 +122,9 @@ def get_multi_marked_dir(self): def get_errors_dir(self): return self.directory_handler.path_utils.errors_dir + def get_eval_dir(self): + return self.directory_handler.path_utils.eval_dir + def read_omr_response(self, input_gray_image, colored_image, file_path): # Convert posix path to string file_path = str(file_path) diff --git a/src/entry.py b/src/entry.py index e43e1aac..9fc1a8ab 100644 --- a/src/entry.py +++ b/src/entry.py @@ -5,6 +5,7 @@ import pandas as pd from rich.table import Table +from rich_tools import table_to_df from src.algorithm.evaluation.config import EvaluationConfig from src.algorithm.evaluation.evaluation import evaluate_concatenated_response @@ -326,9 +327,21 @@ def process_directory_files( logger.info( f"(/{files_counter}) Graded with score: {round(score, 2)}\t {default_answers_summary} \t file: '{file_id}'" ) + if evaluation_config_for_response.get_should_export_csv(): + explanation_table = evaluation_config_for_response.get_explanation_table() + explanation_table = table_to_df(explanation_table) + explanation_table.to_csv( + template.get_eval_dir().joinpath(file_name + ".csv"), + quoting=QUOTE_NONNUMERIC, + index=False, + ) + else: logger.info(f"(/{files_counter}) Processed file: '{file_id}'") + + + # TODO: move this logic inside the class save_marked_dir = template.get_save_marked_dir() @@ -505,4 +518,4 @@ def print_stats(start_time, files_counter, tuning_config): if tuning_config.outputs.show_image_level <= 1: log( "\nTip: To see some awesome visuals, open config.json and increase 'show_image_level'" - ) + ) \ No newline at end of file diff --git a/src/schemas/evaluation_schema.py b/src/schemas/evaluation_schema.py index 716067a5..dc447e5d 100644 --- a/src/schemas/evaluation_schema.py +++ b/src/schemas/evaluation_schema.py @@ -220,6 +220,10 @@ "description": "Whether to print the table explaining question-wise verdicts", "type": "boolean", }, + "should_export_csv": { + "description": "Whether to export the explanation of evaluation results as a CSV file", + "type": "boolean", + }, "draw_score": { "description": "The configuration for drawing the final score", "type": "object", @@ -488,4 +492,4 @@ }, }, "allOf": [*common_evaluation_schema_conditions], -} +} \ No newline at end of file diff --git a/src/utils/file.py b/src/utils/file.py index 1ced89f2..31e67ed2 100644 --- a/src/utils/file.py +++ b/src/utils/file.py @@ -62,6 +62,7 @@ def __init__(self, output_dir): self.manual_dir = output_dir.joinpath("Manual") self.errors_dir = self.manual_dir.joinpath("ErrorFiles") self.multi_marked_dir = self.manual_dir.joinpath("MultiMarkedFiles") + self.eval_dir = output_dir.joinpath("Evaluations") self.debug_dir = output_dir.joinpath("Debug") def create_output_directories(self): @@ -96,6 +97,7 @@ def create_output_directories(self): for save_output_dir in [ self.results_dir, self.image_metrics_dir, + self.eval_dir, ]: if not os.path.exists(save_output_dir): logger.info(f"Created : {save_output_dir}")