S920(I420平面)格式的组织

Organization of S920 (I420 planar) format

目前我正在使用支持 3 种格式的网络摄像头 v4l2-ctl --list-formats:

Index       : 0
Type        : Video Capture
Pixel Format: 'S920'
Name        : GSPCA SN9C20X I420

Index       : 1
Type        : Video Capture
Pixel Format: 'BA81'
Name        : 8-bit Bayer BGBG/GRGR

Index       : 2
Type        : Video Capture
Pixel Format: 'JPEG' (compressed)
Name        : JFIF JPEG

JPEG 工作正常。 当我想使用 I420 原始数据时,问题就出现了。 我正在做 640x480。它 return 正确的字节数。即 460800 (640x480x1.5)。但是经过一整天的实验,我仍然不明白它是如何组织的。

看起来像 return 像素块,即 128(192 字节)。 如果我覆盖凸轮,我可以看到前 128(y 分量)字节很小,然后是 64 个较大的字节。因此我假设它是 128 y,然后是 32 u,然后是 32 v.

这是我覆盖网络摄像头时的示例 hexdump -n 200

0000000 2b2b 2c2b 2e2b 2e2d 2b2b 2c2b 2c2b 2d2c 
0000010 2b2b 2b2b 2b2a 2c2b 2b2b 2b2c 2b2a 2b2c 
0000020 2b2a 2c2b 2b2b 2b2b 2a2a 2b2b 2b2b 2b2c 
0000030 2a2a 2c2b 2b2b 2c2b 2b2c 2c2b 2b2b 2b2b 
0000040 2d2d 2c2c 2c2c 2d2c 2c2d 2c2d 2c2d 2c2c 
0000050 2c2c 2b2b 2c2c 2c2c 2c2c 2b2c 2c2c 2c2c 
0000060 2b2b 2b2b 2b2b 2c2b 2b2c 2c2c 2b2c 2c2c 
0000070 2c2b 2c2b 2c2b 2c2b 2b2b 2c2c 2c2d 2c2b 
0000080 7a7a 7b79 7c79 7a7a 7979 797a 797a 787a
0000090 7a7a 797a 7a77 797a 797a 797a 7978 797a 
00000a0 7e7e 7d7e 7f7e 7f7f 7e7e 7e7f 7e7f 7e7f 
00000b0 7d7e 7e7d 7e7e 7e7e 7e7f 7e7e 7e7e 7e7e
00000c0 2d2c 2c2c 2d2c 2f2d  

然而,经过反复试验,我仍然无法找到这些组件如何映射到整个 640x480。 我尝试将其转换为 yuy2,如下所示:

y0 u0 y1 v0 y2 u0 y3 v0, y4 u1 y5 v1 y6 u1 y7 v1

但看起来不是.. 我用谷歌搜索但没有看到很多人使用这种格式。有人有使用过这种 yuv 格式的经验吗?

更新: 我能得到的最接近的图像是 https://drive.google.com/file/d/0Bz9G05et_k8fOS0zLUNkMXpYalU/view?usp=sharing 我使用每块 16x8 像素,因为 S920 每块输出 128 像素(我假设,基于我看到的模式)。经过反复试验,我认为 16x8 是最接近的。

嗯,这很有趣 :-) 事实证明,GSPCA SN9C20X I42 格式只是 yuv420 的一些变体,实际上它是 yuv420,但框架被分解成瓷砖。

我发现这个 link 引导我朝着正确的方向前进。

输出图像是这样的

这是用于将其转换为 IYUV 的 C 代码,我从 github 获得并使用主函数稍作修改:

/*
 * Sonix SN9C20X decoder
 * Vasily Khoruzhick, (C) 2008-2009
 * Algorithm based on Java code written by Jens on microdia google group
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Note this code was originally licensed under the GNU GPL instead of the
 * GNU LGPL, its license has been changed by its author.
 */

//#include "libv4lconvert-priv.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>



#define DO_SANITY_CHECKS 0

static const int UVTranslate[32] = {0, 1, 2, 3,
            8, 9, 10, 11,
            16, 17, 18, 19,
            24, 25, 26, 27,
            4, 5, 6, 7,
            12, 13, 14, 15,
            20, 21, 22, 23,
            28, 29, 30, 31};

