Open Cv Matrix 详解

本文详细解析了OpenCV中的Matrix结构,包括其内部flags字段的组成,如深度信息、通道数、连续性标志和子矩阵标志。此外,还介绍了如何通过宏定义来获取和设置这些信息。同时,深入探讨了Mat对象的内存管理,包括create函数在内存分配中的作用,以及release函数如何实现内存的释放和引用计数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Matrix的类型

enum { MAGIC_VAL  = 0x42FF0000, AUTO_STEP = 0, CONTINUOUS_FLAG = CV_MAT_CONT_FLAG, SUBMATRIX_FLAG = CV_SUBMAT_FLAG };
    enum { MAGIC_MASK = 0xFFFF0000, TYPE_MASK = 0x00000FFF, DEPTH_MASK = 7 };
/*! includes several bit-fields:
         - the magic signature
         - continuity flag
         - depth
         - number of channels
     */
    int flags;

从定义可以看出flags是int类型,共占32位,包含信息the magic signature,continuity flag,depth,number of channels。

/****************************************************************************************\
*                                  Matrix type (Mat)                                     *
\****************************************************************************************/

#define CV_MAT_CN_MASK          ((CV_CN_MAX - 1) << CV_CN_SHIFT)
#define CV_MAT_CN(flags)        ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)
#define CV_MAT_TYPE_MASK        (CV_DEPTH_MAX*CV_CN_MAX - 1)
#define CV_MAT_TYPE(flags)      ((flags) & CV_MAT_TYPE_MASK)
#define CV_MAT_CONT_FLAG_SHIFT  14
#define CV_MAT_CONT_FLAG        (1 << CV_MAT_CONT_FLAG_SHIFT)
#define CV_IS_MAT_CONT(flags)   ((flags) & CV_MAT_CONT_FLAG)
#define CV_IS_CONT_MAT          CV_IS_MAT_CONT
#define CV_SUBMAT_FLAG_SHIFT    15
#define CV_SUBMAT_FLAG          (1 << CV_SUBMAT_FLAG_SHIFT)
#define CV_IS_SUBMAT(flags)     ((flags) & CV_MAT_SUBMAT_FLAG)

/** Size of each channel item,
这个数值对应图像深度
							16F   64F  32F   32S  16S    16U  8S    8U
   0x28442211 = 0010 1000 0100 0100 0010 0010 0001 0001 ~ array of sizeof(arr_type_elem) */
#define CV_ELEM_SIZE1(type) ((0x28442211 >> CV_MAT_DEPTH(type)*4) & 15)

#define CV_ELEM_SIZE(type) (CV_MAT_CN(type)*CV_ELEM_SIZE1(type))

depth信息

DEPTH_MASK = 7 ,最后3位是深度信息,Depth总共只有8个类型,定义如下,定义了单个通道单个像素的值的类型。

#define CV_CN_MAX     512
#define CV_CN_SHIFT   3
#define CV_DEPTH_MAX  (1 << CV_CN_SHIFT)

#define CV_8U   0
#define CV_8S   1
#define CV_16U  2
#define CV_16S  3
#define CV_32S  4
#define CV_32F  5
#define CV_64F  6
#define CV_16F  7

#define CV_MAT_DEPTH_MASK       (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags)     ((flags) & CV_MAT_DEPTH_MASK)

#define CV_MAKETYPE(depth,cn) (CV_MAT_DEPTH(depth) + (((cn)-1) << CV_CN_SHIFT))
#define CV_MAKE_TYPE CV_MAKETYPE

#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))

#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))

#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))

#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))

#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))

#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))

#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))

#define CV_16FC1 CV_MAKETYPE(CV_16F,1)
#define CV_16FC2 CV_MAKETYPE(CV_16F,2)
#define CV_16FC3 CV_MAKETYPE(CV_16F,3)
#define CV_16FC4 CV_MAKETYPE(CV_16F,4)
#define CV_16FC(n) CV_MAKETYPE(CV_16F,(n))

