12/08/2018, 15:40

Xây dựng hệ thống tự động chấm bài thi trắc nghiệm với OpenCV - Phần 1

Đến hẹn lại lên, cứ mỗi năm sau kì thi THPT quốc gia là mỗi sĩ tử lại bước vào một thời kì ngày ngóng đêm trông cho đến ngày chờ kết quả. Có lẽ rằng trong nếu với xu hướng trắc nghiệm hóa như kì thi năm nay thì việc chấm bài trắc nghiệm chắc hẳn là một việc vô cùng ác cờ mờ nờ mộng đối ...

Đến hẹn lại lên, cứ mỗi năm sau kì thi THPT quốc gia là mỗi sĩ tử lại bước vào một thời kì ngày ngóng đêm trông cho đến ngày chờ kết quả. Có lẽ rằng trong nếu với xu hướng trắc nghiệm hóa như kì thi năm nay thì việc chấm bài trắc nghiệm chắc hẳn là một việc vô cùng ác cờ mờ nờ mộng đối với các thầy cô chấm đề nếu như không có sự trợ giúp của máy móc. Bản thân mình đã từng thấy các thầy cô chấm đề phải soi từng tờ đáp án đã được đục lỗ và đếm số lượng những cái lỗ được tô chì tương ứng với số câu đúng của đề thi. Hành động đếm cua trong lỗ này quả thực là rất mát công sức, mà mình thì vốn dĩ rất lười nên nếu cho mình được ngồi chấm bài thì mình chắc hẳn sẽ làm theo một cách khác. Các bạn có tin không, nếu chúng ta xây dựng được một hệ thống Tự động chấm bài thi trắc nghiệm thì có phải công việc nặng nhọc kia đã trở nên dễ dàng hơn rất nhiều rồi sao. Nếu bạn cũng là một trong những người lười như mình thì chúng ta sẽ bắt đầu thôi

Ý tưởng

Dạo này mình rất có hứng thú với Xử lý ảnh ẻo các thứ các thứ và thật sự thì nó quá thú vị các bạn ạ. Quay trở lại với đề tài của chúng ta nếu như chúng ta muốn xây dựng một hệ thống chấm bài thi trắc nghiệm thì hãy THAY ĐỔI CÁCH NHÌN của chúng ta về bài thi trắc nghiệm nhé, ý của mình là thay vì coi nó như một tờ giấy, thực hiện so sánh thủ công giữa tờ đáp ántờ giấy thi thì chúng ta có thể coi chúng như hai bức ảnh thôi. Việc còn lại của chúng ta đó chính là so sánh hai bức ảnh đó và tìm ra có bao nhiêu điểm khác biệt giữa chúng. Tư tưởng chính là như vậy tuy nhiên để thực hiện được thì chúng ta cần chia nó thành một số bước nhỏ như sau: (***Chia để trị đó mà (^_^)***)

  1. Bước đầu tiên hay còn gọi là bước một:: Nhận dạng phần bài thi trong một bức ảnh, chúng ta cần phải biết phần nào trong bức ảnh là bài thi của chúng ta để mà xử lý chứ phải không nào. Việc này được thực hiện bằng các kĩ thuật xử lý ảnh mà mình sẽ trình bày rõ ràng trong các phần tiếp theo ngay trong bài viết này. Kết quả nó sẽ kiểu như sau
  2. Bước hai: Trong phần bài thi đã được nhận dạng ở bước 1, chúng ta cần nhận dạng các dòng của từng bài thi tương ứng với từng câu trả lời của bài thi
  3. Bước ba: Từ mỗi dòng của câu trả lời chúng ta cần phải nhận dạng được đâu là câu trả lời của thí sinh. Hay nói một cách dễ hiểu hơn là đâu là câu trả lời được tô đậm
  4. ***Bước bốn:***: So sánh câu trả lời của thí sinh với câu trả lời tương ứng trong đáp án chính thức
  5. Bước năm: Lặp lại các bước trên cho từng câu trả lời
  6. Bước sáu: Tổng hợp và đưa ra kết quả cho bài thi

Chuẩn bị vũ khí

Như chúng ta đã bàn bạc ở trên, có cả thảy 6 bước để xử lý bài toán này,. Như vậy là Đường lối tác chiến đã được vạch rõ. Tuy nhiên để đi đến được kết quả chúng ta cần phải trang bị một hệ thống vũ khí tối tân rất cần cho mục đích xử lý ảnh của chúng ta. Gọi là vũ khí tối tân như thực sự nó lại khá phổ biến trong những bài toán xử lý ảnh thế này. Chúng ta cùng điểm mặt qua một số công cụ đó nhé:

OpenCV

