<jupyter_start><jupyter_text>* @author: <EMAIL>  * @site: e-smartdata.org  **Optical Mark Recognition (OMR)****OMR** jest formą automatycznego rozpoznawania danych. Znaki są wykonywane na specjalnie wydrukowanych formularzach, które następnie są odczytywane przez czytnik OMR. Następnie dane są przesyłane do komputera w celu przetworzenia. Jednym z najczęstszych zastosowań OMR jest sprawdzanie arkuszy wielokrotnego wyboru. Piszący egzamin zaznaczają swoje odpowiedzi na specjalnie wydrukowanych arkuszach za pomocą ołówka lub specjalnego markera. Arkusze są następnie gromadzone, a dane odczytywane za pomocą skanera OMR. **Spis treści**:1. [Import bibliotek](a0)2. [Wczytanie obrazu](a1)3. [Konwersja do skali szarości](a2)4. [Rozmycie](a3)5. [Detekcja krawędzi](a4)6. [Ekstrakcja konturów](a5)7. [Wydobycie odpowiednich konturówu](a6)8. [Rozwiązanie dla jednego pytania](a7)9. [Rozwiązanie dla wszystkich pytań](a8)10. [Wyświetlenie rozwiązania](a9) **Uwagi:**>Kurs został stworzony przy pomocy biblioteki **opencv-python** w wersji 4.1.2.  >Aby zaktualizować bibliotekę OpenCV do aktualniej wersji należy użyć polecenia:```!pip install --upgrade opencv-python```Bądź zainstalować wybierając konkretną wersję, np.```!pip install opencv-python==4.1.2.30   Import bibliotek<jupyter_code># !pip -q install --upgrade opencv-python
from google.colab.patches import cv2_imshow
import imutils
from imutils import contours
import numpy as np
import cv2
cv2.__version__
# klucz odpowiedzi
ANSWER_KEY = {0: 1, 1: 3, 2: 0, 3: 2, 4: 1, 5: 3, 6: 4, 7: 1, 8: 3, 9: 0}<jupyter_output><empty_output><jupyter_text>Wczytanie obrazu<jupyter_code>image = cv2.imread('key.png')
cv2_imshow(image)<jupyter_output><empty_output><jupyter_text>Konwersja do skali szarości<jupyter_code>gray = cv2.cvtColor(src=image, code=cv2.COLOR_BGR2GRAY)
cv2_imshow(gray)<jupyter_output><empty_output><jupyter_text>Rozmycie<jupyter_code>blurred = cv2.GaussianBlur(src=gray, ksize=(5, 5), sigmaX=0)
cv2_imshow(blurred)<jupyter_output><empty_output><jupyter_text>Detekcja krawędzi<jupyter_code>thresh = cv2.Canny(image=blurred, threshold1=70, threshold2=70)
cv2_imshow(thresh)<jupyter_output><empty_output><jupyter_text>Ekstrakcja konturów<jupyter_code>cnts = cv2.findContours(image=thresh.copy(), mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
len(cnts)
cnt1 = cv2.drawContours(image=image.copy(), contours=[cnts[0]], contourIdx=-1, color=(0, 255, 0), thickness=5)
cv2_imshow(cnt1)
cnt1 = cv2.drawContours(image=image.copy(), contours=[cnts[1]], contourIdx=-1, color=(0, 255, 0), thickness=5)
cv2_imshow(cnt1)
cnt1 = cv2.drawContours(image=image.copy(), contours=[cnts[4]], contourIdx=-1, color=(0, 255, 0), thickness=5)
cv2_imshow(cnt1)
cnt_img = image.copy()
for contour in cnts:
    cv2.drawContours(cnt_img, [contour], contourIdx=-1, color=(0, 255, 0), thickness=3)
cv2_imshow(cnt_img)<jupyter_output><empty_output><jupyter_text>Wydobycie odpowiednich konturów<jupyter_code>question_contours = []

for contour in cnts:
    (x, y, w, h) = cv2.boundingRect(contour)
    ar = w / float(h)

    if w >= 50 and h >= 50 and ar > 0.9 and ar < 1.1:
        question_contours.append(contour)

print(f'Liczba pól z odpowiedziami: {len(question_contours)}')        

cnt_img = image.copy()
for contour in question_contours:
    cv2.drawContours(cnt_img, [contour], contourIdx=-1, color=(0, 255, 0), thickness=3)
cv2_imshow(cnt_img)<jupyter_output>Liczba pól z odpowiedziami: 50<jupyter_text>Rozwiązanie dla jednego pytania<jupyter_code>cv2_imshow(thresh)
# sortowanie kontur od góry do dołu
question_top_bottom = imutils.contours.sort_contours(question_contours, method='top-to-bottom')[0]

# wydobycie pierwszych 5 kontur i sortowanie od lewej do prawej
first_5 = question_top_bottom[:5]
first_5_left_right = imutils.contours.sort_contours(first_5, method='left-to-right')[0]

img = image.copy()

marked = None

for idx, contour in enumerate(first_5_left_right):
    # tworzymy maskę z samych zer
    mask = np.zeros(thresh.shape, dtype='uint8')

    # rysujemy kontur na masce
    cv2.drawContours(mask, [contour], -1, 255, -1)

    # przeprowadzamy operację bitwise_and na obrazie thresh
    mask = cv2.bitwise_and(thresh, thresh, mask=mask)
    cv2_imshow(mask)

    # zliczamy liczbę pikseli niezerowych
    total = cv2.countNonZero(mask)


    if marked is None or total > marked[0]:
        marked = (total, idx)
    print(marked) 
    
    # kolor czerwony
    color = (0, 0, 255)
         
    key = ANSWER_KEY[0]
    print(key)

    if key == marked[1]:
        # zaznacz na kolor zielony
        color = (0, 255, 0)

cv2.drawContours(img, [first_5_left_right[key]], -1, color, 2)
cv2_imshow(img)
# sortowanie kontur od góry do dołu
question_top_bottom = imutils.contours.sort_contours(question_contours, method='top-to-bottom')[0]

# wydobycie pierwszych 5 kontur i sortowanie od lewej do prawej
first_5 = question_top_bottom[5:10]
first_5_left_right = imutils.contours.sort_contours(first_5, method='left-to-right')[0]

img = image.copy()

marked = None

for idx, contour in enumerate(first_5_left_right):
    # tworzymy maskę z samych zer
    mask = np.zeros(thresh.shape, dtype='uint8')

    # rysujemy kontur na masce
    cv2.drawContours(mask, [contour], -1, 255, -1)

    # przeprowadzamy operację bitwise_and na obrazie thresh
    mask = cv2.bitwise_and(thresh, thresh, mask=mask)
    cv2_imshow(mask)

    # zliczamy liczbę pikseli niezerowych
    total = cv2.countNonZero(mask)


    if marked is None or total > marked[0]:
        marked = (total, idx)
    print(marked) 
    
    # kolor czerwony
    color = (0, 0, 255)
         
    key = ANSWER_KEY[1]
    print(key)

    if key == marked[1]:
        # zaznacz na kolor zielony
        color = (0, 255, 0)

cv2.drawContours(img, [first_5_left_right[key]], -1, color, 2)
cv2_imshow(img)<jupyter_output><empty_output><jupyter_text>Rozwiązanie dla wszystkich pytań<jupyter_code>img = image.copy()

correct = 0

# sortowanie kontur od góry do dołu
question_top_bottom = imutils.contours.sort_contours(question_contours, method='top-to-bottom')[0]

for question, idx in enumerate(range(0, len(question_contours), 5)):

    # wydobycie pierwszych 5 kontur i sortowanie od lewej do prawej
    fields = question_top_bottom[idx:idx + 5]
    fields = imutils.contours.sort_contours(fields, method='left-to-right')[0]

    marked = None

    for cnt_idx, contour in enumerate(fields):
        # tworzymy maskę z samych zer
        mask = np.zeros(thresh.shape, dtype='uint8')

        # rysujemy kontur na masce
        cv2.drawContours(mask, [contour], -1, 255, -1)

        # przeprowadzamy operację bitwise_and na obrazie thresh
        mask = cv2.bitwise_and(thresh, thresh, mask=mask)

        # zliczamy liczbę pikseli niezerowych
        total = cv2.countNonZero(mask)


        if marked is None or total > marked[0]:
            marked = (total, cnt_idx) 
        
    # kolor czerwony
    color = (0, 0, 255)
        
    key = ANSWER_KEY[question]

    if key == marked[1]:
        # zaznacz na kolor zielony
        color = (0, 255, 0)
        correct += 1

    cv2.drawContours(img, [fields[key]], -1, color, 2)

cv2_imshow(img)<jupyter_output><empty_output><jupyter_text>Wyświetlenie rozwiązania<jupyter_code># dodanie górnego obramowania
checked = cv2.copyMakeBorder(
    src=img,
    top=50,
    bottom=0,
    left=0,
    right=0,
    borderType=cv2.BORDER_CONSTANT,
    value=(255, 255, 255)
)

score = (correct / 10)

color = (50, 168, 82) if score >= 0.6 else (71, 7, 219)  
text = 'Passed' if score >= 0.6 else 'Failed'  

cv2.putText(img=checked, text=f'{text}: {score * 100}%', org=(10, 30), fontFace=cv2.FONT_HERSHEY_SIMPLEX, fontScale=0.9, color=color, thickness=2)
cv2_imshow(checked)
cv2.imwrite(filename='checked.png', img=checked)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11906277855596588, 'fraction_numerical': 0.037692601553546416, 'mean_word_length': 4.634146341463414, 'pattern_counts': {'":': 0, '<': 42, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 1, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '592378', 'license': 'MIT', 'n_tokens_mistral': 3330, 'n_tokens_neox': 3185, 'n_words': 670, 'path': '03_optical-mark-recognition/01_omr.ipynb', 'repo_name': 'krakowiakpawel9/computer-vision-course', 'size': '7871'}