Skip to main content

🎯 moodle-quiz-extractor

Trích xuất mỗi câu hỏi thành 1 ảnh riêng từ video screen-recording quiz Moodle

Không sót câu · Không lặp ảnh · Cắt khít từng câu · Chạy được mọi video cùng template

CI Python License OCR OpenCV Code style


Mục lục

Vấn đề

Khi quay màn hình một bài quiz Moodle (màn hình cuộn liên tục), việc cắt thủ công từng câu rất mất công, và các cách tự động đơn giản (lấy frame theo nhịp, bắt lúc dừng đọc) thường sót câu hoặc lặp ảnh. moodle-quiz-extractor giải đúng bài toán này: mỗi câu hỏi → đúng một ảnh sạch, đầy đủ.

Tính năng

  • ✅ Không sót câu — quét dày toàn video, bắt mọi câu từng hiện đầy đủ (kể cả câu chỉ lướt qua).
  • ✅ Không lặp — gom theo số câu (Question N), mỗi câu đúng 1 ảnh.
  • ✅ Ảnh nét — chọn frame nét nhất tại vùng câu, refine frame lân cận để né motion blur khi cuộn nhanh.
  • ✅ Cắt khít — mỗi ảnh đúng 1 block (header + đề + đáp án), tự bỏ lưới nav và header câu kế.
  • ✅ Portable — bám cấu trúc text Moodle + ngưỡng theo tỉ lệ H/W → chạy mọi video cùng template, mọi độ phân giải, không sửa code.
  • ✅ Cache OCR — lần đầu OCR vài phút, lần sau gần như tức thì.
  • ✅ Tự nhận diện layout — auto-detect vùng nội dung từ video, chạy thẳng cả điện thoại dọc lẫn desktop ngang 2 cột mà không cần chỉnh config.
  • ✅ Xuất text — OCR ảnh câu sang questions.json + .md (engine offline EasyOCR hoặc vision LLM).
  • ✅ Watcher tự động — thả video vào data/ là tự xử lý, hỗ trợ nhiều video song song.

Yêu cầu

  • Python 3.10+
  • Phụ thuộc: opencv-python, numpy, easyocr (xem requirements.txt)

Cài đặt

python -m venv .venv
.venv\Scripts\activate          # Windows  •  Linux/macOS: source .venv/bin/activate
pip install -r requirements.txt

Sử dụng

# Mặc định: data/1.mp4 -> output/questions/
python -m quiz_extractor

# Video khác (cùng template Moodle):
python -m quiz_extractor --video data/2.mp4 --output output/quiz2

# OCR lại từ đầu (bỏ cache):
python -m quiz_extractor --force-ocr
Tham số Mặc định Ý nghĩa
--video data/1.mp4 Đường dẫn video .mp4
--output output/questions Thư mục lưu ảnh câu
--cache output/ocr_cache.pkl File cache OCR
--step 4 OCR mỗi STEP frame (nhỏ hơn = kỹ hơn, chậm hơn)
--force-ocr false Xoá cache, OCR lại từ đầu
--config – JSON override marker/tỉ lệ template (xem Template khác)
--no-auto-crop false Tắt tự suy vùng nội dung, dùng tỉ lệ cố định trong config
-v/--verbose false In thêm log DEBUG
-q/--quiet false Chỉ in cảnh báo/lỗi

Kết quả: output/questions/question-01.png, question-02.png, …

OCR in tiến độ dạng stream (1 dòng cập nhật tại chỗ) và chỉ giữ ~1 frame trong RAM (stream video, không nạp toàn bộ → không OOM video dài). Có GPU (CUDA/MPS) thì easyocr tự dùng. Thiếu câu / câu thiếu nội dung sẽ in cảnh báo ở cuối.

Template khác

Vùng nội dung (chrome trình duyệt / status bar / overlay) tự nhận diện — chạy thẳng cả khi đổi hẳn layout (điện thoại dọc ↔ desktop ngang, độ phân giải khác) không cần config. Chỉ còn markers ngôn ngữ là phải override khi Moodle dùng ngôn ngữ/theme khác (vd "Câu hỏi N"), vì không suy ra từ pixel được:

