tkdehf2 commited on
Commit
1adbe0b
ยท
verified ยท
1 Parent(s): c6900bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +99 -44
app.py CHANGED
@@ -3,36 +3,59 @@ from transformers import TrOCRProcessor, VisionEncoderDecoderModel
3
  import torch
4
  from PIL import Image
5
  import numpy as np
 
6
 
7
  # OCR ๋ชจ๋ธ ๋ฐ ํ”„๋กœ์„ธ์„œ ์ดˆ๊ธฐํ™”
8
  processor = TrOCRProcessor.from_pretrained('microsoft/trocr-base-handwritten')
9
  model = VisionEncoderDecoderModel.from_pretrained('microsoft/trocr-base-handwritten')
10
 
11
- # ์ •๋‹ต ๋ฐ ํ•ด์„ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
12
  answer_key = {
13
- "1": {
14
- "answer": "๋ฏผ์ฃผ์ฃผ์˜",
15
- "explanation": "๋ฏผ์ฃผ์ฃผ์˜๋Š” ๊ตญ๋ฏผ์ด ์ฃผ์ธ์ด ๋˜์–ด ๋‚˜๋ผ์˜ ์ค‘์š”ํ•œ ์ผ์„ ๊ฒฐ์ •ํ•˜๋Š” ์ œ๋„์ž…๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋‚˜๋ผ๋Š” ๋ฏผ์ฃผ์ฃผ์˜ ๊ตญ๊ฐ€๋กœ, ๊ตญ๋ฏผ๋“ค์ด ํˆฌํ‘œ๋ฅผ ํ†ตํ•ด ๋Œ€ํ‘œ์ž๋ฅผ ์„ ์ถœํ•˜๊ณ  ์ค‘์š”ํ•œ ๊ฒฐ์ •์— ์ฐธ์—ฌํ•ฉ๋‹ˆ๋‹ค."
16
- },
17
- "2": {
18
- "answer": "์‚ผ๊ถŒ๋ถ„๋ฆฝ",
19
- "explanation": "์‚ผ๊ถŒ๋ถ„๋ฆฝ์€ ์ž…๋ฒ•๋ถ€, ํ–‰์ •๋ถ€, ์‚ฌ๋ฒ•๋ถ€๋กœ ๊ถŒ๋ ฅ์„ ๋‚˜๋ˆ„์–ด ์„œ๋กœ ๊ฒฌ์ œ์™€ ๊ท ํ˜•์„ ์ด๋ฃจ๊ฒŒ ํ•˜๋Š” ์ œ๋„์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ•œ ์ชฝ์— ๊ถŒ๋ ฅ์ด ์ง‘์ค‘๋˜๋Š” ๊ฒƒ์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค."
20
- },
21
- "3": {
22
- "answer": "์ง€๋ฐฉ์ž์น˜์ œ๋„",
23
- "explanation": "์ง€๋ฐฉ์ž์น˜์ œ๋„๋Š” ์ง€์—ญ์˜ ์ผ์„ ๊ทธ ์ง€์—ญ ์ฃผ๋ฏผ๋“ค์ด ์ง์ ‘ ๊ฒฐ์ •ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ์ œ๋„์ž…๋‹ˆ๋‹ค. ์ฃผ๋ฏผ๋“ค์ด ์ง์ ‘ ์ง€๋ฐฉ์ž์น˜๋‹จ์ฒด์žฅ๊ณผ ์ง€๋ฐฉ์˜ํšŒ ์˜์›์„ ์„ ์ถœํ•ฉ๋‹ˆ๋‹ค."
24
- }
25
  }
26
 
27
- def preprocess_image(image):
28
- """์ด๋ฏธ์ง€ ์ „์ฒ˜๋ฆฌ ํ•จ์ˆ˜"""
29
  if isinstance(image, np.ndarray):
30
- image = Image.fromarray(image)
31
- return image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  def recognize_text(image):
34
  """์†๊ธ€์”จ ์ธ์‹ ํ•จ์ˆ˜"""
35
- image = preprocess_image(image)
 
 
36
  pixel_values = processor(image, return_tensors="pt").pixel_values
37
 
38
  with torch.no_grad():
@@ -43,6 +66,10 @@ def recognize_text(image):
43
 
44
  def grade_answer(question_number, student_answer):
45
  """๋‹ต์•ˆ ์ฑ„์  ํ•จ์ˆ˜"""
 
 
 
 
46
  correct_answer = answer_key[question_number]["answer"]