Nói đến lĩnh vực Computer Vision chúng ta không thể không nói đến một công cụ mạnh mẽ đó chính là OpenCV. Công cụ này tích hợp khá nhiều giải thuật về Xử lý ảnhHọc máy cùng với các tính năng tăng tốc GPU trong việc phân tích hình ảnh thời gian thực. Tóm lại là nó gần như đã quá phổ biến đến mức cứ nói đến Xử lý ảnh là người ta nghĩ ngay đến OpenCV

Lý thuyết về Optical Mark Recognition (OMR)

Chúng ta cùng nhau bàn luận một chút về lý thuyết thực hiện cho các phần trên trước khi đi vào thực hành. Có thể rất ít bạn thích đọc những phần lý thuyết này nhưng mình khuyên các bạn không nên bỏ quả nó nếu như muốn hiểu thực sự bản chất của vấn đề là như thế nào. Cũng phải thôi, chiêu thức võ công (chính là các công cụ đó) có đẹp mắt đến mấy nhưng không có khẩu quyết tâm pháp thì mãi mãi cũng không thể tiến xa hơn được phải không nào. Vậy nên chúng ta cần phải biết một chút về lý thuyết đã. Vậy Optical Mark Recognition (OMR) nó là cái gì? Chúng ta có thể tạm dịch ra tiếng việt đó chính là Nhận dạng vùng được đánh dấu, điều này dựa trên một nguyên tắc đó là phản xạ quang học khiến cho những vùng nào có đánh dấu (ví dụ như ô được tô trong phiếu trả lời trắc nghiệm chẳng hạn) sẽ có khả năng phản xạ ảnh sáng yếu hơn các vùng còn lại. Đó là xét trên khía cạnh phần cứng thôi. Các thiết bị phần cứng này có một nhước điểm là với các thiết bị này, yêu cầu về việc đánh dấu, tạo mẫu, cũng như yêu cầu về chất liệu giấy in rất khắt khe. Ngược với các thiết bị đánh dấu truyền thống, các phần mềm nhận dạng đánh dấu (Optical Mark Recognition - OMR) cho phép người dùng tự tạo các mẫu và in chúng trên các chất liệu giấy thông thường. Phần mềm sẽ xử lý ảnh quét của mẫu sau khi điền mà không cần chất lượng ảnh quá tốt. Ví dụ như bức ảnh sau mình sẽ sử dụng làm ví dụ cho bài viết này

Thi triển võ công

OK sau một hồi mày mò nghiên cứu giải pháp chúng ta đã biết được tư tưởng chính của phương pháp này đó chính là OMR và chúng ta sẽ sử dụng OpenCV để thực hiện ý đồ đó. Tất nhiên là chúng ta sẽ phải có một mẫu chuẩn của giáy thi trắc nghiệm rồi đúng không nào. Việc này không phải lo lắng lắm bởi vì mỗi mẫu đề thi trắc nghiệm đều được quy định theo chuẩn của quốc gia để phục vụ cho những kì thi lớn như Thi tốt nghiệp THPT hay Thi đại học. . Sau khi có mẫu giấy thi việc đầu tiên của chúng ta đó là thực hiện việc nhận dạng phần trả lời câu hỏi trong mỗi đề thi nhưng trước tiên chúng ta cần import một số thư viện cần thiết của OpenCV dã các bạn ạ

Import thư viện cần thiết

# import library
from imutils.perspective import four_point_transform
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2

Tiếp theo chúng ta cần khởi tạo một biến toàn cục để lưu trữ đường dẫn của file ảnh chứa đáp án của thí sinh

if __name__ == "__main__":
	img_path = './images/test_01.png'

Hoặc nếu như chúng ta không muốn cấu hình cứng trong code thì chúng ta có thể truyền đường dẫn của ảnh vào trong lúc thực thi chương trình theo thư viện argparse của Python. Chúng ta có thể làm như sau:

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True,
				help="path to the input image")
args = vars(ap.parse_args())

if __name__ == "__main__":
	img_path = args["image"]

Bản thân mình tháy cách truyền thế này hay hơn vì không lẽ mỗi lần muốn chấm đề thi cho mỗi học sinh lại vào chỉnh code thì tù vãi chày (^_^)

Lưu trữ câu trả lời đúng

Trước tiên chúng ta cần khởi tạo một list lưu trữ các đáp án đúng của bài thi đã. Chúng ta có thể lưu chúng thành một list như sau:

ANSWER_KEY = {0: 1, 1: 4, 2: 0, 3: 3, 4: 1}

giống như cái tên của nó chúng ta có thể hình dung biến ANSWER_KEY lưu trữ các đáp án đúng của câu trả lời theo dạng key => value với key = 0 tương ứng với câu trả lời đầu tiên. value = 0 tương ứng với đáp án A. Chính vì thế đáp án chính xác của bài thi tương ứng với ANSWER_KEY mô tả phía trên như sau:

  • Question #1: B
  • Question #2: E
  • Question #3: A
  • Question #4: D
  • Question #5: B