{
  "QUESTION_RE": "câu hỏi\\s*(\\d{1,3})",
  "QUIZ_MARKERS": ["chưa trả lời", "đánh dấu"],
  "OCR_LANGS": ["vi"]
}

Auto-crop bật mặc định; detect bất thường thì fallback tỉ lệ cố định. Muốn ép vùng nội dung thì --no-auto-crop rồi đặt F_CONTENT_TOP/BOT trong --config.

Xuất text (OCR ảnh câu)

Chuyển ảnh câu thành text có cấu trúc (output/questions.json + questions.md):

# Mặc định: easyocr (offline, ~95%, sai dấu lác đác) — không thêm dep
python -m quiz_extractor.to_text

# Chính xác hơn: vision LLM (Anthropic) — cần ANTHROPIC_API_KEY + có chi phí
pip install "anthropic>=0.115"
export ANTHROPIC_API_KEY=sk-ant-...        # Windows: $env:ANTHROPIC_API_KEY="..."
python -m quiz_extractor.to_text --engine llm                 # model mặc định claude-opus-4-8
python -m quiz_extractor.to_text --engine llm --model claude-haiku-4-5   # rẻ hơn

OCR không bao giờ exact 100% (dấu tiếng Việt) — nên rà lại. Engine llm đọc dấu + bố cục a/b/c/d chính xác hơn nhiều nhưng gửi ảnh ra API.

Tự động hóa (watcher)

Thả video vào data/ rồi đi làm việc khác — watcher tự trích ảnh (+ OCR text) ra output/<tên-video>/:

python -m quiz_extractor.watch                  # theo dõi data/, OCR easyocr
python -m quiz_extractor.watch --to-text none   # chỉ trích ảnh, không OCR
python -m quiz_extractor.watch --to-text llm    # OCR vision LLM
python -m quiz_extractor.watch --once           # quét 1 lượt rồi thoát (cho cron)
python -m quiz_extractor.watch --workers 2      # xử lý 2 video song song

Nhiều video: thả nhiều .mp4 vào data/, mỗi cái ra output/<tên-video>/ riêng (cache riêng). Quét bằng polling (mặc định mỗi 5s, không cần dep), chờ file copy xong mới chạy, bỏ qua video đã có kết quả (dùng --reprocess để chạy lại). --workers N chạy N video song song (mỗi video 1 process, ~1.5GB RAM/process; mặc định 1 = tuần tự). Ctrl+C để dừng.

Cách hoạt động

Bám cấu trúc text của template Moodle thay vì vị trí pixel:

flowchart LR
    A[Video .mp4] --> A2[Auto-detect<br/>vùng nội dung]
    A2 --> B[Dense-sample<br/>bỏ frame tĩnh]
    B --> C[OCR header<br/>Question N]
    C --> D{Có marker<br/>quiz?}
    D -- không --> X[Bỏ frame<br/>overlay]
    D -- có --> E[Gom theo<br/>số câu]
    E --> F[Chọn frame<br/>nét nhất /câu]
    F --> G[Cắt block theo<br/>khoảng trắng]
    G --> H[question-NN.png]
  1. Tự nhận diện vùng nội dung: phân tích các cặp frame "cuộn thuần" để loại chrome trình duyệt / status bar / overlay — không cần cấu hình theo thiết bị.
  2. Dense-sample frame toàn video, OCR (EasyOCR, vi+en) tìm header Question N; bỏ qua frame tĩnh trùng nội dung để OCR nhanh hơn.
  3. Bỏ frame overlay / không phải trang quiz (vắng marker); header thật phải có marker grey-box ngay dưới (anchor cấu trúc, tránh nhận nhầm "Flag question").
  4. Gom ứng viên theo số câu; mỗi câu chọn frame nét nhất tại vùng câu (refine ±N frame lân cận).
  5. Cắt block từ header câu này tới ranh giới khoảng trắng trước câu kế / lưới nav.