Chanels信息

TYPE_MASK = 0x00000FFF,类型总共12位,除了最后三位深度信息,其余都是chanels,总共9位,也就是最大支持512

continuity flag

#define CV_MAT_CONT_FLAG_SHIFT 14
#define CV_MAT_CONT_FLAG (1 << CV_MAT_CONT_FLAG_SHIFT)
#define CV_IS_MAT_CONT(flags) ((flags) & CV_MAT_CONT_FLAG)
第15位代表Mat的内存是否连续,一般由creat创建的mat均是连续的,如果是连续,将加快对数据的访问。

Submatrix

#define CV_SUBMAT_FLAG_SHIFT 15
#define CV_SUBMAT_FLAG (1 << CV_SUBMAT_FLAG_SHIFT)
#define CV_IS_SUBMAT(flags) ((flags) & CV_MAT_SUBMAT_FLAG)
第16位代表该Mat是否为某一个Mat的submatrix,一般通过ROI以及row()、col()、rowRange()、colRange()等得到的mat均为submatrix。

magic

MAGIC_MASK = 0xFFFF0000,16-31代表magic signature,暂理解为用来区分Mat的类型

inline
bool Mat::isContinuous() const
{
    return (flags & CONTINUOUS_FLAG) != 0;
}
inline
bool Mat::isSubmatrix() const
{
    return (flags & SUBMATRIX_FLAG) != 0;
}

总结

从低位到高位:

0-2位代表depth即数据类型(如CV_8U),OpenCV的数据类型共7类,故只需3位即可全部表示。

3-11位代表通道数channels,因为OpenCV默认最大通道数为512,故只需要9位即可全部表示,可参照下面求通道数的部分。

0-11位共同代表type即通道数和数据类型(如CV_8UC3)

12-13位暂没发现用处,也许是留着后用,待发现了再补上。

14位代表Mat的内存是否连续,一般由creat创建的mat均是连续的,如果是连续,将加快对数据的访问。

15位代表该Mat是否为某一个Mat的submatrix,一般通过ROI以及row()、col()、rowRange()、colRange()等得到的mat均为submatrix。

16-31代表magic signature,暂理解为用来区分Mat的类型,如果Mat和SparseMat

mat创建

mat的初始化方法有很多,最后都是通过create创建的内存块

void Mat::create(int d, const int* _sizes, int _type)
{
   int i;
   CV_Assert(0 <= d && d <= CV_MAX_DIM && _sizes);
   _type = CV_MAT_TYPE(_type);

   if( data && (d == dims || (d == 1 && dims <= 2)) && _type == type() )
   {
       if( d == 2 && rows == _sizes[0] && cols == _sizes[1] )//需要的二维矩阵和当前一样
           return;
       for( i = 0; i < d; i++ )
           if( size[i] != _sizes[i] )
               break;
       if( i == d && (d > 1 || size[1] == 1))//维度相同,每个维度大小相同
           return;
   }

   int _sizes_backup[CV_MAX_DIM]; // #5991
   if (_sizes == (this->size.p))
   {
       for(i = 0; i < d; i++ )
           _sizes_backup[i] = _sizes[i];
       _sizes = _sizes_backup;
   }

   release();//释放之前的内存
   if( d == 0 )
       return;
   flags = (_type & CV_MAT_TYPE_MASK) | MAGIC_VAL;
   setSize(*this, d, _sizes, 0, true);//设置数据大小

   if( total() > 0 )
   {
       MatAllocator *a = allocator, *a0 = getDefaultAllocator();//如果不设置分配器,就使用默认的分配器
#ifdef HAVE_TGPU
       if( !a || a == tegra::getAllocator() )
           a = tegra::getAllocator(d, _sizes, _type);
#endif
       if(!a)
           a = a0;
       try
       {
           u = a->allocate(dims, size, _type, 0, step.p, ACCESS_RW /* ignored */, USAGE_DEFAULT);//分配内存
           CV_Assert(u != 0);
       }
       catch (...)
       {
           if (a == a0)
               throw;
           u = a0->allocate(dims, size, _type, 0, step.p, ACCESS_RW /* ignored */, USAGE_DEFAULT);//如果设置的分配器分配失败会使用默认的分配器尝试一次分配。如果再次异常,不会处理异常
           CV_Assert(u != 0);
       }
       CV_Assert( step[dims-1] == (size_t)CV_ELEM_SIZE(flags) );//判断分配的内存大小是不是对的。
   }

   addref();//给分配的内存添加一个引用计数
   finalizeHdr(*this);
}

