fix(cv): resolve infinite page duplication bug caused by playback cursor
This commit is contained in:
50
scripts/debug/dump_measure_numbers.py
Normal file
50
scripts/debug/dump_measure_numbers.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
pdf_path = r"C:\Users\Certes\Desktop\guitar_score\output\shintakarajima_perfect.pdf"
|
||||
out_dir = r"C:\Users\Certes\Desktop\guitar_score\scripts\debug"
|
||||
|
||||
img_path = os.path.join(out_dir, "verify_chunk_0.jpg") # from previous sessions
|
||||
if not os.path.exists(img_path):
|
||||
print("verify_chunk_0.jpg not found. extracting a page from PDF...")
|
||||
import fitz
|
||||
doc = fitz.open(pdf_path)
|
||||
page = doc.load_page(0)
|
||||
pix = page.get_pixmap(dpi=150)
|
||||
pix.save(os.path.join(out_dir, "pdf_test_page.png"))
|
||||
img_path = os.path.join(out_dir, "pdf_test_page.png")
|
||||
|
||||
img = cv2.imread(img_path)
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
_, bin_inv = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
|
||||
|
||||
h, w = img.shape[:2]
|
||||
staff_y_top = int(h * 0.3)
|
||||
row_sums = np.sum(bin_inv[:, :min(w, 1000)], axis=1) / 255.0
|
||||
staff_rows = np.where(row_sums > min(w, 1000) * 0.4)[0]
|
||||
if len(staff_rows) >= 6: staff_y_top = staff_rows[0]
|
||||
|
||||
# locate measure bars
|
||||
col_sums = np.sum(bin_inv[staff_y_top:staff_y_top+100, :], axis=0) / 255.0
|
||||
bar_xs = np.where(col_sums > 30)[0]
|
||||
bars = []
|
||||
if len(bar_xs) > 0:
|
||||
curr = [bar_xs[0]]
|
||||
for x in bar_xs[1:]:
|
||||
if x - curr[-1] < 10: curr.append(x)
|
||||
else:
|
||||
bars.append(int(np.mean(curr)))
|
||||
curr = [x]
|
||||
bars.append(int(np.mean(curr)))
|
||||
|
||||
# Crop first 5 measure numbers
|
||||
for i, x in enumerate(bars[:5]):
|
||||
box_y1 = max(0, staff_y_top - 40)
|
||||
box_y2 = staff_y_top
|
||||
box_x1 = x
|
||||
box_x2 = min(w, x + 60)
|
||||
crop = img[box_y1:box_y2, box_x1:box_x2]
|
||||
out_file = os.path.join(out_dir, f"measure_num_{i}.png")
|
||||
cv2.imwrite(out_file, crop)
|
||||
print(f"Saved measure number crop to {out_file}")
|
||||
@@ -1,78 +0,0 @@
|
||||
import cv2
|
||||
from video_cv_tracker import TemporalTracker
|
||||
from youtube_tab_to_pdf import extract_unique_scroll, generate_long_image, generate_pdf, download_video, extract_frames
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Run verification specifically on Shintakarajima
|
||||
url = "https://youtu.be/tJq1n8TofM0"
|
||||
video_path = Path("output/サカナクション/新宝島(エレキギターTAB) 難易度★★★ sakanaction shintakarajima.mp4")
|
||||
|
||||
print("Extracting full video for final 142-measure verification...")
|
||||
cap = cv2.VideoCapture(str(video_path))
|
||||
|
||||
# PRE-CALCULATE Dynamic Crop
|
||||
# Just like extract_unique_scroll does automatically, we detect the white band.
|
||||
ret, initial = cap.read()
|
||||
scale = 1280 / initial.shape[1]
|
||||
resized_init = cv2.resize(initial, (1280, int(initial.shape[0] * scale)))
|
||||
|
||||
from youtube_tab_to_pdf import _find_white_tab_strip
|
||||
crop_top = 0
|
||||
crop_bottom = resized_init.shape[0]
|
||||
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, 500)
|
||||
ret, check_frame = cap.read()
|
||||
if ret:
|
||||
resized_check = cv2.resize(check_frame, (1280, int(check_frame.shape[0] * scale)))
|
||||
bounds = _find_white_tab_strip(resized_check)
|
||||
if bounds:
|
||||
crop_top, crop_bottom = bounds
|
||||
# Preserve D.S. al Coda, ┌─ 1., ┌─ 2., and measure numbers drawn in the black abyss!
|
||||
crop_top = max(0, crop_top - 60)
|
||||
|
||||
print(f"Dynamically Cropping to: Y={crop_top} to {crop_bottom}")
|
||||
|
||||
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
|
||||
frames = []
|
||||
idx = 0
|
||||
tracker = TemporalTracker(diff_threshold=0.05)
|
||||
|
||||
while True:
|
||||
ret, frame = cap.read()
|
||||
if not ret: break
|
||||
|
||||
frame_resized = cv2.resize(frame, (1280, int(frame.shape[0] * scale)))
|
||||
clean_ribbon = frame_resized[crop_top:crop_bottom, :]
|
||||
frames.append(clean_ribbon)
|
||||
idx += 1
|
||||
|
||||
cap.release()
|
||||
|
||||
cv2.imwrite("C:/Users/Certes/.gemini/antigravity/brain/975cea00-dd68-4689-9ee3-f1a2408b4ee6/raw_frame_check.png", frames[30])
|
||||
|
||||
print(f"Extracted {len(frames)} frames. Running sequential page extraction...")
|
||||
try:
|
||||
final_chunks = extract_unique_scroll(frames)
|
||||
print("DEBUG: final_chunks len =", len(final_chunks))
|
||||
if final_chunks:
|
||||
print("DEBUG: final_chunks[0].shape =", final_chunks[0].shape)
|
||||
cv2.imwrite("C:/Users/Certes/.gemini/antigravity/brain/975cea00-dd68-4689-9ee3-f1a2408b4ee6/debug_chunk_0.png", final_chunks[0])
|
||||
|
||||
# Save the chunks to artifact directory to literally look at it
|
||||
artifact_path = Path(os.environ.get('APPDATA', '')) / '..' / 'Local' / 'Google' / 'AndroidStudio2024.1' # Just using relative artifact manually? No, I'll save it to C:\Users\Certes\.gemini\antigravity\brain\975cea00-dd68-4689-9ee3-f1a2408b4ee6\
|
||||
artifact_path = Path(r"C:\Users\Certes\.gemini\antigravity\brain\975cea00-dd68-4689-9ee3-f1a2408b4ee6")
|
||||
output_png = artifact_path / "final_check_100_sec.png"
|
||||
|
||||
generate_long_image(final_chunks, output_png)
|
||||
print(f"Saved successful verification image to: {output_png}")
|
||||
|
||||
if final_chunks:
|
||||
generate_pdf(final_chunks, Path("output/shintakarajima_perfect.pdf"))
|
||||
print("✨ Successfully generated output/shintakarajima_perfect.pdf ✨")
|
||||
else:
|
||||
print("Failed to produce rows.")
|
||||
except Exception as e:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
BIN
scripts/debug/measure_num_0.png
Normal file
BIN
scripts/debug/measure_num_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 694 B |
BIN
scripts/debug/measure_num_1.png
Normal file
BIN
scripts/debug/measure_num_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
scripts/debug/measure_num_2.png
Normal file
BIN
scripts/debug/measure_num_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 751 B |
BIN
scripts/debug/measure_num_3.png
Normal file
BIN
scripts/debug/measure_num_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 697 B |
BIN
scripts/debug/measure_num_4.png
Normal file
BIN
scripts/debug/measure_num_4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 283 B |
24
scripts/debug/print_ascii.py
Normal file
24
scripts/debug/print_ascii.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import sys
|
||||
|
||||
def print_ascii(img_path):
|
||||
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
|
||||
if img is None: return
|
||||
_, bin_inv = cv2.threshold(img, 200, 255, cv2.THRESH_BINARY_INV)
|
||||
|
||||
# Trim empty borders
|
||||
coords = cv2.findNonZero(bin_inv)
|
||||
if coords is not None:
|
||||
x,y,w,h = cv2.boundingRect(coords)
|
||||
bin_inv = bin_inv[y:y+h, x:x+w]
|
||||
|
||||
print(f"\nImage: {img_path}")
|
||||
for row in range(0, bin_inv.shape[0], 2):
|
||||
line = ""
|
||||
for col in range(0, bin_inv.shape[1], 1):
|
||||
line += "##" if bin_inv[row, col] > 127 else " "
|
||||
print(line)
|
||||
|
||||
for i in range(5):
|
||||
print_ascii(rf"C:\Users\Certes\Desktop\guitar_score\scripts\debug\measure_num_{i}.png")
|
||||
36
scripts/debug/render_pdf.py
Normal file
36
scripts/debug/render_pdf.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import fitz # PyMuPDF
|
||||
except ImportError:
|
||||
print("fitz not found, trying to install PyMuPDF...")
|
||||
os.system(f"{sys.executable} -m pip install PyMuPDF")
|
||||
import fitz
|
||||
|
||||
pdf_path = r"C:\Users\Certes\Desktop\guitar_score\output\shintakarajima_perfect.pdf"
|
||||
out_dir = r"C:\Users\Certes\.gemini\antigravity\brain\5805a1e3-c776-4325-8538-351d54b5e0a0"
|
||||
|
||||
try:
|
||||
doc = fitz.open(pdf_path)
|
||||
md_content = "# PDF Visual Inspection\n\n```carousel\n"
|
||||
|
||||
for i in range(min(5, len(doc))): # First 5 pages are enough to see the tangling
|
||||
page = doc.load_page(i)
|
||||
pix = page.get_pixmap(dpi=150)
|
||||
out_file = os.path.join(out_dir, f"pdf_page_{i}.png")
|
||||
pix.save(out_file)
|
||||
|
||||
if i > 0:
|
||||
md_content += "<!-- slide -->\n"
|
||||
md_content += f"\n"
|
||||
|
||||
md_content += "```\n"
|
||||
|
||||
with open(os.path.join(out_dir, "PDF_Inspection.md"), "w", encoding="utf-8") as f:
|
||||
f.write(md_content)
|
||||
|
||||
print("PDF successfully rendered to images and markdown generated.")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error rendering PDF: {e}")
|
||||
87
scripts/debug/rigorous_validator.py
Normal file
87
scripts/debug/rigorous_validator.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import fitz
|
||||
import cv2
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
pdf_path = "output/shintakarajima_perfect.pdf"
|
||||
if not os.path.exists(pdf_path):
|
||||
print("PDF not found!")
|
||||
exit(1)
|
||||
|
||||
doc = fitz.open(pdf_path)
|
||||
total_measures = 0
|
||||
measure_widths = []
|
||||
|
||||
print("Running Rigorous Visual Validation against PDF...")
|
||||
|
||||
for page_num in range(len(doc)):
|
||||
page = doc.load_page(page_num)
|
||||
pix = page.get_pixmap(dpi=150)
|
||||
img = np.frombuffer(pix.samples, dtype=np.uint8).reshape(pix.h, pix.w, pix.n)
|
||||
if img.shape[2] == 4:
|
||||
img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
|
||||
elif img.shape[2] == 1:
|
||||
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
|
||||
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
_, bin_inv = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
|
||||
|
||||
row_sums = np.sum(bin_inv, axis=1) / 255.0
|
||||
staff_rows = np.where(row_sums > bin_inv.shape[1] * 0.4)[0]
|
||||
|
||||
if len(staff_rows) == 0: continue
|
||||
|
||||
staves = []
|
||||
curr = [staff_rows[0]]
|
||||
for y in staff_rows[1:]:
|
||||
if y - curr[-1] < 100: curr.append(y)
|
||||
else:
|
||||
staves.append((curr[0], curr[-1]))
|
||||
curr = [y]
|
||||
staves.append((curr[0], curr[-1]))
|
||||
|
||||
page_measures = 0
|
||||
for i, (top, bottom) in enumerate(staves):
|
||||
top = max(0, top - 20)
|
||||
bottom = min(img.shape[0], bottom + 20)
|
||||
|
||||
staff_region = bin_inv[top:bottom, :]
|
||||
col_sums = np.sum(staff_region, axis=0) / 255.0
|
||||
|
||||
expected_h = bottom - top
|
||||
bar_xs = np.where(col_sums >= expected_h * 0.5)[0]
|
||||
|
||||
grouped_bars = []
|
||||
if len(bar_xs) > 0:
|
||||
c = [bar_xs[0]]
|
||||
for x in bar_xs[1:]:
|
||||
if x - c[-1] < 10: c.append(x)
|
||||
else:
|
||||
grouped_bars.append(int(np.mean(c)))
|
||||
c = [x]
|
||||
grouped_bars.append(int(np.mean(c)))
|
||||
|
||||
if len(grouped_bars) > 1:
|
||||
diffs = np.diff(grouped_bars)
|
||||
for d in diffs:
|
||||
if d > 120: # filter out double bars ||
|
||||
measure_widths.append(d)
|
||||
page_measures += 1
|
||||
total_measures += 1
|
||||
|
||||
print(f"Page {page_num+1:02d}: Extracted {page_measures} measures.")
|
||||
|
||||
print("\n--- Absolute Verdict ---")
|
||||
print(f"Total True Measures Counted: {total_measures}")
|
||||
if measure_widths:
|
||||
print(f"Minimum Width: {np.min(measure_widths):.1f} px")
|
||||
print(f"Maximum Width: {np.max(measure_widths):.1f} px")
|
||||
print(f"Average Width: {np.mean(measure_widths):.1f} px")
|
||||
|
||||
anomalies = [i for i, w in enumerate(measure_widths) if w > 1200 or w < 150]
|
||||
if anomalies:
|
||||
print(f"FAILED: Found {len(anomalies)} structural anomalies (broken or fused measures)!")
|
||||
else:
|
||||
print("PASS: 100% of measures have perfectly valid, biologically possible widths. No mangled Frankesteins.")
|
||||
else:
|
||||
print("FAILED: No measures found!")
|
||||
31
scripts/debug/slice_for_ai.py
Normal file
31
scripts/debug/slice_for_ai.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import cv2
|
||||
import os
|
||||
|
||||
img_path = r"C:\Users\Certes\.gemini\antigravity\brain\975cea00-dd68-4689-9ee3-f1a2408b4ee6\final_check_100_sec.png"
|
||||
out_dir = r"C:\Users\Certes\.gemini\antigravity\brain\5805a1e3-c776-4325-8538-351d54b5e0a0"
|
||||
|
||||
img = cv2.imread(img_path)
|
||||
h, w = img.shape[:2]
|
||||
|
||||
slice_h = 1000
|
||||
num_slices = (h + slice_h - 1) // slice_h
|
||||
|
||||
md_content = "# Visual Inspection of final_check_100_sec.png\n\n```carousel\n"
|
||||
|
||||
for i in range(num_slices):
|
||||
y_start = i * slice_h
|
||||
y_end = min((i + 1) * slice_h, h)
|
||||
slice_img = img[y_start:y_end, :]
|
||||
out_file = os.path.join(out_dir, f"ai_slice_{i}.png")
|
||||
cv2.imwrite(out_file, slice_img)
|
||||
|
||||
if i > 0:
|
||||
md_content += "<!-- slide -->\n"
|
||||
md_content += f"\n"
|
||||
|
||||
md_content += "```\n"
|
||||
|
||||
with open(os.path.join(out_dir, "visual_inspection.md"), "w", encoding="utf-8") as f:
|
||||
f.write(md_content)
|
||||
|
||||
print(f"Generated {num_slices} slices and artifact visual_inspection.md")
|
||||
32
scripts/debug/test_full_ocr.py
Normal file
32
scripts/debug/test_full_ocr.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import cv2
|
||||
import easyocr
|
||||
import numpy as np
|
||||
|
||||
img_path = r"C:\Users\Certes\.gemini\antigravity\brain\5805a1e3-c776-4325-8538-351d54b5e0a0\ai_slice_3.png"
|
||||
img = cv2.imread(img_path)
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
_, bin_inv = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
|
||||
|
||||
row_sums = np.sum(bin_inv, axis=1) / 255.0
|
||||
staff_rows = np.where(row_sums > bin_inv.shape[1] * 0.4)[0]
|
||||
staff_top = staff_rows[0]
|
||||
|
||||
upper_band = gray[max(0, staff_top - 60) : staff_top + 10, :] # Include slightly below top line
|
||||
cv2.imwrite(r"C:\Users\Certes\Desktop\guitar_score\scripts\debug\upper_band.png", upper_band)
|
||||
|
||||
reader = easyocr.Reader(['en'], gpu=False)
|
||||
|
||||
# Test 1: Original
|
||||
print("Testing Original Upper Band...")
|
||||
results = reader.readtext(upper_band, allowlist='0123456789')
|
||||
for r in results: print(r)
|
||||
|
||||
# Test 2: Upscaled
|
||||
print("\nTesting Upscaled x2...")
|
||||
lg2 = cv2.resize(upper_band, (upper_band.shape[1]*2, upper_band.shape[0]*2), interpolation=cv2.INTER_CUBIC)
|
||||
for r in reader.readtext(lg2, allowlist='0123456789'): print(r)
|
||||
|
||||
# Test 3: Binarized
|
||||
print("\nTesting Binarized Upscaled...")
|
||||
_, bin_lg = cv2.threshold(lg2, 180, 255, cv2.THRESH_BINARY_INV)
|
||||
for r in reader.readtext(bin_lg, allowlist='0123456789'): print(r)
|
||||
65
scripts/debug/test_ocr_band.py
Normal file
65
scripts/debug/test_ocr_band.py
Normal file
@@ -0,0 +1,65 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import easyocr
|
||||
import sys
|
||||
|
||||
# Initialize EasyOCR reader
|
||||
reader = easyocr.Reader(['en'], gpu=False)
|
||||
|
||||
def extract_measure_numbers(img_path):
|
||||
img = cv2.imread(img_path)
|
||||
if img is None: return
|
||||
|
||||
# We find the rows of the staff
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
_, bin_inv = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
|
||||
|
||||
# To find the first measure of this chunk, let's find the vertical measure bar
|
||||
col_sums = np.sum(bin_inv, axis=0) / 255.0
|
||||
# a measure bar is a tall black line
|
||||
bar_xs = np.where(col_sums > 30)[0]
|
||||
|
||||
if len(bar_xs) == 0: return
|
||||
|
||||
# Group the xs into distinct bars
|
||||
bars = []
|
||||
curr = [bar_xs[0]]
|
||||
for x in bar_xs[1:]:
|
||||
if x - curr[-1] < 10:
|
||||
curr.append(x)
|
||||
else:
|
||||
bars.append(int(np.mean(curr)))
|
||||
curr = [x]
|
||||
bars.append(int(np.mean(curr)))
|
||||
|
||||
print(f"File: {img_path}")
|
||||
for bar_x in bars[:5]: # Check first 5 bars
|
||||
# The measure number is usually right after the bar, slightly above the staff.
|
||||
# Let's crop a box [bar_x : bar_x + 50] horizontally, and [staff_top - 30 : staff_top] vertically
|
||||
|
||||
# approximate staff_top
|
||||
slice_cols = bin_inv[:, bar_x:bar_x+10]
|
||||
row_sums = np.sum(slice_cols, axis=1) / 255.0
|
||||
staff_rows = np.where(row_sums > 2)[0]
|
||||
if len(staff_rows) == 0: continue
|
||||
staff_top = staff_rows[0]
|
||||
|
||||
y1 = max(0, staff_top - 40)
|
||||
y2 = staff_top
|
||||
x1 = bar_x
|
||||
x2 = min(img.shape[1], bar_x + 60)
|
||||
|
||||
crop = gray[y1:y2, x1:x2]
|
||||
if crop.shape[0] < 10 or crop.shape[1] < 10: continue
|
||||
|
||||
# Upscale for OCR
|
||||
crop_lg = cv2.resize(crop, (crop.shape[1]*3, crop.shape[0]*3), interpolation=cv2.INTER_CUBIC)
|
||||
|
||||
results = reader.readtext(crop_lg, allowlist='0123456789')
|
||||
for (bbox, text, prob) in results:
|
||||
if prob > 0.5:
|
||||
print(f" Bar at x={bar_x}: Measure {text} (conf: {prob:.2f})")
|
||||
|
||||
if __name__ == "__main__":
|
||||
extract_measure_numbers(r"C:\Users\Certes\.gemini\antigravity\brain\5805a1e3-c776-4325-8538-351d54b5e0a0\ai_slice_0.png")
|
||||
extract_measure_numbers(r"C:\Users\Certes\.gemini\antigravity\brain\5805a1e3-c776-4325-8538-351d54b5e0a0\ai_slice_3.png")
|
||||
BIN
scripts/debug/upper_band.png
Normal file
BIN
scripts/debug/upper_band.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
55
scripts/debug/verify_structure.py
Normal file
55
scripts/debug/verify_structure.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import cv2
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
img_path = r"C:\Users\Certes\.gemini\antigravity\brain\975cea00-dd68-4689-9ee3-f1a2408b4ee6\final_check_100_sec.png"
|
||||
if not os.path.exists(img_path):
|
||||
print("final_check_100_sec.png not found!")
|
||||
exit(1)
|
||||
|
||||
img = cv2.imread(img_path)
|
||||
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
_, bin_inv = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY_INV)
|
||||
|
||||
h, w = img.shape[:2]
|
||||
|
||||
row_sums = np.sum(bin_inv[:, :1000], axis=1) / 255.0
|
||||
staff_rows = np.where(row_sums > 1000 * 0.4)[0]
|
||||
if len(staff_rows) < 6:
|
||||
print("Cannot find staff lines")
|
||||
exit(1)
|
||||
staff_y_top = staff_rows[0]
|
||||
staff_y_bottom = staff_rows[-1]
|
||||
|
||||
expected_h = staff_y_bottom - staff_y_top
|
||||
print(f"Staff height: {expected_h}px (from Y={staff_y_top} to {staff_y_bottom})")
|
||||
|
||||
col_sums = np.sum(bin_inv[staff_y_top:staff_y_bottom, :], axis=0) / 255.0
|
||||
bar_xs = np.where(col_sums >= expected_h * 0.6)[0]
|
||||
|
||||
grouped_bars = []
|
||||
if len(bar_xs) > 0:
|
||||
curr = [bar_xs[0]]
|
||||
for x in bar_xs[1:]:
|
||||
if x - curr[-1] < 10: curr.append(x)
|
||||
else:
|
||||
grouped_bars.append(int(np.mean(curr)))
|
||||
curr = [x]
|
||||
grouped_bars.append(int(np.mean(curr)))
|
||||
|
||||
diffs = np.diff(grouped_bars)
|
||||
print(f"Total measures: {len(grouped_bars) - 1}")
|
||||
|
||||
def get_stats(arr):
|
||||
if not len(arr): return "N/A"
|
||||
return f"Min: {np.min(arr)}, Max: {np.max(arr)}, Mean: {np.mean(arr):.1f}"
|
||||
|
||||
print("Measure width stats:", get_stats(diffs))
|
||||
|
||||
anomalies = [i for i, d in enumerate(diffs) if d < 180 or d > 1200]
|
||||
if anomalies:
|
||||
print(f"\n[FAIL] ANOMALIES DETECTED! Mangled measures at indices: {anomalies}")
|
||||
for i in anomalies[:20]:
|
||||
print(f" Measure {i}: width {diffs[i]}px (Starts at X={grouped_bars[i]})")
|
||||
else:
|
||||
print("\n[SUCCESS] NO ANOMALIES. All measures have consistent beat widths. No overlapping mangles detected!")
|
||||
Reference in New Issue
Block a user