Kiểm tra hàng loạt ảnh bằng Python trước khi đưa vào pipeline tạo video
Khi xây dựng một workflow xử lý ảnh hoặc tạo video từ ảnh, có một bước khá dễ bị bỏ qua: kiểm tra file đầu vào.
Một thư mục chứa 100 ảnh có thể trông hoàn toàn bình thường khi xem bằng File Explorer. Nhưng khi chạy xử lý tự động, chúng ta có thể gặp những vấn đề như:
- Ảnh có kích thước quá nhỏ.
- Tỷ lệ khung hình không phù hợp với video đầu ra.
- File có phần mở rộng
.jpgnhưng nội dung thực tế là PNG. - File bị hỏng hoặc không thể đọc.
- Dung lượng ảnh vượt quá giới hạn của pipeline.
Nếu chỉ phát hiện lỗi sau khi đã gửi ảnh tới bước xử lý tiếp theo, chúng ta sẽ mất thời gian để tìm lại file gây ra sự cố.
Trong bài này, chúng ta sẽ viết một công cụ Python nhỏ để kiểm tra ảnh hàng loạt và xuất kết quả thành file CSV.
Công cụ hoạt động hoàn toàn trên máy cục bộ, không cần API bên ngoài.
1. Thiết kế workflow
Mục tiêu là xây dựng một bước kiểm tra đơn giản:
Input Images
|
v
Python Validator
|
+-- Check Format
+-- Check Dimensions
+-- Check Aspect Ratio
+-- Check File Size
+-- Detect Corrupted Files
|
v
Validation Report (CSV)
|
v
Review / Processing Pipeline
Chúng ta sẽ sử dụng thư viện Pillow để đọc và xác minh dữ liệu ảnh.
Bài viết tập trung vào PNG và JPEG. Các định dạng khác sẽ bị đánh dấu là không được hỗ trợ.
2. Chuẩn bị môi trường
Yêu cầu Python 3.10 trở lên.
Tạo thư mục dự án:
mkdir image-validator
cd image-validator
python -m venv .venv
Kích hoạt virtual environment.
Trên Windows:
.venv\Scripts\activate
Trên macOS hoặc Linux:
source .venv/bin/activate
Cài đặt Pillow:
python -m pip install Pillow
Tạo thư mục images để chứa những ảnh cần kiểm tra.
Cấu trúc dự án:
image-validator/
├── images/
├── validate_images.py
└── requirements.txt
Trong requirements.txt, thêm:
Pillow
Khi sử dụng trong dự án thực tế, nên khóa phiên bản thư viện sau khi kiểm thử để đảm bảo môi trường có thể tái tạo.
3. Viết chương trình kiểm tra ảnh
Tạo file validate_images.py:
from pathlib import Path
from PIL import Image, UnidentifiedImageError
import csv
INPUT_DIR = Path("images")
REPORT_FILE = Path("report.csv")
MIN_WIDTH = 1024
MIN_HEIGHT = 576
MAX_SIZE_MB = 10
TARGET_RATIO = 16 / 9
RATIO_TOLERANCE = 0.03
SUPPORTED = {
".jpg": "JPEG",
".jpeg": "JPEG",
".png": "PNG",
}
def validate_image(path):
result = {
"filename": path.name,
"width": "",
"height": "",
"format": "",
"status": "PASS",
"issues": "",
}
issues = []
try:
with Image.open(path) as img:
actual_format = img.format
img.verify()
# Mở lại vì verify() không dùng để đọc pixel.
with Image.open(path) as img:
img.load()
width, height = img.size
result["width"] = width
result["height"] = height
result["format"] = actual_format
expected_format = SUPPORTED.get(
path.suffix.lower()
)
if expected_format != actual_format:
issues.append("Format mismatch or unsupported")
if width < MIN_WIDTH or height < MIN_HEIGHT:
issues.append("Resolution too small")
ratio = width / height
if abs(ratio - TARGET_RATIO) > RATIO_TOLERANCE:
issues.append("Unexpected aspect ratio")
size_mb = path.stat().st_size / (1024 * 1024)
if size_mb > MAX_SIZE_MB:
issues.append("File too large")
except (
UnidentifiedImageError,
OSError,
ValueError,
Image.DecompressionBombError,
) as exc:
issues.append(f"Cannot read image: {type(exc).__name__}")
if issues:
result["status"] = "FAIL"
result["issues"] = "; ".join(issues)
return result
def main():
if not INPUT_DIR.is_dir():
raise SystemExit("Input directory does not exist")
files = sorted(
p for p in INPUT_DIR.iterdir()
if p.is_file()
)
results = [validate_image(p) for p in files]
fields = [
"filename",
"width",
"height",
"format",
"status",
"issues",
]
with REPORT_FILE.open(
"w", newline="", encoding="utf-8-sig"
) as f:
writer = csv.DictWriter(f, fieldnames=fields)
writer.writeheader()
writer.writerows(results)
failed = sum(
row["status"] == "FAIL" for row in results
)
print(f"Checked: {len(results)}")
print(f"Passed: {len(results) - failed}")
print(f"Failed: {failed}")
print(f"Report: {REPORT_FILE}")
if failed:
raise SystemExit(1)
if __name__ == "__main__":
main()
Một vài điểm đáng chú ý
img.verify() không thay thế img.load()
verify() kiểm tra cấu trúc file ở mức Pillow hỗ trợ, nhưng không phải lúc nào cũng giải mã toàn bộ dữ liệu pixel. Vì vậy, chương trình mở lại ảnh và gọi load().
Kiểm tra định dạng thực tế
Chúng ta không chỉ dựa vào phần mở rộng của tên file. Thuộc tính img.format giúp xác định định dạng mà Pillow nhận diện được.
Tolerance cho tỷ lệ khung hình
Thay vì yêu cầu tỷ lệ chính xác tuyệt đối là 16:9, chương trình cho phép sai số 0.03.
Đây là quy tắc kiểm tra của ví dụ, không phải yêu cầu bắt buộc của mọi hệ thống tạo video.
4. Tạo dữ liệu để kiểm thử
Không nên chỉ chạy chương trình trên những ảnh có sẵn rồi cho rằng nó hoạt động đúng.
Chúng ta có thể tạo một số file thử nghiệm:
from PIL import Image
from pathlib import Path
folder = Path("images")
folder.mkdir(exist_ok=True)
Image.new(
"RGB", (1920, 1080), "blue"
).save(folder / "valid.jpg")
Image.new(
"RGB", (640, 360), "red"
).save(folder / "small.jpg")
Image.new(
"RGB", (1080, 1080), "green"
).save(folder / "square.png")
Image.new(
"RGB", (1920, 1080), "white"
).save(folder / "wrong_extension.png", format="JPEG")
(folder / "broken.jpg").write_bytes(
b"not a valid image"
)
Lưu đoạn code trên thành create_samples.py, sau đó chạy:
python create_samples.py
python validate_images.py
Lệnh kiểm tra sẽ trả exit code 1 vì bộ dữ liệu cố ý chứa file không hợp lệ.
Kết quả mong đợi:
Checked: 5
Passed: 1
Failed: 4
Report: report.csv
Chương trình cũng tạo ra report.csv để xem chi tiết từng lỗi.
Bảng dưới đây mô tả kết quả kỳ vọng, không phải benchmark hiệu năng:
| File | Kết quả | Lý do |
|---|---|---|
| valid.jpg | PASS | Đúng các điều kiện |
| small.jpg | FAIL | Độ phân giải thấp |
| square.png | FAIL | Sai tỷ lệ khung hình |
| wrong_extension.png | FAIL | Phần mở rộng không khớp |
| broken.jpg | FAIL | Không đọc được ảnh |
Điểm quan trọng là lỗi được phát hiện trước khi file đi vào các bước xử lý tốn tài nguyên hơn.
5. Tích hợp vào pipeline tạo video bằng AI
Sau khi hoàn thành bước kiểm tra ảnh, chúng ta có thể sử dụng những file đạt yêu cầu làm đầu vào cho các công cụ tạo video.
Ví dụ, PWZ AI là một nền tảng cung cấp các công cụ tạo ảnh và video với nhiều mô hình AI khác nhau. Trong một quy trình thử nghiệm, ảnh đầu vào có thể được kiểm tra bằng script Python trước khi người dùng tải lên nền tảng để tạo video.
Cần lưu ý rằng script trong bài viết hoạt động độc lập, không sử dụng API của PWZ AI và không tự động tải file lên dịch vụ. Việc chọn mô hình, cấu hình và tạo video vẫn được thực hiện theo những chức năng mà nền tảng hỗ trợ.
6. Những giới hạn của cách làm này
Công cụ trên kiểm tra những đặc điểm kỹ thuật cơ bản của file ảnh, nhưng không đánh giá chất lượng nội dung.
Ví dụ, nó không phát hiện được:
- Logo hoặc chữ bị sai trong ảnh.
- Chủ thể bị biến dạng.
- Ánh sáng không tự nhiên.
- Hình ảnh không phù hợp với yêu cầu thương hiệu.
- Metadata EXIF định hướng ảnh chưa được chuẩn hóa.
Ngoài ra, chương trình hiện chỉ quét file trực tiếp trong một thư mục, không duyệt thư mục con.
Với hệ thống production, nên bổ sung kiểm thử tự động, giới hạn tài nguyên, xử lý file không đáng tin cậy trong môi trường cô lập và quy trình quản lý dependency chặt chẽ hơn.
Các giới hạn như 1024 × 576, 10 MB và tỷ lệ 16:9 cần được cấu hình theo yêu cầu thực tế của dự án, thay vì áp dụng cố định cho mọi trường hợp.
7. Kết luận
Trong một pipeline tạo nội dung, không phải lỗi nào cũng cần giải quyết bằng một mô hình AI mạnh hơn.
Nhiều lỗi có thể được phát hiện sớm bằng những bước kiểm tra đơn giản: định dạng file, độ phân giải, tỷ lệ khung hình và khả năng đọc dữ liệu ảnh.
Với Python và Pillow, chúng ta có thể tạo một công cụ validation nhỏ, dễ mở rộng và có thể tích hợp vào quy trình xử lý tự động.
Đây không phải giải pháp kiểm soát chất lượng hình ảnh toàn diện. Nhưng nó là một bước đầu hợp lý để giảm những lỗi đầu vào không cần thiết trước khi chạy các tác vụ xử lý ảnh hoặc tạo video tốn tài nguyên hơn.
All rights reserved