jon-fernandes commited on
Commit
d8445b2
·
verified ·
1 Parent(s): ca056b9

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +11 -115
app.py CHANGED
@@ -23,11 +23,6 @@ GPT_MAX_COMPLETION_TOKENS = 4096
23
  GPT_REASONING_EFFORT = "none"
24
  NOTES_PROMPT = "Transcribe the musical notes in this image. Return only the transcription."
25
  MAX_PREPROCESSED_SIDE = 1800
26
- TREBLE_TEMPLATE_PATHS = [
27
- "examples/000100005-1_1_1.png",
28
- "examples/000100016-1_3_1.png",
29
- "examples/000100059-1_1_1.png",
30
- ]
31
 
32
  CAMERA_CAPTURE_JS = """
33
  function () {
@@ -125,116 +120,17 @@ def _score_training_orientation(image: Image.Image):
125
  if width == 0 or height == 0:
126
  return -1
127
 
128
- dark = gray < 235
129
- aspect_score = width / max(height, 1)
130
- horizontal_score = 0.0
131
- if width > 40 and height > 20:
132
- thresholded = (dark.astype("uint8") * 255)
133
- horizontal_kernel = cv2.getStructuringElement(
134
- cv2.MORPH_RECT,
135
- (max(width // 20, 25), 1),
136
- )
137
- vertical_kernel = cv2.getStructuringElement(
138
- cv2.MORPH_RECT,
139
- (1, max(height // 8, 8)),
140
- )
141
- horizontal = cv2.morphologyEx(thresholded, cv2.MORPH_OPEN, horizontal_kernel)
142
- vertical = cv2.morphologyEx(thresholded, cv2.MORPH_OPEN, vertical_kernel)
143
- horizontal_score = float(horizontal.mean() - vertical.mean()) / 255.0
144
-
145
- left_band = dark[:, :max(width // 5, 1)]
146
- right_band = dark[:, -max(width // 5, 1):]
147
- top_band = dark[:max(height // 2, 1), :]
148
- bottom_band = dark[-max(height // 2, 1):, :]
149
- left_density = float(left_band.mean())
150
- right_density = float(right_band.mean())
151
- top_density = float(top_band.mean())
152
- bottom_density = float(bottom_band.mean())
153
- clef_score = _score_treble_clef_left(gray)
154
-
155
- # Training images read left-to-right: the clef/key signature should produce
156
- # extra ink near the left edge, and the staff should be a wide horizontal strip.
157
- return (
158
- aspect_score * 3.0
159
- + horizontal_score * 8.0
160
- + (left_density - right_density) * 12.0
161
- + (top_density - bottom_density) * 1.5
162
- + clef_score * 20.0
163
- )
164
-
165
-
166
- def _score_treble_clef_left(gray):
167
- try:
168
- import cv2
169
- import numpy as np
170
- except ImportError:
171
- return 0.0
172
-
173
- templates = _load_treble_templates()
174
- if not templates:
175
- return 0.0
176
-
177
- height, width = gray.shape[:2]
178
- search = gray[:, :max(width // 3, 1)]
179
- search = cv2.resize(search, (max(search.shape[1], 60), max(search.shape[0], 60)))
180
- search = 255 - search
181
- best = 0.0
182
-
183
- for template in templates:
184
- for scale in (0.7, 0.9, 1.1, 1.3):
185
- target_height = max(int(height * 0.7 * scale), 24)
186
- aspect = template.shape[1] / max(template.shape[0], 1)
187
- target_width = max(int(target_height * aspect), 12)
188
- if target_height >= search.shape[0] or target_width >= search.shape[1]:
189
- continue
190
- resized = cv2.resize(template, (target_width, target_height))
191
- result = cv2.matchTemplate(search, resized, cv2.TM_CCOEFF_NORMED)
192
- if result.size:
193
- best = max(best, float(np.max(result)))
194
-
195
- return max(best, 0.0)
196
-
197
-
198
- _TREBLE_TEMPLATES = None
199
-
200
-
201
- def _load_treble_templates():
202
- global _TREBLE_TEMPLATES
203
- if _TREBLE_TEMPLATES is not None:
204
- return _TREBLE_TEMPLATES
205
-
206
- try:
207
- import cv2
208
- import numpy as np
209
- except ImportError:
210
- _TREBLE_TEMPLATES = []
211
- return _TREBLE_TEMPLATES
212
-
213
- templates = []
214
- for path in TREBLE_TEMPLATE_PATHS:
215
- if not os.path.exists(path):
216
- continue
217
- gray = np.array(Image.open(path).convert("L"))
218
- height, width = gray.shape[:2]
219
- left_region = gray[:, :max(width // 5, 1)]
220
- ink = left_region < 235
221
- rows = np.where(ink.any(axis=1))[0]
222
- cols = np.where(ink.any(axis=0))[0]
223
- if len(rows) == 0 or len(cols) == 0:
224
- continue
225
-
226
- top = max(int(rows[0]) - 8, 0)
227
- bottom = min(int(rows[-1]) + 8, height - 1)
228
- left = max(int(cols[0]) - 4, 0)
229
- right = min(int(cols[-1]) + 12, left_region.shape[1] - 1)
230
- template = left_region[top:bottom + 1, left:right + 1]
231
- if template.shape[0] < 20 or template.shape[1] < 8:
232
- continue
233
-
234
- templates.append(255 - cv2.GaussianBlur(template, (3, 3), 0))
235
-
236
- _TREBLE_TEMPLATES = templates
237
- return _TREBLE_TEMPLATES
238
 
239
 
240
  def _normalize_to_training_orientation(image: Image.Image):
 
23
  GPT_REASONING_EFFORT = "none"
24
  NOTES_PROMPT = "Transcribe the musical notes in this image. Return only the transcription."
25
  MAX_PREPROCESSED_SIDE = 1800
 
 
 
 
 
26
 
27
  CAMERA_CAPTURE_JS = """
28
  function () {
 
120
  if width == 0 or height == 0:
121
  return -1
122
 
123
+ # Normalize out uneven camera lighting before comparing densities.
124
+ blur_size = min(max(min(width, height) // 6, 15) | 1, 201)
125
+ background = cv2.medianBlur(gray, blur_size)
126
+ normalized = cv2.divide(gray, background, scale=255)
127
+ ink = normalized < 128
128
+
129
+ band = max(width // 5, 1)
130
+ left_density = float(ink[:, :band].mean())
131
+ right_density = float(ink[:, -band:].mean())
132
+ # Treble clef + key sig always sit on the left, making that band denser.
133
+ return (width / max(height, 1)) * 2.0 + (left_density - right_density) * 15.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
 
136
  def _normalize_to_training_orientation(image: Image.Image):