Chỗ duy nhất phụ thuộc template là regex marker trong quiz_extractor/config.py; đổi LMS khác chỉ cần sửa ở đó.

Cấu trúc dự án

.
├── quiz_extractor/        # package chính
│   ├── __init__.py        # version (__version__)
│   ├── config.py          # marker template Moodle, tỉ lệ layout, hằng số ảnh
│   ├── geometry.py        # suy ngưỡng pixel từ (H, W) video
│   ├── ocr.py             # đọc video, auto-detect vùng nội dung, OCR header, cache
│   ├── imaging.py         # đo độ nét theo block, refine frame nét nhất
│   ├── extractor.py       # orchestration: video -> 1 ảnh/câu
│   ├── to_text.py         # OCR ảnh câu -> text (easyocr / vision LLM)
│   ├── watch.py           # watcher tự chạy pipeline khi có video mới
│   └── __main__.py        # CLI
├── tests/                 # pytest (geometry, ocr, imaging, extractor, detect, ...)
├── data/                  # video đầu vào (gitignored)
├── output/                # ảnh câu + cache (gitignored)
├── .github/               # CI/release workflows, dependabot, issue/PR templates
├── README.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── CODE_OF_CONDUCT.md
├── SECURITY.md
├── SUPPORT.md
├── LICENSE
├── pyproject.toml
└── requirements.txt

Giới hạn

Không trích được nội dung chưa từng được quay. Ví dụ trong 1.mp4, câu cuối (Q42) chỉ được quay phần header rồi người quay tắt recording, nên ảnh Q42 thiếu phần đáp án — cần quay lại đoạn đó. Tương tự, đoạn cuộn quá nhanh bị motion blur thì ảnh chọn ra sẽ kém nét hơn (thuật toán đã chọn frame nét nhất có thể).

Roadmap

  • Xuất text từ ảnh câu (questions.json + .md, EasyOCR / vision LLM).
  • Tự nhận diện vùng nội dung — chạy mọi layout không cần config.
  • GPU auto-detect cho EasyOCR.
  • Khử nhiễu ảnh trước OCR (giảm sai dấu ở câu mờ).
  • Hỗ trợ thêm template LMS khác (Google Forms, Azota…) qua file cấu hình marker.
  • Xuất PDF gộp tất cả câu.

Đóng góp

Xem CONTRIBUTING.md. Mọi issue/PR đều được hoan nghênh.

Changelog

Xem CHANGELOG.md (theo Keep a Changelog + SemVer).

License

Phát hành theo giấy phép MIT.

Metadata

Release files for moodle-quiz-extractor 1.1.6

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for moodle-quiz-extractor 1.1.6
File Size Uploaded
moodle_quiz_extractor-1.1.6.tar.gz 29.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for moodle-quiz-extractor 1.1.6
File Interpreter ABI Platform
moodle_quiz_extractor-1.1.6-py3-none-any.whl Python 3 none any Details

Total release size: 57.5 kB

Release files / moodle_quiz_extractor-1.1.6.tar.gz

Download URL moodle_quiz_extractor-1.1.6.tar.gz
Size 29.6 kB
Tags Source
SHA-256 checksum
How to use checksums
7608983e253a6d693f0863d5e6e15fb7d421c8ad7734e8d77f2ce3991ab7e0a2
BLAKE2b-256 checksum
How to use checksums
63836c46cd5edbd77b22eb9cb2a0a867add810ca551261cc53a616653c4973fe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.

Transparency log

Release files / moodle_quiz_extractor-1.1.6-py3-none-any.whl

Download URL moodle_quiz_extractor-1.1.6-py3-none-any.whl
Size 27.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
55b36f411a8de05c6c5804b162a0c8de562a1b0710696741f2d28521d54f9935
BLAKE2b-256 checksum
How to use checksums
c2ade145f7bb47e21e349aa36b892f797a431a70fffd2da56fa660e617954b0c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 2, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.1.6 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page