mat初始化的时候都会在初始化列表将所有成员初始化为0,默认构造函数因为没有参数,所有没有使用create生成mat,其余的一半都会传入一个可以确定mat的维度和每个维度大小也就是矩阵大小的参数,
从CV_MAX_DIM 可以看出来这个矩阵最多有32维。还有一个每个元素大小的type值,也就是通道和颜色深度。

创建新的内存之前会先尝试看是不是需要重新分配,减少操作,一般新建的对象都是需要分配的,这里主要讲分配,不具体分析什么情况不会重新分配。
分配之前会使用release, CV_XADD(&u->refcount, -1),会通过_InterlockedExchangeAdd函数进行支持多线程的数据原子操作,修改指针引用计数,一旦引用为0就会自动释放内存,然后把指针置为空,正常来说,多次调用release是没有问题的,但是有时候,new的mat对象delete之后会有异常,不知道是不是release还有其他问题,所以尽量不要是要new delete操作处理mat对象,我这里是使用shared ptr让系统维护内存,然后就没有问题了。

inline
void Mat::release()
{
    if( u && CV_XADD(&u->refcount, -1) == 1 )
        deallocate();
    u = NULL;
    datastart = dataend = datalimit = data = 0;
    for(int i = 0; i < dims; i++)
        size.p[i] = 0;
#ifdef _DEBUG
    flags = MAGIC_VAL;
    dims = rows = cols = 0;
    if(step.p != step.buf)
    {
        fastFree(step.p);
        step.p = step.buf;
        size.p = &rows;
    }
#endif
}

释放掉之前的内存之后,会设置一下和图片大小相关的一些参数,因为后面需要使用这些参数分配内存。系统有一个默认的内存分配器,mat的构造函数允许你指定分配器,如果你不指定就使用默认的,同样的会对你指定的分配器分配失败之后,抓取异常,重新使用默认的分配器分配一次。

默认的构造是StdMatAllocator ,并且不能再被派生。
class StdMatAllocator CV_FINAL : public MatAllocator,下面是分配的函数

UMatData* allocate(int dims, const int* sizes, int type,
                       void* data0, size_t* step, AccessFlag /*flags*/, UMatUsageFlags /*usageFlags*/) const CV_OVERRIDE
    {
        size_t total = CV_ELEM_SIZE(type);
        for( int i = dims-1; i >= 0; i-- )
        {
            if( step )
            {
                if( data0 && step[i] != CV_AUTOSTEP )
                {
                    CV_Assert(total <= step[i]);
                    total = step[i];
                }
                else
                    step[i] = total;
            }
            total *= sizes[i];
        }
        uchar* data = data0 ? (uchar*)data0 : (uchar*)fastMalloc(total);
        UMatData* u = new UMatData(this);
        u->data = u->origdata = data;
        u->size = total;
        if(data0)
            u->flags |= UMatData::USER_ALLOCATED;

        return u;
    }

可以看到这个分配函数实际上只是算了需要分配的内存的大小,然后将这个内存放到UMatData的date段。这里fastMalloc是直接使用的malloc,然后就是针对不同平台做了些微调。
以上就是mat矩阵的一些分析,后面有时间再补。

