# -- Code Cell --
import cv2 as cv
import matplotlib.pyplot as plt

# -- Code Cell --
img = cv.imread("./test_data/0.png", cv.IMREAD_COLOR)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
plt.imshow(gray, cmap="gray")

# -- Code Cell --
gray = cv.bitwise_not(gray)
bw = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, \
                            cv.THRESH_BINARY, 15, -2)
plt.imshow(bw, cmap="gray")

# -- Code Cell --
import numpy as np

# -- Code Cell --
horizontal = np.copy(bw)
vertical = np.copy(bw)

# -- Code Cell --
# Specify size on horizontal axis
cols = horizontal.shape[1]
horizontal_size = cols // 15

# Create structure element for extracting horizontal lines through morphology operations
horizontalStructure = cv.getStructuringElement(cv.MORPH_RECT, (horizontal_size, 1))

# Apply morphology operations
horizontal = cv.erode(horizontal, horizontalStructure)
horizontal = cv.dilate(horizontal, horizontalStructure)

# Show extracted horizontal lines
plt.imshow(horizontal, cmap="gray")

# -- Code Cell --
# Specify size on vertical axis
rows = vertical.shape[0]
verticalsize = rows // 26

# Create structure element for extracting vertical lines through morphology operations
verticalStructure = cv.getStructuringElement(cv.MORPH_RECT, (1, verticalsize))

# Apply morphology operations
vertical = cv.erode(vertical, verticalStructure)
vertical = cv.dilate(vertical, verticalStructure)

# Show extracted vertical lines
plt.imshow(vertical, cmap="gray")

# -- Code Cell --
output = vertical.copy()
output.shape
output = output[0:output.shape[0],25:output.shape[1]]
output.shape

# -- Code Cell --
plt.imshow(output)

# -- Code Cell --
rows = vertical.shape[0]
circles = cv.HoughCircles(output, cv.HOUGH_GRADIENT, 1, rows / 8,
                            param1=100, param2=8,
                            minRadius=1, maxRadius=30)

# -- Code Cell --
if circles is not None:
    circles = np.uint16(np.around(circles))
    for i in circles[0, :]:
        center = (i[0], i[1])
        # circle center
        cv.circle(output, center, 1, (0, 100, 100), 3)
        # circle outline
        radius = i[2]
        cv.circle(output, center, radius, (255, 0, 255), 3)

# -- Code Cell --
cv.imshow("detected circles", output)
if circles is not None:
    circles = np.uint16(np.around(circles))
    num_circles = len(circles[0])
else:
    num_circles = 0

print(num_circles)
cv.waitKey(0)

# -- Code Cell --
import os

# -- Code Cell --
answer = []
for i in range(len(os.listdir("./test_data"))):
    img = cv.imread(f"./test_data/{i}.png", cv.IMREAD_COLOR)
    gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
    gray = cv.bitwise_not(gray)
    bw = cv.adaptiveThreshold(gray, 255, cv.ADAPTIVE_THRESH_MEAN_C, \
                                cv.THRESH_BINARY, 15, -2)
    horizontal = np.copy(bw)
    vertical = np.copy(bw)
    # Specify size on horizontal axis
    cols = horizontal.shape[1]
    horizontal_size = cols // 15

    # Create structure element for extracting horizontal lines through morphology operations
    horizontalStructure = cv.getStructuringElement(cv.MORPH_RECT, (horizontal_size, 1))

    # Apply morphology operations
    horizontal = cv.erode(horizontal, horizontalStructure)
    horizontal = cv.dilate(horizontal, horizontalStructure)
    # Specify size on vertical axis
    rows = vertical.shape[0]
    verticalsize = rows // 26

    # Create structure element for extracting vertical lines through morphology operations
    verticalStructure = cv.getStructuringElement(cv.MORPH_RECT, (1, verticalsize))

    # Apply morphology operations
    vertical = cv.erode(vertical, verticalStructure)
    vertical = cv.dilate(vertical, verticalStructure)
    output = vertical.copy()
    output = output[0:output.shape[0],25:output.shape[1]]
    rows = vertical.shape[0]
    circles = cv.HoughCircles(output, cv.HOUGH_GRADIENT, 1, rows / 8,param1=100, param2=10,minRadius=5,maxRadius=20)
    if circles is not None:
        circles = np.uint16(np.around(circles))
        for i in circles[0, :]:
            center = (i[0], i[1])
            # circle center
            cv.circle(output, center, 1, (0, 100, 100), 3)
            # circle outline
            radius = i[2]
            cv.circle(output, center, radius, (255, 0, 255), 3)
    if circles is not None:
        circles = np.uint16(np.around(circles))
        num_circles = len(circles[0])
    else:
        num_circles = 0
    answer.append(num_circles)

# -- Code Cell --
answer

