Top Ad unit 728 × 90


Sử dụng xử lý ảnh, thư viện OpenCV viết chương trình chấm điểm thi trắc nghiệm.

Optical Mark Recognition là gì (OMR)?

Optical Mark Recognition, hoặc OMR đơn giản là quá trình tự dộng phân tích tài liệu được con người tạo ra và  làm sáng tỏ kết quả.

Tiết hành làm chương trình scan biểu test bubble và and phân loại xử dụng OMR, Python, và OpenCV


Dưới đây là một ví dụ của một biểu test mà chúng ta sẽ sử dụng để quét và phân loại trong bài viết này:

Ảnh 1: Ví dụ về biểu test trắc nghiệm

Chúng ta sẽ sử dụng form này như ví dụ quét form kiểm tra trắc nghiệm.

Các bước trong bài

  • Step #1: Phát hiện biểu kiểm tra trong một ảnh.
  • Step #2: Áp dụng perspective transform để trích ra trên-dưới, birds-eye-view của biểu kiểm tra.
  • Step #3: Extract the set of bubbles (i.e., the possible answer choices) from the perspective transformed exam.
  • Step #4: Sắp xếp các câu hỏi/ô tròn vào hàng.
  • Step #5: Xác định các ô tròn đã được đánh dấu (trả lời) vào 
  • Step #6:  Tìm câu trả lời đúng trong đáp án để xác định kết quả của người được kiểm tra có chọn đúng đáp án hay không.
  • Step #7:  Lặp lại các câu hỏi trong bài kiểm tra trắc nghiệm. 
Bài này chúng ta sẽ dùng OpenCV và Python để giải quyết vấn đề.
Dầu tiên mở một file mới và đặt tên là test_grader.py : 
Bạn phải đảm bảo rằng máy tính của bạn đã được cài OpenCV và Numpy cùng với imutils.
để cài imutils  (hoặc update version mới nhất), các bạn cần chạy lệnh dưới đây.
Dòng 10-12 các biến  --image ,  --i , chính là tham số để đưa ảnh bài kiểm tra trắc nghiệm.
Dòng 17 định nghĩa từ khóa cho câu trả lời ANSWER_KEY . Các bạn có thể tùy chỉnh key này tùy thuộc vào form trả lời chuẩn của bạn.
Trong trường hợp này Key 0 là key cho câu hỏi đầu tiên, và câu trả lời đúng là B (tương đương với index  =1 ). Và cứ theo quy luật như vậy cho các trường hợp còn lại.
Cụ thể trong bài này, form trả lời chính xác để đối chiếu là:
  • Question #1: B
  • Question #2: E
  • Question #3: A
  • Question #4: D
  • Question #5: B
Tiếp theo chúng ta sẽ xử lý ảnh đầu vào :
Dòng Line 21 tải ảnh vào
Dòng Line 22 chuyển đổi sang g
Dòng Line 23 làm mờ ảnh
Dòng Line 24 dùng hàm Canny để tìm cạnh của đối tượng trong ảnh.
Sau khi thực hiện các lệnh trên sẽ cho ra kết quả:

Figure 2: Xử lý ảnh ban đầu, tìm cạnh
Lưu ý rằng cách các cạnh của tài liệu cần được xác định rõ ràng, với cả bốn đỉnh của ảnh _ bài trắc nghiệm scan_ Việc này rất quan trọng trong bước tiếp theo của chúng ta, vì chúng ta sẽ sử dụng nó như một điểm đánh dấu để kéo giãn và xóa hiệu ứng mắt chim:
Bây giờ chúng ta đẵ có đường viền bên ngoài của ảnh bài trắc nghiệm, chúng ta áp dụng cv2.findContours  để tìm  find the lines that correspond to the exam itself.
Chúng ta sẽ tiến hành xắp xếp theo độ lớn của contour từ lớn tới bé ở Dòng 36 là hàm để xắp xếp. Sau khi thực hiện sort chúng ta sẽ có được contour lớn sẽ nằm ở đầu list và bé nằm ở cuối cùng.
Tiếp tới ở dòng Line 40/41. ở mỗi contour chúng ta sẽ tìm các góc của contours sau khi approximated. 
ảnh dưới đây là kết qua sau khi tìm ra vùng của bài trắc nghiệm và cạnh của nó docCnt  được vẽ như đường màu đỏ.

Figure 3: Tìm khung bài trắc nghiệm
Bây giờ chúng ta tiến hành sử dụng perspective transform để kéo giãn khung bài trắc nghiệm.
Trong trường hợp này chúng ta sẽ sử dụng hàm four_point_transform  với chức năng là:
  1. Xác định tọa độ (x, y)- contours với khả năng specific, reproducible manner.
  2. áp dụng perspective transform cho các vùng.

