cv mat保存图片_(七)神秘的Mat
時間為友,記錄點滴。
不知道你有沒有跟我一樣,看到一些關鍵的類或結構體總想看看他的結構是什么?因為我覺得單單知其然是不夠的,知其所以然才能走得更遠。
雖然我們只寫了兩個例子,但是很明顯,這里面的核心內容是Mat類。
The class Mat represents an n-dimensional dense numerical single-channel or multi-channel array. It can be used to store real or complex-valued vectors and 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 ).上面的一段話引用自官方的文檔,Mat類用于表示一個多維的單通道或者多通道的稠密數組。能夠用來保存實數或復數的向量、矩陣,灰度或彩色圖像,立體元素,點云,張量以及直方圖(高維的直方圖使用SparseMat保存比較好)。簡而言之,Mat就是用來保存多維的矩陣的。
如果你在mat.hpp中點開Mat的類定義,估計要被它一長長串的構造函數和重載看崩潰,我們直接略過令人崩潰的地方(在實際使用中,常用的定義一個Mat類型的對象的方法就那幾個),看到下面的成員變量:
- data:
uchar型的指針。Mat類分為了兩個部分:矩陣頭和指向矩陣數據部分的指針,data就是指向矩陣數據的指針。
- dims:
矩陣的維度,例如5*6矩陣是二維矩陣,則dims=2,三維矩陣dims=3.
- rows:
矩陣的行數
- cols:
矩陣的列數
- size:
矩陣的大小,size(cols,rows),如果矩陣的維數大于2,則是size(-1,-1)
- channels:
矩陣元素擁有的通道數,例如常見的彩色圖像,每一個像素由BGR三部分組成,則channels = 3。
- type:
表示了矩陣中元素的類型以及矩陣的通道個數,它是一系列的預定義的常量,其命名規則為CV_(位數)+(數據類型)+(通道數)。這里U(unsigned integer)表示的是無符號整數,S(signed integer)是有符號整數,F(float)是浮點數。
例如:CV_16UC2,表示的是元素類型是一個16位的無符號整數,通道為2. C1,C2,C3,C4則表示通道是1,2,3,4。type一般是在創建Mat對象時設定,如果要取得Mat的元素類型,則無需使用type,使用下面的depth
輸出的數值,對照表- depth
矩陣中元素的一個通道的數據類型,這個值和type是相關的。例如 type為 CV_16SC2,一個2通道的16位的有符號整數。那么,depth則是CV_16S。depth也是一系列的預定義值,
將type的預定義值去掉通道信息就是depth值:
CV_8U CV_8S CV_16U CV_16S CV_32S CV_32F CV_64F
- elemSize
矩陣一個元素占用的字節數,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes
- elemSize1
矩陣元素一個通道占用的字節數,例如:type是CV_16CS3,那么elemSize1 = 16 / 8 = 2 bytes = elemSize / channels
如何創建一個Mat也有好多中方法(我最常用的就是下面這三種):
多說無益,我們用代碼來驗證,可以寫一個函數來把img的各個參數打印出來:
現有圖片測試
static void showImgPara(Mat &img) {cout << "sizeof(img) is: " << sizeof(img) << ", img size is: " << img.size << endl;cout << "rows x cols: (" << img.rows << " x " << img.cols << ")" << endl;cout << "dims: " << img.dims << endl;cout << "channels: " << img.channels() << endl;cout << "type: " << img.type() << endl;cout << "depth:" << img.depth() << endl;cout << "elemSize:" << img.elemSize() << " (Bytes per element)" << endl;cout << "elemSize1:" << img.elemSize1() << "(Bytes per channel)" << endl;cout << "step[0]: " << img.step[0] << " (Bytes per cows only when 2 dims)" << endl;cout << "step[1]: " << img.step[1] << " (Bytes per element only when 2 dims)" << endl;cout << "step1(0): " << img.step1(0) << ", step1(1): " << img.step1(1) << " (step / elemSize1)" << endl; } static bool pictureTest(Mat &img, string name) {if (img.empty()){cout << "Load picture fail!" << endl;return false;}cout << endl << "/******************pictureTest******************/" << endl;showImgPara(img);namedWindow(name);imshow(name, img);return true; }int main() {Mat pic = imread("girl.jpg");pictureTest(pic, "girl");waitKey(0);return 0; }運行結果自建Mat類型:
除了看自己創建Mat對象的一些屬性,主要考量把自建對象的數據dump出來,可以看到使用了step。
static bool matTest(Mat &img) {uchar* pTemp = NULL;cout << endl << "/******************matTest******************/" << endl;cout << img << endl;showImgPara(img);for (int i = 0; i < img.rows; i++){for(int j = 0; j < img.cols; j++){cout << "[";for (int k = 0; k < img.step[1]; k++){pTemp = img.data + img.step[0] * i + img.step[1] * j + k;cout << (int)*pTemp << " ";}cout << "] ";}cout << endl;}return true; }創建一個3行4列, 每個元素是8bit(unsigned char),包含兩個通道,并且初始化為[1, 2]
int main() {Mat m2(3, 4, CV_8UC2, Scalar_<uchar>(1, 2));matTest(m2);waitKey(0);return 0; } matTest隨意創建一張涂色圖片
我們除了提前給data賦值,也可以通過data指針修改圖像中的任意像素。比如下圖可以修改創建圖片的中間方塊為任意顏色(本例子中全部涂黑),并且通過imshow直觀的體現出來。
static bool picCreat(Mat &img, string name) {for (int i = 0; i < img.rows; i++){uchar *p = img.ptr<uchar>(i);for (int j = 0; j < img.cols * img.channels(); j += img.channels()){if (i > img.rows/4 && i < img.rows*3/4 && j > img.cols*img.channels()/4 && j < img.cols*img.channels()*3/4){p[j] = 0;p[j + 1] = 0;p[j + 2] = 0;}else{p[j] = 255;p[j + 1] = 255;p[j + 2] = 255;}}}cout << endl << "/******************picCreat******************/" << endl;showImgPara(img);namedWindow(name);imshow(name, img);return true; }創建一個200x300大小的圖片。
int main() {Mat m1 = Mat::zeros(200, 400, CV_8UC3);picCreat(m1, "White");waitKey(0);return 0; }通過填充數據的方法生成一張中間是黑色的圖片picCreate圖片導成文件
為了肉眼觀測方便,實現了一個多通道轉單通道的函數。(暴力得是把第一個通道的數據取出來)
static bool tran2singleChannel(Mat &src, Mat &tar) {vector<Mat> channels;split(src, channels);tar = channels.at(0);return true; }把一個任意圖片,轉換成單通道,把data dump出來,存在文件中。
static bool pic2digit(Mat &img, string picName, string fileName) {uchar* pTemp = NULL;Mat signalChannel;ofstream output;if (img.empty()){cout << "Load picture fail!" << endl;return false;}cout << endl << "/******************pic2digit******************/" << endl;//showImgPara(img);//namedWindow(picName);//imshow(picName, img);tran2singleChannel(img, signalChannel);//namedWindow(picName + "_signalChannel");//imshow(picName + "_signalChannel", signalChannel);showImgPara(signalChannel);output.open(fileName);for (int i = 0; i < signalChannel.rows; i++){for (int j = 0; j < signalChannel.cols; j++){for (int k = 0; k < signalChannel.step[1]; k++){pTemp = signalChannel.data + signalChannel.step[0] * i + signalChannel.step[1] * j + k;output << setw(2) << setfill('0') << (int)*pTemp << " ";}}output << endl;}output.close();imwrite(picName + "_signalChannel.jpg", signalChannel);return true; }照一張簡單的對勾圖片
int main() {Mat pic1 = imread("check-circle.png");pic2digit(pic1, "check", "check-circle.txt");waitKey(0);return 0; }pic2digit生成的文件,跟原圖比輪廓一致全部代碼:
#include <opencv2/opencv.hpp> #include <iostream> #include <string> #include <fstream>using namespace std; using namespace cv;static void showImgPara(Mat &img); static bool pictureTest(Mat &img, string name); static bool matTest(Mat &img); static bool picCreat(Mat &img, string name); static bool pic2digit(Mat &img, string picName, string fileName); static bool tran2singleChannel(Mat &src, Mat &tar);int main() {Mat pic = imread("girl.jpg");pictureTest(pic, "girl");Mat m1 = Mat::zeros(200, 400, CV_8UC3);picCreat(m1, "White");Mat m2(3, 4, CV_8UC2, Scalar_<uchar>(1, 2));matTest(m2);Mat pic1 = imread("check-circle.png");pic2digit(pic1, "check", "check-circle.txt");waitKey(0);return 0; }static void showImgPara(Mat &img) {cout << "sizeof(img) is: " << sizeof(img) << ", img size is: " << img.size << endl;cout << "rows x cols: (" << img.rows << " x " << img.cols << ")" << endl;cout << "dims: " << img.dims << endl;cout << "channels: " << img.channels() << endl;cout << "type: " << img.type() << endl;cout << "depth:" << img.depth() << endl;cout << "elemSize:" << img.elemSize() << " (Bytes per element)" << endl;cout << "elemSize1:" << img.elemSize1() << "(Bytes per channel)" << endl;cout << "step[0]: " << img.step[0] << " (Bytes per cows only when 2 dims)" << endl;cout << "step[1]: " << img.step[1] << " (Bytes per element only when 2 dims)" << endl;cout << "step1(0): " << img.step1(0) << ", step1(1): " << img.step1(1) << " (step / elemSize1)" << endl; }static bool pictureTest(Mat &img, string name) {if (img.empty()){cout << "Load picture fail!" << endl;return false;}cout << endl << "/******************pictureTest******************/" << endl;showImgPara(img);namedWindow(name);imshow(name, img);return true; }static bool matTest(Mat &img) {uchar* pTemp = NULL;cout << endl << "/******************matTest******************/" << endl;cout << img << endl;showImgPara(img);for (int i = 0; i < img.rows; i++){for(int j = 0; j < img.cols; j++){cout << "[";for (int k = 0; k < img.step[1]; k++){pTemp = img.data + img.step[0] * i + img.step[1] * j + k;cout << (int)*pTemp << " ";}cout << "] ";}cout << endl;}return true; }static bool picCreat(Mat &img, string name) {for (int i = 0; i < img.rows; i++){uchar *p = img.ptr<uchar>(i);for (int j = 0; j < img.cols * img.channels(); j += img.channels()){if (i > img.rows/4 && i < img.rows*3/4 && j > img.cols*img.channels()/4 && j < img.cols*img.channels()*3/4){p[j] = 0;p[j + 1] = 0;p[j + 2] = 0;}else{p[j] = 255;p[j + 1] = 255;p[j + 2] = 255;}}}cout << endl << "/******************picCreat******************/" << endl;showImgPara(img);namedWindow(name);imshow(name, img);return true; }static bool pic2digit(Mat &img, string picName, string fileName) {uchar* pTemp = NULL;Mat signalChannel;ofstream output;if (img.empty()){cout << "Load picture fail!" << endl;return false;}cout << endl << "/******************pic2digit******************/" << endl;//showImgPara(img);//namedWindow(picName);//imshow(picName, img);tran2singleChannel(img, signalChannel);//namedWindow(picName + "_signalChannel");//imshow(picName + "_signalChannel", signalChannel);showImgPara(signalChannel);output.open(fileName);for (int i = 0; i < signalChannel.rows; i++){for (int j = 0; j < signalChannel.cols; j++){for (int k = 0; k < signalChannel.step[1]; k++){pTemp = signalChannel.data + signalChannel.step[0] * i + signalChannel.step[1] * j + k;output << setw(2) << setfill('0') << (int)*pTemp << " ";}}output << endl;}output.close();imwrite(picName + "_signalChannel.jpg", signalChannel);return true; }static bool tran2singleChannel(Mat &src, Mat &tar) {vector<Mat> channels;split(src, channels);tar = channels.at(0);return true; }check-circle.pnggirl.jpg寫在后面:
Mat類中有兩個數據部分組成:
- 矩陣頭:包括矩陣尺寸、存儲方法、存儲地址等。
- 指針:該指針指向存儲所有像素值的矩陣。
因為圖片的數據量比較大,所以OpenCV中的Mat定義都是只申請了矩陣頭和指針,盡量避免對指針指向的內容進行拷貝操作,所以如下操作都是共享的數據:
Mat A, C; // Identify header onlycout << "sizeof(A): " << sizeof(A) << "; sizeof(C): " << sizeof(C) << endl;cout << "A:n" << A << endl;A = Mat::ones(Size(3, 3), CV_8UC1);C = A;cout << "sizeof(A): " << sizeof(A) << "; sizeof(C): " << sizeof(C) << endl;cout << "A:n" << A << endl;Mat B(A);cout << "B:n" << B << endl;B.at<uchar>(1, 1) = 255;cout << "C:n" << C << endl;Mat D = A.clone();cout << "D:n" << D << endl;D.at<uchar>(0, 0) = 123;cout << "A:n" << A << endl;cout << "D:n" << D << endl;Mat E;A.copyTo(E);cout << "E:n" << E << endl;E.at<uchar>(2, 2) = 100;cout << "A:n" << A << endl;cout << "E:n" << E << endl;所以,關于Mat類型的變量定義以及賦值分為:
公用一個數據塊:
- =
- 構造函數
不公用一個數據塊:
- clone
- copyto
總結
以上是生活随笔為你收集整理的cv mat保存图片_(七)神秘的Mat的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 和lua的效率对比测试_N99 KF94
- 下一篇: CUDA 纹理内存