Skip to main content

Trích xuất 1 ảnh/câu từ video screen-recording quiz Moodle (cuộn)

Project description

🎯 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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

moodle_quiz_extractor-1.1.6.tar.gz (29.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

moodle_quiz_extractor-1.1.6-py3-none-any.whl (27.9 kB view details)

Uploaded Python 3

File details

Details for the file moodle_quiz_extractor-1.1.6.tar.gz.

File metadata

  • Download URL: moodle_quiz_extractor-1.1.6.tar.gz
  • Upload date:
  • Size: 29.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for moodle_quiz_extractor-1.1.6.tar.gz
Algorithm Hash digest
SHA256 7608983e253a6d693f0863d5e6e15fb7d421c8ad7734e8d77f2ce3991ab7e0a2
MD5 afd02850ec271d3121a01c882a573f8c
BLAKE2b-256 63836c46cd5edbd77b22eb9cb2a0a867add810ca551261cc53a616653c4973fe

See more details on using hashes here.

Provenance

The following attestation bundles were made for moodle_quiz_extractor-1.1.6.tar.gz:

Publisher: release.yml on MinhThang1009/moodle-quiz-extractor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file moodle_quiz_extractor-1.1.6-py3-none-any.whl.

File metadata

File hashes

Hashes for moodle_quiz_extractor-1.1.6-py3-none-any.whl
Algorithm Hash digest
SHA256 55b36f411a8de05c6c5804b162a0c8de562a1b0710696741f2d28521d54f9935
MD5 82031f16e056d3af4a0c0a1fbb90cda6
BLAKE2b-256 c2ade145f7bb47e21e349aa36b892f797a431a70fffd2da56fa660e617954b0c

See more details on using hashes here.

Provenance

The following attestation bundles were made for moodle_quiz_extractor-1.1.6-py3-none-any.whl:

Publisher: release.yml on MinhThang1009/moodle-quiz-extractor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page