Học AI & Lập trình

WinForms hiển thị camera và kết quả OK/NG với OpenCvSharp

Xây giao diện vận hành: vòng lặp camera trên luồng nền, cập nhật giao diện an toàn, giải phóng Bitmap tránh rò rỉ bộ nhớ.

WinForms hiển thị camera và kết quả OK/NG với OpenCvSharp
Nội dung bài viết
  1. Tạo dự án
  2. Giao diện
  3. Vòng lặp camera trên luồng nền
  4. Những điểm quan trọng
  5. Nâng cấp cho môi trường nhà máy

Một trạm kiểm tra cần giao diện cho người vận hành: thấy hình camera trực tiếp, kết quả OK/NG to rõ, bộ đếm và nút điều khiển. Bài này xây ứng dụng WinForms đơn giản hiển thị camera bằng OpenCvSharp mà không làm treo giao diện.

Tạo dự án

dotnet new winforms -n TramVision
cd TramVision
dotnet add package OpenCvSharp4
dotnet add package OpenCvSharp4.runtime.win
dotnet add package OpenCvSharp4.Extensions

Gói Extensions cung cấp hàm chuyển Mat sang Bitmap để hiển thị trong PictureBox.

Giao diện

  • PictureBox picCamera với SizeMode = Zoom.
  • Label lblKetQua chữ lớn, nền xanh/đỏ.
  • Label lblDem hiển thị số OK/NG.
  • Nút btnStart, btnStop.

Vòng lặp camera trên luồng nền

using OpenCvSharp;
using OpenCvSharp.Extensions;

public partial class Form1 : Form
{
    private CancellationTokenSource? _cts;
    private int _ok, _ng;

    private async void btnStart_Click(object sender, EventArgs e)
    {
        _cts = new CancellationTokenSource();
        btnStart.Enabled = false;
        await Task.Run(() => CameraLoop(_cts.Token));
        btnStart.Enabled = true;
    }

    private void btnStop_Click(object sender, EventArgs e) => _cts?.Cancel();

    private void CameraLoop(CancellationToken token)
    {
        using var cap = new VideoCapture(0);
        using var frame = new Mat();
        using var gray = new Mat();
        using var bin = new Mat();

        while (!token.IsCancellationRequested && cap.Read(frame) && !frame.Empty())
        {
            Cv2.CvtColor(frame, gray, ColorConversionCodes.BGR2GRAY);
            Cv2.Threshold(gray, bin, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
            double tiLeSang = Cv2.CountNonZero(bin) * 100.0 / bin.Total();
            bool ok = tiLeSang > 20;                    // luật kiểm tra ví dụ

            var bmp = frame.ToBitmap();                  // tạo Bitmap mới cho mỗi khung
            BeginInvoke(() => CapNhatGiaoDien(bmp, ok, tiLeSang));
        }
    }

    private void CapNhatGiaoDien(Bitmap bmp, bool ok, double tiLe)
    {
        var cu = picCamera.Image;
        picCamera.Image = bmp;
        cu?.Dispose();                                   // giải phóng ảnh cũ, tránh rò rỉ GDI

        if (ok) _ok++; else _ng++;
        lblKetQua.Text = ok ? "OK" : "NG";
        lblKetQua.BackColor = ok ? Color.SeaGreen : Color.Firebrick;
        lblDem.Text = $"OK: {_ok}   NG: {_ng}   ({tiLe:F1}%)";
    }
}

Những điểm quan trọng

  • Không truy cập control từ luồng nền. Mọi cập nhật giao diện phải qua BeginInvoke (hoặc Invoke).
  • Giải phóng Bitmap cũ mỗi khung hình, nếu không bộ nhớ GDI tăng dần đến khi ứng dụng sập.
  • Dừng sạch: dùng CancellationToken, giải phóng camera khi đóng form (xử lý sự kiện FormClosing).
  • Giao diện chỉ cần cập nhật khoảng 15–30 lần/giây; nếu camera nhanh hơn, có thể bỏ bớt khung hiển thị nhưng vẫn kiểm tra đầy đủ.

Nâng cấp cho môi trường nhà máy

  • Thay VideoCapture bằng SDK camera công nghiệp, chụp theo trigger phần cứng.
  • Thêm màn hình cài đặt tham số có mật khẩu, lưu cấu hình ra file JSON.
  • Lưu ảnh NG kèm thời gian vào thư mục theo ngày.
  • Gửi kết quả cho PLC, xem Modbus TCP từ C#.
Ban biên tập HọcFree

Chúng tôi viết những bài hướng dẫn dễ hiểu, thực tế để ai cũng có thể tự học lập trình và ngoại ngữ, hoàn toàn miễn phí.