OpenCV Basics - Mat
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);