47
  explanation = answer_key[question_number]["explanation"]
48
 
@@ -50,44 +77,72 @@ def grade_answer(question_number, student_answer):
50
  is_correct = student_answer.replace(" ", "").lower() == correct_answer.replace(" ", "").lower()
51
 
52
  return {
53
- "์ •๋‹ต ์—ฌ๋ถ€": "์ •๋‹ต" if is_correct else "์˜ค๋‹ต",
 
 
54
  "์ •๋‹ต": correct_answer,
55
  "ํ•ด์„ค": explanation
56
  }
57
 
58
- def process_submission(image, question_number):
59
- """์ „์ฒด ์ฒ˜๋ฆฌ ํ•จ์ˆ˜"""
60
- if not image or not question_number:
61
- return "์ด๋ฏธ์ง€์™€ ๋ฌธ์ œ ๋ฒˆํ˜ธ๋ฅผ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."
62
 
63
- # ์†๊ธ€์”จ ์ธ์‹
64
- recognized_text = recognize_text(image)
65
-
66
- # ์ฑ„์  ๋ฐ ํ•ด์„ค
67
- result = grade_answer(question_number, recognized_text)
68
-
69
- # ๊ฒฐ๊ณผ ํฌ๋งทํŒ…
70
- output = f"""
71
- ์ธ์‹๋œ ๋‹ต์•ˆ: {recognized_text}
72
- ์ฑ„์  ๊ฒฐ๊ณผ: {result['์ •๋‹ต ์—ฌ๋ถ€']}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  ์ •๋‹ต: {result['์ •๋‹ต']}
74
-
75
- [ํ•ด์„ค]
76
- {result['ํ•ด์„ค']}
77
- """
78
 
79
- return output
 
80
 
81
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
82
  iface = gr.Interface(
83
- fn=process_submission,
84
- inputs=[
85
- gr.Image(label="๋‹ต์•ˆ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜์„ธ์š”", type="numpy"),
86
- gr.Dropdown(choices=["1", "2", "3"], label="๋ฌธ์ œ ๋ฒˆํ˜ธ๋ฅผ ์„ ํƒํ•˜์„ธ์š”")
87
- ],
88
  outputs=gr.Textbox(label="์ฑ„์  ๊ฒฐ๊ณผ"),
89
  title="์ดˆ๋“ฑํ•™๊ต ์‚ฌํšŒ ์‹œํ—˜์ง€ ์ฑ„์  ํ”„๋กœ๊ทธ๋žจ",
90
- description="์†๊ธ€์”จ๋กœ ์ž‘์„ฑ๋œ ์‚ฌํšŒ ์‹œํ—˜ ๋‹ต์•ˆ์„ ์ฑ„์ ํ•˜๊ณ  ํ•ด์„ค์„ ์ œ๊ณตํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค.",
 
 
 
 
91
  )
92
 
93
  if __name__ == "__main__":
 
3
  import torch
4
  from PIL import Image
5
  import numpy as np
6
+ import cv2
7
 
8
  # OCR ๋ชจ๋ธ ๋ฐ ํ”„๋กœ์„ธ์„œ ์ดˆ๊ธฐํ™”
9
  processor = TrOCRProcessor.from_pretrained('microsoft/trocr-base-handwritten')
10
  model = VisionEncoderDecoderModel.from_pretrained('microsoft/trocr-base-handwritten')
11
 
