1. 程式人生 > >OpenCV中對Mat裡面depth,dims,channels,step,data,elemSize和資料地址計算的理解 (轉)

OpenCV中對Mat裡面depth,dims,channels,step,data,elemSize和資料地址計算的理解 (轉)

cv::Mat
depth/dims/channels/step/data/elemSize
The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store (Mat類的物件用於表示一個多維度的單通道或者多通道稠密陣列,它可以用來儲存以下東西)
real or complex-valued vectors or matrices (實數值或複合值向量、矩陣)
grayscale or color images (灰度圖或者彩色圖)
voxel volumes (立體元素)

vector fields (向量場)
point clouds (點雲)
tensors (張量)
histograms (though, very high-dimensional histograms may be better stored in a SparseMat ) (直方圖,高緯度的最好存放在SparseMat中)
舊版本的OpenCV中的C結構體有 CvMat 和 CvMatND,目前我用的是 2.3 版,裡面的文件指出 CvMat 和 CvMatND 棄用了,在C++封裝中用 Mat 代替,另外舊版還有一個 IplImage,同樣用 Mat 代替(可以參考博文 OpenCV中的結構體、類與Emgu.CV的對應表).

矩陣 (M) 中資料元素的地址計算公式:
addr(Mi0,i1,…im-1) = M.data + M.step[0] * i0 + M.step[1] * i1 + … + M.step[m-1] * im-1 (其中 m = M.dims M的維度)

data:Mat物件中的一個指標,指向記憶體中存放矩陣資料的一塊記憶體 (uchar* data)
dims:Mat所代表的矩陣的維度,如 3 * 4 的矩陣為 2 維, 3 * 4 * 5 的為3維
channels:通道,矩陣中的每一個矩陣元素擁有的值的個數,比如說 3 * 4 矩陣中一共 12 個元素,如果每個元素有三個值,那麼就說這個矩陣是 3 通道的,即 channels = 3。常見的是一張彩色圖片有紅、綠、藍三個通道。

depth:深度,即每一個畫素的位數(bits),在opencv的Mat.depth()中得到的是一個 0 – 6 的數字,分別代表不同的位數:enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 }; 可見 0和1都代表8位, 2和3都代表16位,4和5代表32位,6代表64位;
step:是一個數組,定義了矩陣的佈局,具體見下面圖片分析,另外注意 step1 (step / elemSize1),M.step[m-1] 總是等於 elemSize,M.step1(m-1)總是等於 channels;
elemSize : 矩陣中每一個元素的資料大小,如果Mat中的資料的資料型別是 CV_8U 那麼 elemSize = 1,CV_8UC3 那麼 elemSize = 3,CV_16UC2 那麼 elemSize = 4;記住另外有個 elemSize1 表示的是矩陣中資料型別的大小,即 elemSize / channels 的大小
圖片分析1:考慮二維情況(stored row by row)按行儲存





上面是一個 3 X 4 的矩陣,假設其資料型別為 CV_8U,也就是單通道的 uchar 型別

這是一個二維矩陣,那麼維度為 2 (M.dims == 2);
M.rows == 3; M.cols == 4;
sizeof(uchar) = 1,那麼每一個數據元素大小為 1 (M.elemSize() == 1, M.elemSize1() == 1);
CV_8U 得到 M.depth() == 0, M.channels() == 1;
因為是二維矩陣,那麼 step 陣列只有兩個值, step[0] 和 step[1] 分別代表一行的資料大小和一個元素的資料大小,則 M.step[0] == 4, M.step[1] == 1;
M.step1(0) == M.cols = 4; M.step1(1) == 1;
假設上面的矩陣資料型別是 CV_8UC3,也就是三通道

M.dims == 2; M.channels() == 3;M.depth() == 0;
M.elemSize() == 3 (每一個元素包含3個uchar值) M.elemSize1() == 1 (elemSize / channels)
M.step[0] == M.cols * M.elemSize() == 12, M.step[1] == M.channels() * M.elemSize1() == M.elemSize() == 3;
M.step(0) == M.cols * M.channels() == 12 ; M.step(1) == M.channels() == 3;
圖片分析2:考慮三維情況(stored plane by plane)按面儲存



上面是一個 3 X 4 X 6 的矩陣,假設其資料型別為 CV_16SC4,也就是 short 型別