# -- Code Cell --
answer = []
for z in range(len(os.listdir("./test_data"))):
    img = cv.imread(f"./test_data/{z}.png", cv.IMREAD_GRAYSCALE)
    img = img[:, 25:]

    _, bw = cv.threshold(img, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)

    kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (2, 2))
    bw = cv.dilate(bw, kernel, iterations=1)

    num_labels, labels, stats, centroids = cv.connectedComponentsWithStats(bw, connectivity=8)

    output = cv.cvtColor(img, cv.COLOR_GRAY2BGR)

    count = 0
    for i in range(1, num_labels):
        x = stats[i, cv.CC_STAT_LEFT]
        y = stats[i, cv.CC_STAT_TOP]
        w = stats[i, cv.CC_STAT_WIDTH]
        h = stats[i, cv.CC_STAT_HEIGHT]
        area = stats[i, cv.CC_STAT_AREA]

        if 10 < area < 2000 and 2 < w < 80 and 2 < h < 80:
            ratio = w / h
            if 0.2 < ratio < 3.0:
                count += 1
                cv.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 2)

    answer.append(count)
answer

# -- Code Cell --
answer

# -- Code Cell --
ids = [i for i in range(2000)]
np.argmax(ids)

# -- Code Cell --
import pandas as pd

# -- Code Cell --
sub1 = pd.DataFrame({
    'subtaskID':1,
    "datapointID":ids,
    "answer":answer
})
sub2 = pd.DataFrame({
    'subtaskID':2,
    "datapointID":ids,
    "answer":answer
})
sub3 = pd.DataFrame({
    'subtaskID':3,
    "datapointID":ids,
    "answer":answer
})
final = pd.concat([sub1,sub2,sub3]).to_csv("subs.csv",index=False)

# -- Markdown Cell --
# # TASK1

# -- Code Cell --


# -- Code Cell --
img = cv.imread("./test_data/5.png", cv.IMREAD_GRAYSCALE)
img.shape
img = img[0:img.shape[0], 25:img.shape[1]]
for i in range(5):
    cv.line(img,(0,13+(7*i)),(img.shape[1],13+(7*i)),(255,255,255),1)
blur = cv.GaussianBlur(img,(7,7),0)
ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
plt.imshow(th3,cmap="gray")

# -- Code Cell --
n_labels, labels, stats, centroids = cv.connectedComponentsWithStats(th3)
boxes= []
for i in range(1,n_labels):
    x = stats[i, cv.CC_STAT_LEFT]
    y = stats[i, cv.CC_STAT_TOP]
    w = stats[i, cv.CC_STAT_WIDTH]
    h = stats[i, cv.CC_STAT_HEIGHT]
    area = stats[i, cv.CC_STAT_AREA]

    if area > 10:
        boxes.append((x, y, w, h))

# -- Code Cell --
boxes

# -- Code Cell --
img_color = cv.cvtColor(th3, cv.COLOR_GRAY2BGR)

for (x, y, w, h) in boxes:
    cv.rectangle(img_color, (x, y), (x + w, y + h), (0, 255, 0), 2)

plt.figure(figsize=(10,4))
plt.imshow(cv.cvtColor(img_color, cv.COLOR_BGR2RGB))
plt.show()

# -- Code Cell --
task1 = []
keep_all_boxes_for_task2=[]
for i in range(len(os.listdir("./test_data"))):
    img = cv.imread(f"./test_data/{i}.png", cv.IMREAD_GRAYSCALE)
    img.shape
    img = img[0:img.shape[0], 25:img.shape[1]]
    for i in range(5):
        cv.line(img,(0,13+(7*i)),(img.shape[1],13+(7*i)),(255,255,255),1)
    blur = cv.GaussianBlur(img,(7,7),0)
    ret3,th3 = cv.threshold(blur,0,255,cv.THRESH_BINARY_INV+cv.THRESH_OTSU)
    n_labels, labels, stats, centroids = cv.connectedComponentsWithStats(th3)
    boxes= []
    for i in range(1,n_labels):
        x = stats[i, cv.CC_STAT_LEFT]
        y = stats[i, cv.CC_STAT_TOP]
        w = stats[i, cv.CC_STAT_WIDTH]
        h = stats[i, cv.CC_STAT_HEIGHT]
        area = stats[i, cv.CC_STAT_AREA]
        if area > 10:
            boxes.append((x, y, w, h))
    n_detected_notes = len(boxes)
    task1.append(n_detected_notes)
    keep_all_boxes_for_task2.append(boxes)

# -- Code Cell --
task1

# -- Markdown Cell --
# # TASK2

# -- Code Cell --
train_img = cv.imread("./train_data/all_piano_notes_duration.png")
train_img = cv.cvtColor(train_img, cv.COLOR_BGR2GRAY)

note1 = train_img[0:50, 10:30]
note2 = train_img[0:50, 70:100]
note3 = train_img[0:50, 120:150]
note4 = train_img[0:50, 170:190]
note5 = train_img[0:50, 200:265]

notes = [note1, note2, note3, note4, note5]
processed_notes = []

