Course Content
Pycardano: The Ultimate Course for Python and AI Developers

Bài 7: Face Detection + IPFS (MediaPipe + Pinata)

Chào mừng các bạn quay trở lại với chuỗi series xây dựng DApp AI + Blockchain! Ở Bài 6, chúng ta đã hoàn thành xuất sắc phần lõi on-chain với Smart Contract viết bằng Aiken.

Hôm nay, trong Bài 7, chúng ta sẽ tạm rời xa blockchain để bước vào thế giới của Trí tuệ Nhân tạo (AI)Lưu trữ phi tập trung (Off-chain Storage). Đây chính là những mảnh ghép quan trọng để tạo ra dữ liệu đầu vào cho Smart Contract của chúng ta.

Mục Tiêu Bài Học

Kết thúc bài viết này, bạn sẽ có khả năng:

  1. Sử dụng MediaPipe Tasks API (v0.10+) của Google để phát hiện và trích xuất đặc trưng khuôn mặt.

  2. Hiểu và thực hành pipeline 5 bước tạo Face Embedding – biến khuôn mặt thành một vector 512 chiều (“dấu vân tay số”).

  3. Tải dữ liệu vector này lên mạng lưới phi tập trung IPFS thông qua dịch vụ Pinata.

  4. Lấy mã băm CID để chuẩn bị cho việc lưu trữ lên Cardano blockchain ở bài sau.

Lý Thuyết: MediaPipe Tasks API

MediaPipe là một framework AI mã nguồn mở cực kỳ mạnh mẽ của Google, chuyên giải quyết các bài toán về thị giác máy tính (Computer Vision). Đặc điểm ăn tiền nhất của nó là có thể chạy cực nhanh và mượt mà ngay trên CPU mà không cần đến GPU đắt đỏ.

Lưu ý cực kỳ quan trọng: Từ phiên bản 0.10.14, Google đã xóa hoàn toàn bộ API cũ mp.solutions. Do đó, nếu bạn tìm kiếm tutorial trên mạng mà thấy dòng code mp.solutions.face_detection thì nó đã lỗi thời và sẽ gây crash. Chúng ta bắt buộc phải dùng Tasks API mới (mp.tasks.vision).

Trong dự án này, chúng ta sử dụng kết hợp 2 module:

  1. FaceDetector (Phát hiện khuôn mặt):

    • Sử dụng model blaze_face_short_range.tflite (siêu nhẹ, chỉ ~200KB).

    • Trả về Bounding box (tọa độ hình chữ nhật bao quanh khuôn mặt) và Confidence score (độ tin cậy).

  2. FaceLandmarker (Điểm mốc khuôn mặt):

    • Trả về 478 điểm tọa độ 3D (x, y, z) phác họa chi tiết đường nét khuôn mặt (mắt, mũi, miệng,…).

Tại sao cần cả hai? Chúng ta dùng FaceDetector để xác định vị trí nhằm cắt (crop) ảnh khuôn mặt, và FaceLandmarker để lấy dữ liệu chi tiết chuẩn bị cho việc trích xuất đặc trưng. Code dự án sẽ được thiết lập để tự động tải các file model (.tflite / .task) này về máy trong lần chạy đầu tiên.

Pipeline 5 Bước: Tạo Face Embedding

Làm sao để máy tính hiểu được hai bức ảnh là của cùng một người? Giải pháp là biến bức ảnh thành Face Embedding — một vector gồm các con số toán học đại diện cho đặc trưng của khuôn mặt đó.

Dưới đây là pipeline 5 bước để trích xuất embedding được viết trong hàm extract_embedding:

def extract_embedding(self, frame, bbox):
    x, y, w, h = bbox
    # Bước 1: Crop vùng mặt theo bounding box
    face_roi = frame[y:y+h, x:x+w]

    # Bước 2: Resize về kích thước chuẩn 128x128
    face_resized = cv2.resize(face_roi, (128, 128))

    # Bước 3: Chuyển BGR → RGB và chuẩn hóa pixel về [0, 1]
    face_rgb = cv2.cvtColor(face_resized, cv2.COLOR_BGR2RGB)
    flat = face_rgb.flatten().astype(np.float32) / 255.0

    # Bước 4: Chuẩn hóa thành Unit Vector (độ dài = 1)
    norm = np.linalg.norm(flat)
    if norm > 0:
        flat = flat / norm

    # Bước 5: Cắt ngắn (truncate) hoặc đệm (pad) về đúng 512 chiều
    if len(flat) < 512:
        flat = np.pad(flat, (0, 512 - len(flat)))
    else:
        flat = flat[:512]

    return flat.tolist()

Giải Thích Chi Tiết:

  • Bước 1 (Crop): Dựa vào tọa độ (Bounding Box) từ FaceDetector để cắt bỏ phông nền, chỉ lấy đúng khuôn mặt.

  • Bước 2 (Resize): Vì người dùng có thể đứng xa hoặc gần camera, ta cần chuẩn hóa tất cả khuôn mặt về một kích thước chung là 128x128 pixel.

  • Bước 3 (RGB & Normalize): Đổi dải màu OpenCV từ BGR sang RGB. Việc chia cho 255.0 giúp thu hẹp giá trị pixel từ [0, 255] về khoảng [0, 1], giúp các phép tính toán học sau này ổn định hơn. Sau bước này ta có một mảng $128 \times 128 \times 3 = 49,152$ chiều.

  • Bước 4 (Unit Vector): Chuẩn hóa vector sao cho độ dài (norm) của nó bằng 1. Điều này rất quan trọng để tính toán Cosine Similarity (độ tương đồng) ở các bước sau.

  • Bước 5 (Truncate 512D): Một vector 49,152 chiều là quá lớn và dư thừa. Chúng ta chỉ lấy 512 giá trị đầu tiên. Con số 512D là một quy chuẩn phổ biến trong các mô hình Face Recognition hiện đại: đủ nhỏ gọn để lưu trữ nhưng vẫn đảm bảo khả năng phân biệt đặc trưng cực tốt.