static const int Y_coords_624x[128][2] = {
{ 0,  0}, { 1,  0}, { 2,  0}, { 3,  0}, { 4,  0}, { 5,  0}, { 6,  0}, { 7,  0},
{ 0,  1}, { 1,  1}, { 2,  1}, { 3,  1}, { 4,  1}, { 5,  1}, { 6,  1}, { 7,  1},
{ 0,  2}, { 1,  2}, { 2,  2}, { 3,  2}, { 4,  2}, { 5,  2}, { 6,  2}, { 7,  2},
{ 0,  3}, { 1,  3}, { 2,  3}, { 3,  3}, { 4,  3}, { 5,  3}, { 6,  3}, { 7,  3},

{ 0,  4}, { 1,  4}, { 2,  4}, { 3,  4}, { 4,  4}, { 5,  4}, { 6,  4}, { 7,  4},
{ 0,  5}, { 1,  5}, { 2,  5}, { 3,  5}, { 4,  5}, { 5,  5}, { 6,  5}, { 7,  5},
{ 0,  6}, { 1,  6}, { 2,  6}, { 3,  6}, { 4,  6}, { 5,  6}, { 6,  6}, { 7,  6},
{ 0,  7}, { 1,  7}, { 2,  7}, { 3,  7}, { 4,  7}, { 5,  7}, { 6,  7}, { 7,  7},

{ 8,  0}, { 9,  0}, {10,  0}, {11,  0}, {12,  0}, {13,  0}, {14,  0}, {15,  0},
{ 8,  1}, { 9,  1}, {10,  1}, {11,  1}, {12,  1}, {13,  1}, {14,  1}, {15,  1},
{ 8,  2}, { 9,  2}, {10,  2}, {11,  2}, {12,  2}, {13,  2}, {14,  2}, {15,  2},
{ 8,  3}, { 9,  3}, {10,  3}, {11,  3}, {12,  3}, {13,  3}, {14,  3}, {15,  3},

{ 8,  4}, { 9,  4}, {10,  4}, {11,  4}, {12,  4}, {13,  4}, {14,  4}, {15,  4},
{ 8,  5}, { 9,  5}, {10,  5}, {11,  5}, {12,  5}, {13,  5}, {14,  5}, {15,  5},
{ 8,  6}, { 9,  6}, {10,  6}, {11,  6}, {12,  6}, {13,  6}, {14,  6}, {15,  6},
{ 8,  7}, { 9,  7}, {10,  7}, {11,  7}, {12,  7}, {13,  7}, {14,  7}, {15,  7}
};

static void do_write_u(const unsigned char *buf, unsigned char *ptr,
    int i, int j)
{
    *ptr = buf[i + 128 + j];
}

static void do_write_v(const unsigned char *buf, unsigned char *ptr,
    int i, int j)
{
    *ptr = buf[i + 160 + j];
}

void v4lconvert_sn9c20x_to_yuv420(const unsigned char *raw, unsigned char *i420,
  int width, int height, int yvu)
{
    int i = 0, x = 0, y = 0, j, relX, relY, x_div2, y_div2;
    const unsigned char *buf = raw;
    unsigned char *ptr;
    int frame_size = width * height;
    int frame_size_div2 = frame_size >> 1;
    int frame_size_div4 = frame_size >> 2;
    int width_div2 = width >> 1;
    int height_div2 = height >> 1;
    void (*do_write_uv1)(const unsigned char *buf, unsigned char *ptr, int i,
    int j) = NULL;
    void (*do_write_uv2)(const unsigned char *buf, unsigned char *ptr, int i,
    int j) = NULL;

    if (yvu) {
        do_write_uv1 = do_write_v;
        do_write_uv2 = do_write_u;
    }
    else {
        do_write_uv1 = do_write_u;
        do_write_uv2 = do_write_v;
    }

    while (i < (frame_size + frame_size_div2)) {
        for (j = 0; j < 128; j++) {
            relX = x + Y_coords_624x[j][0];
            relY = y + Y_coords_624x[j][1];

#if (DO_SANITY_CHECKS==1)
            if ((relX < width) && (relY < height)) {
#endif
                ptr = i420 + relY * width + relX;
                *ptr = buf[i + j];
#if (DO_SANITY_CHECKS==1)
            }
#endif

        }
        x_div2 = x >> 1;
        y_div2 = y >> 1;
        for (j = 0; j < 32; j++) {
            relX = (x_div2) + (j & 0x07);
            relY = (y_div2) + (j >> 3);

#if (DO_SANITY_CHECKS==1)
            if ((relX < width_div2) && (relY < height_div2)) {
#endif
                ptr = i420 + frame_size +
                    relY * width_div2 + relX;
                do_write_uv1(buf, ptr, i, j);
                ptr += frame_size_div4;
                do_write_uv2(buf, ptr, i, j);
#if (DO_SANITY_CHECKS==1)
            }
#endif
        }

        i += 192;
        x += 16;
        if (x >= width) {
            x = 0;
            y += 8;
        }
    }
}

int main(int argc, char** argv)
{
    int cnt;
    FILE* fd;
    size_t size = 640*480*3/2;

    fd = fopen(argv[1], "rb");
    if (!fd) {
        fprintf(stderr, "Error opening %s\n", argv[1]);
        return EXIT_FAILURE;
    }

    uint8_t* raw = malloc(size*sizeof(uint8_t));
    uint8_t* out = malloc(size*sizeof(uint8_t));

    if (!raw || !out) {
        goto cleanup;
    }

    fread(raw, sizeof(uint8_t), size, fd);
    fclose(fd);

    v4lconvert_sn9c20x_to_yuv420(raw, out, 640, 480, 0);

    fd = fopen("out_640x480.yuv", "wb");
    if (!fd) {
        fprintf(stderr, "Error opening %s\n", argv[1]);
        goto cleanup;
    }
    fwrite(out, sizeof(uint8_t), size, fd);

cleanup:
    free(raw);
    free(out);

    return EXIT_SUCCESS;
}