M.dims == 3 ; M.channels() == 4 ; M.elemSize1() == sizeof(short) == 2 ;
M.rows == M.cols == –1;
M.elemSize() == M.elemSize1() * M.channels() == M.step[M.dims-1] == M.step[2] == 2 * 4 == 8;
M.step[0] == 4 * 6 * M.elemSize() == 192;
M.step[1] == 6 * M.elemSize() == 48;
M.step[2] == M.elemSize() == 8;
M.step1(0) == M.step[0] / M.elemSize() == 48 / 2 == 96 (第一維度(即面的元素個數) * 通道數);
M.step1(1) == M.step[1] / M.elemSize() == 12 / 2 == 24(第二維度(即行的元素個數/列寬) * 通道數);
M.step1(2) == M.step[2] / M.elemSize() == M.channels() == 4(第三維度(即元素) * 通道數);
End :

Author : Ggicci

本文講解Mat 的一些基本的初始化

// m為3*5的矩陣,float型的單通道,把每個點都初始化為1
Mat m(3, 5, CV_32FC1, 1);
或者 Mat m(3, 5, CV_32FC1, Scalar(1));
cout<<m;
輸出為:
[1, 1, 1, 1, 1;
  1, 1, 1, 1, 1;
  1, 1, 1, 1, 1]

// m為3*5的矩陣,float型的2通道,把每個點都初始化為1 2
 Mat m(3, 5, CV_32FC2, Scalar(1, 2));
cout<<m;
輸出為
[1, 2, 1, 2, 1, 2, 1, 2, 1, 2;
  1, 2, 1, 2, 1, 2, 1, 2, 1, 2;
  1, 2, 1, 2, 1, 2, 1, 2, 1, 2]

// m為3*5的矩陣,float型的3通道,把每個點都初始化為1 2 3
Mat m(3, 5, CV_32FC3, Scalar(1, 2, 3));
cout << m;
輸出為
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3;
  1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3;
  1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

// 從已有的資料來源初始化
double *data = new double[15];
for (int i = 0; i < 15; i++)
{
   data[i] = 1.2;
}
Mat m(3, 5, CV_32FC1, data);
cout << m;
輸出為:
[1.2, 1.2, 1.2, 1.2, 1.2;
  1.2, 1.2, 1.2, 1.2, 1.2;
  1.2, 1.2, 1.2, 1.2, 1.2]

如果接著
delete [] data;
cout << m;
輸出為:
[-1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144;
  -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144;
  -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144, -1.456815990147463e+144]
可見,這裡只是進行了淺拷貝,當資料來源不在的時候,Mat裡的資料也就是亂碼了。

// 從影象初始化
 Mat m = imread("1.jpg", CV_LOAD_IMAGE_GRAYSCALE);
 cout<< "channels ="<<m.channels()<<endl;
 cout << "cols ="<<m.cols<<endl;
 cout << "rows ="<<m.rows<<endl;
 cout << m;
輸出為:
channels =1
cols =13
rows =12
[179, 173, 175, 189, 173, 163, 148, 190, 68, 14, 19, 31, 22;
  172, 172, 172, 180, 172, 177, 162, 190, 64, 13, 19, 30, 17;
  177, 180, 176, 175, 169, 184, 165, 181, 58, 12, 23, 38, 25;
  181, 183, 178, 178, 170, 181, 163, 182, 52, 8, 23, 37, 23;
  176, 173, 173, 184, 175, 178, 164, 195, 60, 14, 24, 35, 16;
  179, 175, 176, 187, 176, 175, 158, 191, 70, 21, 28, 37, 20;
  182, 183, 180, 184, 174, 179, 155, 174, 54, 1, 5, 15, 2;
  173, 182, 178, 176, 173, 191, 165, 169, 157, 101, 100, 107, 93;
  181, 182, 180, 177, 177, 177, 171, 162, 183, 185, 186, 185, 182;
  178, 180, 179, 177, 178, 179, 174, 167, 172, 174, 175, 174, 172;
  175, 178, 179, 178, 180, 182, 179, 173, 172, 174, 175, 175, 174;
  175, 179, 181, 180, 181, 183, 181, 177, 178, 180, 182, 183, 182]


內容來自《OpenCV 2 Computer Vision Application Programming Cookbook》

OpenCV2 訪問影象的各個畫素有各種方法

我們來用各種方法來實現減少影象的顏色數量

color = color/div*div +div/2;

若div為8,則原來RGB每個通道的256種顏色減少為32種。

若div為64,則原來RGB每個通道的256種顏色減少為4種,此時三通道所有能表示的顏色有4×4×4 = 64 種

首先,我們來看一個函式

