OpenCV 将 CV_32FC1 解码为 png

OpenCV decode CV_32FC1 into png

我想将 OpenCV CV_32FC1 Mat 转换为 png 以保存它,然后在 Unity Shader 中使用它。 我想对其进行解码,以便第一个通道包含最高 8 位,第二个通道包含接下来的 8 位,第三个通道包含接下来的 8 位。

Edit> 我实际上是指尾数的最高位。否则丢弃 8 位(因为我需要 3 个通道用于 imwrite)会破坏浮点表示。

我已经用这个函数反过来工作了:

Mat prepareLUT(char* filename){
    Mat first;
    first = imread(filename, CV_LOAD_IMAGE_COLOR);
    Mat floatmat;
    first.convertTo(floatmat, CV_32F);
    std::vector<Mat> channels(3);
    split(floatmat, channels);
    Mat res(Size(960,1080), CV_32FC1);
    res = channels[2]/255 + channels[1]/(255.0*255.0) + channels[0]/(255.0*255.0*255.0);
    return res;
}

但是反过来我做不到。

我的第一个想法如下:

void saveLUT(Mat in, char const* filename){
    Mat m1 = Mat(imageSize, CV_8UC1);
    Mat m2 = Mat(imageSize, CV_8UC1);
    Mat m3 = Mat(imageSize, CV_8UC1);

    m1 = (in*(0xFF*0xFF*0xFF-1));
    m2 = (in*(0xFF*0xFF-1));
    m3 = (in*(0xFF-1));

    std::vector<Mat> channels;
    channels.push_back(m1);
    channels.push_back(m2);
    channels.push_back(m3);
    Mat out;
    merge(channels, out);
    imwrite(filename, out);
}

我以为我的 8 位范围的左右所有位都会被截断,给我正确的垫子,但它总是输出一些灰色图像。

第二种方法是使用浮动垫,然后将它们转换为字符垫以截断尾随数字:

void saveLUT(Mat in, char const* filename){
    Mat m1f(imageSize, CV_32FC1);
    Mat m2f(imageSize, CV_32FC1);
    Mat m3f(imageSize, CV_32FC1);

    Mat m1, m2, m3;

    m3f = in*255;
    m3f.convertTo(m3, CV_8UC1);
    m3.convertTo(m3f, CV_32FC1);

    m2f = (in*255-m3f)*255;
    m2f.convertTo(m2, CV_8UC1);
    m2.convertTo(m2f, CV_32FC1);

    m1f = ((in*255-m3f)*255-m2f)*255;
    m1f.convertTo(m1, CV_8UC1);

    std::vector<Mat> channels;
    channels.push_back(m1);
    channels.push_back(m2);
    channels.push_back(m3);
    Mat out;
    merge(channels, out);
    imwrite(filename, out);
}

这样我总是通过在乘法之前减去前一个通道的结果来减去太高的数字,但这仍然给我一个灰色的结果,如下所示。 知道如何解决这个问题吗?

您想要实现的基本上是从类型 CV_32FC1 到类型 CV_8UC4 的转换,然后您可以将其另存为 PNG 文件。

这可以在 C++ 中使用数据指针在一行中实现:

cv::Mat floatImage; // Your CV_32FC1 Mat
cv::Mat pngImage(floatImage.rows, floatImage.cols, CV_8UC4, (cv::Vec4b*)floatImage.data);

您获得的是一张 4 通道 8 位精度图像,其中每个像素包含原始图像中的一个浮点值,分为 4 个 8 位块。

反变换也是可以的:

cv::Mat pngImage;
cv::Mat floatImage(pngImage.rows, pngImage.cols, CV_32FC1, (float*)pngImage.data);

我找到了一种方法,但不是很漂亮。

我只是对每个值执行转换,像这样进行一些位操作和位移:

void saveLUT(Mat in, char const* filename){
    int i,j;

    Mat c1(imageSize, CV_8UC1);
    Mat c2(imageSize, CV_8UC1);
    Mat c3(imageSize, CV_8UC1);

    for(i = 0; i < in.cols; i++){
        for(j = 0; j < in.rows; j++){
            float orig = in.at<float>(j,i);

            uint32_t orig_int = orig*(256.0*256.0*256.0-1);
            c1.at<uint8_t>(j,i) = (uint8_t)((orig_int&0xFF0000) >> 16);
            c2.at<uint8_t>(j,i) = (uint8_t)((orig_int&0x00FF00) >> 8);
            c3.at<uint8_t>(j,i) = (uint8_t)((orig_int&0x0000FF));
        }
    }

    std::vector<Mat> channels;
    channels.push_back(c1);
    channels.push_back(c2);
    channels.push_back(c3);
    Mat out;
    merge(channels, out);
    imwrite(filename, out);

    Mat encoded(imageSize, CV_8UC4);

}

它看起来不太好,我不得不假设有更快的方法可以做到这一点,但我没有找到任何方法,而且它运行得足够快,可以满足我的目的。