OpenCV Basics - Mat

转自OpenCV入门教程

1. Mat - ROI Rect

Rect截取ROI, 共享原始Mat内存

imageROI= image(Rect(800,350,logo.cols,logo.rows));  //使用矩形界定
// or 使用行和列界定
imageROI= image(Range(350,350+logo.rows),Range(800,800+logo.cols));
// copyTo, 可以有mask参数,只copy部分非0像素
logoImage.copyTo(imageROI,mask);

另外,copyTo和clone函数区别:当矩阵头文件已经存在管理空间时copyTo函数不会重新申请空间,而clone函数依然会重新申请空间。 重载元算赋‘=’,被赋值的矩阵和赋值矩阵之间空间共享

2. Mat - Init

初始化

Mat I = Mat::eye(4, 4, CV_64F);  // ones, zeros
I.at<double>(1,1) = CV_PI;  // 注意这么引用!

// 小矩阵可以这么写
Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);

Mat r = Mat(10, 3, CV_8UC3);
randu(r, Scalar::all(0), Scalar::all(255)); // 随机值填充,设定上下限

除了imread直接初始化Mat,还可以通过二进制流初始化Mat

std::ifstream ifs(image_path, std::ios::binary | std::ios::ate);  // open and move to end
if (!ifs.fail()) {
    std::vector<char> buffer(ifs.tellg());
    if (!ifs.seekg(0, std::ios::beg).read(buffer.data(), static_cast<std::streamsize>(buffer.size()))) {
        std::cerr << "Error Read file " << std::endl;
        return;
    }
    cv::Mat src = cv::imdecode(buffer, cv::IMREAD_ANYCOLOR);
}

从内存存取mat

// python:
// 从内存取
fin = open(file_path, 'rb')
content = fin.read()
data = np.fromstring(content, dtype='uint8')
data = cv2.imdecode(data, cv2.IMREAD_COLOR)
cv2.imwrite('test.png', data)  // jpg is also OK
// 写到内存
print(data.shape)  // nd.array, 比如二维数组,就是单channel图像
retval, buffer = cv2.imencode('.png', data)
fout = open("test2.png", 'wb')
fout.write(buffer)

// c++:
std::vector<uchar> matdata(data_in, data_in + env->GetArrayLength(data));
cv::Mat src;
src = cv::imdecode(matdata, cv::IMREAD_COLOR);  // always get 3 channel, not IMREAD_ANYCOLOR

std::vector<uchar> buffer;
bool res = cv::imencode(".png", mat, buffer);

3. Mat - Cout Format

输出格式 format(r, Formatter::FMT_PYTHON)

cout << "r (OpenCV默认风格) = " << r << ";" << endl << endl;
// Python风格 更清晰
cout << "r (Python风格) = " << format(r, Formatter::FMT_PYTHON) << ";" << endl << endl;
cout << "r (Numpy风格) = " <<  format(r, Formatter::FMT_NUMPY )<< ";" << endl << endl;
cout << "r (逗号分隔风格) = " << format(r, Formatter::FMT_CSV   )<< ";" << endl<< endl;
cout << "r (C语言风格) = " <<  format(r, Formatter::FMT_C     ) << ";" << endl << endl;

4. Get Pixel in Mat

Use C style ptr

void colorReduce(Mat& inputImage, Mat& outputImage, int div) {
  //参数准备
  outputImage = inputImage.clone();  //拷贝实参到临时变量
  int rowNumber = outputImage.rows;  //行数
  //列数 x 通道数=每一行元素的个数
  int colNumber = outputImage.cols * outputImage.channels();
  //双重循环,遍历所有的像素值
  for (int i = 0; i < rowNumber; i++) {
    uchar* data = outputImage.ptr<uchar>(i);  //获取第i行的首地址
    for (int j = 0; j < colNumber; j++) {
      // ---------【开始处理每个像素】-------------
      data[j] = data[j]/div*div + div/2;
      // !!! 或使用 *data++= *data/div*div + div/2
      // data[j] & mask 快4倍,mask = 0xFF << 6
		}
	}  
}  

Iterator, 更安全,不会指针越界,但需事先知道type Vec3b

void colorReduce(Mat& inputImage, Mat& outputImage, int div) {
  //参数准备
  outputImage = inputImage.clone();  //拷贝实参到临时变量
  //获取迭代器
  Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();  //初始位置的迭代器
  Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();  //终止位置的迭代器
  //存取彩色图像像素
  for (; it != itend; ++it) {
    // -----------【开始处理每个像素】--------------------
    (*it)[0] = (*it)[0] / div * div + div / 2;
    (*it)[1] = (*it)[1] / div * div + div / 2;
    (*it)[2] = (*it)[2] / div * div + div / 2;
	}  
}

at方法,直观,但速度慢(Release之下应该速度相同,推荐,可读性好),需要知道类型 vec3b

void colorReduce(Mat& inputImage, Mat& outputImage, int div) {
  //参数准备
  outputImage = inputImage.clone();  //拷贝实参到临时变量
  int rowNumber = outputImage.rows;  //行数
  int colNumber = outputImage.cols;  //列数
  //存取彩色图像像素
  for (int i = 0; i < rowNumber; i++) {
    for (int j = 0; j < colNumber; j++) {
      // ------------【开始处理每个像素】--------------------
      outputImage.at<Vec3b>(i, j)[0] =
          outputImage.at<Vec3b>(i, j)[0] / div * div + div / 2;  //蓝 通道
      outputImage.at<Vec3b>(i, j)[1] =
          outputImage.at<Vec3b>(i, j)[1] / div * div + div / 2;  //绿 通道
      outputImage.at<Vec3b>(i, j)[2] =
          outputImage.at<Vec3b>(i, j)[2] / div * div + div / 2;  //红 通道
    }  // 行处理结束     
	}  
}

5. Mat - Mix Channels

mixChannels: 最灵活的重排图像通道的方法

// rgba mat --> bgr + alpha
Mat bgra( 100, 100, CV_8UC4, Scalar(255,0,0,255) );
Mat bgr( bgra.rows, bgra.cols, CV_8UC3 );
Mat alpha( bgra.rows, bgra.cols, CV_8UC1 );
// forming an array of matrices is a quite efficient operation,
// because the matrix data is not copied, only the headers
Mat out[] = { bgr, alpha };
// bgra[0] -> bgr[2], bgra[1] -> bgr[1],
// bgra[2] -> bgr[0], bgra[3] -> alpha[0]
int from_to[] = { 0,2, 1,1, 2,0, 3,3 };
mixChannels( &bgra, 1, out, 2, from_to, 4 );

6. Mat - 一些属性

  • data: 数据数组的首地址
  • elemSize(): 表示每一个元素的数据大小,CV_8UC1是1;CV_8UC3或CV_8SC3是3;CV_16UC3是6。即elemSize是以8bit(一个字节)为一个单位
  • elemSize1(): 每一个元素中,单个通道的数据大小,也是字节单位,elemSize/channels. CV_16SC3是2(16是2个字节)
  • step: 图像矩阵,一行的长度(字节为单位),elemSize() * cols
  • step1(): 一行的长度(每个通道的单个元素的大小为单位),step/eleSize1()
  • total(): rows * cols

7. Mat - uchar数组转换

// Mat --> uchar 数组
cv::Mat input = ...;
uchar *dst = new uchar[input.total() * input.elemSize()];
if(src.isContinuous()) 
  dst = input.data;
else 
  memcpy(dst, input.data, input.total() * input.elemSize() * sizeof(uchar));
// uchar 数组 --> Mat
cv::Mat input(Size size, int type, void* data);
Loading Disqus comments...
Table of Contents