Figure 4: ảnh sau khi đã được perspective transform
Vậy là chúng ta đã tìm và kéo khung bài trắc nghiệm thành công, để đảm bảo ảnh không bị méo giống như ta dùng máy scan. 
Bước tiếp theo là chúng ta tiến hành nhị phân hóa ảnh :
ảnh sau khi nhị phân hóa

Figure 5: ostus nhị phân hóa
ảnh được nhị phân này sẽ giúp chúng ta sử dụng phép tìm contour để tìm các khung tròn đáp án trên bài trắc nghiệm.
Dòng 64-67 là để tìm ontours trên ảnh nhị phân thresh  , và chúng ta khởi tạo questionCnts ,nó chính là danh sách contours tương ứng với các câu hỏi,trả lời/khoanh tròn trên bài trắc nghiệm.
để xác định vùng nào của ảnh là phần trả lời/câu hỏi/khoanh tròn chúng ta cần phép lặp trên mỗi contours (Dòng 70).
Với mỗi contours, chúng ta tính bounding box (Dòng 72), ở đây nó cũng cho phép chúng ta tính được tỉ lệ  "aspect ratio" - mình không biết tiếng việt nên dịch là gì-, hoặc đơn giản hơn là tỉnh lệ của chiều rộng với chiều cao (Dòng 73).
Và chúng ta cần set điều kiện để kiểm tra xem contours đó có phải là vung tròn / câu trả lời của bài trắc nghiệm hay không:
  1. Chiều rộng và cao phải thích hợp như ở trong ví dụ này sẽ là > 20 pixels .
  2. Cần có tỉ lệ "aspect ratio"" xấp xỉ = 1.
Và dự vào điều kiện chúng ta sẽ tìm được các vùng tròn/câu trả lời questionCnts 
ảnh dưới đây là kết quả tìm vùng tròn/câu trả lời trên bài trắc nghiệm questionCnts  

Figure 6: Vùng trả lời được nhận dạng và khoanh đỏ
Bây giờ bước tiếp theo là nhận dạng các câu trả lời của người dùng trên bài trắc nghiệm:

đầu tiên xắp xếp questionCnts  từ trên xuống dưới, điều này sẽ đảm bảo rằng dòng trả lời đầu tiên nằm ngay phía trên cùng cứ như vậy cho tới dòng trả lời cuối cùng.
Chúng ta cũng khởi tạo biến "bookkeeper" để luôn track số lượng câu trả lời đúng correct .
ở  Dòng 89 chúng tiến hành loop các câu hỏi. Như form chuẩn mỗi câu hỏi sẽ có thể có 5 đáp án. Chúng ta sẽ sử dụng NumPy array slicing và contour sorting để sắp xếp contours từ trái qua phải.
Lý do phương pháp này hoạt động là vì chúng ta đã sắp xếp các contours dòng trả lời từ trên xuống dưới. Chúng ta biết rằng 5 vòng tròn cho mỗi câu hỏi sẽ xuất hiện tuần tự trong danh sách của chúng ta - nhưng chúng ta không biết liệu các vòng tròn này liệu có được sẽ được sắp xếp từ trái sang phải. Việc sắp xếp contours trên Dòng 93 sẽ giải quyết vấn đề này và đảm bảo mỗi hàng của contours được sắp xếp thành hàng, từ trái sang phải.
Để hình dung khái niệm này, ảnh chụp màn hình dưới đây mô tả mỗi hàng câu hỏi như một màu riêng biệt:

Figure 7: Bằng cách phân loại các contours của chúng ta từ trên xuống dưới, tiếp theo là từ trái sang phải, chúng ta có thể trích xuất từng hàng khoanh tròn. 
Với mỗi hàng câu trả lời chúng ta tiến hành tìm kiếm các câu được trả lời bên trong ảnh.
Chúng ta có thể thực hiện điều này bằng cách sử dụng ảnh thresh  và đếm số lượng điểm ảnh có giá trị = 0 trên mỗi vùng khoanh tròn.
Dòng 98 thực hiện phép lặp trên mỗi khoanh tròn trên mỗi dòng.
Chúng khởi tạo mask trên vùng khoanh tròn hiện tại sau đó là tính số lương pixels = 0 trong vùng masked  (Dòng 107 và 108). Với vùng khoanh tròn có nhiều pixcel  giá trị  =0 thì chính là câu trả lời trắc nghiệm của người dùng.

Figure 8: Ví dụ sử dụng mark cho mỗi vùng khoanh tròn
Rất rõ ràng, vùng khoanh tròn với ký hiệu "B" có nhiều pixcel giá trị =0, chính vì vậy nó là câu trả lời của người dùng điền vào.

Code tiếp theo là chúng ta kiểm tra xem câu trả lời có đúng với đáp án không ANSWER_KEY :
Tiếp đến là chúng ta sẽ thực hiện việc highlight tô màu xanh đối với các câu trả lời đúng và tô màu đỏ đối với đáp án đúng mà người dùng đã trả lời sai.