C++: uchar* Mat::ptr(int i=0)
i 是行號,返回的是該行資料的指標。
在OpenCV中,一張3通道影象的一個畫素點是按BGR的順序儲存的。
先來看看第一種訪問方案
void colorReduce1(cv::Mat& image, cv::Mat& result, int div=64){
    int nrow = image.rows;
    int ncol = image.cols * image.channels();
    for(int i=0; i<nrow; i++){
        uchar* data = image.ptr<uchar>(i);
        uchar* data_out = result.ptr<uchar>(i);
        for(int j=0; j<ncol; j++){
            data_out[j] = data[j]/div*div +div/2;
        }
    }
}

第二種方案:

先來看如下函式:

C++: bool Mat::isContinuous() const

C++: Mat Mat::reshape(int cn, int rows=0) const

出於效能方面的考慮,在影象每一行的最後可能會填充一些畫素,這樣影象的資料就不是連續的了

我們可以用函式isContinuous()來判斷影象的資料是否連續

reshape函式的作用如下:

Changes the shape and/or the number of channels of a 2D matrix without copying the data.

這樣,我們就提出了對第一種方法的改進

void colorReduce2(cv::Mat& image, cv::Mat& result, int div){
    if(image.isContinuous()){
        image.reshape(1,image.cols*image.rows);
    }
    int nrow = image.rows;
    int ncol = image.cols * image.channels();
    for(int i=0; i<nrow; i++){
        uchar* data = image.ptr<uchar>(i);
        uchar* data_out = result.ptr<uchar>(i);
        for(int j=0; j<ncol; j++){
            data_out[j] = data[j]/div*div +div/2;
        }
    }
}
第三種方案:
先來看看下面的函式
C++: template<typename T> T& Mat::at(int i, int j)
其作用是Returns a reference to the specified array element.
void colorReduce3(cv::Mat& image, cv::Mat& result, int div){
    int nrow = image.rows;
    int ncol = image.cols * image.channels();
    for(int i=0; i<nrow; i++){
        for(int j=0; j<ncol; j++){
            image.at<cv::Vec3b>(j,i)[0]= image.at<cv::Vec3b>(j,i)[0]/div*div + div/2;
            image.at<cv::Vec3b>(j,i)[1]= image.at<cv::Vec3b>(j,i)[1]/div*div + div/2;
            image.at<cv::Vec3b>(j,i)[2]= image.at<cv::Vec3b>(j,i)[2]/div*div + div/2;
        }
    }
}
第四種方案是使用迭代器
會使用到如下函式:
C++: template<typename _Tp> MatIterator_<_Tp> Mat::begin()
C++: MatIterator_<_Tp> Mat::end()
void colorReduce4(cv::Mat& image, cv::Mat& result, int div){
    cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator itout = result.begin<cv::Vec3b>();
    for(; it!=itend; ++it,++itout){
        (*itout)[0] = (*it)[0]/div*div + div/2;
        (*itout)[1] = (*it)[1]/div*div + div/2;
        (*itout)[2] = (*it)[2]/div*div + div/2;
    }
}
OpenCV中矩陣資料的訪問(二)(Learning OpenCV第三章3)

2009-08-14 21:45:19| 分類: 科研學習 |字號 訂閱
上一篇文章提到了訪問矩陣中元素的前兩種方式,下面講第三種方式:正確的訪問矩陣中資料的方式:

正確的方式
前面介紹的一些讀取和寫入矩陣資料的方式,實際上,你可能很少會使用它們。因為,在大多數情況下,你需要使用最有效率的方式來訪問矩陣中的資料。如果使用以上的函式介面來訪問資料,效率比較低,你應該使用指標方式來直接訪問矩陣中資料。特別是,如果你想遍歷矩陣中所有元素時,就更需要這樣做了。
在用指標直接訪問矩陣元素時,就需要格外注意矩陣結構體中的step成員。該成員是以位元組為單位的每行的長度。而矩陣結構體的cols或width就不適合此時使用,因為為了訪問效率,矩陣中的記憶體分配上,是以每四個位元組做為最小單位的。因此如果一個矩陣的寬度是三個位元組,那麼就會在寬度上分配四個位元組,而此時每行最後一個位元組會被忽略掉。所以我們用step則會準確地按行訪問資料。
我們可以通過以下例子,看一下rows,cols,height,width,step的資料,你可以通過改變矩陣的元素型別定義,來檢視step的改變:
#pragma comment(lib,"cxcore.lib")
#include"cv.h"
#include<stdio.h>
void main()
{
    //矩陣元素為三通道8位浮點數
    CvMat *mat=cvCreateMat(3,3,CV_32FC3 );
    printf("rows=%d,cols=%d,height=%d,width=%d,step=%d\n",mat->rows,mat->cols,mat->height,mat->width,mat->step);

}
如果我們的矩陣儲存的是浮點型(或整數型別)資料,此時矩陣中每個元素佔4位元組,則如果我們用float型別指標指向下一行時,我們實際上要用float型別指標挪動step/4的長度,因為float型別指標每挪動一個單位就是4個位元組長度。
如果我們的矩陣儲存的是double型別資料,此時矩陣中每個元素佔8位元組,則如果我們用double型別指標指向下一行時,我們實際上要用double型別指標挪動step/8的長度,因為double型別指標每挪動一個單位就是8個位元組長度。
我們重新看一下CvMat型別的資料結構定義,其中,data就是資料部分,指向data的指標可以是多種資料型別的:
typedef struct CvMat {
    int type;
    int step;
    int* refcount; // for internal use only
    union {
         uchar* ptr;
         short* s;
         int* i;
         float* fl;
         double* db;
    } data;//資料部分
    union {
         int rows;
         int height;
    };
    union {
         int cols;
         int width;
    };
} CvMat;

