Hệ thống kiểm tra thị giác (Vision Inspection System – VIS), còn được gọi là kiểm tra thị giác tự động (Automated Visual Inspection – AVI) hoặc hệ thống máy thị giác (Machine Vision System), là một cấu hình dựa trên máy tính sử dụng camera và phần mềm xử lý ảnh để tự động kiểm tra sản phẩm, vật liệu hoặc quy trình nhằm phát hiện lỗi, sai sót hoặc đánh giá mức độ tuân thủ tiêu chuẩn chất lượng.
Một hệ thống kiểm tra thị giác (VIS) thường bao gồm các thành phần chính sau:
- Camera (một hoặc nhiều camera): Camera dùng để chụp ảnh; đi kèm chiếu sáng có kiểm soát nhằm làm nổi bật các đặc trưng quan trọng của đối tượng cần kiểm tra.
- Bộ xử lý (Processing Unit): Có thể là PC công nghiệp, workstation GPU hoặc thiết bị nhúng (ví dụ Jetson, FPGA) để chạy các thuật toán thị giác máy tính.
- Phần mềm (Software): “Bộ não” của hệ thống kiểm tra thị giác. Phần mềm có thể là:
- Thuật toán thị giác cổ điển (ví dụ phát hiện biên, khớp mẫu, lọc hình thái học),
- Mô hình học sâu (ví dụ CNN cho phân loại; YOLO, Faster R-CNN, RF-DETR cho phát hiện đối tượng; U-Net cho phân đoạn),
- Thuật toán phát hiện bất thường (ví dụ autoencoder, mô hình one-class).
- Phần cứng tích hợp dây chuyền (Hardware/Integration): Các hệ thống phần cứng kết nối với dây chuyền sản xuất, PLC hoặc robot để đưa ra quyết định đạt/không đạt, phân loại, hoặc kích hoạt hành động tiếp theo.
>>> Xem thêm các bài viết khác:
- Top 7 Công cụ Theo dõi Đối tượng Mã nguồn mở Tốt Nhất 2025
- Top 5 trình soạn thảo mã cho thị giác máy tính tốt nhất

Hệ thống kiểm tra thị giác có chức năng gì?
Một hệ thống kiểm tra thị giác có thể đảm nhiệm nhiều tác vụ kiểm tra, đo lường và xác minh trong công nghiệp, điển hình gồm:
Phát hiện lỗi
Đây là ứng dụng phổ biến nhất của hệ thống kiểm tra thị giác: nhận diện các lỗi như vết xước, nứt, móp, thiếu chi tiết, biến dạng trên sản phẩm. Ví dụ:
- Trong sản xuất ô tô, VIS phát hiện các vết nứt rất nhỏ ở linh kiện động cơ.
- Trong điện tử, VIS phát hiện mối hàn thiếu hoặc chip đặt sai vị trí trên PCB (bảng mạch in).
>>> Xem thêm:
- Xây dựng quy trình Vision AI nghiên cứu khoa học
- Vertex AI là gì? Nền tảng học máy của Google Cloud

Đo lường và kiểm tra kích thước
Hệ thống kiểm tra thị giác được dùng rộng rãi để đo kiểm kích thước hình học như chiều dài, chiều rộng, đường kính, góc, và thậm chí độ nhám bề mặt. Ví dụ:
- Trong hàng không vũ trụ, VIS đảm bảo cánh tuabin hoặc bánh răng đạt dung sai kích thước nghiêm ngặt.
- Trong gia công kim loại, VIS đo bước ren và độ thẳng hàng của lỗ.

Xác minh mẫu và nhãn
Hệ thống kiểm tra thị giác xác minh logo, chữ in, mã vạch và mã định danh (serialization) có tồn tại, đúng vị trí, và đọc được. Các hệ VIS nâng cao còn kiểm tra chất lượng in, phát hiện lem mực hoặc phai mực.
Xác minh lắp ráp
Trong quá trình lắp ráp, hệ thống kiểm tra thị giác đảm bảo mọi thành phần:
- Có mặt đầy đủ,
- Đặt đúng vị trí,
- Căn chỉnh chính xác.
Hệ thống có thể kiểm tra hướng của vít, độ khớp của đầu nối, hoặc tình trạng gioăng đã được đặt đúng hay chưa. Ví dụ: trên dây chuyền lắp ráp ô tô, VIS xác nhận túi khí, vị trí gá dây an toàn và bó dây điện được lắp đúng trước khi xe chuyển sang công đoạn tiếp theo.
>>> Xem thêm: Xây Dựng Mô Hình Ngôn Ngữ Thị Giác với Next.js & Roboflow