Figure 9:  "xanh lục" để đánh dấu "chính xác" hoặc "đỏ" để đánh dấu "không chính xác".
Cuối cùng ở phần cuối của code chúng ta tiến hành tính điểm cho cả bài trắc nghiệm và hiển thị trên màn hình.
Kết quả như ảnh dưới:

Figure 10: Finishing our OMR system for grading human-taken exams.
để chạy chương trình chúng ta sẽ đưa ảnh vào như lệnh dưới đây :
Kết thúc chương trình, ta gặp một số vấn đề như sau
1. Điều gì xảy ra nếu người dùng không trả lời câu nào cả
2. Điều gì xảy ra nếu người dùng điền nhiều hơn một câu trả lời.
Để xử lý hai vấn đề này chúng ta cần thêm một số điều kiện trong code của chúng ta như sau.
#Vấn đề 1: 
- Đầu tiên ta cần đặt giá trị nhỏ nhất có thể khi tiến hành threshold.
- Khi kiểm tra điều kiện nếu không có vung tròn nào có tổng số pixcel có giá trị 0 đặt mức như yêu cầu chúng ta có thể đánh dấu câu hỏi được bỏ qua từ người dùng.

Figure 11: 
#Vấn đề 2: 
- Kiểm tra nếu trên mỗi dòng trả lời có tới hai hoặc nhiều hơn hai vùng tròn được đánh dấu thì chúng ta đánh dấu câu hỏi trả lời sai từ người dùng.


Figure 12: .
Mã nguồn: Tải về ở đây
Video:

Nguồn: từ pyimagesearch.

Sử dụng xử lý ảnh, thư viện OpenCV viết chương trình chấm điểm thi trắc nghiệm. Reviewed by Jacky on tháng 12 21, 2017 Rating: 5

8 nhận xét:

  1. có thể giúp mình sửa lỗi này ko ạ, mình mới tìm hiểu về cái này chưa lâu nên không có kinh nghiệm ạ, mình cảm ơn:

    Traceback (most recent call last):
    File "C:\Users\Acer\Desktop\optical-mark-recognition\test_grader.py", line 5, in
    from imutils.perspective import four_point_transform
    File "C:\Python27\lib\site-packages\imutils\perspective.py", line 5, in
    from scipy.spatial import distance as dist
    ImportError: No module named scipy.spatial

    Trả lờiXóa
    Trả lời
    1. bạn thiếu thư viện scipy nha, vào pip install scipy là ok

      Xóa
    2. Này bị lồi gì vậy, anh có thể giúp em với được không anh

      File "D:\python\test_grader.py", line 18
      python test_grader.py --image images/test_02.png
      ^
      SyntaxError: invalid syntax
      [Finished in 0.2s with exit code 1]

      Xóa
  2. Cho mình hỏi là tại sao có nhiều pixel có giá trị 0 là đáp án vậy ?! Vì nếu nhìn trong hình ví dụ thì phải là có nhiều pixel có giá trị 255 mới là đáp án chứ ?!

    Trả lờiXóa
    Trả lời
    1. theo tôi nhớ màu trắng trong hệ màu rgb là 255, màu đen ở đây là 0, đáp án của bài này là tô đen nên mức 0 là đúng rồi, đối với bài toán này chỉ cần lọc màu ra là ok rồi tại vì có tọa độ cụ thể rồi, phiếu điểm chỉ có 2 màu trắng đen thì mình chỉ cần lọc mức ảnh là ta có đáp án rồi. Còn ông viết topic này thì hình như là ông ấy sub cái bài viết của ông Adrian bên Pyimage thì phải. có gì thắc mắc thì cứ mail cho tôi, nếu tôi có biết tôi có thể chia sẻ với bạn.

      Xóa
  3. mã nguồn mình không mở được hả ad

    Trả lờiXóa
  4. Này bị lồi gì vậy, anh có thể giúp em với được không anh

    C:\Users\Admin\AppData\Local\Programs\Python\Python37\python.exe C:/Users/Admin/PycharmProjects/phieuthi/test_grader.py
    usage: test_grader.py [-h] -i IMAGE
    test_grader.py: error: the following arguments are required: -i/--image

    Trả lờiXóa
  5. cho hỏi làm cách nào để sửa lỗi này ạ?

    File "trac_nhiem.py", line 45, in
    paper = four_point_transform(image, docCnt.reshape(4, 2))
    AttributeError: 'NoneType' object has no attribute 'reshape'

    Trả lờiXóa

All Rights Reserved by Cộng Đồng OpenCV © 2017
Edit bởi: Jacky Le | Youtube Channel: JACKY LE

Biểu mẫu liên hệ

Tên

Email *

Thông báo *

Được tạo bởi Blogger.