最近使用OpenCV的函数保存图像,发现这里面存在诸多的坑,经过多次尝试,这才发现原来保存图像也是一门学问哦!之前已经写过一篇图像读写的博客:opencv读写图像问题!,这里再写一篇作为补充。
一、类型转换
1.Mat数据的类型
opencv中的mat数据类型共包含28种:
类型 | C1 | C2 | C3 | C4 |
---|---|---|---|---|
CV_8U | 0 | 8 | 16 | 24 |
CV_8S | 1 | 9 | 17 | 25 |
CV_16U | 2 | 10 | 18 | 26 |
CV_16S | 3 | 11 | 19 | 27 |
CV_32S | 4 | 12 | 20 | 28 |
CV_32F | 5 | 13 | 21 | 29 |
CV_64F | 6 | 14 | 22 | 30 |
opencv提供了获取类型的函数:type()
这里提供一个简单的获取类型名的代码:
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
using namesapce cv;
string getImageType(Mat img)
{
int type = img.type();
string channel = "";
string depth = "";
// 判断通道数
if (type >= 0 && type <= 6)
{
channel += "C1";
}
else if (type >= 8 && type <= 14)
{
channel += "C2";
}
else if (type >= 16 && type <= 22)
{
channel += "C3";
}
else if (type >= 24 && type <= 30)
{
channel += "C4";
}
// 判断位深
if (0 == type || 8 == type || 16 == type || 24 == type)
{
depth += "CV_8U";
}
else if (1 == type || 9 == type || 17 == type || 25 == type)
{
depth += "CV_8S";
}
else if (2 == type || 10 == type || 18 == type || 26 == type)
{
depth += "CV_16U";
}
else if (3 == type || 11 == type || 19 == type || 27 == type)
{
depth += "CV_16S";
}
else if (4 == type || 12 == type || 20 == type || 28 == type)
{
depth += "CV_32S";
}
else if (5 == type || 13 == type || 21 == type || 29 == type)
{
depth += "CV_32F";
}
else if (6 == type || 14 == type || 22 == type || 30 == type)
{
depth += "CV_64F";
}
return depth + channel;
}
2.类型转换
opencv中提供了类型转换的函数convertTo()用于进行图像类型转换,函数简介如下:
/** @brief Converts an array to another data type with optional scaling.
The method converts source pixel values to the target data type. saturate_cast\<\> is applied at
the end to avoid possible overflows:
\f[m(x,y) = saturate \_ cast<rType>( \alpha (*this)(x,y) + \beta )\f]
@param m output matrix; if it does not have a proper size or type before the operation, it is
reallocated.
@param rtype desired output matrix type or, rather, the depth since the number of channels are the
same as the input has; if rtype is negative, the output matrix will have the same type as the input.
@param alpha optional scale factor.
@param beta optional delta added to the scaled values.
*/
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;
示例如下:
string path = "C:\\Users\\admin\\Desktop\\0.png";
Mat img = imread(path, -1);
cout << getImageType(img) << endl;
Mat cv8u;
cvtColor(img, cv8u, CV_BGRA2GRAY);
cout << getImageType(cv8u) << endl;
Mat cv8uc3;
cvtColor(img, cv8uc3, CV_BGRA2BGR);
cout << getImageType(cv8uc3) << endl;
Mat cv16u;
cv8u.convertTo(cv16u, CV_16UC1);
cout << getImageType(cv16u) << endl;
Mat cv16uc3;
cv8uc3.convertTo(cv16uc3, CV_16UC3);
cout << getImageType(cv16uc3) << endl;
// 其他类型依此类推
注意:在转换过程中如果想要转换为3通道的图像,必须保证被转换的图像也为3通道的图像,否则最终转换过来仍然是单通道。
二、图像保存
1.保存类型
我在这里经过大量测试,总结成一个表格,每种格式的图像可保存的类型如下:
类型/格式 | TIFF/TIF(*.tiff) | JPG(*.jpg) | PNG(*.png) | BMP(*.bmp) |
---|---|---|---|---|
CV_8U | √ | √ | √ | √ |
CV_8UC3 | √ | √ | √ | √ |
CV_8S | × | × | × | × |
CV_8SC3 | × | × | × | × |
CV_16U | √ | × | √ | × |
CV_16UC3 | √ | × | √ | × |
CV_16S | × | × | × | × |
CV_16SC3 | × | × | × | × |
CV_32S | × | × | × | × |
CV_32SC3 | × | × | × | × |
CV_32F | √ | × | × | × |
CV_32FC3 | √ | × | × | × |
CV_64F | √ | × | × | × |
CV_64FC3 | × | × | × | × |
可以看到,以上四种格式都无法保存所有类型的Mat,想要保存任何一种Mat,可以使用XML或YAML格式,opencv提供了FileStorage类用于将数据存储为xml或yaml格式的文件
2.图像保存的接口
CV_EXPORTS_W bool imwrite( const String& filename, InputArray img, const std::vector<int>& params = std::vector<int>());