Tiền xử lý ảnh với OpenCV

Bước đầu tiên trước khi thực hiện bất kì một bài toán về Machine Learning nào chúng ta cũng cần phải có một bước gọi là Tiền xử lý dữ liệu và trong trường hợp này cũng không ngoại lệ. Chúng ta cần phải Tiền xử lý ảnh đầu vào giúp chuyển chúng về những định dạng mà dễ dàng xử lý hơn cho máy tính. Bước đầu tiên trong xử lý ảnh này đó chính là chuyển về Grayscale (ảnh xám) bởi vì ảnh xám là định dạng thích hợp để xử lý ảnh. Ngay cả trong trường hợp yêu cầu nhiều màu sắc, một hình ảnh màu RGB có thể bị phân tách và xử lý thành ba hình ảnh grayscale riêng biệt. Hình ảnh Indexed cũng được chuyển đổi sang màu xám hoặc RGB cho hầu hết các thao tác. Chúng ta có thể tham khảo về định dạng này ở đây. Để thực hiện điều này với Python thì vô cung đơn giản, thư viện OpenCV đã trợ giúp chúng ta làm điều đó rất dễ dàng như sau:

# Convert to grayscale
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

Bước tiếp theo đó là giảm độ nhiễu của ảnh với Gaussian blur để đơn giản chúng ta có thể hiểu đây chính là bước khiến cho ảnh dễ dàng xử lý hơn với máy tính khi loại bỏ đi những góc cạnh và những vùng ảnh ít có ý nghĩa đối với việc xử lý từ đó khiến cho những vùng chính được nổi bật hơn. Định nghĩa cụ thể của nó như sau

Gaussian Blur là cách làm mờ một ảnh bằng hàm Gaussian. Phương pháp này được ứng dụng một cách rộng rãi và hiệu quả trong các phần mềm xử lý đồ họa. Nó cũng là công cụ phổ biến để thực hiện quá trình tiền xử lý (preprocessing) hình ảnh dùng làm dữ liệu đầu vào tốt cho các phân tích cao cấp hơn như trong Computer Vision, hoặc cho các giải thuật được thực hiện trong một tỉ lệ khác của hình được cho. Nó có thể giúp làm giảm nhiễu (Noise) và mức độ chi tiết (không mong muốn) của hình ảnh.

Bước này cũng được thực hiện bằng OpenCV một cách rất đơn giản như sau:

# Blur image using Gaussian blur
blurred = cv2.GaussianBlur(gray, (5, 5), 0)

Nhận dạng khung bài kiểm tra

Việc nhận dạng ra khung bài kiểm tra vô cùng quan trọng trong bài toán này, lẽ dĩ nhiên bởi vì nếu không có khung bài kiểm tra thì chúng ta sẽ lấy cái gì mà chấm điểm bây giờ ??? Trong bài viết này không đi quá sâu về phương pháp tách khung của một bức ảnh tuy nhiên nếu các bạn muốn có thêm hiểu biết về nó thì chúng ta có thể tham khảo bài viết sau. Còn ở mức độ ứng dụng thì OpenCV đã thực hiện điều này cho chúng ta một cách dễ dàng

# Canny edge detector
edged = cv2.Canny(blurred, 75, 200)

Và đây là kết quả khi áp dụng phân tách khung bài kiểm tra

Lưu ý cách các cạnh của khung bài kiểm tra được xác định rõ ràng, với tất cả bốn đỉnh của bài kiểm tra được hiện diện trong hình ảnh. Vì vậy nên nếu bài kiểm tra bị rách thì nó sẽ không thể phân biệt được đâu các bạn ạ. Việc lấy khung của bài thi là một việc làm rất quan trọng. Chúng ta cần phải thực hiện điều đó trước khi tiến hành bước xử lý tiếp theo đó chính là nhận dạng từng dòng của đáp án trong bài kiểm tra, và bước cuối cùng đó chính là nhận dạng vị trí của đáp án trong bài kiểm tra đó

Tóm tắt phần 1

OK do lượng kiến thức truyền tài trong bài viết cũng khá dài và không muốn các bạn phải tẩu hỏa nhập ma thì mình xin được phép dừng phần đầu tiên của bài viết tại đây. Hi vọng với những gì mình chia sẻ các bạn sẽ có thêm một chút kiến thức về các bước xử lý ảnh cơ bản bằng OpenCV và ứng dụng trong nhận dạng phiếu trả lời trắc nghiệm Phần sau mình sẽ trình bày nốt các bước xử lý để có kết quả cuối cùng. Xin chào các bạn và hẹn gặp lại trong phần tiếp theo

Tham khảo

OpenCV PyImageSearch

0