我們可以通過為矩陣賦值,和讀取的例子,檢視怎樣使用step:
#pragma comment(lib,"cxcore.lib")
#include"cv.h"
#include<stdio.h>
void main()
{
    //矩陣元素為三通道8位浮點數
    CvMat *mat=cvCreateMat(3,3,CV_32FC3 );
    float *p;
    int row,col;
    for(row=0; row< mat->rows; row++)
    {
        p = mat->data.fl + row * (mat->step/4);
        for(col = 0; col < mat->cols; col++)
        {
            *p = (float) row+col;
            *(p+1) = (float) row+col+1;
            *(p+2) =(float) row+col+2;
            p+=3;
        }
    }

    for(row = 0; row < mat->rows; row++)
    {
        p = mat->data.fl + row * (mat->step/4);
        for(col = 0; col < mat->cols; col++)
        {
            printf("%f,%f,%f\t",*p,*(p+1),*(p+2));
            p+=3;
        }
        printf("\n");
    }
}

如果我們使用的指標型別為uchar*型別,則事情可能會簡單一些,不用考慮step/4,step/8等類似情況,我們推薦用這種方式。如下例所示:

#pragma comment(lib,"cxcore.lib")
#include"cv.h"
#include<stdio.h>
void main()
{
    //矩陣元素為三通道8位浮點數
    CvMat *mat=cvCreateMat(3,3,CV_32FC3 );
    float *p;
    int row,col;
    for(row=0; row< mat->rows; row++)
    {
        p = (float*)(mat->data.ptr + row * mat->step);
        for(col = 0; col < mat->cols; col++)
        {
            *p = (float) row+col;
            *(p+1) = (float) row+col+1;
            *(p+2) =(float) row+col+2;
            p+=3;

相關推薦

OpenCVMat裡面depth,dims,channels,step,data,elemSize資料地址計算理解

cv::Matdepth/dims/channels/step/data/elemSizeThe class Mat represents an n-dimensional dense numerical single-channel or multi-channel ar

OpenCVMat裏面depth,dims,channels,step,data,elemSize和數據地址計算理解

ima 忽略 learning note 數組 進行 每一個 ber 初始 cv::Matdepth/dims/channels/step/data/elemSizeThe class Mat represents an n-dimensional dense numeri

音頻采樣位數,采樣率,比特率的名詞解釋

工程 性能 dvd 工作室 轉化 術語 意思 普通 時間間隔 采樣位數: 采樣位數可以理解為采集卡處理聲音的解析度。這個數值越大,解析度就越高,錄制和回放的聲音就越真實。我們首先要知道:電腦中的聲音文件是用數字0和1來表示的。所以在電腦上錄音的本質就是把模擬聲音信號轉換成

C++sizeofstruct怎麽計算

color 發現 pre blank str io7 bsp com 說明 struct為空時,大小為1. 1、 sizeof應用在結構上的情況 請看下面的結構: 1 struct MyStruct 2 { 3 double dda1; 4 char dda; 5 int

TCP協議的三次握手四次揮手(圖解)

繼續 丟失 get 所有 如果 idt 請求報文 網絡 center 轉自:http://blog.csdn.net/whuslei/article/details/6667471 建立TCP需要三次握手才能建立,而斷開連接則需要四次握手。整個過程如下圖所示: 先來看看如

SpringAOP的一個通俗易懂的理解

學會 事物 nbsp 連接 新的 之前 天都 這不 proxy 這是看到的一個最易懂得AOP簡介了,適合初學者理解。 轉自:http://www.verydemo.com/demo_c143_i20837.html 1.我所知道的aop   初看aop,上來就是一大堆術語,

Java泛型TClass<T>以及Class<?>的理解

tcl ota 特定 類型 基本 ext pla enum extend 註意:class是java的關鍵字, 在聲明Java類時使用; Class類的實例表示Java應用運行時的類(class ans enum)或接口(interface and annotatio

關於JS變量提升的規則原理的一點理解

cnblogs 打印 blog javascrip 誤區 down mark fun ont 上篇文章中講到變量提升和函數提升的先後順序時蒙了,後來去查了一下資料,特別整理一下。 在《你不知道的JavaScript(上卷)》一書的第40頁中寫到:函數會首先被提升,然後才是變

Spring框架的理解

體系 工廠方法 面向對象編程 整合 控制 應用 aop 低耦合 ati ① spring框架是一個開源而輕量級的框架,是一個IOC和AOP容器 ② spring的核心就是控制反轉(IOC)和面向切面編程(AOP) ③ 控制反轉(IOC):是面向對象編程中的一種設計原則,

Linux硬盤物理扇區與文件系統文件對應關系

通過命令 wid lsp www sta fdisk -l echo net 我們 1 概述 系統讀寫文件過程中,如下面內核打印信息,報告讀寫某個扇區錯誤。那麽我們如何能夠通過sector找到讀寫哪個文件錯誤? kernel: end_reque

TCP協議握手的理解

向上 重新啟動 應該 開始 不發送 開發 釋放 還要 knowledge 目錄: 31.Tcp握手的一些問題? 21.Tcp三次握手及SYN攻擊; 四次握手? 為什麽建立連接是三次握手,而關閉連接卻是四次揮手? 13.TCP釋放連接四次握手 12.TCP建立連接三次握

python3k-mean演算法的理解

    1.隨機選取k個質心(k值取決於你想聚成幾類)    random.sample(dataSet, k)  k你是想聚類的個數 dataset是資料集合 是陣列    2.dataSet 取出一條資料 然後分別與centroidList中的k的值進行歐氏距離

JMeter返回Json資料的處理方法

Json 作為一種資料交換格式在網路開發,特別是 Ajax 與 Restful 架構中應用的越來越廣泛。而 Apache 的 JMeter 也是較受歡迎的壓力測試工具之一,但是它本身沒有提供對於 Json&nb

無線網絡,使用MDK3把指定的用戶或者熱點踢到掉線

欺騙 超過 bsp 信息 code www 參數 參考 wifi熱點 閱讀目錄   準備   驗證洪水攻擊 / Authentication Flood Attack   取消身份驗證攻擊 / Deauth攻擊   參考 回到頂部   準備   1:系統環境為ubu

Javaabstract class interface 的解釋他們的異同點

(一)概述    在Java語言中, abstract class 和interface 是支援抽象類定義的兩種機制。正是由於這兩種機制的存 在,才賦予了Java強大的 面向物件能力。abstract class和interface之間在對於抽象類定義的支援方面具有 很大的

代理模式理解

代理,代表打理,以他人的名義代表委託人打理其本職工作之外或不所能及的事務,達成合作關係並更高效地促成事務完成的目的。例如明星經紀人,他們並沒有像明星一樣會唱歌、跳舞或演戲,而是替明星打理一些無暇顧及的事務(這並不代表可以代理分外之事),比如推廣與宣傳,合同談判啊之類,達成和約後他們才會通知明星去表演

LRURL方式錄製基於HTML錄製指令碼

1、 如何在lr中錄製js等 在錄製設定中選擇url_base,即可 2、cookie問題? Simulate a new user on each iteration意味著每次Iteration的時候LR會把cookie和session之類的清除。所以如果指令碼中登入過程放

mysql樂觀鎖、悲觀鎖、共享鎖、排它鎖、行鎖、表鎖概念的理解

而在 狀態 line 主鍵 n) efault 你家 不一致 開啟 實驗環境: mysql5.6 存儲引擎:innoDB 我們在操作數據庫的時候,可能會由於並發問題而引起的數據的不一致性(數據沖突) 樂觀鎖樂觀鎖不是數據庫自帶的,需要我們自己去實現。樂觀鎖是指操作數據庫

MySQLcharacter set與collation的理解

var 數量 field nis ins character href The 方法 character set和collation的是什麽? character set即字符集 我們常看到的UTF-8、GB2312、GB18030都是相互獨立的character se

opencvMat型別向Eigen的Matrix型別轉換

https://blog.csdn.net/yangliuqing19/article/details/60874290 參考:http://www.cnblogs.com/shang-slam/p/6064905.html