for note in notes:
    blur = cv.GaussianBlur(note, (7,7), 0)
    _, th = cv.threshold(blur, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
    processed_notes.append(th)

# -- Code Cell --
def compute_hu(img):
    moments = cv.moments(img)
    hu = cv.HuMoments(moments).flatten()
    hu = -np.sign(hu) * np.log10(np.abs(hu) + 1e-10)
    return hu

hu1 = compute_hu(processed_notes[0])
hu2 = compute_hu(processed_notes[1])
hu3 = compute_hu(processed_notes[2])
hu4 = compute_hu(processed_notes[3])
hu5 = compute_hu(processed_notes[4])

all_templtes = [hu1, hu2, hu3, hu4, hu5]

# -- Code Cell --
img = cv.imread("./test_data/1.png", cv.IMREAD_GRAYSCALE)
img = img[:, 25:]

for i in range(5):
    cv.line(img, (0, 13 + 7*i), (img.shape[1], 13 + 7*i), 255, 1)

blur = cv.GaussianBlur(img, (7,7), 0)
_, th3 = cv.threshold(blur, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)

salvez_peaici = []

for box in keep_all_boxes_for_task2[1]:
    x, y, w, h = box
    crop = th3[y:y+h, x:x+w]

    crop_hu = compute_hu(crop)

    distances = []
    for template_hu in all_templtes:
        dist = np.linalg.norm(crop_hu - template_hu)
        distances.append(dist)

    best_label = np.argmin(distances) + 1
    salvez_peaici.append(best_label)

# -- Code Cell --
img = cv.imread(f"./test_data/{8}.png", cv.IMREAD_GRAYSCALE)
plt.imshow(img)

# -- Code Cell --
salvez_peaici

# -- Code Cell --
mapping = {1: 1,2: 1/2,3: 1/4,4: 1/8,5: 1/16}
rezultate_finale = []

# -- Code Cell --
len(os.listdir("./test_data"))

# -- Code Cell --
for i in range(len(keep_all_boxes_for_task2)):
    img = cv.imread(f"./test_data/{i}.png", cv.IMREAD_GRAYSCALE)
    img = img[:, 25:]
    for j in range(5):
        cv.line(img, (0, 13 + 7*j), (img.shape[1], 13 + 7*j), 255, 1)

    blur = cv.GaussianBlur(img, (7,7), 0)
    _, th3 = cv.threshold(blur, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)
    salvez_peaici = []

    for box in keep_all_boxes_for_task2[i]:
        x, y, w, h = box
        crop = th3[y:y+h, x:x+w]

        crop_hu = compute_hu(crop)

        distances = []
        for template_hu in all_templtes:
            dist = np.linalg.norm(crop_hu - template_hu)
            distances.append(dist)

        best_label = np.argmin(distances) + 1

        salvez_peaici.append(mapping[best_label])

    rezultate_finale.append(sum(salvez_peaici))

# -- Code Cell --
rezultate_finale

# -- Markdown Cell --
# # TASK3

# -- Code Cell --
def pitch_from_box(y, h):
    bottom = y + h

    lowest_ref = 48   
    step = 3.5

    idx = round((lowest_ref - bottom) / step)

    notes = ['A', 'B', 'C', 'D', 'E', 'F', 'G']
    return notes[idx % 7]

# -- Code Cell --
task3 = []

for i in range(len(keep_all_boxes_for_task2)):
    img = cv.imread(f"./test_data/{i}.png", cv.IMREAD_GRAYSCALE)
    img = img[:, 25:]

    for j in range(5):
        cv.line(img, (0, 13 + 7*j), (img.shape[1], 13 + 7*j), 255, 1)

    blur = cv.GaussianBlur(img, (7,7), 0)
    _, th3 = cv.threshold(blur, 0, 255, cv.THRESH_BINARY_INV + cv.THRESH_OTSU)

    boxes = sorted(keep_all_boxes_for_task2[i], key=lambda b: b[0])

    note_strings = []

    for box in boxes:
        x, y, w, h = box
        crop = th3[y:y+h, x:x+w]

        crop_hu = compute_hu(crop)
        distances = []

        for template_hu in all_templtes:
            dist = np.linalg.norm(crop_hu - template_hu)
            distances.append(dist)

        best_label = np.argmin(distances) + 1
        duration = mapping[best_label]

        pitch = pitch_from_box(y, h)

        note_strings.append(f"{pitch}-{duration}")

    task3.append(" ".join(note_strings))

# -- Code Cell --
import pandas as pd
ids = [i for i in range(2000)]

# -- Code Cell --
len(rezultate_finale)

# -- Code Cell --
sub1 = pd.DataFrame({
    'subtaskID':1,
    "datapointID":ids,
    "answer":task1
})
sub2 = pd.DataFrame({
    'subtaskID':2,
    "datapointID":ids,
    "answer":rezultate_finale
})
sub3 = pd.DataFrame({
    'subtaskID':3,
    "datapointID":ids,
    "answer":task3
})
final = pd.concat([sub1,sub2,sub3]).to_csv("subs.csv",index=False)

# -- Code Cell --
