OpenCV将CV_32FC1解码为png

时间:2016-07-28 17:03:10

标签: opencv floating-point mat

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

编辑>我实际上是指尾数的最高位。否则丢弃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位范围左右两侧的所有位都会被截断,给我正确的Mat,但它总是输出一些灰色图像。

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

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);
}

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

2 个答案:

答案 0 :(得分:2)

您希望实现的目标实际上是从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);

答案 1 :(得分:0)

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

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

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);

}

看起来并不漂亮,我不得不假设有更快的方法可以做到这一点,但我没有找到任何方法,而且它的运行速度足够快。