Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added feature to export the evaluation summary table in csv format #229

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion samples/sample4/evaluation.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"D",
"C"
],
"should_explain_scoring": true
"should_explain_scoring": true,
"enable_evaluation_table_to_csv": true
},
"marking_schemes": {
"DEFAULT": {
Expand Down
2 changes: 1 addition & 1 deletion src/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ def read_omr_response(self, template, image, name, save_dir=None):
global_thr, _, _ = self.get_global_threshold(all_q_vals, looseness=4)

logger.info(
f"Thresholding:\tglobal_thr: {round(global_thr, 2)} \tglobal_std_THR: {round(global_std_thresh, 2)}\t{'(Looks like a Xeroxed OMR)' if (global_thr == 255) else ''}"
f"Thresholding: \tglobal_thr: {round(global_thr, 2)} \tglobal_std_THR: {round(global_std_thresh, 2)}\t{'(Looks like a Xeroxed OMR)' if (global_thr == 255) else ''}"
)
# plt.show()
# hist = getPlotImg()
Expand Down
21 changes: 13 additions & 8 deletions src/entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ def process_files(
for file_path in omr_files:
files_counter += 1
file_name = file_path.name
evaluation_path = os.path.join(
outputs_namespace.paths.evaluation_dir, file_path.stem
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's directly pass evaluation_output_dir (= outputs_namespace.paths.evaluation_dir) into the function below

)

in_omr = cv2.imread(str(file_path), cv2.IMREAD_GRAYSCALE)

Expand Down Expand Up @@ -273,7 +276,9 @@ def process_files(

score = 0
if evaluation_config is not None:
score = evaluate_concatenated_response(omr_response, evaluation_config)
score = evaluate_concatenated_response(
omr_response, evaluation_config, evaluation_path
)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
score = evaluate_concatenated_response(
omr_response, evaluation_config, evaluation_path
)
score = evaluate_concatenated_response(
omr_response, evaluation_config, file_path, evaluation_output_dir
)

logger.info(
f"(/{files_counter}) Graded with score: {round(score, 2)}\t for file: '{file_id}'"
)
Expand Down Expand Up @@ -342,25 +347,25 @@ def print_stats(start_time, files_counter, tuning_config):
time_checking = max(1, round(time() - start_time, 2))
log = logger.info
log("")
log(f"{'Total file(s) moved':<27}: {STATS.files_moved}")
log(f"{'Total file(s) not moved':<27}: {STATS.files_not_moved}")
log(f"{'Total file(s) moved': <27}: {STATS.files_moved}")
log(f"{'Total file(s) not moved': <27}: {STATS.files_not_moved}")
log("--------------------------------")
log(
f"{'Total file(s) processed':<27}: {files_counter} ({'Sum Tallied!' if files_counter == (STATS.files_moved + STATS.files_not_moved) else 'Not Tallying!'})"
f"{'Total file(s) processed': <27}: {files_counter} ({'Sum Tallied!' if files_counter == (STATS.files_moved + STATS.files_not_moved) else 'Not Tallying!'})"
)

if tuning_config.outputs.show_image_level <= 0:
log(
f"\nFinished Checking {files_counter} file(s) in {round(time_checking, 1)} seconds i.e. ~{round(time_checking/60, 1)} minute(s)."
f"\nFinished Checking {files_counter} file(s) in {round(time_checking, 1)} seconds i.e. ~{round(time_checking / 60, 1)} minute(s)."
)
log(
f"{'OMR Processing Rate':<27}:\t ~ {round(time_checking/files_counter,2)} seconds/OMR"
f"{'OMR Processing Rate': <27}: \t ~ {round(time_checking / files_counter, 2)} seconds/OMR"
)
log(
f"{'OMR Processing Speed':<27}:\t ~ {round((files_counter * 60) / time_checking, 2)} OMRs/minute"
f"{'OMR Processing Speed': <27}: \t ~ {round((files_counter * 60) / time_checking, 2)} OMRs/minute"
)
else:
log(f"\n{'Total script time':<27}: {time_checking} seconds")
log(f"\n{'Total script time': <27}: {time_checking} seconds")

if tuning_config.outputs.show_image_level <= 1:
log(
Expand Down
26 changes: 25 additions & 1 deletion src/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import re
from copy import deepcopy
from csv import QUOTE_NONNUMERIC

import cv2
import pandas as pd
Expand Down Expand Up @@ -197,6 +198,9 @@ def __init__(self, curr_dir, evaluation_path, template, tuning_config):
self.should_explain_scoring = options.get("should_explain_scoring", False)
self.has_non_default_section = False
self.exclude_files = []
self.enable_evaluation_table_to_csv = options.get(
"enable_evaluation_table_to_csv", False
)

if source_type == "csv":
csv_path = curr_dir.joinpath(options["answer_key_csv_path"])
Expand Down Expand Up @@ -356,6 +360,23 @@ def conditionally_print_explanation(self):
if self.should_explain_scoring:
console.print(self.explanation_table, justify="center")

# Explanation Table to CSV
def conditionally_save_explanation_csv(self, evaluation_path):
if self.enable_evaluation_table_to_csv:
data = {col.header: col._cells for col in self.explanation_table.columns}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. Can you add a screenshot of what the output csv looks like now?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Screenshot from 2024-10-13 17-10-38
Screenshot from 2024-10-13 17-11-53


output_dir = os.path.join(
os.getcwd(),
f"{evaluation_path}.csv",
)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's use evaluation_output_dir here

Suggested change
def conditionally_save_explanation_csv(self, evaluation_path):
if self.enable_evaluation_table_to_csv:
data = {col.header: col._cells for col in self.explanation_table.columns}
output_dir = os.path.join(
os.getcwd(),
f"{evaluation_path}.csv",
)
def conditionally_save_explanation_csv(self, file_path, evaluation_output_dir):
if self.enable_evaluation_table_to_csv:
data = {col.header: col._cells for col in self.explanation_table.columns}
output_path = os.path.join(
evaluation_output_dir,
f"{file_path.stem}_evaluation.csv",
)


pd.DataFrame(data, dtype=str).to_csv(
output_dir,
mode="a",
quoting=QUOTE_NONNUMERIC,
index=False,
)

def get_should_explain_scoring(self):
return self.should_explain_scoring

Expand Down Expand Up @@ -507,7 +528,9 @@ def conditionally_add_explanation(
self.explanation_table.add_row(*row)


def evaluate_concatenated_response(concatenated_response, evaluation_config):
def evaluate_concatenated_response(
concatenated_response, evaluation_config, evaluation_path
):
evaluation_config.prepare_and_validate_omr_response(concatenated_response)
current_score = 0.0
for question in evaluation_config.questions_in_order:
Expand All @@ -518,5 +541,6 @@ def evaluate_concatenated_response(concatenated_response, evaluation_config):
current_score += delta

evaluation_config.conditionally_print_explanation()
evaluation_config.conditionally_save_explanation_csv(evaluation_path)

return current_score
4 changes: 4 additions & 0 deletions src/schemas/evaluation_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@
]
},
"questions_in_order": ARRAY_OF_STRINGS,
"enable_evaluation_table_to_csv": {
"type": "boolean",
"default": False,
},
},
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/utils/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def __init__(self, output_dir):
self.save_marked_dir = output_dir.joinpath("CheckedOMRs")
self.results_dir = output_dir.joinpath("Results")
self.manual_dir = output_dir.joinpath("Manual")
self.evaluation_dir = output_dir.joinpath("Evaluation")
self.errors_dir = self.manual_dir.joinpath("ErrorFiles")
self.multi_marked_dir = self.manual_dir.joinpath("MultiMarkedFiles")

Expand All @@ -44,6 +45,11 @@ def setup_dirs_for_paths(paths):
logger.info(f"Created : {save_output_dir}")
os.makedirs(save_output_dir)

for save_output_dir in [paths.evaluation_dir]:
if not os.path.exists(save_output_dir):
logger.info(f"Created : {save_output_dir}")
os.makedirs(save_output_dir)

for save_output_dir in [paths.multi_marked_dir, paths.errors_dir]:
if not os.path.exists(save_output_dir):
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for save_output_dir in [paths.evaluation_dir]:
if not os.path.exists(save_output_dir):
logger.info(f"Created : {save_output_dir}")
os.makedirs(save_output_dir)
for save_output_dir in [paths.multi_marked_dir, paths.errors_dir]:
if not os.path.exists(save_output_dir):
for save_output_dir in [paths.multi_marked_dir, paths.errors_dir, paths.evaluation_dir]:
if not os.path.exists(save_output_dir):

logger.info(f"Created : {save_output_dir}")
Expand Down