Upload app.py with huggingface_hub
Browse files
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 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 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):
|