Kiểm tra chất lượng bề mặt
Hệ thống kiểm tra thị giác phát hiện lỗi bề mặt trên sơn, lớp phủ, đường hàn, kính, hoặc đường mạch trên PCB. Ví dụ:
- Trong thiết bị điện tử tiêu dùng, VIS phát hiện vết xước trên màn hình điện thoại.
- Trong sản xuất thép, VIS phát hiện rỗ bề mặt, bong vảy (scaling) hoặc vết xước trên tấm cán.

Xác minh màu sắc và sắc độ
Một số sản phẩm phải tuân thủ nghiêm ngặt chuẩn màu hoặc lớp hoàn thiện. Khi đó, hệ thống kiểm tra thị giác sẽ so sánh màu thu được từ camera với giá trị tham chiếu để đảm bảo tính nhất quán màu sắc.
>>> Xem thêm:
- Các mô hình phát hiện đối tượng trên iOS tốt nhất hiện nay
- Các mô hình phát hiện đối tượng tốt nhất năm 2025

Định vị và căn chỉnh
Ngoài kiểm tra chất lượng, hệ thống kiểm tra thị giác còn làm “mắt dẫn đường” cho robot/máy bằng cách phản hồi vị trí và hướng của chi tiết. Ví dụ: trong hệ pick-and-pack ngành điện tử, VIS căn chỉnh chip với độ chính xác dưới milimet trước khi gắp đặt lên PCB.

Đếm và phân loại
Hệ thống kiểm tra thị giác có thể đếm số lượng vật thể trong một lô (ví dụ số viên thuốc trong vỉ, số trứng trong khay) và tự động phân loại theo nhóm tốt/xấu, theo dải kích thước hoặc theo loại lỗi. Ứng dụng này rất phổ biến trong logistics, nông nghiệp và chế biến thực phẩm.
>>> Xem thêm:
- TOP 20 công cụ Chat AI tiếng Việt miễn phí thông minh phổ biến
- TOP 25 công cụ AI miễn phí, phổ biến, tốt nhất hiện nay

Kiểm tra an toàn và tuân thủ
Trong các ngành có quy định chặt, hệ thống kiểm tra thị giác đảm bảo sản phẩm đạt yêu cầu về dấu hiệu an toàn, tem niêm phong chống giả/mở, và ký hiệu quy chuẩn. Ví dụ: trong đóng gói dược phẩm, VIS kiểm tra nhãn an toàn trẻ em bị thiếu hoặc thông tin liều dùng in sai.

Các loại hệ thống kiểm tra thị giác
Hệ thống kiểm tra thị giác thường được phân loại theo cách chụp ảnh, phân tích ảnh, và cách triển khai. Tài liệu này trình bày theo ba nhóm chính:
1. Theo phương thức thu nhận ảnh
- Hệ thống thị giác 2D: dùng camera area-scan hoặc line-scan để thu ảnh phẳng; ứng dụng điển hình gồm kiểm tra PCB, xác minh nhãn và kiểm tra chai/lon.
- Hệ thống thị giác 3D: dùng stereo vision, laser triangulation, structured light hoặc camera ToF để thu độ sâu và biên dạng bề mặt; ứng dụng gồm kiểm tra đường hàn, đo hình dạng và dẫn hướng robot.
- Hệ thống đa phổ/siêu phổ (Multispectral/Hyperspectral): thu thông tin vượt dải nhìn thấy (hồng ngoại, UV, hyperspectral) để phát hiện thành phần hóa học, nhiễm bẩn hoặc các lỗi rất tinh vi.

2. Theo phương pháp xử lý
- Hệ thống kiểm tra thị giác dựa luật/cổ điển (Rule-Based/Classical VIS): dựa trên đặc trưng định nghĩa trước, ngưỡng (threshold), phát hiện biên và khớp mẫu; ưu điểm là nhanh và dễ giải thích nhưng kém linh hoạt.
- Hệ thống kiểm tra thị giác học sâu (Deep Learning Based VIS): dùng CNN và các kiến trúc học sâu hiện đại cho phân loại, phát hiện, phân đoạn; thường bền vững hơn trước thay đổi chiếu sáng, tư thế và nhiễu.
- Hệ thống kiểm tra thị giác phát hiện bất thường (Anomaly Detection VIS): huấn luyện chủ yếu trên mẫu “tốt”, sau đó đánh dấu các sai lệch; phù hợp khi mẫu lỗi hiếm hoặc khó định nghĩa (ví dụ các bộ dữ liệu MVTec AD, Kolektor).
3. Theo nhiệm vụ kiểm tra
Các hệ thống thuộc danh mục này được phân nhóm dựa trên nhiệm vụ cụ thể mà chúng thực hiện:
- Hệ thống phân loại: quyết định đạt/không đạt hoặc gán nhóm lỗi (ví dụ vải OK vs NG).

