opencv 无法打开 yuv422 图像而 rawpixels.net 可以显示图像

时间:2021-01-15 07:04:42

标签: c++ image opencv yuv libyuv

我正在尝试打开 yuv 格式的图像。我可以用rawpixels.net打开,设置如下后显示

width:1920
height:1080
predefined format: yuv420 (nv12)
pixel format yuv

但是如果我用下面的代码用opencv打开我就打不开。

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/opencv.hpp>

int main() {
    std::cout << "OpenCV version: " << CV_VERSION << std::endl;


    cv::Mat image = cv::imread("camera_capture_256_2020_10_07_11_11_02.yuv");
    if (image.empty() == true) {

        std::cout << "image empty"<< std::endl;

        return 0;
    }   
        
    cv::imshow("opencv_logo", image);
    cv::waitKey(0);    

    return 0;
}

程序打印为“图像为空”。

我很困惑为什么我不能用opencv打开文件。

找到示例图像 here

使用 rawpixels.net 打开的 yuv 图像看起来像这样。

enter image description here

谢谢,

1 个答案:

答案 0 :(得分:2)

处理原始(RGB、BGR、YUV、NV12 和其他)图像时要做的第一件事就是知道图像的像素尺寸 - 没有这些你真的很迷茫 - 虽然你可以做一些技巧寻找相关性以找到行宽,因为每一行基本上都与上面的行相似。


接下来是检查文件大小是否正确。因此,如果它是 RGB 和 8 位 1920x1080,则您的文件大小必须为 1920x1080x3 像素 - 如果不是,则有问题。您的图像是 1920x1080 和 NV12,它是 12 位或每像素 1.5 字节,所以我希望您的文件是 1920x1080*1.5 字节。不是那样,所以马上就有问题了。存在标题、多个帧或尾随数据或其他一些问题。

那么,文件中的图像数据在哪里?在开始时?在末尾?解决此问题的一种方法是将文件视为纯粹的灰度图像,看看是否有大块的黑色为零字节或填充。由于没有已知的图像大小,我通常以字节为单位获取文件大小,然后转到 Wolfram Alpha 网站并输入 "factors of XXX" 其中 XXX 是文件大小,然后选择 2文件大小平方根附近的数字,所以我得到一个方形的图像。因此,对于您,我选择了 2720x3072 并将您的文件视为该大小的单个灰度图像。在终端中使用 ImageMagick

magick -depth 8 -size 2720x3072 gray:camera_preview_250_2020_10_07_11_11_02.yuv image.jpg

enter image description here

一目了然,数据在文件的开头,文件的结尾是零填充,即黑色。如果黑色在图像的开头,我会占用最后的 H x W x 1.5 字节。

此步骤的另一种替代方法是将文件大小(以字节为单位)除以图像宽度以获取行数并查看其外观。所以您的文件是 8355840 字节,即 8355840/1920 或 4,325 行。让我们试试:

magick -depth 8 -size 1920x4352 gray:camera_preview_250_2020_10_07_11_11_02.yuv image.jpg

enter image description here

这非常令人鼓舞,因为我们可以看到文件开头的 Y(灰度)图像和后面的一些低分辨率 UV 通道,而且没有 2 个单独的通道以下可能意味着它们是交错、交替的 UV 样本,而不是平面 U 样本后跟 V 样本。


好的,如果您的数据是 YUV 或 NV12,那么最好的工具是 ffmpeg。我们已经知道数据位于文件的开头,并且我们知道尺寸和格式。我们也知道图像后面有填充,所以我们只需要像这样取第一帧:

ffmpeg -s 1920x1080 -pix_fmt nv12 -i cam*yuv -frames:v 1 image.png

enter image description here


现在我们对尺寸和格式有信心,我们需要 OpenCV 来阅读。普通的 cv2.imread() 无法读取,因为它只是原始数据,并且与 JPEG 或 PNG 或 TIFF 不同,标题中没有图像高度和宽度 - 它只是纯粹的传感器数据。

因此,您需要使用常规的 C/C++ read() 系统调用来获取前 1920x1080x1.5 个字节。然后您需要对接收到的缓冲区调用 cv2.cvtColor() 以将其转换为常规 BGR 格式 Mat

相关问题