使用OpenCV保存CV_32FC1图像

8

我正在使用的程序正在读取一些位图,并且需要32FC1图像。

我正在尝试创建这些图像。

cv::Mat M1(255, 255, CV_32FC1, cv::Scalar(0,0,0));
cv::imwrite( "my_bitmap.bmp", M1 );

然而,当我检查深度时,它总是CV_8U。

我该如何创建包含正确信息的文件?

更新:使用不同的文件扩展名(例如.tif或.png)没有任何区别。

我正在使用已经实现的代码 - 使用cvLoadImage读取它。

我正在尝试创建现有代码可以使用的文件 - 该代码检查图像类型。

我无法在现有代码中转换文件。现有代码不会尝试读取随机图像类型并将其转换为所需类型,而是检查文件是否属于需要的类型。

我发现 - 感谢答案 - cv :: imwrite仅写入整数类型图像。

是否有其他方法 - 使用OpenCV或其他方式 - 来编写图像,以便最终获得CV_32F类型?

再次更新:读取图像的代码...如果进入一个cv :: Mat:

cv::Mat x = cv::imread(x_files, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);

现有代码如下:
IplImage *I = cvLoadImage(x_files.c_str(), CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);

你是给外部程序什么?文件路径还是 cv::Mat 对象? - Jacob
我们能看到使用cvLoadImage读取文件的代码吗? - Bull
我给它文件的路径 - Thalia
3个回答

4

cv::imwrite() .bmp编码器假定通道为8位。

如果你只需要使用OpenCV写入.bmp文件,可以将32FC1图像转换为8UC4,然后使用cv::imwrite()进行写入,这样你就会得到一个每像素32位的.bmp文件。 我猜测你读取文件的程序将把32位像素解释为32FC1。.bmp格式没有显式的通道结构,只有每像素的位数。因此,在OpenCV中,你应该能够将32位像素写成8位的4个通道,并在另一个程序中读取它们作为单通道的32位像素——如果你这样做,你需要了解读取器的字节序假设。类似下面的代码应该起作用:

cv::Mat m1(rows, cols, CV_32FC1);
...  // fill m1
cv::Mat m2(rows, cols, CV_8UC4, m1.data); // provide different view of m1 data
// depending on endianess of reader, you may need to swap byte order of m2 pixels
cv::imwrite("my_bitmap.bmp", m2);

由于OpenCV中的.bmp解码器假定文件是8位数据的1或3个通道(即无法读取32位像素),因此您将无法正确阅读在OpenCV中创建的文件。

编辑

也许更好的选择是使用OpenEXR格式,OpenCV有一个编解码器。我假设你只需要用.exr扩展名保存你的文件。


谢谢,我不知道imwrite总是创建CV_8U。请查看更新。即使在写入之前进行转换,我仍然会得到CV_8U(这是根据文档)。那么...我的情况是没有希望了吗?还是有任何方法可以创建这些图像? - Thalia
你有读取 .bmp 文件的程序源代码吗?我想知道它对像素格式做了什么样的假设。 - Bull

3

您的问题在于位图将数据内部存储为整数而不是浮点数。如果您在保存时遇到舍入误差,您需要使用不同的文件格式或在保存之前将数据进行缩放,然后在保存后再缩放回来。如果您只想将读取文件后得到的矩阵转换为浮点数,可以使用cv::convertto。


很遗憾,读取后我无法转换类型,图像必须是正确的类型。在编写之前,我尝试了文件格式和缩放...然后我从文档中发现imwrite只能写入CV_8U。有其他替代方案吗? - Thalia
出于好奇,为什么读取后不能转换类型?根据您需要的精度和是否有已知的值集,您可以将图像乘以某个常数(比如100),将其保存为16位PNG格式,然后在读取时除以100。即使如此,这仍然需要改变类型。 - Hammer

0

我也曾经为同样的问题苦苦挣扎。最终,我决定编写一个自定义函数,可以写入和加载任意的CV Mat,这样会更容易一些。

bool writeRawImage(const cv::Mat& image, const std::string& filename)
{
    ofstream file;
    file.open (filename, ios::out|ios::binary);
    if (!file.is_open())
        return false;
    file.write(reinterpret_cast<const char *>(&image.rows), sizeof(int));
    file.write(reinterpret_cast<const char *>(&image.cols), sizeof(int));
    const int depth = image.depth();
    const int type  = image.type();
    const int channels = image.channels();
    file.write(reinterpret_cast<const char *>(&depth), sizeof(depth));
    file.write(reinterpret_cast<const char *>(&type), sizeof(type));
    file.write(reinterpret_cast<const char *>(&channels), sizeof(channels));
    int sizeInBytes = image.step[0] * image.rows;
    file.write(reinterpret_cast<const char *>(&sizeInBytes), sizeof(int));
    file.write(reinterpret_cast<const char *>(image.data), sizeInBytes);
    file.close();
    return true;
}

bool readRawImage(cv::Mat& image, const std::string& filename)
{
    int rows, cols, data, depth, type, channels;
    ifstream file (filename, ios::in|ios::binary);
    if (!file.is_open())
        return false;
    try {
        file.read(reinterpret_cast<char *>(&rows), sizeof(rows));
        file.read(reinterpret_cast<char *>(&cols), sizeof(cols));
        file.read(reinterpret_cast<char *>(&depth), sizeof(depth));
        file.read(reinterpret_cast<char *>(&type), sizeof(type));
        file.read(reinterpret_cast<char *>(&channels), sizeof(channels));
        file.read(reinterpret_cast<char *>(&data), sizeof(data));
        image = cv::Mat(rows, cols, type);
        file.read(reinterpret_cast<char *>(image.data), data);
    } catch (...) {
        file.close();
        return false;
    }

    file.close();
    return true;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接