- Hệ thống phát hiện đối tượng : định vị và nhận diện lỗi/thiếu linh kiện (ví dụ PCB bị short, thiếu vít).

- Hệ thống phân đoạn: tạo bản đồ lỗi theo từng pixel để đo kích thước/hình dạng chính xác (ví dụ nứt, lỗi bề mặt thép).

- Hệ thống OCR: đọc và xác minh chữ in/khắc, số seri, mã vạch, QR và hạn sử dụng trên sản phẩm.

Cách chọn công cụ hệ thống kiểm tra thị giác phù hợp
Việc chọn đúng hệ thống kiểm tra thị giác bắt đầu bằng cách đối chiếu công cụ với bài toán. Trước tiên là yếu tố thu nhận ảnh: kiểm tra tiêu chuẩn như PCB có thể chỉ cần camera thường, trong khi các bài toán chuyên biệt về an toàn thực phẩm hoặc dược phẩm có thể cần ảnh đa phổ/siêu phổ.
Tiếp theo là phương pháp xử lý: thị giác cổ điển nhanh và dễ giải thích nhưng hạn chế linh hoạt; học sâu xử lý biến thiên và độ phức tạp tốt hơn; phát hiện bất thường hữu ích khi lỗi hiếm hoặc khó định nghĩa. Một nền tảng tốt cần hỗ trợ huấn luyện, triển khai và bảo trì mô hình thuận lợi trên các hướng tiếp cận này.
Bạn cũng cần đảm bảo hệ thống hỗ trợ đúng nhiệm vụ kiểm tra: phân loại cho đạt/không đạt, phát hiện đối tượng cho thiếu linh kiện, phân đoạn cho đo lường chính xác, hoặc OCR cho văn bản và mã định danh. Có nền tảng chỉ hỗ trợ một tác vụ, trong khi các nền tảng như Roboflow có thể hỗ trợ tổng hợp.
Cuối cùng là khả năng mở rộng và tích hợp: một hệ thống kiểm tra thị giác tốt phải kết nối được với hệ tự động hóa (API, thiết bị biên, hoặc giao thức công nghiệp như MQTT và OPC-UA) và mở rộng từ thử nghiệm sang sản xuất.
Roboflow được mô tả như một nền tảng “tất cả trong một”: hỗ trợ nhiều loại camera (webcam, IP, RTSP, camera công nghiệp) giúp không cần thay phần cứng; chuẩn hóa quản trị dữ liệu, gán nhãn, huấn luyện, đánh giá và triển khai. Mô hình có thể chạy trên thiết bị biên như Raspberry Pi/Jetson hoặc chạy trên cloud qua API; đồng thời hỗ trợ huấn luyện với dữ liệu nhỏ (vài trăm ảnh gán nhãn), cho phép ghép nhiều tác vụ kiểm tra trong cùng workflow và theo dõi hiệu năng mô hình theo thời gian.
Hướng dẫn xây dựng hệ thống kiểm tra thị giác với Roboflow (bài toán lỗi PCB)
Phần này minh họa cách xây dựng một hệ thống kiểm tra thị giác để phát hiện lỗi PCB, cụ thể là phát hiện “missing holes” (lỗ bị thiếu) trên PCB. Quy trình gồm: huấn luyện mô hình trong Roboflow, tạo workflow hoàn chỉnh và triển khai qua ứng dụng Flet để kiểm tra trực tiếp.
PCB là “xương sống” của điện tử hiện đại; chỉ một lỗi nhỏ cũng có thể gây trục trặc, giảm độ tin cậy hoặc làm hỏng toàn bộ sản phẩm. Vì vậy, hệ thống kiểm tra thị giác là thành phần trọng yếu trong sản xuất PCB, nhằm tự động phát hiện các lỗi như missing holes, mouse bites, open circuits, shorts, spurs và các vệt đồng thừa (spurious copper traces). So với kiểm tra thủ công, kiểm tra thị giác tự động nhanh hơn, chính xác hơn và ổn định hơn, đặc biệt trong môi trường sản xuất số lượng lớn.

