2025-07-24 Unity插件 OpenCVForUnity1——OpenCVForUnity 简介

文章目录

  • [1 插件介绍](#1 插件介绍)
  • [2 OpenCV基础](#2 OpenCV基础)
    • [2.1 Mat](#2.1 Mat)
    • [2.2 CvType](#2.2 CvType)
    • [2.3 Scalar](#2.3 Scalar)
  • [3 基础操作](#3 基础操作)
    • [3.1 图像读取Mat](#3.1 图像读取Mat)
    • [3.2 Mat基本操作](#3.2 Mat基本操作)
    • [3.3 Mat输出图像](#3.3 Mat输出图像)
    • [3.4 合理的操作](#3.4 合理的操作)

1 插件介绍

​ OpenCV for Unity是一款强大的Unity资产插件,它将流行的计算机视觉库OpenCV无缝集成到Unity引擎中,使开发者能够在游戏开发、AR/VR应用、工业自动化等多个领域实现复杂的图像处理功能。它基于OpenCV 4.4.0(最新版本3.0.0支持OpenCV 4.12.0),提供与OpenCV Java相同的API接口。

  • 支持 iOSAndroid
  • 支持 Windows10 UWP
  • 支持 Lumin ( MagicLeap )
  • 支持 WebGL
  • 支持 WinMacLinux 平台
  • 支持在编辑器中预览

资源链接:OpenCV for Unity | Unity Asset Store

​ 学习OpenCV for Unity,建议以下方式:

2 OpenCV基础

2.1 Mat

​ Mat是OpenCV中最基础的数据结构,代表一个N*M的矩阵,用于存储图像数据。创建Mat对象的基本语法

csharp 复制代码
// 创建一个3x3的单通道8位无符号整数矩阵
Mat mat1 = new Mat(3, 3, CvType.CV_8UC1);

// 创建一个全零矩阵
Mat zeros = Mat.zeros(3, 3, CvType.CV_8UC1);

// 创建一个全1矩阵
Mat ones = Mat.ones(3, 3, CvType.CV_8UC1);

// 创建单位矩阵(对角线为1)
Mat eyes = Mat.eye(3, 3, CvType.CV_8UC1);

​ Mat结构有两个部分:

  1. 矩阵头(用于记录大小、存储方式、地址等)
  2. 包含像素(用于记录具体数据)。头文件大小是固定的,而具体数据则不是。

​ OpenCV使用引用计数系统,每个Mat对像都有自己的头,通过使它们的矩阵指针指向相同的地址。他们可同共享这这个地址,而复制操作则只是复制指针,而不是数据本身。

​ 定期检查未释放的Mat对象,大型Mat会快速消耗内存。许多OpenCV类实现了IDisposable接口,应使用using语句或手动调用Dispose()来释放资源:

csharp 复制代码
using (Mat image = Imgcodecs.imread("image.jpg")) {
    // 使用image...
} // 自动释放

2.2 CvType

​ CvType的命名遵循特定的模式,格式为:CV_<位数><类型><通道数>,例如CV_8UC1CV_32FC3等。

  1. CV_:所有类型的前缀,表示这是OpenCV的类型定义
  2. 位数 :表示每个通道使用的位数
    • 8U:8位无符号整数(0-255)
    • 8S:8位有符号整数(-128-127)
    • 16U:16位无符号整数(0-65535)
    • 16S:16位有符号整数(-32768-32767)
    • 32S:32位有符号整数
    • 32F:32位浮点数
    • 64F:64位浮点数(双精度)
  3. 类型 :表示数据类型
    • U:无符号整数(unsigned)
    • S:有符号整数(signed)
    • F:浮点数(float)
  4. 通道数 :表示矩阵的通道数
    • C1:单通道(如灰度图像)
    • C2:双通道(较少使用)
    • C3:三通道(如RGB/BGR彩色图像)
    • C4:四通道(如RGBA带透明度的图像)

​ 偶尔会发现不带通道数的类型,如:CV_32SCV_8U等等。不这些带通道数的类型默认通道数为1。

​ 例如,CV_8U就等同于CV_8UC1CV_32S就等同于CV_32SC1

注意:

  1. 颜色顺序:OpenCV默认使用BGR顺序而非RGB,在显示或处理时需要注意
  2. 性能考虑:8位整数运算比浮点运算快,在可能的情况下优先使用8位类型

2.3 Scalar

​ Scalar是OpenCV中用于表示多通道数值的基础数据结构,本质上是一个4元素的double数组。在图像处理中常用于表示和传递像素值(BGR/RGB颜色值)或填充值。

csharp 复制代码
// 1. 单值构造(适用于灰度图像)
Scalar grayValue = new Scalar(128); // 等同于Scalar(128, 0, 0, 0)

// 2. 三值构造(BGR彩色图像)
Scalar blueColor = new Scalar(255, 0, 0); // 纯蓝色(BGR格式)

// 3. 四值构造(带Alpha通道)
Scalar transparentRed = new Scalar(0, 0, 255, 128); // 半透明红色(BGRA)

// 4. 使用静态方法
Scalar white = Scalar.all(255); // 所有通道设为255

3 基础操作

配置环境:

  • Unity 6000.0.42f
  • OpenCV for Unity 3.0.0

3.1 图像读取Mat

(1)从文件系统读取图像

​ 在OpenCV for Unity中,最常用的图像读取方式是使用Imgcodecs.imread()函数:

csharp 复制代码
// 获取图片路径(注意:Application.dataPath在移动平台需要特殊处理)
string imagePath = Application.dataPath + "/OpenCVForUnity/Examples/Resources/face.jpg";

// 读取图像
Mat image = Imgcodecs.imread(OpenCVEnv.GetFilePath(imagePath), Imgcodecs.IMREAD_COLOR);

// 检查是否成功加载
if (image.empty())
{
    Debug.LogError("无法加载图像: " + imagePath);
    return;
}

// 释放资源
image.Dispose();

参数说明

  • OpenCVEnv.GetFilePath():处理不同平台的路径问题
  • 加载标志(常用):
    • Imgcodecs.IMREAD_COLOR:始终将图像转换为3通道BGR彩色
    • Imgcodecs.IMREAD_GRAYSCALE:始终将图像转换为单通道灰度
    • Imgcodecs.IMREAD_UNCHANGED:按原样加载(包括alpha通道)

(2)从Texture2D读取图像

​ OpenCV默认使用BGR顺序,而Unity通常使用RGB,需要注意转换:

csharp 复制代码
Texture2D tex = Resources.Load<Texture2D>("face");
Mat image = new Mat(tex.height, tex.width, CvType.CV_8UC4);
OpenCVMatUtils.Texture2DToMat(tex, image);

// 如果需要从BGR(A)转换为RGB(A)
if(image.channels() == 3) {
    Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2RGB);
} else if(image.channels() == 4) {
    Imgproc.cvtColor(image, image, Imgproc.COLOR_BGRA2RGBA);
}

// 释放资源
image.Dispose();

(3)OpenCVMatUtils

​ 命名空间OpenCVForUnity.UnityIntegrationOpenCVMatUtils类提供了图像转Mat的其他方法。

3.2 Mat基本操作

(1)创建空Mat

csharp 复制代码
// 创建黑色空白图像(3通道BGR)
Mat blankMat = new Mat(480, 640, CvType.CV_8UC3, new Scalar(0, 0, 0));

// 创建白色空白图像
Mat whiteMat = Mat.ones(480, 640, CvType.CV_8UC3);
Core.multiply(whiteMat, new Scalar(255, 255, 255), whiteMat);

// 创建特定颜色的图像
Mat redMat = new Mat(480, 640, CvType.CV_8UC3, new Scalar(0, 0, 255)); // OpenCV使用BGR顺序

(2)访问和修改像素

  1. 单通道图像(灰度):
csharp 复制代码
var row = 0;
var col = 0;
byte[] pixel = new byte[1];
image.get(row, col, pixel); // 读取像素
pixel[0] = 255; // 修改像素值
image.put(row, col, pixel); // 写回像素
  1. 多通道图像(彩色):
csharp 复制代码
byte[] pixel = new byte[3]; // 3通道
image.get(row, col, pixel);
// pixel[0]: 蓝色通道
// pixel[1]: 绿色通道
// pixel[2]: 红色通道
pixel[0] = 0; // 将蓝色通道设为0
image.put(row, col, pixel);

3.3 Mat输出图像

(1)保存到文件系统

csharp 复制代码
// 保存为JPEG(质量参数0-100)
Imgcodecs.imwrite(Application.dataPath + "/output.jpg", image, 
    new MatOfInt(Imgcodecs.IMWRITE_JPEG_QUALITY, 95));

// 保存为PNG(压缩级别0-9)
Imgcodecs.imwrite(Application.dataPath + "/output.png", image, 
    new MatOfInt(Imgcodecs.IMWRITE_PNG_COMPRESSION, 5));

(2)显示在Unity UI中

  1. 使用RawImage
csharp 复制代码
using OpenCVForUnity.UnityIntegration;
using UnityEngine;
using UnityEngine.UI;

public class ReadImgTest : MonoBehaviour
{
    // 在Unity中创建一个RawImage组件并关联
    public RawImage rawImage;
    private Mat image;

    void Start()
    {
        image = new Mat(480, 640, CvType.CV_8UC3, new Scalar(0, 0, 0));

        // 将Mat转换为Texture2D
        Texture2D texture = new Texture2D(image.cols(), image.rows(), TextureFormat.RGBA32, false);
        OpenCVMatUtils.MatToTexture2D(image, texture);

        // 应用纹理
        rawImage.texture = texture;
    }
}
  1. 使用Renderer
csharp 复制代码
// 适用于3D对象的材质
Renderer renderer = GetComponent<Renderer>();
Texture2D texture = new Texture2D(image.cols(), image.rows(), TextureFormat.RGBA32, false);
OpenCVMatUtils.MatToTexture2D(image, texture);
renderer.material.mainTexture = texture;

3.4 合理的操作

  1. 避免频繁分配内存

    csharp 复制代码
    // 不好:每次Update都新建Mat和Texture2D
    // 好:在Start/Awake中初始化,重复使用
  2. 使用Mat的ROI(Region of Interest)

    csharp 复制代码
    // 只处理图像的一部分
    Rect roi = new Rect(100, 100, 200, 200);
    Mat subMat = new Mat(image, roi);
  3. 批量操作代替逐像素操作

    csharp 复制代码
    // 不好:逐像素循环
    // 好:使用OpenCV内置函数(如add, subtract, multiply等)
  4. 适当降低分辨率

    csharp 复制代码
    // 对实时处理,可以缩小图像
    Mat small = new Mat();
    Imgproc.resize(image, small, new Size(image.cols()/2, image.rows()/2));