Lưu Trữ Lên IPFS Bằng Pinata

Giờ đây, chúng ta đã có một danh sách 512 con số (float). Dù đã thu gọn, việc lưu trữ mảng dữ liệu này trực tiếp lên blockchain Cardano (qua Smart Contract ở Bài 6) vẫn là một thảm họa về chi phí (phí giao dịch sẽ cực kỳ đắt đỏ do dung lượng lớn).

Giải pháp hoàn hảo: IPFS (InterPlanetary File System).

  • IPFS là một hệ thống tệp tin phân tán. Điểm đặc biệt của nó là Content-Addressed (Định danh bằng nội dung). Thay vì định vị tệp bằng đường dẫn URL truyền thống (ví dụ: domain.com/file.jpg), IPFS định vị bằng một mã băm duy nhất gọi là CID (Content Identifier).

  • Nếu file bị thay đổi dù chỉ một ký tự, mã CID sẽ lập tức biến thành một chuỗi hoàn toàn khác. Nhờ vậy, tính toàn vẹn của dữ liệu được đảm bảo tuyệt đối.

Chúng ta sẽ dùng Pinata, một dịch vụ gateway cung cấp API miễn phí để đẩy file lên mạng IPFS. Dưới đây là đoạn mã cốt lõi trong class PinataIPFS:

def upload_json(self, data: dict, name: str = "face_embedding") -> dict:
    payload = {
        "pinataContent": data,
        "pinataMetadata": {"name": name},
        "pinataOptions": {"cidVersion": 0},     # Sử dụng CIDv0 (Bắt đầu bằng chữ "Qm...")
    }
    resp = requests.post(
        f"{PINATA_API_URL}/pinning/pinJSONToIPFS",
        json=payload,
        headers={**self.headers, "Content-Type": "application/json"},
        timeout=30,
    )
    cid = resp.json()["IpfsHash"]
    return {"cid": cid, "url": f"[https://gateway.pinata.cloud/ipfs/](https://gateway.pinata.cloud/ipfs/){cid}"}

Mã code của dự án sẽ lấy dữ liệu JSON chứa vector 512 chiều, gọi hàm upload_json và trả về một mã CID ngắn gọn (chỉ khoảng 46 byte). Mã CID này chính là trường face_ipfs_hash mà chúng ta truyền vào DIDDatum trên blockchain!

Chạy Thử (Live Demo)

Để chạy thử, hãy setup môi trường và chạy script face_detect.py:

# 1. Phát hiện khuôn mặt và trích xuất embedding
python face_detect.py --image face.jpg

Kết quả hiển thị trên Terminal:

Downloading model: blaze_face_short_range.tflite ...
Saved to models/blaze_face_short_range.tflite
FaceDetector initialized
Loading image: face.jpg
Detected 1 face(s)
Face 0: confidence=0.91, bbox=(2197, 1147, 961, 961)
Embedding: 512-dimensional vector
Saved to: face_embedding.json

Một tấm ảnh có khung viền xanh quanh khuôn mặt sẽ hiện lên, đồng thời file face_embedding.json được tạo thành công!

Tiếp theo, tải lên IPFS:

# 2. Upload file JSON lên mạng IPFS
python ipfs_upload.py --file face_embedding.json

Kết quả:

Pinata authentication OK
Uploading JSON to Pinata IPFS...
Upload successful!
CID: QmXLaBYop7bGLQ2uWtDUo5tk7niVDLdKpLTRfULAAwp6gz
CID đã lưu vào: face_embedding.cid

Tổng Kết Bài 7

Trong bài viết này, chúng ta đã hoàn tất phần AI và lưu trữ phi tập trung:

  1. Nắm được cách vận hành của MediaPipe Tasks API để lấy Bounding Box và Landmarks.

  2. Hiểu rõ quy trình chuẩn hóa ảnh và trích xuất thành Face Embedding (512 chiều) đại diện cho khuôn mặt.

  3. Học cách đẩy lượng dữ liệu này lên hệ thống phân tán IPFS thông qua dịch vụ Pinata, lấy về mã CID nhỏ gọn.

Tiếp theo là gì? Chúng ta đã có Smart Contract (Bài 6) và giờ là Dữ liệu CID (Bài 7). Trong Bài 8, chúng ta sẽ chính thức viết mã Off-chain bằng Python (PyCardano) để lắp ghép 2 thành phần này lại: Tạo giao dịch khóa ADA và ghi mã CID khuôn mặt của bạn trực tiếp lên nền tảng Cardano Testnet!

Hẹn gặp lại các bạn ở bài tiếp theo! 
Hẹn gặp lại các bạn trong bài viết tiếp theo! Chúc các bạn code vui vẻ!
Chi tiết về source code toàn bộ bài học các bạn có thể tham khảo tại!!!
Pycardano integration with AI Implementation Example

SLOT88
totoslot777
https://nextlevelacademygr.es/
SLOT88
CARVALHO-MANZON Digital Arts
https://kaizen7.pe/
slot maxwin
BANDAR SLOT
SLOT THAILAND
https://www.ui-academy.co.uk/faq/
https://harton.be/
SLOT THAILAND
ANGKATOTO
SLOT THAILAND