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

opencv can't open a yuv422 image while rawpixels.net can display the image

我正在尝试打开 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图像是这样的。

谢谢,

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


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

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

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

我一看,数据在文件的开头,文件的结尾是zero-padding,即黑色。如果黑色位于图像的开头,我会采用最后的 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

这非常令人鼓舞,因为我们可以在文件开头看到 Y(灰度)图像,然后是一些 lower-resolution UV 通道,事实上后面没有 2 个单独的通道可能意味着它们是交错的,交替 UV 样本而不是平面 U 样本后跟 V 样本。


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

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


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

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