Các lỗi này được thể hiện trong hình trên, đại diện cho những vấn đề chất lượng phổ biến nhưng nghiêm trọng cần được xác định trước khi lắp ráp và triển khai. Hệ thống kiểm tra thị giác tự động mang lại kết quả nhanh hơn, chính xác hơn và nhất quán hơn so với việc kiểm tra thủ công, khiến nó trở nên không thể thiếu trong các môi trường sản xuất khối lượng lớn. Trong ví dụ này, chúng tôi tập trung vào việc phát hiện các lỗ bị thiếu (missing holes), một trong những lỗi PCB cơ bản nhất sử dụng quy trình thị giác máy tính toàn diện của Roboflow.
Bước 1: Chuẩn bị dữ liệu cho phát hiện lỗi PCB
Bước đầu tiên để xây dựng hệ thống kiểm tra thị giác cho PCB là tạo một project Object Detection mới. Bạn tải lên ảnh PCB có chứa lỗi nhìn thấy được, tập trung vào loại lỗi muốn phát hiện (trong ví dụ này là missing holes). Sau đó dùng công cụ gán nhãn để vẽ bounding box quanh từng vị trí lỗ bị thiếu, gán nhãn “missing hole” cho mỗi box; nếu muốn mở rộng, có thể thêm các nhóm lỗi khác như short, open circuit hoặc spur.

Gán nhãn chính xác là điều kiện quyết định: bounding box cần ôm sát vùng lỗi, tránh lấy quá nhiều nền để mô hình học đúng đặc trưng của lỗi.

Sau khi gán nhãn, bạn tạo các phiên bản dataset trong Roboflow với tiền xử lý (auto-orient, resize) và tăng cường dữ liệu (xoay, chỉnh sáng, crop). Mục tiêu là làm mô hình bền vững hơn trước biến thiên thực tế như khác biệt ánh sáng hoặc góc camera.
Roboflow cũng hỗ trợ chia bộ dữ liệu thành train/validation/test để đánh giá công bằng; mỗi phiên bản vẫn giữ nhãn gốc, cho phép bạn thử nhiều chiến lược tiền xử lý và augmentation, huấn luyện nhiều mô hình và so sánh hiệu năng để xây dựng workflow phát hiện lỗi đáng tin cậy.

Bước 2: Huấn luyện mô hình phát hiện đối tượng
Khi dataset sẵn sàng, bước tiếp theo là huấn luyện mô hình phát hiện lỗi PCB. Quá trình huấn luyện bắt đầu bằng việc chọn kiến trúc; Roboflow hỗ trợ các lựa chọn như RF-DETR, Roboflow 3.0, YOLOv11… mỗi lựa chọn có ưu/nhược về độ chính xác, tốc độ và hiệu quả. Trong ví dụ, tác giả chọn Roboflow 3.0 làm mô hình nền.

Tiếp theo là chọn kích thước mô hình (Fast, Accurate, Medium, Large, Extra Large) để cân bằng tốc độ và độ chính xác theo yêu cầu triển khai. Với bài toán kiểm tra PCB, lựa chọn “Accurate” nhằm đảm bảo phát hiện đáng tin cậy các lỗi nhỏ như missing holes.
Roboflow cũng mô tả các chiến lược huấn luyện:
- Train from Previous Checkpoint: tiếp tục cải thiện một mô hình đã huấn luyện.
- Train from Public Checkpoint: khởi tạo từ trọng số pretrained (ví dụ MS COCO) rồi fine-tune cho lỗi PCB.
- Train from Random Initialization: huấn luyện từ đầu (không khuyến nghị trừ khi dữ liệu rất lớn).
Trong ví dụ này, tác giả chọn Train from Public Checkpoint (MS COCO) để tận dụng pretrained trên bộ dữ liệu chuẩn lớn và thích nghi cho tác vụ phát hiện lỗi PCB. Sau khi xác nhận thiết lập, nhấn Start Training để chạy huấn luyện; hoàn tất xong thì mô hình sẵn sàng để đánh giá và triển khai trong workflow kiểm tra PCB.

Bước 3: Xây dựng Roboflow Workflow cho kiểm tra lỗi PCB
Bước tiếp theo là tạo workflow kiểm tra lỗi PCB trong Roboflow để kết nối mô hình với các khối hiển thị và đầu ra. Workflow nhận ảnh PCB đầu vào, chạy qua mô hình phát hiện đối tượng đã huấn luyện và tạo:
- Kết quả trực quan (bounding box quanh missing holes),
- Kết quả dạng JSON cấu trúc (tọa độ và confidence).
Các khối trong workflow gồm:
- Inputs: định nghĩa dữ liệu đầu vào (ảnh PCB có thể có lỗi).
- Object Detection Model: gọi mô hình phát hiện lỗi (ví dụ pcb-missing-hole-detection/5). Với mỗi dự đoán, mô hình xuất:
- chiều rộng/cao ảnh,
- tọa độ tâm bounding box (x, y),
- chiều rộng/cao bounding box,
- điểm tin cậy (confidence).
- Bounding Box Visualization: phủ bounding box lên ảnh PCB để đối chiếu trực quan vị trí lỗi.
- Label Visualization: hiển thị nhãn lỗi (ví dụ “missing hole”) kèm confidence phía trên bounding box.
- Outputs: thu kết quả cuối dưới hai dạng:
- ảnh PCB đã chú thích (bounding box + nhãn),
- JSON cấu trúc chứa kích thước, tọa độ và confidence để phục vụ báo cáo, phân tích hoặc tích hợp vào hệ QA sản xuất.