12
+ # ์ •๋‹ต ๋ฐ ํ•ด์„ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค (20๋ฌธ์ œ)
13
  answer_key = {
14
+ "1": {"answer": "๋ฏผ์ฃผ์ฃผ์˜", "explanation": "๋ฏผ์ฃผ์ฃผ์˜๋Š” ๊ตญ๋ฏผ์ด ์ฃผ์ธ์ด ๋˜์–ด ๋‚˜๋ผ์˜ ์ค‘์š”ํ•œ ์ผ์„ ๊ฒฐ์ •ํ•˜๋Š” ์ œ๋„์ž…๋‹ˆ๋‹ค."},
15
+ "2": {"answer": "์‚ผ๊ถŒ๋ถ„๋ฆฝ", "explanation": "์‚ผ๊ถŒ๋ถ„๋ฆฝ์€ ์ž…๋ฒ•๋ถ€, ํ–‰์ •๋ถ€, ์‚ฌ๋ฒ•๋ถ€๋กœ ๊ถŒ๋ ฅ์„ ๋‚˜๋ˆ„์–ด ์„œ๋กœ ๊ฒฌ์ œ์™€ ๊ท ํ˜•์„ ์ด๋ฃจ๊ฒŒ ํ•˜๋Š” ์ œ๋„์ž…๋‹ˆ๋‹ค."},
16
+ "3": {"answer": "์ง€๋ฐฉ์ž์น˜์ œ๋„", "explanation": "์ง€๋ฐฉ์ž์น˜์ œ๋„๋Š” ์ง€์—ญ์˜ ์ผ์„ ๊ทธ ์ง€์—ญ ์ฃผ๋ฏผ๋“ค์ด ์ง์ ‘ ๊ฒฐ์ •ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ์ œ๋„์ž…๋‹ˆ๋‹ค."},
17
+ "4": {"answer": "ํ—Œ๋ฒ•", "explanation": "ํ—Œ๋ฒ•์€ ๊ตญ๊ฐ€์˜ ์ตœ๊ณ  ๋ฒ•์œผ๋กœ, ๊ตญ๋ฏผ์˜ ๊ธฐ๋ณธ๊ถŒ๊ณผ ์ •๋ถ€ ์กฐ์ง์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์›์น™์„ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค."},
18
+ "5": {"answer": "๊ตญํšŒ", "explanation": "๊ตญํšŒ๋Š” ๋ฒ•๋ฅ ์„ ๋งŒ๋“ค๊ณ  ์ •๋ถ€๋ฅผ ๊ฐ์‹œํ•˜๋Š” ์ž…๋ฒ•๋ถ€์˜ ์—ญํ• ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค."},
19
+ # 6~20๋ฒˆ๊นŒ์ง€ ๋ฌธ์ œ ์ถ”๊ฐ€ (์‹ค์ œ ์šด์˜ ์‹œ์—๋Š” ์—ฌ๊ธฐ์— ์ถ”๊ฐ€)
 
 
 
 
 
 
20
  }
21
 
22
+ def segment_answers(image):
23
+ """์‹œํ—˜์ง€์—์„œ ๋‹ต์•ˆ ์˜์—ญ์„ ๋ถ„ํ• ํ•˜๋Š” ํ•จ์ˆ˜"""
24
  if isinstance(image, np.ndarray):
25
+ pil_image = Image.fromarray(image)
26
+ else:
27
+ return None
28
+
29
+ # ์ด๋ฏธ์ง€๋ฅผ ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ๋กœ ๋ณ€ํ™˜
30
+ gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
31
+
32
+ # ์ด๋ฏธ์ง€ ์ด์ง„ํ™”
33
+ _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
34
+
35
+ # ์œค๊ณฝ์„  ์ฐพ๊ธฐ
36
+ contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
37
+
38
+ # ๋‹ต์•ˆ ์˜์—ญ ์ถ”์ถœ
39
+ answer_regions = []
40
+ for contour in contours:
41
+ x, y, w, h = cv2.boundingRect(contour)
42
+ if w > 50 and h > 20: # ์ตœ์†Œ ํฌ๊ธฐ ํ•„ํ„ฐ๋ง
43
+ region = image[y:y+h, x:x+w]
44
+ answer_regions.append({
45
+ 'image': region,
46
+ 'position': (y, x) # y์ขŒํ‘œ๋กœ ์ •๋ ฌํ•˜๊ธฐ ์œ„ํ•ด (y,x) ์ˆœ์„œ๋กœ ์ €์žฅ
47
+ })
48
+
49
+ # y์ขŒํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ (์œ„์—์„œ ์•„๋ž˜๋กœ)
50
+ answer_regions.sort(key=lambda x: x['position'][0])
51
+
52
+ return [region['image'] for region in answer_regions]
53
 
54
  def recognize_text(image):
55
  """์†๊ธ€์”จ ์ธ์‹ ํ•จ์ˆ˜"""
56
+ if isinstance(image, np.ndarray):
57
+ image = Image.fromarray(image)
58
+
59
  pixel_values = processor(image, return_tensors="pt").pixel_values
60
 
61
  with torch.no_grad():
 
66
 
67
  def grade_answer(question_number, student_answer):
68
  """๋‹ต์•ˆ ์ฑ„์  ํ•จ์ˆ˜"""
69
+ question_number = str(question_number)
70
+ if question_number not in answer_key:
71
+ return None
72
+
73
  correct_answer = answer_key[question_number]["answer"]
74
  explanation = answer_key[question_number]["explanation"]