<think>好的,用户现在问的是“rotate中的matrix表示什么”。我需要先确定用户的问题背景。通常,“rotate”可能指的是图像处理或图形编程中的旋转操作,比如在使用Python的Pillow库或者OpenCV时遇到的旋转矩阵。用户可能是在处理图像旋转时遇到了matrix参数,想知道它的具体含义和作用。 首先,我得回忆一下图像旋转中使用的矩阵。在二维旋转中,旋转矩阵是一个2x2的矩阵,用来表示绕原点的旋转。对于三维旋转,矩阵会更复杂,但用户的问题可能更偏向二维,尤其是图像处理。Pillow库中的Image.transform方法可能会用到这个矩阵,或者OpenCV中的仿射变换函数。 接下来,我需要解释旋转矩阵的数学原理。二维旋转矩阵的通用形式是: $$ \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} $$ 这里θ是旋转角度。这个矩阵乘以坐标点的向量,就会得到旋转后的新坐标。不过,在图像处理中,旋转通常是绕图像中心而不是原点,因此需要考虑平移变换,这时候矩阵可能会扩展为3x3的仿射变换矩阵,包括平移部分。 用户可能在使用某个库的时候遇到了需要输入matrix参数的情况,比如Pillow的Image.transform方法,这时候需要明确矩阵的结构。例如,在Pillow中,仿射变换的矩阵是一个6元组(a, b, c, d, e, f),对应的3x3矩阵是: $$ \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} $$ 这里的旋转矩阵需要结合平移来调整中心点,否则旋转会以左上角为原点,导致图像位置偏移。 此外,用户可能对如何构造这样的矩阵感到困惑,尤其是当涉及到绕中心旋转时。这时候需要计算将原点平移到图像中心,应用旋转矩阵,然后再平移回来的组合矩阵。例如,绕中心旋转θ角度的矩阵需要三个步骤的合成:平移、旋转、逆平移。 还需要注意,不同的库可能对矩阵参数的格式有不同的要求。比如,OpenCV中的cv2.getRotationMatrix2D函数直接生成一个2x3的仿射矩阵,而Pillow可能需要6个参数的元组。用户可能在转换不同库的代码时遇到问题,需要明确说明这些差异。 另外,用户可能想知道矩阵中的每个参数具体代表什么,比如a、b、c、d、e、f在Pillow中的含义。这时候需要详细解释每个参数对应的矩阵位置和它们在变换中的作用,例如a和e对应缩放和旋转的cosθ,b和d对应旋转的sinθ部分,而c和f对应平移。 最后,用户可能需要实际的代码示例来理解如何生成和使用这样的矩阵。例如,在Pillow中如何构造一个绕中心旋转的矩阵,并应用到图像上,同时处理可能的图像裁剪或填充问题。这部分的示例代码需要准确,并且注释清楚每个步骤的作用,帮助用户理解矩阵的应用过程。 总结来说,用户的问题核心在于理解旋转操作中的矩阵参数是什么,如何构造它,以及它在图像旋转中的具体作用。需要从数学原理、库的具体实现、实际应用示例等方面进行详细解释,确保用户能够全面理解并正确应用旋转矩阵。</think>### **核心结论** - **`rotate中的matrix`** 表示**旋转操作的变换矩阵**,用于计算旋转后像素的新坐标。 - **核心作用**:通过矩阵乘法将原始坐标映射到旋转后的位置,支持**平移、旋转、缩放**等组合变换。 - **典型应用**:图像处理库(如 Pillow、OpenCV)中的仿射变换函数。 --- ### **详解旋转矩阵** #### 1. **数学原理** - **二维旋转矩阵**(绕原点旋转 $\theta$ 角度): $$ \text{旋转矩阵 } R = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} $$ - **输入**:原始坐标 $(x, y)$。 - **输出**:旋转后坐标 $(x', y') = (x\cos\theta - y\sin\theta, x\sin\theta + y\cos\theta)$。 - **仿射变换矩阵**(支持平移): $$ \text{3×3 矩阵 } M = \begin{bmatrix} a & b & c \\ d & e & f \\ 0 & 0 & 1 \end{bmatrix} $$ - **作用**:将 $(x, y)$ 映射为 $(ax + by + c, dx + ey + f)$。 - **Pillow 格式**:矩阵参数以 6 元组表示 `(a, b, c, d, e, f)`。 --- #### 2. **绕图像中心旋转的矩阵构造** - **关键步骤**: 1. **平移图像中心到原点**:将旋转中心从默认左上角移动到图像中心。 2. **应用旋转矩阵**:绕原点旋转指定角度。 3. **平移回原始坐标系**:恢复图像位置。 - **矩阵合成公式**: $$ M = T^{-1} \cdot R \cdot T $$ - **$T$**:平移矩阵(将中心点 $(w/2, h/2)$ 移到原点)。 - **$R$**:旋转矩阵- **$T^{-1}$**:反向平移矩阵- **具体参数计算**(以 Pillow 为例): ```python import math def get_rotation_matrix(angle, width, height): theta = math.radians(angle) cos_theta = math.cos(theta) sin_theta = math.sin(theta) # 平移矩阵 T(移动到原点) tx = -width / 2 ty = -height / 2 # 旋转矩阵 R a = cos_theta b = -sin_theta c = tx * cos_theta - ty * sin_theta d = sin_theta e = cos_theta f = tx * sin_theta + ty * cos_theta # 反向平移 T^{-1} c += width / 2 f += height / 2 return (a, b, c, d, e, f) ``` --- #### 3. **代码示例(Pillow 实现)** ```python from PIL import Image # 读取图像 im = Image.open("input.jpg") # 计算绕中心旋转 30 度的矩阵 matrix = get_rotation_matrix(30, im.width, im.height) # 应用仿射变换 rotated = im.transform( im.size, # 输出尺寸(与原图一致) Image.AFFINE, # 变换类型 matrix, # 旋转矩阵 Image.BICUBIC # 插值方法 ) rotated.save("output_rotated.jpg") ``` --- ### **参数解析** | 矩阵参数 | 对应 3×3 矩阵位置 | 作用 | |----------|-------------------|-------------------------------| | `a` | $M[0][0]$ | x 方向缩放/旋转的余弦分量 | | `b` | $M[0][1]$ | x 方向旋转的正弦分量(负值) | | `c` | $M[0][2]$ | x 方向平移量 | | `d` | $M[1][0]$ | y 方向旋转的正弦分量 | | `e` | $M[1][1]$ | y 方向缩放/旋转的余弦分量 | | `f` | $M[1][2]$ | y 方向平移量 | --- ### **常见问题** #### **Q1:为什么旋转后图像被裁剪?** - **原因**:默认输出尺寸与原图一致,旋转后超出边界的部分被丢弃。 - **解决方案**:扩大输出画布,计算新尺寸: $$ \text{新宽度} = w|\cos\theta| + h|\sin\theta| \\ \text{新高度} = w|\sin\theta| + h|\cos\theta| $$ #### **Q2:如何避免旋转锯齿?** - **插值方法**:使用高质量插值(如 `Image.BICUBIC` 或 `Image.LANCZOS`)。 - **抗锯齿设置**: ```python rotated = im.transform(..., resample=Image.BICUBIC) ``` --- ### **对比 OpenCV 实现** - **OpenCV 旋转矩阵生成**: ```python import cv2 # 获取旋转矩阵(中心点、角度、缩放因子) matrix = cv2.getRotationMatrix2D( center=(width/2, height/2), angle=30, scale=1.0 ) # 输出矩阵为 2×3 格式: # [[a, b, c], # [d, e, f]] ``` --- ### **总结** - **矩阵本质**:旋转矩阵是仿射变换的核心工具,通过数学运算精确控制像素位置。 - **实践要点**: - 绕中心旋转需合成平移和旋转操作。 - 注意输出尺寸和插值方法对效果的影响。 - 不同库的矩阵格式可能不同,需仔细查阅文档。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值