Hệ thống kiểm tra thị giác này tiếp nhận ảnh PCB (Bảng mạch in), phát hiện và định vị các lỗ bị thiếu, cung cấp ảnh có chú thích để kiểm tra nhanh, và xuất dữ liệu dự đoán có cấu trúc để sử dụng trong các ứng dụng kiểm soát chất lượng tiếp theo.

Bước 4: Triển khai workflow vào ứng dụng kiểm tra trực tiếp (Flet)
Khi workflow hoàn tất, bước cuối cùng là triển khai thành ứng dụng chạy thực tế. Trong ví dụ, tác giả xây dựng ứng dụng desktop dựa trên Flet, kết nối Roboflow workflow, xử lý frame video/luồng camera theo thời gian thực và hiển thị:
- ảnh trực quan có bounding box + nhãn,
- cảnh báo trực tiếp khi phát hiện lỗi.
Các thư viện cần thiết:
- pip install inference-sdk flet
Dưới đây là mã nguồn tham khảo (giữ nguyên để bạn có thể chạy trực tiếp theo đúng logic của ví dụ):
# app.py
import base64
import threading
import cv2
import flet as ft
import numpy as np
import os
import time
from typing import Any, Dict, List, Tuple
from inference import InferencePipeline
TRANSPARENT_PX = (
“iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAA”
“AAC0lEQVR42mP8/x8AAwMB/axpQX0AAAAASUVORK5CYII=”
)
def np_to_b64_jpg(arr, assume_bgr=True, quality=85):
if not assume_bgr:
try:
arr = cv2.cvtColor(arr, cv2.COLOR_RGB2BGR)
except Exception:
pass
ok, buf = cv2.imencode(“.jpg”, arr, [cv2.IMWRITE_JPEG_QUALITY, quality])
if not ok:
return None
return base64.b64encode(buf).decode(“ascii”)
def main(page: ft.Page):
page.title = “Roboflow Workflow (Model Visualization + Snapshot + Alerts)”
page.window_width, page.window_height = 1120, 780
# Controls
api_key = ft.TextField(label=”API Key”, password=True,
can_reveal_password=True, width=360)
workspace = ft.TextField(label=”Workspace”, value=”tim-4ijf0″, width=200)
workflow_id = ft.TextField(label=”Workflow ID”, value=”pcb-defect-inspection”,
width=240)
source = ft.TextField(label=”Video Source (0 / path / rtsp://…)”, value=”0″,
width=360)
assume_bgr_chk = ft.Checkbox(label=”Assume visualization is BGR”, value=True)
start_btn = ft.ElevatedButton(“Start”)
stop_btn = ft.ElevatedButton(“Stop”, disabled=True)
snapshot_btn = ft.ElevatedButton(“Snapshot”, disabled=True)
status = ft.Text(“Idle”)
info = ft.Text(“”)
debug = ft.Text(“”, size=11, color=ft.Colors.GREY_700) # shows parsed preds/frame
save_info = ft.Text(“”)
img_vis = ft.Image(src_base64=TRANSPARENT_PX, width=820, height=615,
fit=ft.ImageFit.CONTAIN)
# Alerts panel (right) — snapshot style (no scrolling/append)
alerts_header = ft.Text(“Alerts (live)”, weight=ft.FontWeight.BOLD, size=16)
alerts_count = ft.Text(“No detections yet”)
clear_alerts_btn = ft.TextButton(“Clear”, icon=ft.Icons.CLEANING_SERVICES)
# A Column we re-render entirely when detections change
alert_list_col = ft.Column(spacing=6) # no auto-scroll, no append spam
alerts_panel = ft.Container(
width=280,
height=650,
padding=10,
bgcolor=ft.Colors.BLUE_GREY_50,
content=ft.Column(
[
ft.Row([alerts_header, clear_alerts_btn],
alignment=ft.MainAxisAlignment.SPACE_BETWEEN),
alerts_count,
ft.Divider(),
alert_list_col,
],
expand=True,
),
border_radius=12,
)
left_col = ft.Column(
[
ft.Row([api_key, workspace, workflow_id]),
ft.Row([source, assume_bgr_chk]),
ft.Row([start_btn, stop_btn, snapshot_btn, status]),
ft.Row([info]),
img_vis,
debug,
save_info,
],
spacing=8,
)
page.add(ft.Row([left_col, alerts_panel],
alignment=ft.MainAxisAlignment.START, spacing=12))
# Runtime state
state = {
“running”: False,
“pipeline”: None,
“thread”: None,
“started”: False,
“last_vis_bgr”: None,
“last_alert_signature”: None, # NEW: signature of last rendered detections
}
last_lock = threading.Lock()
# Rendering helpers
MAX_SHOW = 20 # cap cards shown to avoid overflow
def make_alert_card(clz: str, x: float, y: float, conf: float, det_id: str) -> ft.Container:
ts = time.strftime(“%H:%M:%S”)
return ft.Container(
bgcolor=ft.Colors.AMBER_50,
border=ft.border.all(1, ft.Colors.AMBER_200),
border_radius=8,
padding=8,
content=ft.Column(
[
ft.Row(
[
ft.Icon(ft.Icons.WARNING_AMBER, size=16, color=ft.Colors.AMBER_700),
ft.Text(clz, weight=ft.FontWeight.BOLD, size=13, color=ft.Colors.AMBER_900),
],
spacing=6,
),
ft.Text(f”({x:.1f}, {y:.1f}) • conf {conf:.2%}”, size=12, color=ft.Colors.GREY_800),
ft.Text(f”id: {det_id[:8]}… at {ts}”, size=11, color=ft.Colors.GREY_700),
],
spacing=2,
),
)
def render_alert_snapshot(preds: List[Dict[str, float | str]]):
“””Replaces the alert panel contents with the current detections (no scrolling).”””
alert_list_col.controls.clear()
if not preds:
# Calm empty state
alert_list_col.controls.append(
ft.Container(
bgcolor=ft.Colors.GREEN_50,
border=ft.border.all(1, ft.Colors.GREEN_200),
border_radius=8,
padding=10,
content=ft.Row(
[
ft.Icon(ft.Icons.CHECK_CIRCLE, color=ft.Colors.GREEN_600, size=18),
ft.Text(“No defects detected”, color=ft.Colors.GREEN_800, size=12),
],
spacing=8,
),
)
)
alerts_count.value = “No detections”
else:
shown = 0
for p in preds:
if shown >= MAX_SHOW:
break
alert_list_col.controls.append(
make_alert_card(p[“class”], p[“x”], p[“y”], p[“confidence”], p[“detection_id”])
)
shown += 1
plural = “s” if len(preds) != 1 else “”
alerts_count.value = f”{len(preds)} detection{plural}”
def predictions_from_detections_obj(det_obj: Any) -> List[Dict[str, Any]]:
“””
Extracts predictions from a Roboflow `Detections` object with fields:
– xyxy: Nx4 -> [x1, y1, x2, y2]
– confidence: (N,)
– data: dict with arrays (e.g. ‘class_name’, ‘detection_id’)
Returns list of dicts with keys: class, x, y, confidence, detection_id
“””
preds: List[Dict[str, Any]] = []
if det_obj is None:
return preds
xyxy = getattr(det_obj, “xyxy”, None)
conf = getattr(det_obj, “confidence”, None)
data = getattr(det_obj, “data”, {}) or {}
if xyxy is None or conf is None:
return preds
try:
xyxy = np.asarray(xyxy)
conf = np.asarray(conf)
except Exception:
return preds
n = min(len(xyxy), len(conf))
if n == 0:
return preds
class_names = (data.get(“class_name”) if isinstance(data, dict) else None)
det_ids = (data.get(“detection_id”) if isinstance(data, dict) else None)
try:
class_names = np.asarray(class_names) if class_names is not None else None
except Exception:
class_names = None
try:
det_ids = np.asarray(det_ids) if det_ids is not None else None
except Exception:
det_ids = None
for i in range(n):
try:
x1, y1, x2, y2 = [float(v) for v in xyxy[i]]
cx = (x1 + x2) / 2.0
cy = (y1 + y2) / 2.0
c = float(conf[i])
cls_name = None
if class_names is not None and i < len(class_names):
cls_name = str(class_names[i])
if not cls_name:
cls_name = “defect”
did = None
if det_ids is not None and i < len(det_ids):
did = str(det_ids[i])
if not did:
did = f”{cls_name}@{round(cx)},{round(cy)}”
preds.append({“class”: cls_name, “x”: cx, “y”: cy, “confidence”: c, “detection_id”: did})
except Exception:
continue
return preds
def signature_for_preds(preds: List[Dict[str, Any]]) -> Tuple:
# Round numeric fields to reduce jitter; sort to make order-independent.
sig_items = []
for p in preds:
sig_items.append((
p.get(“detection_id”),
p.get(“class”),
round(float(p.get(“x”, 0.0)), 1),
round(float(p.get(“y”, 0.0)), 1),
round(float(p.get(“confidence”, 0.0)), 3),
))
sig_items.sort()
return tuple(sig_items)
def on_prediction(result, video_frame):
try:
# Visualization
lv = None
if isinstance(result, dict):
lv = result.get(“label_visualization”)
elif hasattr(result, “label_visualization”):
lv = getattr(result, “label_visualization”)
if lv is not None and hasattr(lv, “numpy_image”):
vis_arr = lv.numpy_image
if not state[“started”]:
state[“started”] = True
status.value = “Running”
if assume_bgr_chk.value:
vis_bgr = vis_arr
else:
try:
vis_bgr = cv2.cvtColor(vis_arr, cv2.COLOR_RGB2BGR)
except Exception:
vis_bgr = vis_arr
with last_lock:
state[“last_vis_bgr”] = vis_bgr.copy()
b64 = np_to_b64_jpg(vis_arr, assume_bgr=assume_bgr_chk.value, quality=85)
if b64:
img_vis.src_base64 = b64
h, w = vis_arr.shape[:2]
info.value = f”{w}x{h}”
snapshot_btn.disabled = False
# Extract predictions (Detections object)
det_obj = None
if isinstance(result, dict):
det_obj = result.get(“model_predictions”)
elif hasattr(result, “model_predictions”):
det_obj = getattr(result, “model_predictions”)
preds = predictions_from_detections_obj(det_obj)
# Update alerts ONLY if the set changed (signature diff)
sig = signature_for_preds(preds)
changed = sig != state[“last_alert_signature”]
if changed:
render_alert_snapshot(preds)
state[“last_alert_signature”] = sig
debug.value = f”Debug: parsed {len(preds)} preds; changed={changed}”
page.update()
except Exception as e:
status.value = f”Frame error: {e}”
page.update()
def run_pipeline():
src = source.value.strip()
try:
video_reference = int(src)
except ValueError:
video_reference = src
try:
status.value = “Starting pipeline…”
state[“started”] = False
page.update()
pipe = InferencePipeline.init_with_workflow(
api_key=api_key.value.strip(),
workspace_name=workspace.value.strip(),
workflow_id=workflow_id.value.strip(),
video_reference=video_reference,
max_fps=30,
on_prediction=on_prediction,
)
state[“pipeline”] = pipe
pipe.start()
if hasattr(pipe, “join”):
pipe.join()
else:
while state[“running”]:
time.sleep(0.1)
except Exception as ex:
status.value = f”Start failed: {ex}”
page.update()
finally:
stop_btn.disabled = True
start_btn.disabled = False
snapshot_btn.disabled = True
status.value = “Stopped”
page.update()
state[“running”] = False
state[“pipeline”] = None
with last_lock:
state[“last_vis_bgr”] = None
def start_clicked(e):
if state[“running”]:
return
state[“running”] = True
start_btn.disabled = True
stop_btn.disabled = False
snapshot_btn.disabled = True
save_info.value = “”
status.value = “Starting…”
state[“started”] = False
state[“last_alert_signature”] = None
# Fresh empty panel
alert_list_col.controls.clear()
alerts_count.value = “Cleared. Waiting for detections…”
debug.value = “”
page.update()
t = threading.Thread(target=run_pipeline, daemon=True)
state[“thread”] = t
t.start()
def stop_pipeline():
try:
if state[“pipeline”] is not None:
if hasattr(state[“pipeline”], “stop”):
state[“pipeline”].stop()
elif hasattr(state[“pipeline”], “terminate”):
state[“pipeline”].terminate()
except Exception:
pass
finally:
state[“pipeline”] = None
def stop_clicked(e):
status.value = “Stopping…”
page.update()
state[“running”] = False
stop_pipeline()
stop_btn.disabled = True
start_btn.disabled = False
snapshot_btn.disabled = True
page.update()
def snapshot_clicked(e):
with last_lock:
frame = state[“last_vis_bgr”].copy() if state[“last_vis_bgr”] is not None else None
if frame is None:
save_info.value = “No frame available yet.”
page.update()
return
os.makedirs(“snapshots”, exist_ok=True)
fname = time.strftime(“snapshots/vis_%Y%m%d_%H%M%S.jpg”)
ok = cv2.imwrite(fname, frame)
save_info.value = f”Saved: {fname}” if ok else “Failed to save snapshot.”
page.update()
def clear_alerts(e=None):
alert_list_col.controls.clear()
alerts_count.value = “Cleared. Waiting for detections…”
state[“last_alert_signature”] = None
page.update()
clear_alerts_btn.on_click = clear_alerts
def on_disconnect(e):
state[“running”] = False
stop_pipeline()
start_btn.on_click = start_clicked
stop_btn.on_click = stop_clicked
snapshot_btn.on_click = snapshot_clicked
page.on_disconnect = on_disconnect
if __name__ == “__main__”:
ft.app(target=main)
Ứng dụng được thiết kế với hai panel chính:
- Bên trái: hiển thị video/camera trực tiếp của PCB, vẽ bounding box quanh lỗi phát hiện được.
- Bên phải: panel cảnh báo hiển thị thông báo dạng “snapshot” mỗi khi phát hiện lỗi (ví dụ missing hole). Danh sách cảnh báo được làm mới khi tập phát hiện thay đổi để người vận hành nắm nhanh trạng thái hiện tại.
Ứng dụng cung cấp các điều khiển để nhập API key, workspace, workflow ID, chọn nguồn video (webcam, file video, RTSP), cùng các nút start/stop/snapshot để kiểm soát phiên kiểm tra. Snapshot có thể lưu lại để báo cáo hoặc truy vết.
Ở tầng xử lý, ứng dụng chạy pipeline inference của Roboflow trên một luồng riêng, xử lý liên tục các frame từ nguồn video. Các lỗi phát hiện được được chuyển thành dự đoán có cấu trúc (class, tọa độ, confidence), hiển thị đồng thời trên ảnh trực quan và panel cảnh báo. Khi không có lỗi, hệ thống tự làm sạch cảnh báo; khi phát hiện mới xuất hiện, bộ đếm được cập nhật.
Lệnh chạy ví dụ:
- flet run app.py hoặc python app.py
Bạn sẽ thấy đầu ra tương tự như sau:

Sau đây là đoạn video minh họa:
- Chạy web app: flet run –web app.py
Việc này sẽ khởi động máy chủ web và chạy ứng dụng trong trình duyệt web.

Bước triển khai này biến quy trình công việc Roboflow đã được đào tạo thành một công cụ kiểm tra thực tiễn, thân thiện với người vận hành, có khả năng trực quan hóa theo thời gian thực, cảnh báo lỗi, và chụp ảnh nhanh, giúp việc kiểm tra PCB có thể sử dụng được trong môi trường sản xuất thực tế.
Kết luận về hệ thống kiểm tra thị giác
Hệ thống kiểm tra thị giác ngày càng trở thành hạ tầng thiết yếu trong sản xuất hiện đại, giúp sản phẩm đạt tiêu chuẩn chất lượng, an toàn và hiệu năng. Từ phát hiện lỗi, đo lường, xác minh nhãn, đếm và phân loại cho đến kiểm tra tuân thủ, thị giác máy tính đang mở rộng phạm vi ứng dụng trên nhiều ngành như điện tử, ô tô, dược phẩm và chế biến thực phẩm. Cùng với tiến bộ của học sâu, phần cứng thu nhận ảnh và nền tảng triển khai, các hệ thống này ngày càng chính xác, dễ mở rộng và linh hoạt hơn.
Trong tài liệu, chúng ta đã đi qua nền tảng của VIS: thành phần, phân loại và ứng dụng; đồng thời xây dựng một ví dụ đầu-cuối phát hiện missing holes trên PCB và triển khai thành công cụ thân thiện cho vận hành. Ví dụ này cho thấy việc thiết kế và triển khai giải pháp kiểm tra tùy biến đã trở nên dễ tiếp cận hơn, không nhất thiết phải “phát minh lại bánh xe” từ đầu
Nguồn tham khảo: https://blog.roboflow.com/vision-inspection-systems/
TOT là đơn vị tiên phong trong hành trình chuyển đổi số. Chúng tôi mang đến giải pháp thiết kế website, mobile app và viết phần mềm theo yêu cầu với dịch vụ linh hoạt, tối ưu theo đúng nhu cầu của doanh nghiệp.
Lấy cảm hứng từ triết lý “Công nghệ vì con người”, TOT giúp doanh nghiệp vận hành hiệu quả hơn, nâng tầm trải nghiệm khách hàng và tạo dấu ấn bền vững cho thương hiệu.
Thông tin liên hệ TopOnTech (TOT):
📞 Hotline/WhatsApp/Zalo: 0906 712 137
✉️ Email: long.bui@toponseek.com
🏢 Địa chỉ: 31 Hoàng Diệu, Phường 12, Quận 4, Thành phố Hồ Chí Minh, Việt Nam