75
 
 
77
  is_correct = student_answer.replace(" ", "").lower() == correct_answer.replace(" ", "").lower()
78
 
79
  return {
80
+ "๋ฌธ์ œ๋ฒˆํ˜ธ": question_number,
81
+ "ํ•™์ƒ๋‹ต์•ˆ": student_answer,
82
+ "์ •๋‹ต์—ฌ๋ถ€": "O" if is_correct else "X",
83
  "์ •๋‹ต": correct_answer,
84
  "ํ•ด์„ค": explanation
85
  }
86
 
87
+ def process_full_exam(image):
88
+ """์ „์ฒด ์‹œํ—˜์ง€ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜"""
89
+ if image is None or not isinstance(image, np.ndarray):
90
+ return "์‹œํ—˜์ง€ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•ด์ฃผ์„ธ์š”."
91
 
92
+ try:
93
+ # ๋‹ต์•ˆ ์˜์—ญ ๋ถ„ํ• 
94
+ answer_regions = segment_answers(image)
95
+ if not answer_regions:
96
+ return "๋‹ต์•ˆ ์˜์—ญ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”."
97
+
98
+ # ์ฑ„์  ๊ฒฐ๊ณผ ์ €์žฅ
99
+ results = []
100
+ total_correct = 0
101
+
102
+ # ๊ฐ ๋‹ต์•ˆ ์˜์—ญ ์ฒ˜๋ฆฌ
103
+ for idx, region in enumerate(answer_regions, 1):
104
+ if idx > len(answer_key): # ์ •์˜๋œ ๋ฌธ์ œ ์ˆ˜๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ์ค‘๋‹จ
105
+ break
106
+
107
+ # ํ…์ŠคํŠธ ์ธ์‹
108
+ recognized_text = recognize_text(region)
109
+
110
+ # ์ฑ„์ 
111
+ result = grade_answer(idx, recognized_text)
112
+ if result:
113
+ results.append(result)
114
+ if result["์ •๋‹ต์—ฌ๋ถ€"] == "O":
115
+ total_correct += 1
116
+
117
+ # ๊ฒฐ๊ณผ ํฌ๋งทํŒ…
118
+ score = (total_correct / len(results)) * 100
119
+ output = f"์ด์ : {score:.1f}์  (20๋ฌธ์ œ ์ค‘ {total_correct}๊ฐœ ์ •๋‹ต)\n\n"
120
+ output += "=== ์ƒ์„ธ ์ฑ„์  ๊ฒฐ๊ณผ ===\n\n"
121
+
122
+ for result in results:
123
+ output += f"""
124
+ [{result['๋ฌธ์ œ๋ฒˆํ˜ธ']}๋ฒˆ] {'โœ“' if result['์ •๋‹ต์—ฌ๋ถ€']=='O' else 'โœ—'}
125
+ ํ•™์ƒ๋‹ต์•ˆ: {result['ํ•™์ƒ๋‹ต์•ˆ']}
126
  ์ •๋‹ต: {result['์ •๋‹ต']}
127
+ ํ•ด์„ค: {result['ํ•ด์„ค']}
128
+ """
129
+
130
+ return output
131
 
132
+ except Exception as e:
133
+ return f"์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
134
 
135
  # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
136
  iface = gr.Interface(
137
+ fn=process_full_exam,
138
+ inputs=gr.Image(label="์‹œํ—˜์ง€ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜์„ธ์š”", type="numpy"),
 
 
 
139
  outputs=gr.Textbox(label="์ฑ„์  ๊ฒฐ๊ณผ"),
140
  title="์ดˆ๋“ฑํ•™๊ต ์‚ฌํšŒ ์‹œํ—˜์ง€ ์ฑ„์  ํ”„๋กœ๊ทธ๋žจ",
141
+ description="""
142
+ ์ „์ฒด ์‹œํ—˜์ง€๋ฅผ ํ•œ ๋ฒˆ์— ์ฑ„์ ํ•˜๋Š” ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค.
143
+ ์‹œํ—˜์ง€์˜ ๋‹ต์•ˆ์ด ์ž˜ ๋ณด์ด๋„๋ก ๊นจ๋—ํ•˜๊ฒŒ ์Šค์บ”ํ•˜๊ฑฐ๋‚˜ ์ดฌ์˜ํ•ด์ฃผ์„ธ์š”.
144
+ """,
145
+ examples=[], # ์˜ˆ์‹œ ์ด๋ฏธ์ง€๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
146
  )
147
 
148
  if __name__ == "__main__":