文章目录
- [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接口。
- 支持 iOS 和 Android
- 支持 Windows10 UWP
- 支持 Lumin ( MagicLeap )
- 支持 WebGL
- 支持 Win 、 Mac 和 Linux 平台
- 支持在编辑器中预览
学习OpenCV for Unity,建议以下方式:
- 查看Unity中插件Samples代码。
- 搜索插件API文档:OpenCV for Unity: Class List
- 学习OpenCV文档:OpenCV: OpenCV modules
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结构有两个部分:
- 矩阵头(用于记录大小、存储方式、地址等)
- 包含像素(用于记录具体数据)。头文件大小是固定的,而具体数据则不是。
OpenCV使用引用计数系统,每个Mat对像都有自己的头,通过使它们的矩阵指针指向相同的地址。他们可同共享这这个地址,而复制操作则只是复制指针,而不是数据本身。
定期检查未释放的Mat对象,大型Mat会快速消耗内存。许多OpenCV类实现了IDisposable接口,应使用
using
语句或手动调用Dispose()来释放资源:
csharpusing (Mat image = Imgcodecs.imread("image.jpg")) { // 使用image... } // 自动释放
2.2 CvType
CvType的命名遵循特定的模式,格式为:CV_<位数><类型><通道数>
,例如CV_8UC1
、CV_32FC3
等。
- CV_:所有类型的前缀,表示这是OpenCV的类型定义
- 位数 :表示每个通道使用的位数
8U
:8位无符号整数(0-255)8S
:8位有符号整数(-128-127)16U
:16位无符号整数(0-65535)16S
:16位有符号整数(-32768-32767)32S
:32位有符号整数32F
:32位浮点数64F
:64位浮点数(双精度)
- 类型 :表示数据类型
U
:无符号整数(unsigned)S
:有符号整数(signed)F
:浮点数(float)
- 通道数 :表示矩阵的通道数
C1
:单通道(如灰度图像)C2
:双通道(较少使用)C3
:三通道(如RGB/BGR彩色图像)C4
:四通道(如RGBA带透明度的图像)
偶尔会发现不带通道数的类型,如:CV_32S
、CV_8U
等等。不这些带通道数的类型默认通道数为1。
例如,CV_8U
就等同于CV_8UC1
,CV_32S
就等同于CV_32SC1
。
注意:
- 颜色顺序:OpenCV默认使用BGR顺序而非RGB,在显示或处理时需要注意
- 性能考虑: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.UnityIntegration
下OpenCVMatUtils
类提供了图像转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)访问和修改像素
- 单通道图像(灰度):
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); // 写回像素
- 多通道图像(彩色):
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中
- 使用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;
}
}

- 使用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 合理的操作
-
避免频繁分配内存:
csharp// 不好:每次Update都新建Mat和Texture2D // 好:在Start/Awake中初始化,重复使用
-
使用Mat的ROI(Region of Interest):
csharp// 只处理图像的一部分 Rect roi = new Rect(100, 100, 200, 200); Mat subMat = new Mat(image, roi);
-
批量操作代替逐像素操作:
csharp// 不好:逐像素循环 // 好:使用OpenCV内置函数(如add, subtract, multiply等)
-
适当降低分辨率:
csharp// 对实时处理,可以缩小图像 Mat small = new Mat(); Imgproc.resize(image, small, new Size(image.cols()/2, image.rows()/2));